diff --git a/src/aot/aot-compiler.ts b/src/aot/aot-compiler.ts index 8abadd6c..c30c2037 100644 --- a/src/aot/aot-compiler.ts +++ b/src/aot/aot-compiler.ts @@ -9,8 +9,8 @@ import AngularCompilerOptions from '@angular/tsc-wrapped/src/options'; import { HybridFileSystem } from '../util/hybrid-file-system'; import { getInstance as getHybridFileSystem } from '../util/hybrid-file-system-factory'; -import { getInstance } from './compiler-host-factory'; -import { NgcCompilerHost } from './compiler-host'; +import { getInMemoryCompilerHostInstance } from './compiler-host-factory'; +import { InMemoryCompilerHost } from './compiler-host'; import { getFallbackMainContent, replaceBootstrap } from './utils'; import { Logger } from '../logger/logger'; import { printDiagnostics, clearDiagnostics, DiagnosticsType } from '../logger/logger-diagnostics'; @@ -26,7 +26,7 @@ export class AotCompiler { private tsConfig: ParsedTsConfig; private angularCompilerOptions: AngularCompilerOptions; private program: Program; - private compilerHost: NgcCompilerHost; + private compilerHost: InMemoryCompilerHost; private fileSystem: HybridFileSystem; private lazyLoadedModuleDictionary: any; @@ -39,7 +39,7 @@ export class AotCompiler { }); this.fileSystem = getHybridFileSystem(false); - this.compilerHost = getInstance(this.tsConfig.parsed.options); + this.compilerHost = getInMemoryCompilerHostInstance(this.tsConfig.parsed.options); this.program = createProgram(this.tsConfig.parsed.fileNames, this.tsConfig.parsed.options, this.compilerHost); } @@ -68,16 +68,6 @@ export class AotCompiler { Logger.debug('[AotCompiler] compile: Creating and validating new TypeScript Program ...'); this.program = errorCheckProgram(this.context, this.tsConfig, this.compilerHost, this.program); Logger.debug('[AotCompiler] compile: Creating and validating new TypeScript Program ... DONE'); - }) - .then(() => { - - Logger.debug('[AotCompiler] compile: The following files are included in the program: '); - for ( const fileName of this.tsConfig.parsed.fileNames) { - Logger.debug(`[AotCompiler] compile: ${fileName}`); - const cleanedFileName = normalize(resolve(fileName)); - const content = readFileSync(cleanedFileName).toString(); - this.context.fileCache.set(cleanedFileName, { path: cleanedFileName, content: content}); - } }).then(() => { Logger.debug('[AotCompiler] compile: Starting to process and modify entry point ...'); const mainFile = this.context.fileCache.get(this.options.entryPoint); @@ -114,7 +104,7 @@ export class AotCompiler { } } -function errorCheckProgram(context: BuildContext, tsConfig: ParsedTsConfig, compilerHost: NgcCompilerHost, cachedProgram: Program) { +function errorCheckProgram(context: BuildContext, tsConfig: ParsedTsConfig, compilerHost: InMemoryCompilerHost, cachedProgram: Program) { // Create a new Program, based on the old one. This will trigger a resolution of all // transitive modules, which include files that might just have been generated. const program = createProgram(tsConfig.parsed.fileNames, tsConfig.parsed.options, compilerHost, cachedProgram); diff --git a/src/aot/compiler-host-factory.ts b/src/aot/compiler-host-factory.ts index 5c747eb3..51b25d1d 100644 --- a/src/aot/compiler-host-factory.ts +++ b/src/aot/compiler-host-factory.ts @@ -1,12 +1,12 @@ import { CompilerOptions } from 'typescript'; -import { NgcCompilerHost } from './compiler-host'; +import { InMemoryCompilerHost } from './compiler-host'; import { getInstance as getFileSystemInstance } from '../util/hybrid-file-system-factory'; -let instance: NgcCompilerHost = null; +let instance: InMemoryCompilerHost = null; -export function getInstance(options: CompilerOptions) { +export function getInMemoryCompilerHostInstance(options: CompilerOptions) { if (!instance) { - instance = new NgcCompilerHost(options, getFileSystemInstance(false)); + instance = new InMemoryCompilerHost(options, getFileSystemInstance(false)); } return instance; } diff --git a/src/aot/compiler-host.ts b/src/aot/compiler-host.ts index 8eafc631..aec1440f 100644 --- a/src/aot/compiler-host.ts +++ b/src/aot/compiler-host.ts @@ -7,7 +7,7 @@ export interface OnErrorFn { (message: string): void; } -export class NgcCompilerHost implements CompilerHost { +export class InMemoryCompilerHost implements CompilerHost { private sourceFileMap: Map; private diskCompilerHost: CompilerHost; @@ -68,7 +68,6 @@ export class NgcCompilerHost implements CompilerHost { this.sourceFileMap.set(filePath, typescriptSourceFile); return typescriptSourceFile; } - // dang, it's not in memory, load it from disk and cache it const diskSourceFile = this.diskCompilerHost.getSourceFile(filePath, languageVersion, onError); this.sourceFileMap.set(filePath, diskSourceFile); return diskSourceFile; diff --git a/src/build.spec.ts b/src/build.spec.ts index b1bba08a..c0661c79 100644 --- a/src/build.spec.ts +++ b/src/build.spec.ts @@ -2,10 +2,12 @@ import * as Constants from './util/constants'; import { BuildContext } from './util/interfaces'; import * as helpers from './util/helpers'; import * as build from './build'; +import * as buildUtils from './build/util'; import * as bundle from './bundle'; import * as copy from './copy'; import * as clean from './clean'; +import * as deepLinking from './deep-linking'; import * as lint from './lint'; import * as minify from './minify'; import * as ngc from './ngc'; @@ -26,8 +28,10 @@ describe('build', () => { }); }); + spyOn(buildUtils, buildUtils.scanSrcTsFiles.name).and.returnValue(Promise.resolve()); spyOn(bundle, bundle.bundle.name).and.returnValue(Promise.resolve()); spyOn(copy, copy.copy.name).and.returnValue(Promise.resolve()); + spyOn(deepLinking, deepLinking.deepLinking.name).and.returnValue(Promise.resolve()); spyOn(minify, minify.minifyCss.name).and.returnValue(Promise.resolve()); spyOn(minify, minify.minifyJs.name).and.returnValue(Promise.resolve()); spyOn(lint, lint.lint.name).and.returnValue(Promise.resolve()); @@ -50,19 +54,19 @@ describe('build', () => { const getBooleanPropertyValueSpy = spyOn(helpers, helpers.getBooleanPropertyValue.name).and.returnValue(true); return build.build(context).then(() => { + expect(buildUtils.scanSrcTsFiles).toHaveBeenCalled(); expect(helpers.readFileAsync).toHaveBeenCalled(); expect(copy.copy).toHaveBeenCalled(); + expect(deepLinking.deepLinking).toHaveBeenCalled(); expect(ngc.ngc).toHaveBeenCalled(); expect(bundle.bundle).toHaveBeenCalled(); expect(minify.minifyJs).toHaveBeenCalled(); expect(sass.sass).toHaveBeenCalled(); expect(minify.minifyCss).toHaveBeenCalled(); expect(lint.lint).toHaveBeenCalled(); - expect(getBooleanPropertyValueSpy.calls.first().args[0]).toEqual(Constants.ENV_ENABLE_LINT); + expect(getBooleanPropertyValueSpy.calls.all()[1].args[0]).toEqual(Constants.ENV_ENABLE_LINT); expect(transpile.transpile).not.toHaveBeenCalled(); - }).catch(err => { - expect(true).toEqual(false); }); }); @@ -78,20 +82,20 @@ describe('build', () => { const getBooleanPropertyValueSpy = spyOn(helpers, helpers.getBooleanPropertyValue.name).and.returnValue(true); return build.build(context).then(() => { + expect(buildUtils.scanSrcTsFiles).toHaveBeenCalled(); expect(helpers.readFileAsync).toHaveBeenCalled(); expect(copy.copy).toHaveBeenCalled(); + expect(deepLinking.deepLinking).toHaveBeenCalled(); expect(transpile.transpile).toHaveBeenCalled(); expect(bundle.bundle).toHaveBeenCalled(); expect(sass.sass).toHaveBeenCalled(); expect(lint.lint).toHaveBeenCalled(); - expect(getBooleanPropertyValueSpy.calls.first().args[0]).toEqual(Constants.ENV_ENABLE_LINT); + expect(getBooleanPropertyValueSpy.calls.all()[1].args[0]).toEqual(Constants.ENV_ENABLE_LINT); expect(postprocess.postprocess).toHaveBeenCalled(); expect(preprocess.preprocess).toHaveBeenCalled(); expect(ngc.ngc).not.toHaveBeenCalled(); expect(minify.minifyJs).not.toHaveBeenCalled(); expect(minify.minifyCss).not.toHaveBeenCalled(); - }).catch(err => { - expect(true).toEqual(false); }); }); @@ -107,20 +111,19 @@ describe('build', () => { const getBooleanPropertyValueSpy = spyOn(helpers, helpers.getBooleanPropertyValue.name).and.returnValue(false); return build.build(context).then(() => { + expect(buildUtils.scanSrcTsFiles).toHaveBeenCalled(); expect(helpers.readFileAsync).toHaveBeenCalled(); expect(copy.copy).toHaveBeenCalled(); expect(transpile.transpile).toHaveBeenCalled(); expect(bundle.bundle).toHaveBeenCalled(); expect(sass.sass).toHaveBeenCalled(); expect(lint.lint).not.toHaveBeenCalled(); - expect(getBooleanPropertyValueSpy.calls.first().args[0]).toEqual(Constants.ENV_ENABLE_LINT); + expect(getBooleanPropertyValueSpy.calls.all()[1].args[0]).toEqual(Constants.ENV_ENABLE_LINT); expect(postprocess.postprocess).toHaveBeenCalled(); expect(preprocess.preprocess).toHaveBeenCalled(); expect(ngc.ngc).not.toHaveBeenCalled(); expect(minify.minifyJs).not.toHaveBeenCalled(); expect(minify.minifyCss).not.toHaveBeenCalled(); - }).catch(err => { - expect(true).toEqual(false); }); }); }); @@ -190,10 +193,11 @@ describe('test project requirements before building', () => { }); }); - it('should succeed if IONIC_TS_CONFIG file contains compilerOptions.sourceMap === true', () => { + it('should succeed if IONIC_TS_CONFIG file contains compilerOptions.sourceMap is true', () => { process.env[Constants.ENV_APP_ENTRY_POINT] = 'src/app/main.ts'; process.env[Constants.ENV_TS_CONFIG] = 'tsConfig.js'; + spyOn(buildUtils, buildUtils.scanSrcTsFiles.name).and.returnValue(Promise.resolve()); spyOn(bundle, bundle.bundle.name).and.returnValue(Promise.resolve()); spyOn(clean, clean.clean.name); spyOn(copy, copy.copy.name).and.returnValue(Promise.resolve()); diff --git a/src/build.ts b/src/build.ts index f0756003..9a7d7266 100644 --- a/src/build.ts +++ b/src/build.ts @@ -1,3 +1,5 @@ +import { join } from 'path'; +import { scanSrcTsFiles } from './build/util'; import * as Constants from './util/constants'; import { BuildContext, BuildState, BuildUpdateMessage, ChangedFile } from './util/interfaces'; import { BuildError } from './util/errors'; @@ -6,6 +8,7 @@ import { getBooleanPropertyValue, readFileAsync, setContext } from './util/helpe import { bundle, bundleUpdate } from './bundle'; import { clean } from './clean'; import { copy } from './copy'; +import { deepLinking, deepLinkingUpdate } from './deep-linking'; import { lint, lintUpdate } from './lint'; import { Logger } from './logger/logger'; import { minifyCss, minifyJs } from './minify'; @@ -98,9 +101,17 @@ function buildProject(context: BuildContext) { buildId++; const copyPromise = copy(context); - const compilePromise = (context.runAot) ? ngc(context) : transpile(context); - return compilePromise + return scanSrcTsFiles(context) + .then(() => { + if (getBooleanPropertyValue(Constants.ENV_PARSE_DEEPLINKS)) { + return deepLinking(context); + } + }) + .then(() => { + const compilePromise = (context.runAot) ? ngc(context) : transpile(context); + return compilePromise; + }) .then(() => { return preprocess(context); }) @@ -224,6 +235,12 @@ function buildUpdateTasks(changedFiles: ChangedFile[], context: BuildContext) { }; return loadFiles(changedFiles, context) + .then(() => { + // DEEP LINKING + if (getBooleanPropertyValue(Constants.ENV_PARSE_DEEPLINKS)) { + return deepLinkingUpdate(changedFiles, context); + } + }) .then(() => { // TEMPLATE if (context.templateState === BuildState.RequiresUpdate) { diff --git a/src/build/util.ts b/src/build/util.ts new file mode 100644 index 00000000..146a3490 --- /dev/null +++ b/src/build/util.ts @@ -0,0 +1,18 @@ +import { join } from 'path'; +import { GlobResult, globAll } from '../util/glob-util'; +import { readFileAsync } from '../util/helpers'; +import { BuildContext } from '../util/interfaces'; + +export function scanSrcTsFiles(context: BuildContext) { + const tsFileGlob = join(context.srcDir, '**', '*.ts'); + return globAll([tsFileGlob]).then((results: GlobResult[]) => { + const promises = results.map(result => { + const promise = readFileAsync(result.absolutePath); + promise.then((fileContent: string) => { + context.fileCache.set(result.absolutePath, { path: result.absolutePath, content: fileContent}); + }); + return promise; + }); + return Promise.all(promises); + }); +} diff --git a/src/deep-linking.spec.ts b/src/deep-linking.spec.ts index 24fc9197..55b1a0fb 100644 --- a/src/deep-linking.spec.ts +++ b/src/deep-linking.spec.ts @@ -3,17 +3,13 @@ import { join } from 'path'; import * as deepLinking from './deep-linking'; import * as deeplinkUtils from './deep-linking/util'; import * as Constants from './util/constants'; -import { BuildState, ChangedFile } from './util/interfaces'; +import { BuildState, ChangedFile, DeepLinkConfigEntry } from './util/interfaces'; import { FileCache } from './util/file-cache'; import * as helpers from './util/helpers'; describe('Deep Linking task', () => { describe('deepLinkingWorkerImpl', () => { - beforeEach(() => { - deepLinking.reset(); - }); - it('should not update app ngmodule when it has an existing deeplink config', () => { const appNgModulePath = join('some', 'fake', 'path', 'myApp', 'src', 'app', 'app.module.ts'); const context = { @@ -22,338 +18,16 @@ describe('Deep Linking task', () => { const knownFileContent = 'someFileContent'; const knownDeepLinkString = 'someDeepLinkString'; context.fileCache.set(appNgModulePath, { path: appNgModulePath, content: knownFileContent}); - spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(appNgModulePath); - spyOn(deeplinkUtils, deeplinkUtils.getDeepLinkData.name).and.returnValue([1]); - spyOn(deeplinkUtils, deeplinkUtils.hasExistingDeepLinkConfig.name).and.returnValue(true); - spyOn(deeplinkUtils, deeplinkUtils.convertDeepLinkConfigEntriesToString.name).and.returnValue(knownDeepLinkString); - spyOn(deeplinkUtils, deeplinkUtils.updateAppNgModuleAndFactoryWithDeepLinkConfig.name); - - const promise = deepLinking.deepLinkingWorkerImpl(context, null); - return promise.then(() => { - expect(deepLinking.cachedUnmodifiedAppNgModuleFileContent).toEqual(knownFileContent); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).not.toHaveBeenCalled(); - expect(deeplinkUtils.updateAppNgModuleAndFactoryWithDeepLinkConfig).not.toHaveBeenCalled(); - }); - }); - - it('should not update app ngmodule when no deeplinks were found', () => { - const appNgModulePath = join('some', 'fake', 'path', 'myApp', 'src', 'app', 'app.module.ts'); - const context = { - fileCache: new FileCache() - }; - const knownFileContent = 'someFileContent'; - const knownDeepLinkString = 'someDeepLinkString'; - context.fileCache.set(appNgModulePath, { path: appNgModulePath, content: knownFileContent}); spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(appNgModulePath); - spyOn(deeplinkUtils, deeplinkUtils.getDeepLinkData.name).and.returnValue([]); - spyOn(deeplinkUtils, deeplinkUtils.hasExistingDeepLinkConfig.name).and.returnValue(false); - spyOn(deeplinkUtils, deeplinkUtils.convertDeepLinkConfigEntriesToString.name).and.returnValue(knownDeepLinkString); - spyOn(deeplinkUtils, deeplinkUtils.updateAppNgModuleAndFactoryWithDeepLinkConfig.name); + spyOn(helpers, helpers.readAndCacheFile.name).and.returnValue(Promise.resolve(knownFileContent)); + spyOn(deeplinkUtils, deeplinkUtils.hasExistingDeepLinkConfig.name).and.returnValue(true); const promise = deepLinking.deepLinkingWorkerImpl(context, null); - return promise.then(() => { - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).not.toHaveBeenCalled(); - expect(deeplinkUtils.updateAppNgModuleAndFactoryWithDeepLinkConfig).not.toHaveBeenCalled(); - }); - }); - - it('should update deeplink config', () => { - const appNgModulePath = join('some', 'fake', 'path', 'myApp', 'src', 'app', 'app.module.ts'); - const context = { - fileCache: new FileCache(), - runAot: true - }; - const knownFileContent = 'someFileContent'; - const knownDeepLinkString = 'someDeepLinkString'; - const knownMockDeepLinkArray = [1]; - const changedFiles: ChangedFile[] = []; - context.fileCache.set(appNgModulePath, { path: appNgModulePath, content: knownFileContent}); - spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(appNgModulePath); - spyOn(deeplinkUtils, deeplinkUtils.getDeepLinkData.name).and.returnValue(knownMockDeepLinkArray); - spyOn(deeplinkUtils, deeplinkUtils.hasExistingDeepLinkConfig.name).and.returnValue(false); - spyOn(deeplinkUtils, deeplinkUtils.convertDeepLinkConfigEntriesToString.name).and.returnValue(knownDeepLinkString); - spyOn(deeplinkUtils, deeplinkUtils.updateAppNgModuleAndFactoryWithDeepLinkConfig.name); - - const promise = deepLinking.deepLinkingWorkerImpl(context, changedFiles); - - return promise.then(() => { - expect(deepLinking.cachedDeepLinkString).toEqual(knownDeepLinkString); - expect(helpers.getStringPropertyValue).toBeCalledWith(Constants.ENV_APP_NG_MODULE_PATH); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledWith(appNgModulePath, context.fileCache, context.runAot); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledWith(appNgModulePath, knownFileContent); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledWith(knownMockDeepLinkArray); - expect(deeplinkUtils.updateAppNgModuleAndFactoryWithDeepLinkConfig).toHaveBeenCalledWith(context, knownDeepLinkString, changedFiles, context.runAot); - }); - }); - - it('should update deeplink config on subsequent updates when the deeplink string is different', () => { - const appNgModulePath = join('some', 'fake', 'path', 'myApp', 'src', 'app', 'app.module.ts'); - const context = { - fileCache: new FileCache(), - runAot: true - }; - const knownFileContent = 'someFileContent'; - const knownDeepLinkString = 'someDeepLinkString'; - const knownDeepLinkString2 = 'someDeepLinkString2'; - const knownMockDeepLinkArray = [1]; - const changedFiles: ChangedFile[] = null; - context.fileCache.set(appNgModulePath, { path: appNgModulePath, content: knownFileContent}); - - spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(appNgModulePath); - spyOn(deeplinkUtils, deeplinkUtils.getDeepLinkData.name).and.returnValue(knownMockDeepLinkArray); - spyOn(deeplinkUtils, deeplinkUtils.hasExistingDeepLinkConfig.name).and.returnValue(false); - - let hasConvertDeepLinkConfigToStringBeenCalled = false; - spyOn(deeplinkUtils, deeplinkUtils.convertDeepLinkConfigEntriesToString.name).and.callFake(() => { - if (!hasConvertDeepLinkConfigToStringBeenCalled) { - hasConvertDeepLinkConfigToStringBeenCalled = true; - return knownDeepLinkString; - } - return knownDeepLinkString2; - }); - - const spy = spyOn(deeplinkUtils, deeplinkUtils.updateAppNgModuleAndFactoryWithDeepLinkConfig.name); - - const promise = deepLinking.deepLinkingWorkerImpl(context, changedFiles); - - return promise.then(() => { - expect(deepLinking.cachedDeepLinkString).toEqual(knownDeepLinkString); - expect(helpers.getStringPropertyValue).toBeCalledWith(Constants.ENV_APP_NG_MODULE_PATH); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledWith(appNgModulePath, context.fileCache, context.runAot); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledWith(appNgModulePath, knownFileContent); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledWith(knownMockDeepLinkArray); - expect(spy.calls.first().args[0]).toEqual(context); - expect(spy.calls.first().args[1]).toEqual(knownDeepLinkString); - expect(spy.calls.first().args[2]).toEqual(changedFiles); - expect(spy.calls.first().args[3]).toEqual(context.runAot); - - return deepLinking.deepLinkingWorkerImpl(context, changedFiles); - }).then((result) => { - expect(deepLinking.cachedDeepLinkString).toEqual(knownDeepLinkString2); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledTimes(2); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledWith(appNgModulePath, context.fileCache, context.runAot); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledTimes(2); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledWith(appNgModulePath, knownFileContent); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledWith(knownMockDeepLinkArray); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledTimes(2); - expect(spy).toHaveBeenCalledTimes(2); - expect(spy.calls.mostRecent().args[0]).toEqual(context); - expect(spy.calls.mostRecent().args[1]).toEqual(knownDeepLinkString2); - expect(spy.calls.mostRecent().args[2]).toEqual(changedFiles); - expect(spy.calls.mostRecent().args[3]).toEqual(context.runAot); - }); - }); - - it('should not update deeplink config on subsequent updates when the deeplink string is the same', () => { - const appNgModulePath = join('some', 'fake', 'path', 'myApp', 'src', 'app', 'app.module.ts'); - const context = { - fileCache: new FileCache(), - runAot: true - }; - const knownFileContent = 'someFileContent'; - const knownDeepLinkString = 'someDeepLinkString'; - const knownMockDeepLinkArray = [1]; - const changedFiles: ChangedFile[] = null; - context.fileCache.set(appNgModulePath, { path: appNgModulePath, content: knownFileContent}); - - spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(appNgModulePath); - spyOn(deeplinkUtils, deeplinkUtils.getDeepLinkData.name).and.returnValue(knownMockDeepLinkArray); - spyOn(deeplinkUtils, deeplinkUtils.hasExistingDeepLinkConfig.name).and.returnValue(false); - - spyOn(deeplinkUtils, deeplinkUtils.convertDeepLinkConfigEntriesToString.name).and.returnValue(knownDeepLinkString); - - const spy = spyOn(deeplinkUtils, deeplinkUtils.updateAppNgModuleAndFactoryWithDeepLinkConfig.name); - - const promise = deepLinking.deepLinkingWorkerImpl(context, changedFiles); - - return promise.then(() => { - expect(deepLinking.cachedDeepLinkString).toEqual(knownDeepLinkString); - expect(helpers.getStringPropertyValue).toBeCalledWith(Constants.ENV_APP_NG_MODULE_PATH); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledWith(appNgModulePath, context.fileCache, context.runAot); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledWith(appNgModulePath, knownFileContent); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledWith(knownMockDeepLinkArray); - expect(spy.calls.first().args[0]).toEqual(context); - expect(spy.calls.first().args[1]).toEqual(knownDeepLinkString); - expect(spy.calls.first().args[2]).toEqual(changedFiles); - expect(spy.calls.first().args[3]).toEqual(context.runAot); - - return deepLinking.deepLinkingWorkerImpl(context, changedFiles); - }).then((result) => { - expect(result).toEqual(knownMockDeepLinkArray); - expect(deepLinking.cachedDeepLinkString).toEqual(knownDeepLinkString); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledTimes(2); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledWith(appNgModulePath, context.fileCache, context.runAot); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledTimes(2); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledWith(appNgModulePath, knownFileContent); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledWith(knownMockDeepLinkArray); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledTimes(2); - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - it('should update the deeplink config and cached deeplink string no matter what when the app.module.ts is changed', () => { - const appNgModulePath = join('some', 'fake', 'path', 'myApp', 'src', 'app', 'app.module.ts'); - const context = { - fileCache: new FileCache(), - runAot: true - }; - const knownFileContent = ` -import { BrowserModule } from '@angular/platform-browser'; -import { HttpModule } from '@angular/http'; -import { NgModule, ErrorHandler } from '@angular/core'; - -import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular'; - -import { InAppBrowser } from '@ionic-native/in-app-browser'; -import { SplashScreen } from '@ionic-native/splash-screen'; - -import { IonicStorageModule } from '@ionic/storage'; - -import { ConferenceApp } from './app.component'; - -import { ConferenceData } from '../providers/conference-data'; -import { UserData } from '../providers/user-data'; - -@NgModule({ - declarations: [ - ConferenceApp - ], - imports: [ - BrowserModule, - HttpModule, - IonicModule.forRoot(ConferenceApp, { - preloadModules: true - }), - IonicStorageModule.forRoot() - ], - bootstrap: [IonicApp], - entryComponents: [ - ConferenceApp - ], - providers: [ - { provide: ErrorHandler, useClass: IonicErrorHandler }, - ConferenceData, - UserData, - InAppBrowser, - SplashScreen - ] -}) -export class AppModule { } -`; - - const knownFileContent2 = ` -import { BrowserModule } from '@angular/platform-browser'; -import { HttpModule } from '@angular/http'; -import { NgModule, ErrorHandler } from '@angular/core'; - -import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular'; - -import { InAppBrowser } from '@ionic-native/in-app-browser'; -import { SplashScreen } from '@ionic-native/splash-screen'; - -import { IonicStorageModule } from '@ionic/storage'; - -import { ConferenceApp } from './app.component'; - -import { ConferenceData } from '../providers/conference-data'; -import { UserData } from '../providers/user-data'; - -@NgModule({ - declarations: [ - ConferenceApp, - SomeNewComponent - ], - imports: [ - BrowserModule, - HttpModule, - IonicModule.forRoot(ConferenceApp, { - preloadModules: true - }), - IonicStorageModule.forRoot() - ], - bootstrap: [IonicApp], - entryComponents: [ - ConferenceApp - ], - providers: [ - { provide: ErrorHandler, useClass: IonicErrorHandler }, - ConferenceData, - UserData, - InAppBrowser, - SplashScreen - ] -}) -export class AppModule { } -`; - const knownDeepLinkString = 'someDeepLinkString'; - const knownMockDeepLinkArray = [1]; - const changedFiles: ChangedFile[] = []; - context.fileCache.set(appNgModulePath, { path: appNgModulePath, content: knownFileContent}); - - spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(appNgModulePath); - spyOn(deeplinkUtils, deeplinkUtils.getDeepLinkData.name).and.returnValue(knownMockDeepLinkArray); - spyOn(deeplinkUtils, deeplinkUtils.hasExistingDeepLinkConfig.name).and.returnValue(false); - - spyOn(deeplinkUtils, deeplinkUtils.convertDeepLinkConfigEntriesToString.name).and.returnValue(knownDeepLinkString); - - const spy = spyOn(deeplinkUtils, deeplinkUtils.updateAppNgModuleAndFactoryWithDeepLinkConfig.name); - - const promise = deepLinking.deepLinkingWorkerImpl(context, changedFiles); - - return promise.then(() => { - expect(deepLinking.cachedUnmodifiedAppNgModuleFileContent).toEqual(knownFileContent); - expect(deepLinking.cachedDeepLinkString).toEqual(knownDeepLinkString); - expect(helpers.getStringPropertyValue).toBeCalledWith(Constants.ENV_APP_NG_MODULE_PATH); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledWith(appNgModulePath, context.fileCache, context.runAot); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledWith(appNgModulePath, knownFileContent); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledWith(knownMockDeepLinkArray); - expect(spy.calls.first().args[0]).toEqual(context); - expect(spy.calls.first().args[1]).toEqual(knownDeepLinkString); - expect(spy.calls.first().args[2]).toEqual(changedFiles); - expect(spy.calls.first().args[3]).toEqual(context.runAot); - - // add a changed file to the fray - changedFiles.push({ - event: 'change', - ext: '.ts', - filePath: appNgModulePath - }); - context.fileCache.set(appNgModulePath, { path: appNgModulePath, content: knownFileContent2}); - return deepLinking.deepLinkingWorkerImpl(context, changedFiles); - }).then((result) => { - expect(result).toEqual(knownMockDeepLinkArray); - expect(deepLinking.cachedDeepLinkString).toEqual(knownDeepLinkString); - expect(deepLinking.cachedUnmodifiedAppNgModuleFileContent).toEqual(knownFileContent2); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledTimes(2); - expect(deeplinkUtils.getDeepLinkData).toHaveBeenCalledWith(appNgModulePath, context.fileCache, context.runAot); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledTimes(2); - expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalledWith(appNgModulePath, knownFileContent); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledWith(knownMockDeepLinkArray); - expect(deeplinkUtils.convertDeepLinkConfigEntriesToString).toHaveBeenCalledTimes(2); - expect(spy).toHaveBeenCalledTimes(2); - }); - }); - - describe('deepLinkingUpdate', () => { - it('should clear an existing cached deep link string so it can generate a new one', () => { - const context = { - deepLinkState: BuildState.RequiresBuild, - fileCache: new FileCache() - }; - - const somePath = 'somePath'; - context.fileCache.set(somePath, { path: somePath, content: 'someContent'}); - const originalCachedDeepLinkString = 'someKnownString'; - deepLinking.setCachedDeepLinkString(originalCachedDeepLinkString); - spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(somePath); - spyOn(deeplinkUtils, deeplinkUtils.hasExistingDeepLinkConfig.name).and.returnValue(false); - const setDeeplinkConfigSpy = spyOn(helpers, helpers.setParsedDeepLinkConfig.name); - return deepLinking.deepLinkingUpdate([], context).then(() => { - expect(setDeeplinkConfigSpy.calls.mostRecent().args[0].length).toEqual(0); - expect(deepLinking.cachedDeepLinkString).toEqual(null); - }); + return promise.then((results: Map) => { + expect(deeplinkUtils.hasExistingDeepLinkConfig).toHaveBeenCalled(); + expect(results.size).toEqual(0); }); }); }); diff --git a/src/deep-linking.ts b/src/deep-linking.ts index be145c5d..b5c85414 100644 --- a/src/deep-linking.ts +++ b/src/deep-linking.ts @@ -1,34 +1,36 @@ +import { extname } from 'path'; +import { scanSrcTsFiles } from './build/util'; import { Logger } from './logger/logger'; import * as Constants from './util/constants'; import { BuildError } from './util/errors'; -import { getStringPropertyValue, setParsedDeepLinkConfig } from './util/helpers'; +import { getParsedDeepLinkConfig, getStringPropertyValue, readAndCacheFile, setParsedDeepLinkConfig } from './util/helpers'; import { BuildContext, BuildState, ChangedFile, DeepLinkConfigEntry } from './util/interfaces'; -import { convertDeepLinkConfigEntriesToString, getDeepLinkData, hasExistingDeepLinkConfig, updateAppNgModuleAndFactoryWithDeepLinkConfig } from './deep-linking/util'; +import { + convertDeepLinkConfigEntriesToString, + getDeepLinkData, + hasExistingDeepLinkConfig +} from './deep-linking/util'; -/* - * We want to cache a local, in-memory copy of the App's main NgModule file content. - * Each time we do a build, a new DeepLinkConfig is generated and inserted into the - * app's main NgModule. By keeping a copy of the original and using it to determine - * if the developer had an existing config, we will get an accurate answer where - * as the cached version of the App's main NgModule content will basically always - * have a generated deep likn config in it. -*/ -export let cachedUnmodifiedAppNgModuleFileContent: string = null; -export let cachedDeepLinkString: string = null; +export let existingDeepLinkConfigString: string = null; + +export function setExistingDeepLinkConfig(newString: string) { + existingDeepLinkConfigString = newString; +} export function deepLinking(context: BuildContext) { const logger = new Logger(`deeplinks`); - return deepLinkingWorker(context).then((deepLinkConfigEntries: DeepLinkConfigEntry[]) => { - setParsedDeepLinkConfig(deepLinkConfigEntries); - logger.finish(); - }) - .catch((err: Error) => { - const error = new BuildError(err.message); - error.isFatal = true; - throw logger.fail(error); - }); + + return deepLinkingWorker(context).then((map: Map) => { + setParsedDeepLinkConfig(map); + logger.finish(); + }) + .catch((err: Error) => { + const error = new BuildError(err.message); + error.isFatal = true; + throw logger.fail(error); + }); } @@ -36,48 +38,36 @@ function deepLinkingWorker(context: BuildContext) { return deepLinkingWorkerImpl(context, []); } -export function deepLinkingWorkerImpl(context: BuildContext, changedFiles: ChangedFile[]) { - return Promise.resolve().then(() => { - const appNgModulePath = getStringPropertyValue(Constants.ENV_APP_NG_MODULE_PATH); - const appNgModuleFile = context.fileCache.get(appNgModulePath); - if (!appNgModuleFile) { - throw new Error(`The main app NgModule was not found at the following path: ${appNgModulePath}`); - } - if (!cachedUnmodifiedAppNgModuleFileContent || hasAppModuleChanged(changedFiles, appNgModulePath)) { - cachedUnmodifiedAppNgModuleFileContent = appNgModuleFile.content; - } +export async function deepLinkingWorkerImpl(context: BuildContext, changedFiles: ChangedFile[]): Promise> { - // is there is an existing (legacy) deep link config, just move on and don't look for decorators - const hasExisting = hasExistingDeepLinkConfig(appNgModulePath, cachedUnmodifiedAppNgModuleFileContent); - if (hasExisting) { - return []; - } + // get the app.module.ts content from ideally the cache, but fall back to disk if needed + const appNgModulePath = getStringPropertyValue(Constants.ENV_APP_NG_MODULE_PATH); + const appNgModuleFileContent = await getAppMainNgModuleFile(appNgModulePath); - const deepLinkConfigEntries = getDeepLinkData(appNgModulePath, context.fileCache, context.runAot) || []; - if (deepLinkConfigEntries.length) { - const newDeepLinkString = convertDeepLinkConfigEntriesToString(deepLinkConfigEntries); - // 1. this is the first time running this, so update the build either way - // 2. we have an existing deep link string, and we have a new one, and they're different - so go ahead and update the config - // 3. the app's main ngmodule has changed, so we need to rewrite the config - if (!cachedDeepLinkString || newDeepLinkString !== cachedDeepLinkString || hasAppModuleChanged(changedFiles, appNgModulePath)) { - cachedDeepLinkString = newDeepLinkString; - updateAppNgModuleAndFactoryWithDeepLinkConfig(context, newDeepLinkString, changedFiles, context.runAot); - } - } - return deepLinkConfigEntries; - }); -} - -export function hasAppModuleChanged(changedFiles: ChangedFile[], appNgModulePath: string) { - if (!changedFiles) { - changedFiles = []; + // is there is an existing (legacy) deep link config, just move on and don't look for decorators + const hasExisting = hasExistingDeepLinkConfig(appNgModulePath, appNgModuleFileContent); + if (hasExisting) { + return new Map(); } - for (const changedFile of changedFiles) { - if (changedFile.filePath === appNgModulePath) { - return true; + + // okay cool, we need to get the data from each file + const results = getDeepLinkData(appNgModulePath, context.fileCache, context.runAot) || new Map(); + + const newDeepLinkString = convertDeepLinkConfigEntriesToString(results); + if (!existingDeepLinkConfigString || newDeepLinkString !== existingDeepLinkConfigString || hasAppModuleChanged(changedFiles, appNgModulePath)) { + + existingDeepLinkConfigString = newDeepLinkString; + + if (changedFiles) { + changedFiles.push({ + event: 'change', + filePath: appNgModulePath, + ext: extname(appNgModulePath).toLowerCase() + }); } } - return false; + + return results; } export function deepLinkingUpdate(changedFiles: ChangedFile[], context: BuildContext) { @@ -94,8 +84,9 @@ export function deepLinkingUpdateImpl(changedFiles: ChangedFile[], context: Buil return Promise.resolve(); } const logger = new Logger('deeplinks update'); - return deepLinkingWorkerImpl(context, changedFiles).then((deepLinkConfigEntries: DeepLinkConfigEntry[]) => { - setParsedDeepLinkConfig(deepLinkConfigEntries); + return deepLinkingWorkerImpl(context, changedFiles).then((map: Map) => { + // okay, now that the existing config is updated, go ahead and reset it + setParsedDeepLinkConfig(map); logger.finish(); }).catch((err: Error) => { Logger.warn(err.message); @@ -106,26 +97,33 @@ export function deepLinkingUpdateImpl(changedFiles: ChangedFile[], context: Buil export function deepLinkingWorkerFullUpdate(context: BuildContext) { const logger = new Logger(`deeplinks update`); - // when a full build is required (when a template fails to update, etc), remove the cached deep link string to force a new one - // to be inserted - cachedDeepLinkString = null; - return deepLinkingWorker(context).then((deepLinkConfigEntries: DeepLinkConfigEntry[]) => { - setParsedDeepLinkConfig(deepLinkConfigEntries); - logger.finish(); - }) - .catch((err: Error) => { - Logger.warn(err.message); - const error = new BuildError(err.message); - throw logger.fail(error); - }); + return deepLinkingWorker(context).then((map: Map) => { + setParsedDeepLinkConfig(map); + logger.finish(); + }) + .catch((err: Error) => { + Logger.warn(err.message); + const error = new BuildError(err.message); + throw logger.fail(error); + }); } -// these functions are purely for testing -export function reset() { - cachedUnmodifiedAppNgModuleFileContent = null; - cachedDeepLinkString = null; +export async function getAppMainNgModuleFile(appNgModulePath: string) { + try { + return await readAndCacheFile(appNgModulePath); + } catch (ex) { + throw new Error(`The main app NgModule was not found at the following path: ${appNgModulePath}`); + } } -export function setCachedDeepLinkString(input: string) { - cachedDeepLinkString = input; +export function hasAppModuleChanged(changedFiles: ChangedFile[], appNgModulePath: string) { + if (!changedFiles) { + changedFiles = []; + } + for (const changedFile of changedFiles) { + if (changedFile.filePath === appNgModulePath) { + return true; + } + } + return false; } diff --git a/src/deep-linking/util.spec.ts b/src/deep-linking/util.spec.ts index b4bbbdf4..87d0626d 100644 --- a/src/deep-linking/util.spec.ts +++ b/src/deep-linking/util.spec.ts @@ -1,5 +1,7 @@ import { join } from 'path'; +import * as ts from 'typescript'; + import * as util from './util'; import * as transpile from '../transpile'; @@ -825,9 +827,8 @@ export class PageThreeModule { spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue('.module.ts'); - const results = util.getDeepLinkData(appNgModulePath, fileCache, false); - expect(Array.isArray(results)).toBeTruthy(); - expect(results.length).toEqual(0); + const map = util.getDeepLinkData(appNgModulePath, fileCache, false); + expect(map.size).toEqual(0); }); it('should return an a list of deeplink configs from all pages that have them, and not include pages that dont', () => { @@ -1017,8 +1018,8 @@ export class PageThreeModule { } }); - const results = util.getDeepLinkData(appNgModulePath, fileCache, false); - expect(results.length).toEqual(2); + const map = util.getDeepLinkData(appNgModulePath, fileCache, false); + expect(map.size).toEqual(2); }); it('should return an a list of deeplink configs from all pages that have them', () => { @@ -1210,34 +1211,37 @@ export class PageThreeModule { } }); - const results = util.getDeepLinkData(appNgModulePath, fileCache, false); - expect(results.length).toEqual(3); - - expect(results[0].name).toEqual('SomeOtherName'); - expect(results[0].segment).toEqual('page-one'); - expect(results[0].priority).toEqual('low'); - expect(results[0].defaultHistory.length).toEqual(0); - expect(results[0].absolutePath).toEqual(join(srcDir, 'pages', 'page-one', 'page-one.module.ts')); - expect(results[0].userlandModulePath).toEqual('../pages/page-one/page-one.module'); - expect(results[0].className).toEqual('PageOneModule'); - - expect(results[1].name).toEqual('PageTwo'); - expect(results[1].segment).toEqual('page-two'); - expect(results[1].priority).toEqual('low'); - expect(results[1].defaultHistory.length).toEqual(0); - expect(results[1].absolutePath).toEqual(join(srcDir, 'pages', 'page-two', 'page-two.module.ts')); - expect(results[1].userlandModulePath).toEqual('../pages/page-two/page-two.module'); - expect(results[1].className).toEqual('PageTwoModule'); - - expect(results[2].name).toEqual('PageThree'); - expect(results[2].segment).toEqual('someSegmentBro'); - expect(results[2].priority).toEqual('high'); - expect(results[2].defaultHistory.length).toEqual(2); - expect(results[2].defaultHistory[0]).toEqual('page-one'); - expect(results[2].defaultHistory[1]).toEqual('page-two'); - expect(results[2].absolutePath).toEqual(join(srcDir, 'pages', 'settings-page', 'fake-dir', 'settings-page.module.ts')); - expect(results[2].userlandModulePath).toEqual('../pages/settings-page/fake-dir/settings-page.module'); - expect(results[2].className).toEqual('PageThreeModule'); + const map = util.getDeepLinkData(appNgModulePath, fileCache, false); + expect(map.size).toEqual(3); + + const entryOne = map.get('SomeOtherName'); + expect(entryOne.name).toEqual('SomeOtherName'); + expect(entryOne.segment).toEqual('page-one'); + expect(entryOne.priority).toEqual('low'); + expect(entryOne.defaultHistory.length).toEqual(0); + expect(entryOne.absolutePath).toEqual(join(srcDir, 'pages', 'page-one', 'page-one.module.ts')); + expect(entryOne.userlandModulePath).toEqual('../pages/page-one/page-one.module'); + expect(entryOne.className).toEqual('PageOneModule'); + + const entryTwo = map.get('PageTwo'); + expect(entryTwo.name).toEqual('PageTwo'); + expect(entryTwo.segment).toEqual('page-two'); + expect(entryTwo.priority).toEqual('low'); + expect(entryTwo.defaultHistory.length).toEqual(0); + expect(entryTwo.absolutePath).toEqual(join(srcDir, 'pages', 'page-two', 'page-two.module.ts')); + expect(entryTwo.userlandModulePath).toEqual('../pages/page-two/page-two.module'); + expect(entryTwo.className).toEqual('PageTwoModule'); + + const entryThree = map.get('PageThree'); + expect(entryThree.name).toEqual('PageThree'); + expect(entryThree.segment).toEqual('someSegmentBro'); + expect(entryThree.priority).toEqual('high'); + expect(entryThree.defaultHistory.length).toEqual(2); + expect(entryThree.defaultHistory[0]).toEqual('page-one'); + expect(entryThree.defaultHistory[1]).toEqual('page-two'); + expect(entryThree.absolutePath).toEqual(join(srcDir, 'pages', 'settings-page', 'fake-dir', 'settings-page.module.ts')); + expect(entryThree.userlandModulePath).toEqual('../pages/settings-page/fake-dir/settings-page.module'); + expect(entryThree.className).toEqual('PageThreeModule'); }); it('should throw when it cant find an NgModule as a peer to the page with a deep link config', () => { @@ -1574,8 +1578,9 @@ export class AppModule {} describe('convertDeepLinkConfigEntriesToString', () => { it('should convert list of decorator data to legacy ionic data structure as a string', () => { - const list: DeepLinkConfigEntry[] = []; - list.push({ + const map = new Map(); + + map.set('HomePage', { name: 'HomePage', segment: 'idkMan', defaultHistory: ['page-two', 'page-three', 'page-four'], @@ -1585,7 +1590,8 @@ export class AppModule {} userlandModulePath: '../pages/home-page/home-page.module', className: 'HomePageModule' }); - list.push({ + + map.set('PageTwo', { name: 'PageTwo', segment: null, defaultHistory: [], @@ -1595,7 +1601,8 @@ export class AppModule {} userlandModulePath: '../pages/page-two/page-two.module', className: 'PageTwoModule' }); - list.push({ + + map.set('SettingsPage', { name: 'SettingsPage', segment: null, defaultHistory: [], @@ -1606,7 +1613,7 @@ export class AppModule {} className: 'SettingsPageModule' }); - const result = util.convertDeepLinkConfigEntriesToString(list); + const result = util.convertDeepLinkConfigEntriesToString(map); expect(result.indexOf('links: [')).toBeGreaterThanOrEqual(0); expect(result.indexOf(`{ loadChildren: '../pages/home-page/home-page.module#HomePageModule', name: 'HomePage', segment: 'idkMan', priority: 'low', defaultHistory: ['page-two', 'page-three', 'page-four'] },`)).toBeGreaterThanOrEqual(0); expect(result.indexOf(`{ loadChildren: '../pages/page-two/page-two.module#PageTwoModule', name: 'PageTwo', segment: null, priority: 'low', defaultHistory: [] },`)).toBeGreaterThanOrEqual(0); @@ -1896,439 +1903,6 @@ export class AppModule {} }); }); - describe('getUpdatedAppNgModuleFactoryContentWithDeepLinksConfig', () => { - it('should find and replace the content for DeepLinkConfigToken when existing content is null', () => { - - const knownDeepLinkString = `this._DeepLinkConfigToken_21 = (null as any);`; - - const knownContent = ` -/** - * @fileoverview This file is generated by the Angular template compiler. - * Do not edit. - * @suppress {suspiciousCode,uselessCode,missingProperties} - */ - /* tslint:disable */ - -import * as import0 from '@angular/core'; -import * as import1 from './app.module'; -import * as import2 from '@angular/common'; -import * as import3 from '@angular/platform-browser'; -import * as import4 from '@angular/forms'; -import * as import5 from 'ionic-angular/index'; -import * as import6 from '../pages/home/home.module'; -import * as import7 from 'ionic-angular/platform/dom-controller'; -import * as import8 from 'ionic-angular/components/menu/menu-controller'; -import * as import9 from 'ionic-angular/components/app/app'; -import * as import10 from 'ionic-angular/gestures/gesture-controller'; -import * as import11 from 'ionic-angular/util/ng-module-loader'; -import * as import12 from 'ionic-angular/components/action-sheet/action-sheet-controller'; -import * as import13 from 'ionic-angular/components/alert/alert-controller'; -import * as import14 from 'ionic-angular/util/events'; -import * as import15 from 'ionic-angular/util/form'; -import * as import16 from 'ionic-angular/tap-click/haptic'; -import * as import17 from 'ionic-angular/platform/keyboard'; -import * as import18 from 'ionic-angular/components/loading/loading-controller'; -import * as import19 from 'ionic-angular/components/modal/modal-controller'; -import * as import20 from 'ionic-angular/components/picker/picker-controller'; -import * as import21 from 'ionic-angular/components/popover/popover-controller'; -import * as import22 from 'ionic-angular/tap-click/tap-click'; -import * as import23 from 'ionic-angular/components/toast/toast-controller'; -import * as import24 from 'ionic-angular/transitions/transition-controller'; -import * as import25 from '../../node_modules/ionic-angular/components/action-sheet/action-sheet-component.ngfactory'; -import * as import26 from '../../node_modules/ionic-angular/components/alert/alert-component.ngfactory'; -import * as import27 from '../../node_modules/ionic-angular/components/app/app-root.ngfactory'; -import * as import28 from '../../node_modules/ionic-angular/components/loading/loading-component.ngfactory'; -import * as import29 from '../../node_modules/ionic-angular/components/modal/modal-component.ngfactory'; -import * as import30 from '../../node_modules/ionic-angular/components/picker/picker-component.ngfactory'; -import * as import31 from '../../node_modules/ionic-angular/components/popover/popover-component.ngfactory'; -import * as import32 from '../../node_modules/ionic-angular/components/toast/toast-component.ngfactory'; -import * as import33 from '../pages/home/home.ngfactory'; -import * as import34 from './app.component.ngfactory'; -import * as import35 from '../pages/home/home'; -import * as import36 from './app.component'; -import * as import37 from 'ionic-angular/navigation/url-serializer'; -import * as import38 from 'ionic-angular/navigation/deep-linker'; -import * as import39 from 'ionic-angular/platform/platform-registry'; -import * as import40 from 'ionic-angular/platform/platform'; -import * as import41 from 'ionic-angular/config/config'; -import * as import42 from 'ionic-angular/util/module-loader'; -import * as import43 from 'ionic-angular/config/mode-registry'; -import * as import44 from 'ionic-angular/components/app/app-root'; -class AppModuleInjector extends import0.ɵNgModuleInjector { - _CommonModule_0:import2.CommonModule; - _ApplicationModule_1:import0.ApplicationModule; - _BrowserModule_2:import3.BrowserModule; - _ɵba_3:import4.ɵba; - _FormsModule_4:import4.FormsModule; - _ReactiveFormsModule_5:import4.ReactiveFormsModule; - _IonicModule_6:import5.IonicModule; - _IonicPageModule_7:import5.IonicPageModule; - _HomePageModule_8:import6.HomePageModule; - _AppModule_9:import1.AppModule; - __LOCALE_ID_10:any; - __NgLocalization_11:import2.NgLocaleLocalization; - _ErrorHandler_12:any; - _ConfigToken_13:any; - _PlatformConfigToken_14:any; - _Platform_15:any; - _Config_16:any; - _DomController_17:import7.DomController; - _MenuController_18:import8.MenuController; - _App_19:import9.App; - _GestureController_20:import10.GestureController; - _DeepLinkConfigToken_21:any; - _Compiler_22:import0.Compiler; - _NgModuleLoader_23:import11.NgModuleLoader; - _ModuleLoader_24:any; - _APP_INITIALIZER_25:any[]; - _ApplicationInitStatus_26:import0.ApplicationInitStatus; - _ɵf_27:import0.ɵf; - __ApplicationRef_28:any; - __APP_ID_29:any; - __IterableDiffers_30:any; - __KeyValueDiffers_31:any; - __DomSanitizer_32:import3.ɵe; - __Sanitizer_33:any; - __HAMMER_GESTURE_CONFIG_34:import3.HammerGestureConfig; - __EVENT_MANAGER_PLUGINS_35:any[]; - __EventManager_36:import3.EventManager; - _ɵDomSharedStylesHost_37:import3.ɵDomSharedStylesHost; - __ɵDomRendererFactoryV2_38:import3.ɵDomRendererFactoryV2; - __RendererFactoryV2_39:any; - __ɵSharedStylesHost_40:any; - __Testability_41:import0.Testability; - __Meta_42:import3.Meta; - __Title_43:import3.Title; - __ɵi_44:import4.ɵi; - __FormBuilder_45:import4.FormBuilder; - __LAZY_LOADED_TOKEN_46:any; - __AppRootToken_47:any; - __APP_BASE_HREF_48:any; - __ActionSheetController_49:import12.ActionSheetController; - __AlertController_50:import13.AlertController; - __Events_51:import14.Events; - __Form_52:import15.Form; - __Haptic_53:import16.Haptic; - __Keyboard_54:import17.Keyboard; - __LoadingController_55:import18.LoadingController; - __LocationStrategy_56:any; - __Location_57:import2.Location; - __UrlSerializer_58:any; - __DeepLinker_59:any; - __ModalController_60:import19.ModalController; - __PickerController_61:import20.PickerController; - __PopoverController_62:import21.PopoverController; - __TapClick_63:import22.TapClick; - __ToastController_64:import23.ToastController; - __TransitionController_65:import24.TransitionController; - constructor(parent:import0.Injector) { - super(parent,[ - import25.ActionSheetCmpNgFactory, - import26.AlertCmpNgFactory, - import27.IonicAppNgFactory, - import28.LoadingCmpNgFactory, - import29.ModalCmpNgFactory, - import30.PickerCmpNgFactory, - import31.PopoverCmpNgFactory, - import32.ToastCmpNgFactory, - import33.HomePageNgFactory, - import34.MyAppNgFactory - ] - ,[import27.IonicAppNgFactory]); - } - get _LOCALE_ID_10():any { - if ((this.__LOCALE_ID_10 == null)) { (this.__LOCALE_ID_10 = import0.ɵo(this.parent.get(import0.LOCALE_ID,(null as any)))); } - return this.__LOCALE_ID_10; - } - get _NgLocalization_11():import2.NgLocaleLocalization { - if ((this.__NgLocalization_11 == null)) { (this.__NgLocalization_11 = new import2.NgLocaleLocalization(this._LOCALE_ID_10)); } - return this.__NgLocalization_11; - } - get _ApplicationRef_28():any { - if ((this.__ApplicationRef_28 == null)) { (this.__ApplicationRef_28 = this._ɵf_27); } - return this.__ApplicationRef_28; - } - get _APP_ID_29():any { - if ((this.__APP_ID_29 == null)) { (this.__APP_ID_29 = import0.ɵg()); } - return this.__APP_ID_29; - } - get _IterableDiffers_30():any { - if ((this.__IterableDiffers_30 == null)) { (this.__IterableDiffers_30 = import0.ɵm()); } - return this.__IterableDiffers_30; - } - get _KeyValueDiffers_31():any { - if ((this.__KeyValueDiffers_31 == null)) { (this.__KeyValueDiffers_31 = import0.ɵn()); } - return this.__KeyValueDiffers_31; - } - get _DomSanitizer_32():import3.ɵe { - if ((this.__DomSanitizer_32 == null)) { (this.__DomSanitizer_32 = new import3.ɵe(this.parent.get(import3.DOCUMENT))); } - return this.__DomSanitizer_32; - } - get _Sanitizer_33():any { - if ((this.__Sanitizer_33 == null)) { (this.__Sanitizer_33 = this._DomSanitizer_32); } - return this.__Sanitizer_33; - } - get _HAMMER_GESTURE_CONFIG_34():import3.HammerGestureConfig { - if ((this.__HAMMER_GESTURE_CONFIG_34 == null)) { (this.__HAMMER_GESTURE_CONFIG_34 = new import3.HammerGestureConfig()); } - return this.__HAMMER_GESTURE_CONFIG_34; - } - get _EVENT_MANAGER_PLUGINS_35():any[] { - if ((this.__EVENT_MANAGER_PLUGINS_35 == null)) { (this.__EVENT_MANAGER_PLUGINS_35 = [ - new import3.ɵDomEventsPlugin(this.parent.get(import3.DOCUMENT)), - new import3.ɵKeyEventsPlugin(this.parent.get(import3.DOCUMENT)), - new import3.ɵHammerGesturesPlugin(this.parent.get(import3.DOCUMENT),this._HAMMER_GESTURE_CONFIG_34) - ] - ); } - return this.__EVENT_MANAGER_PLUGINS_35; - } - get _EventManager_36():import3.EventManager { - if ((this.__EventManager_36 == null)) { (this.__EventManager_36 = new import3.EventManager(this._EVENT_MANAGER_PLUGINS_35,this.parent.get(import0.NgZone))); } - return this.__EventManager_36; - } - get _ɵDomRendererFactoryV2_38():import3.ɵDomRendererFactoryV2 { - if ((this.__ɵDomRendererFactoryV2_38 == null)) { (this.__ɵDomRendererFactoryV2_38 = new import3.ɵDomRendererFactoryV2(this._EventManager_36,this._ɵDomSharedStylesHost_37)); } - return this.__ɵDomRendererFactoryV2_38; - } - get _RendererFactoryV2_39():any { - if ((this.__RendererFactoryV2_39 == null)) { (this.__RendererFactoryV2_39 = this._ɵDomRendererFactoryV2_38); } - return this.__RendererFactoryV2_39; - } - get _ɵSharedStylesHost_40():any { - if ((this.__ɵSharedStylesHost_40 == null)) { (this.__ɵSharedStylesHost_40 = this._ɵDomSharedStylesHost_37); } - return this.__ɵSharedStylesHost_40; - } - get _Testability_41():import0.Testability { - if ((this.__Testability_41 == null)) { (this.__Testability_41 = new import0.Testability(this.parent.get(import0.NgZone))); } - return this.__Testability_41; - } - get _Meta_42():import3.Meta { - if ((this.__Meta_42 == null)) { (this.__Meta_42 = new import3.Meta(this.parent.get(import3.DOCUMENT))); } - return this.__Meta_42; - } - get _Title_43():import3.Title { - if ((this.__Title_43 == null)) { (this.__Title_43 = new import3.Title(this.parent.get(import3.DOCUMENT))); } - return this.__Title_43; - } - get _ɵi_44():import4.ɵi { - if ((this.__ɵi_44 == null)) { (this.__ɵi_44 = new import4.ɵi()); } - return this.__ɵi_44; - } - get _FormBuilder_45():import4.FormBuilder { - if ((this.__FormBuilder_45 == null)) { (this.__FormBuilder_45 = new import4.FormBuilder()); } - return this.__FormBuilder_45; - } - get _LAZY_LOADED_TOKEN_46():any { - if ((this.__LAZY_LOADED_TOKEN_46 == null)) { (this.__LAZY_LOADED_TOKEN_46 = import35.HomePage); } - return this.__LAZY_LOADED_TOKEN_46; - } - get _AppRootToken_47():any { - if ((this.__AppRootToken_47 == null)) { (this.__AppRootToken_47 = import36.MyApp); } - return this.__AppRootToken_47; - } - get _APP_BASE_HREF_48():any { - if ((this.__APP_BASE_HREF_48 == null)) { (this.__APP_BASE_HREF_48 = '/'); } - return this.__APP_BASE_HREF_48; - } - get _ActionSheetController_49():import12.ActionSheetController { - if ((this.__ActionSheetController_49 == null)) { (this.__ActionSheetController_49 = new import12.ActionSheetController(this._App_19,this._Config_16)); } - return this.__ActionSheetController_49; - } - get _AlertController_50():import13.AlertController { - if ((this.__AlertController_50 == null)) { (this.__AlertController_50 = new import13.AlertController(this._App_19,this._Config_16)); } - return this.__AlertController_50; - } - get _Events_51():import14.Events { - if ((this.__Events_51 == null)) { (this.__Events_51 = new import14.Events()); } - return this.__Events_51; - } - get _Form_52():import15.Form { - if ((this.__Form_52 == null)) { (this.__Form_52 = new import15.Form()); } - return this.__Form_52; - } - get _Haptic_53():import16.Haptic { - if ((this.__Haptic_53 == null)) { (this.__Haptic_53 = new import16.Haptic(this._Platform_15)); } - return this.__Haptic_53; - } - get _Keyboard_54():import17.Keyboard { - if ((this.__Keyboard_54 == null)) { (this.__Keyboard_54 = new import17.Keyboard(this._Config_16,this._Platform_15,this.parent.get(import0.NgZone),this._DomController_17)); } - return this.__Keyboard_54; - } - get _LoadingController_55():import18.LoadingController { - if ((this.__LoadingController_55 == null)) { (this.__LoadingController_55 = new import18.LoadingController(this._App_19,this._Config_16)); } - return this.__LoadingController_55; - } - get _LocationStrategy_56():any { - if ((this.__LocationStrategy_56 == null)) { (this.__LocationStrategy_56 = import5.provideLocationStrategy(this.parent.get(import2.PlatformLocation),this._APP_BASE_HREF_48,this._Config_16)); } - return this.__LocationStrategy_56; - } - get _Location_57():import2.Location { - if ((this.__Location_57 == null)) { (this.__Location_57 = new import2.Location(this._LocationStrategy_56)); } - return this.__Location_57; - } - get _UrlSerializer_58():any { - if ((this.__UrlSerializer_58 == null)) { (this.__UrlSerializer_58 = import37.setupUrlSerializer(this._DeepLinkConfigToken_21)); } - return this.__UrlSerializer_58; - } - get _DeepLinker_59():any { - if ((this.__DeepLinker_59 == null)) { (this.__DeepLinker_59 = import38.setupDeepLinker(this._App_19,this._UrlSerializer_58,this._Location_57,this._ModuleLoader_24,this)); } - return this.__DeepLinker_59; - } - get _ModalController_60():import19.ModalController { - if ((this.__ModalController_60 == null)) { (this.__ModalController_60 = new import19.ModalController(this._App_19,this._Config_16,this._DeepLinker_59)); } - return this.__ModalController_60; - } - get _PickerController_61():import20.PickerController { - if ((this.__PickerController_61 == null)) { (this.__PickerController_61 = new import20.PickerController(this._App_19,this._Config_16)); } - return this.__PickerController_61; - } - get _PopoverController_62():import21.PopoverController { - if ((this.__PopoverController_62 == null)) { (this.__PopoverController_62 = new import21.PopoverController(this._App_19,this._Config_16,this._DeepLinker_59)); } - return this.__PopoverController_62; - } - get _TapClick_63():import22.TapClick { - if ((this.__TapClick_63 == null)) { (this.__TapClick_63 = new import22.TapClick(this._Config_16,this._Platform_15,this._DomController_17,this._App_19,this.parent.get(import0.NgZone),this._GestureController_20)); } - return this.__TapClick_63; - } - get _ToastController_64():import23.ToastController { - if ((this.__ToastController_64 == null)) { (this.__ToastController_64 = new import23.ToastController(this._App_19,this._Config_16)); } - return this.__ToastController_64; - } - get _TransitionController_65():import24.TransitionController { - if ((this.__TransitionController_65 == null)) { (this.__TransitionController_65 = new import24.TransitionController(this._Platform_15,this._Config_16)); } - return this.__TransitionController_65; - } - createInternal():import1.AppModule { - this._CommonModule_0 = new import2.CommonModule(); - this._ApplicationModule_1 = new import0.ApplicationModule(); - this._BrowserModule_2 = new import3.BrowserModule(this.parent.get(import3.BrowserModule,(null as any))); - this._ɵba_3 = new import4.ɵba(); - this._FormsModule_4 = new import4.FormsModule(); - this._ReactiveFormsModule_5 = new import4.ReactiveFormsModule(); - this._IonicModule_6 = new import5.IonicModule(); - this._IonicPageModule_7 = new import5.IonicPageModule(); - this._HomePageModule_8 = new import6.HomePageModule(); - this._AppModule_9 = new import1.AppModule(); - this._ErrorHandler_12 = import3.ɵa(); - this._ConfigToken_13 = {}; - this._PlatformConfigToken_14 = import39.providePlatformConfigs(); - this._Platform_15 = import40.setupPlatform(this.parent.get(import3.DOCUMENT),this._PlatformConfigToken_14,this.parent.get(import0.NgZone)); - this._Config_16 = import41.setupConfig(this._ConfigToken_13,this._Platform_15); - this._DomController_17 = new import7.DomController(this._Platform_15); - this._MenuController_18 = new import8.MenuController(); - this._App_19 = new import9.App(this._Config_16,this._Platform_15,this._MenuController_18); - this._GestureController_20 = new import10.GestureController(this._App_19); - ${knownDeepLinkString} - this._Compiler_22 = new import0.Compiler(); - this._NgModuleLoader_23 = new import11.NgModuleLoader(this._Compiler_22,this.parent.get(import11.NgModuleLoaderConfig,(null as any))); - this._ModuleLoader_24 = import42.provideModuleLoader(this._NgModuleLoader_23,this); - this._APP_INITIALIZER_25 = [ - import0.ɵp, - import3.ɵc(this.parent.get(import3.NgProbeToken,(null as any)),this.parent.get(import0.NgProbeToken,(null as any))), - import43.registerModeConfigs(this._Config_16), - import14.setupProvideEvents(this._Platform_15,this._DomController_17), - import22.setupTapClick(this._Config_16,this._Platform_15,this._DomController_17,this._App_19,this.parent.get(import0.NgZone),this._GestureController_20), - import42.setupPreloading(this._Config_16,this._DeepLinkConfigToken_21,this._ModuleLoader_24,this.parent.get(import0.NgZone)) - ] - ; - this._ApplicationInitStatus_26 = new import0.ApplicationInitStatus(this._APP_INITIALIZER_25); - this._ɵf_27 = new import0.ɵf(this.parent.get(import0.NgZone),this.parent.get(import0.ɵConsole),this,this._ErrorHandler_12,this,this._ApplicationInitStatus_26); - this._ɵDomSharedStylesHost_37 = new import3.ɵDomSharedStylesHost(this.parent.get(import3.DOCUMENT)); - return this._AppModule_9; - } - getInternal(token:any,notFoundResult:any):any { - if ((token === import2.CommonModule)) { return this._CommonModule_0; } - if ((token === import0.ApplicationModule)) { return this._ApplicationModule_1; } - if ((token === import3.BrowserModule)) { return this._BrowserModule_2; } - if ((token === import4.ɵba)) { return this._ɵba_3; } - if ((token === import4.FormsModule)) { return this._FormsModule_4; } - if ((token === import4.ReactiveFormsModule)) { return this._ReactiveFormsModule_5; } - if ((token === import5.IonicModule)) { return this._IonicModule_6; } - if ((token === import5.IonicPageModule)) { return this._IonicPageModule_7; } - if ((token === import6.HomePageModule)) { return this._HomePageModule_8; } - if ((token === import1.AppModule)) { return this._AppModule_9; } - if ((token === import0.LOCALE_ID)) { return this._LOCALE_ID_10; } - if ((token === import2.NgLocalization)) { return this._NgLocalization_11; } - if ((token === import0.ErrorHandler)) { return this._ErrorHandler_12; } - if ((token === import41.ConfigToken)) { return this._ConfigToken_13; } - if ((token === import39.PlatformConfigToken)) { return this._PlatformConfigToken_14; } - if ((token === import40.Platform)) { return this._Platform_15; } - if ((token === import41.Config)) { return this._Config_16; } - if ((token === import7.DomController)) { return this._DomController_17; } - if ((token === import8.MenuController)) { return this._MenuController_18; } - if ((token === import9.App)) { return this._App_19; } - if ((token === import10.GestureController)) { return this._GestureController_20; } - if ((token === import37.DeepLinkConfigToken)) { return this._DeepLinkConfigToken_21; } - if ((token === import0.Compiler)) { return this._Compiler_22; } - if ((token === import11.NgModuleLoader)) { return this._NgModuleLoader_23; } - if ((token === import42.ModuleLoader)) { return this._ModuleLoader_24; } - if ((token === import0.APP_INITIALIZER)) { return this._APP_INITIALIZER_25; } - if ((token === import0.ApplicationInitStatus)) { return this._ApplicationInitStatus_26; } - if ((token === import0.ɵf)) { return this._ɵf_27; } - if ((token === import0.ApplicationRef)) { return this._ApplicationRef_28; } - if ((token === import0.APP_ID)) { return this._APP_ID_29; } - if ((token === import0.IterableDiffers)) { return this._IterableDiffers_30; } - if ((token === import0.KeyValueDiffers)) { return this._KeyValueDiffers_31; } - if ((token === import3.DomSanitizer)) { return this._DomSanitizer_32; } - if ((token === import0.Sanitizer)) { return this._Sanitizer_33; } - if ((token === import3.HAMMER_GESTURE_CONFIG)) { return this._HAMMER_GESTURE_CONFIG_34; } - if ((token === import3.EVENT_MANAGER_PLUGINS)) { return this._EVENT_MANAGER_PLUGINS_35; } - if ((token === import3.EventManager)) { return this._EventManager_36; } - if ((token === import3.ɵDomSharedStylesHost)) { return this._ɵDomSharedStylesHost_37; } - if ((token === import3.ɵDomRendererFactoryV2)) { return this._ɵDomRendererFactoryV2_38; } - if ((token === import0.RendererFactoryV2)) { return this._RendererFactoryV2_39; } - if ((token === import3.ɵSharedStylesHost)) { return this._ɵSharedStylesHost_40; } - if ((token === import0.Testability)) { return this._Testability_41; } - if ((token === import3.Meta)) { return this._Meta_42; } - if ((token === import3.Title)) { return this._Title_43; } - if ((token === import4.ɵi)) { return this._ɵi_44; } - if ((token === import4.FormBuilder)) { return this._FormBuilder_45; } - if ((token === import42.LAZY_LOADED_TOKEN)) { return this._LAZY_LOADED_TOKEN_46; } - if ((token === import44.AppRootToken)) { return this._AppRootToken_47; } - if ((token === import2.APP_BASE_HREF)) { return this._APP_BASE_HREF_48; } - if ((token === import12.ActionSheetController)) { return this._ActionSheetController_49; } - if ((token === import13.AlertController)) { return this._AlertController_50; } - if ((token === import14.Events)) { return this._Events_51; } - if ((token === import15.Form)) { return this._Form_52; } - if ((token === import16.Haptic)) { return this._Haptic_53; } - if ((token === import17.Keyboard)) { return this._Keyboard_54; } - if ((token === import18.LoadingController)) { return this._LoadingController_55; } - if ((token === import2.LocationStrategy)) { return this._LocationStrategy_56; } - if ((token === import2.Location)) { return this._Location_57; } - if ((token === import37.UrlSerializer)) { return this._UrlSerializer_58; } - if ((token === import38.DeepLinker)) { return this._DeepLinker_59; } - if ((token === import19.ModalController)) { return this._ModalController_60; } - if ((token === import20.PickerController)) { return this._PickerController_61; } - if ((token === import21.PopoverController)) { return this._PopoverController_62; } - if ((token === import22.TapClick)) { return this._TapClick_63; } - if ((token === import23.ToastController)) { return this._ToastController_64; } - if ((token === import24.TransitionController)) { return this._TransitionController_65; } - return notFoundResult; - } - destroyInternal():void { - this._ɵf_27.ngOnDestroy(); - this._ɵDomSharedStylesHost_37.ngOnDestroy(); - } -} -export const AppModuleNgFactory:import0.NgModuleFactory = new import0.NgModuleFactory(AppModuleInjector,import1.AppModule); - `; - - const contentToInject = `{ - links: [ - { loadChildren: '../pages/home-page/home-page.module#HomePageModule', name: 'HomePage', segment: 'idkMan', priority: 'low', defaultHistory: ['page-two', 'page-three', 'page-four'] }, - { loadChildren: '../pages/page-two/page-two.module#PageTwoModule', name: 'PageTwo', segment: null, priority: 'low', defaultHistory: [] }, - { loadChildren: '../pages/settings-page/setting-page.module#SettingsPageModule', name: 'SettingsPage', segment: null, priority: 'low', defaultHistory: [] } - ] -} -`; - - const expectedDeepLinkString = `this._DeepLinkConfigToken_21 =${contentToInject}`; - - const result = util.getUpdatedAppNgModuleFactoryContentWithDeepLinksConfig(knownContent, contentToInject); - expect(result.indexOf(knownDeepLinkString)).toEqual(-1); - expect(result.indexOf(expectedDeepLinkString)).toBeGreaterThanOrEqual(0); - }); - }); - describe('generateDefaultDeepLinkNgModuleContent', () => { it('should generate a default NgModule for a DeepLinked component', () => { const knownFileContent = ` @@ -2354,7 +1928,7 @@ export class PageOneModule {} }); }); - describe('updateAppNgModuleAndFactoryWithDeepLinkConfig', () => { + describe('updateAppNgModuleWithDeepLinkConfig', () => { it('should throw when app ng module is not in cache', () => { const fileCache = new FileCache(); const knownContext = { @@ -2375,7 +1949,7 @@ export class PageOneModule {} const knownErrorMsg = 'should never get here'; try { - util.updateAppNgModuleAndFactoryWithDeepLinkConfig(knownContext, knownDeepLinkString, null, false); + util.updateAppNgModuleWithDeepLinkConfig(knownContext, knownDeepLinkString, null); throw new Error(knownErrorMsg); } catch (ex) { expect(ex.message).not.toEqual(knownErrorMsg); @@ -2383,7 +1957,7 @@ export class PageOneModule {} } }); - it('should update the cache with updated ts file, transpiled js file and map w/o aot', () => { + it('should update the cache with updated ts file', () => { const fileCache = new FileCache(); const knownContext = { fileCache: fileCache @@ -2421,569 +1995,70 @@ import { HomePageModule } from '../pages/home/home.module'; export class AppModule {} `; + const knownAppNgModulePath = join(process.cwd(), 'myApp', 'src', 'app.module.ts'); fileCache.set(knownAppNgModulePath, { path: knownAppNgModulePath, content: ngModuleContent}); spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(knownAppNgModulePath); - spyOn(fileCache, 'get').and.callThrough(); - spyOn(transpile, transpile.transpileTsString.name).and.callFake((context: BuildContext, filePath: string, contentToTranspile: string) => { - return { - sourceMapText: 'sourceMapText', - outputText: contentToTranspile - }; - }); const changedFiles: ChangedFile[] = []; - util.updateAppNgModuleAndFactoryWithDeepLinkConfig(knownContext, knownDeepLinkString, changedFiles, false); + util.updateAppNgModuleWithDeepLinkConfig(knownContext, knownDeepLinkString, changedFiles); - expect(fileCache.getAll().length).toEqual(3); + expect(fileCache.getAll().length).toEqual(1); expect(fileCache.get(knownAppNgModulePath).content.indexOf(knownDeepLinkString)).toBeGreaterThanOrEqual(0); - expect(fileCache.get(helpers.changeExtension(knownAppNgModulePath, '.js')).content.indexOf(knownDeepLinkString)).toBeGreaterThanOrEqual(0); - expect(fileCache.get(helpers.changeExtension(knownAppNgModulePath, '.js.map')).content).toEqual('sourceMapText'); expect(changedFiles.length).toEqual(1); expect(changedFiles[0].event).toEqual('change'); expect(changedFiles[0].ext).toEqual('.ts'); expect(changedFiles[0].filePath).toEqual(knownAppNgModulePath); - }); + }); - it('should throw when ng factory is not in cache', () => { - const fileCache = new FileCache(); - const knownContext = { - fileCache: fileCache - }; - - const knownDeepLinkString = `{ - links: [ - { loadChildren: '../pages/home-page/home-page.module#HomePageModule', name: 'HomePage', segment: 'idkMan', priority: 'low', defaultHistory: ['page-two', 'page-three', 'page-four'] }, - { loadChildren: '../pages/page-two/page-two.module#PageTwoModule', name: 'PageTwo', segment: null, priority: 'low', defaultHistory: [] }, - { loadChildren: '../pages/settings-page/setting-page.module#SettingsPageModule', name: 'SettingsPage', segment: null, priority: 'low', defaultHistory: [] } - ] -} -`; - - const ngModuleContent = ` -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { IonicApp, IonicModule } from 'ionic-angular'; -import { MyApp } from './app.component'; + describe('purgeDeepLinkDecorator', () => { + it('should remove the IonicPage decorator from the ts source', () => { + const input = ` +import { Component } from '@angular/core'; -import { HomePageModule } from '../pages/home/home.module'; +import { IonicPage, PopoverController } from 'ionic-angular'; -@NgModule({ - declarations: [ - MyApp, - ], - imports: [ - BrowserModule, - IonicModule.forRoot(MyApp, {}, null), - HomePageModule, - ], - bootstrap: [IonicApp], - providers: [] +@IonicPage() +@Component({ + selector: 'page-about', + templateUrl: 'about.html' }) -export class AppModule {} -`; - - const knownAppNgModulePath = join(process.cwd(), 'myApp', 'src', 'app.module.ts'); - fileCache.set(knownAppNgModulePath, { path: knownAppNgModulePath, content: ngModuleContent}); - spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(knownAppNgModulePath); - spyOn(fileCache, 'get').and.callThrough(); - spyOn(transpile, transpile.transpileTsString.name).and.callFake((context: BuildContext, filePath: string, contentToTranspile: string) => { - return { - sourceMapText: 'sourceMapText', - outputText: contentToTranspile - }; - }); +export class AboutPage { + conferenceDate = '2047-05-17'; - const changedFiles: ChangedFile[] = []; - const knownErrorMsg = 'should never happen'; - try { - util.updateAppNgModuleAndFactoryWithDeepLinkConfig(knownContext, knownDeepLinkString, changedFiles, true); - throw new Error(knownErrorMsg); - } catch (ex) { - expect(ex.message).not.toEqual(knownErrorMsg); - } + constructor(public popoverCtrl: PopoverController) { } - }); - - it('should update the cache with updated ts file, transpiled js file and map with aot', () => { - const fileCache = new FileCache(); - const knownContext = { - fileCache: fileCache - }; - - const knownDeepLinkString = `{ - links: [ - { loadChildren: '../pages/home-page/home-page.module#HomePageModule', name: 'HomePage', segment: 'idkMan', priority: 'low', defaultHistory: ['page-two', 'page-three', 'page-four'] }, - { loadChildren: '../pages/page-two/page-two.module#PageTwoModule', name: 'PageTwo', segment: null, priority: 'low', defaultHistory: [] }, - { loadChildren: '../pages/settings-page/setting-page.module#SettingsPageModule', name: 'SettingsPage', segment: null, priority: 'low', defaultHistory: [] } - ] + presentPopover(event: Event) { + let popover = this.popoverCtrl.create('PopoverPage'); + popover.present({ ev: event }); + } } `; - const ngModuleContent = ` -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { IonicApp, IonicModule } from 'ionic-angular'; -import { MyApp } from './app.component'; +const expectedContent = ` +import { Component } from '@angular/core'; -import { HomePageModule } from '../pages/home/home.module'; +import { IonicPage, PopoverController } from 'ionic-angular'; -@NgModule({ - declarations: [ - MyApp, - ], - imports: [ - BrowserModule, - IonicModule.forRoot(MyApp, {}, null), - HomePageModule, - ], - bootstrap: [IonicApp], - providers: [] + +@Component({ + selector: 'page-about', + templateUrl: 'about.html' }) -export class AppModule {} -`; +export class AboutPage { + conferenceDate = '2047-05-17'; - const knownNgFactoryContent = ` -/** - * @fileoverview This file is generated by the Angular template compiler. - * Do not edit. - * @suppress {suspiciousCode,uselessCode,missingProperties} - */ - /* tslint:disable */ - -import * as import0 from '@angular/core'; -import * as import1 from './app.module'; -import * as import2 from '@angular/common'; -import * as import3 from '@angular/platform-browser'; -import * as import4 from '@angular/forms'; -import * as import5 from 'ionic-angular/index'; -import * as import6 from '../pages/home/home.module'; -import * as import7 from 'ionic-angular/platform/dom-controller'; -import * as import8 from 'ionic-angular/components/menu/menu-controller'; -import * as import9 from 'ionic-angular/components/app/app'; -import * as import10 from 'ionic-angular/gestures/gesture-controller'; -import * as import11 from 'ionic-angular/util/ng-module-loader'; -import * as import12 from 'ionic-angular/components/action-sheet/action-sheet-controller'; -import * as import13 from 'ionic-angular/components/alert/alert-controller'; -import * as import14 from 'ionic-angular/util/events'; -import * as import15 from 'ionic-angular/util/form'; -import * as import16 from 'ionic-angular/tap-click/haptic'; -import * as import17 from 'ionic-angular/platform/keyboard'; -import * as import18 from 'ionic-angular/components/loading/loading-controller'; -import * as import19 from 'ionic-angular/components/modal/modal-controller'; -import * as import20 from 'ionic-angular/components/picker/picker-controller'; -import * as import21 from 'ionic-angular/components/popover/popover-controller'; -import * as import22 from 'ionic-angular/tap-click/tap-click'; -import * as import23 from 'ionic-angular/components/toast/toast-controller'; -import * as import24 from 'ionic-angular/transitions/transition-controller'; -import * as import25 from '../../node_modules/ionic-angular/components/action-sheet/action-sheet-component.ngfactory'; -import * as import26 from '../../node_modules/ionic-angular/components/alert/alert-component.ngfactory'; -import * as import27 from '../../node_modules/ionic-angular/components/app/app-root.ngfactory'; -import * as import28 from '../../node_modules/ionic-angular/components/loading/loading-component.ngfactory'; -import * as import29 from '../../node_modules/ionic-angular/components/modal/modal-component.ngfactory'; -import * as import30 from '../../node_modules/ionic-angular/components/picker/picker-component.ngfactory'; -import * as import31 from '../../node_modules/ionic-angular/components/popover/popover-component.ngfactory'; -import * as import32 from '../../node_modules/ionic-angular/components/toast/toast-component.ngfactory'; -import * as import33 from '../pages/home/home.ngfactory'; -import * as import34 from './app.component.ngfactory'; -import * as import35 from '../pages/home/home'; -import * as import36 from './app.component'; -import * as import37 from 'ionic-angular/navigation/url-serializer'; -import * as import38 from 'ionic-angular/navigation/deep-linker'; -import * as import39 from 'ionic-angular/platform/platform-registry'; -import * as import40 from 'ionic-angular/platform/platform'; -import * as import41 from 'ionic-angular/config/config'; -import * as import42 from 'ionic-angular/util/module-loader'; -import * as import43 from 'ionic-angular/config/mode-registry'; -import * as import44 from 'ionic-angular/components/app/app-root'; -class AppModuleInjector extends import0.ɵNgModuleInjector { - _CommonModule_0:import2.CommonModule; - _ApplicationModule_1:import0.ApplicationModule; - _BrowserModule_2:import3.BrowserModule; - _ɵba_3:import4.ɵba; - _FormsModule_4:import4.FormsModule; - _ReactiveFormsModule_5:import4.ReactiveFormsModule; - _IonicModule_6:import5.IonicModule; - _IonicPageModule_7:import5.IonicPageModule; - _HomePageModule_8:import6.HomePageModule; - _AppModule_9:import1.AppModule; - __LOCALE_ID_10:any; - __NgLocalization_11:import2.NgLocaleLocalization; - _ErrorHandler_12:any; - _ConfigToken_13:any; - _PlatformConfigToken_14:any; - _Platform_15:any; - _Config_16:any; - _DomController_17:import7.DomController; - _MenuController_18:import8.MenuController; - _App_19:import9.App; - _GestureController_20:import10.GestureController; - _DeepLinkConfigToken_21:any; - _Compiler_22:import0.Compiler; - _NgModuleLoader_23:import11.NgModuleLoader; - _ModuleLoader_24:any; - _APP_INITIALIZER_25:any[]; - _ApplicationInitStatus_26:import0.ApplicationInitStatus; - _ɵf_27:import0.ɵf; - __ApplicationRef_28:any; - __APP_ID_29:any; - __IterableDiffers_30:any; - __KeyValueDiffers_31:any; - __DomSanitizer_32:import3.ɵe; - __Sanitizer_33:any; - __HAMMER_GESTURE_CONFIG_34:import3.HammerGestureConfig; - __EVENT_MANAGER_PLUGINS_35:any[]; - __EventManager_36:import3.EventManager; - _ɵDomSharedStylesHost_37:import3.ɵDomSharedStylesHost; - __ɵDomRendererFactoryV2_38:import3.ɵDomRendererFactoryV2; - __RendererFactoryV2_39:any; - __ɵSharedStylesHost_40:any; - __Testability_41:import0.Testability; - __Meta_42:import3.Meta; - __Title_43:import3.Title; - __ɵi_44:import4.ɵi; - __FormBuilder_45:import4.FormBuilder; - __LAZY_LOADED_TOKEN_46:any; - __AppRootToken_47:any; - __APP_BASE_HREF_48:any; - __ActionSheetController_49:import12.ActionSheetController; - __AlertController_50:import13.AlertController; - __Events_51:import14.Events; - __Form_52:import15.Form; - __Haptic_53:import16.Haptic; - __Keyboard_54:import17.Keyboard; - __LoadingController_55:import18.LoadingController; - __LocationStrategy_56:any; - __Location_57:import2.Location; - __UrlSerializer_58:any; - __DeepLinker_59:any; - __ModalController_60:import19.ModalController; - __PickerController_61:import20.PickerController; - __PopoverController_62:import21.PopoverController; - __TapClick_63:import22.TapClick; - __ToastController_64:import23.ToastController; - __TransitionController_65:import24.TransitionController; - constructor(parent:import0.Injector) { - super(parent,[ - import25.ActionSheetCmpNgFactory, - import26.AlertCmpNgFactory, - import27.IonicAppNgFactory, - import28.LoadingCmpNgFactory, - import29.ModalCmpNgFactory, - import30.PickerCmpNgFactory, - import31.PopoverCmpNgFactory, - import32.ToastCmpNgFactory, - import33.HomePageNgFactory, - import34.MyAppNgFactory - ] - ,[import27.IonicAppNgFactory]); - } - get _LOCALE_ID_10():any { - if ((this.__LOCALE_ID_10 == null)) { (this.__LOCALE_ID_10 = import0.ɵo(this.parent.get(import0.LOCALE_ID,(null as any)))); } - return this.__LOCALE_ID_10; - } - get _NgLocalization_11():import2.NgLocaleLocalization { - if ((this.__NgLocalization_11 == null)) { (this.__NgLocalization_11 = new import2.NgLocaleLocalization(this._LOCALE_ID_10)); } - return this.__NgLocalization_11; - } - get _ApplicationRef_28():any { - if ((this.__ApplicationRef_28 == null)) { (this.__ApplicationRef_28 = this._ɵf_27); } - return this.__ApplicationRef_28; - } - get _APP_ID_29():any { - if ((this.__APP_ID_29 == null)) { (this.__APP_ID_29 = import0.ɵg()); } - return this.__APP_ID_29; - } - get _IterableDiffers_30():any { - if ((this.__IterableDiffers_30 == null)) { (this.__IterableDiffers_30 = import0.ɵm()); } - return this.__IterableDiffers_30; - } - get _KeyValueDiffers_31():any { - if ((this.__KeyValueDiffers_31 == null)) { (this.__KeyValueDiffers_31 = import0.ɵn()); } - return this.__KeyValueDiffers_31; - } - get _DomSanitizer_32():import3.ɵe { - if ((this.__DomSanitizer_32 == null)) { (this.__DomSanitizer_32 = new import3.ɵe(this.parent.get(import3.DOCUMENT))); } - return this.__DomSanitizer_32; - } - get _Sanitizer_33():any { - if ((this.__Sanitizer_33 == null)) { (this.__Sanitizer_33 = this._DomSanitizer_32); } - return this.__Sanitizer_33; - } - get _HAMMER_GESTURE_CONFIG_34():import3.HammerGestureConfig { - if ((this.__HAMMER_GESTURE_CONFIG_34 == null)) { (this.__HAMMER_GESTURE_CONFIG_34 = new import3.HammerGestureConfig()); } - return this.__HAMMER_GESTURE_CONFIG_34; - } - get _EVENT_MANAGER_PLUGINS_35():any[] { - if ((this.__EVENT_MANAGER_PLUGINS_35 == null)) { (this.__EVENT_MANAGER_PLUGINS_35 = [ - new import3.ɵDomEventsPlugin(this.parent.get(import3.DOCUMENT)), - new import3.ɵKeyEventsPlugin(this.parent.get(import3.DOCUMENT)), - new import3.ɵHammerGesturesPlugin(this.parent.get(import3.DOCUMENT),this._HAMMER_GESTURE_CONFIG_34) - ] - ); } - return this.__EVENT_MANAGER_PLUGINS_35; - } - get _EventManager_36():import3.EventManager { - if ((this.__EventManager_36 == null)) { (this.__EventManager_36 = new import3.EventManager(this._EVENT_MANAGER_PLUGINS_35,this.parent.get(import0.NgZone))); } - return this.__EventManager_36; - } - get _ɵDomRendererFactoryV2_38():import3.ɵDomRendererFactoryV2 { - if ((this.__ɵDomRendererFactoryV2_38 == null)) { (this.__ɵDomRendererFactoryV2_38 = new import3.ɵDomRendererFactoryV2(this._EventManager_36,this._ɵDomSharedStylesHost_37)); } - return this.__ɵDomRendererFactoryV2_38; - } - get _RendererFactoryV2_39():any { - if ((this.__RendererFactoryV2_39 == null)) { (this.__RendererFactoryV2_39 = this._ɵDomRendererFactoryV2_38); } - return this.__RendererFactoryV2_39; - } - get _ɵSharedStylesHost_40():any { - if ((this.__ɵSharedStylesHost_40 == null)) { (this.__ɵSharedStylesHost_40 = this._ɵDomSharedStylesHost_37); } - return this.__ɵSharedStylesHost_40; - } - get _Testability_41():import0.Testability { - if ((this.__Testability_41 == null)) { (this.__Testability_41 = new import0.Testability(this.parent.get(import0.NgZone))); } - return this.__Testability_41; - } - get _Meta_42():import3.Meta { - if ((this.__Meta_42 == null)) { (this.__Meta_42 = new import3.Meta(this.parent.get(import3.DOCUMENT))); } - return this.__Meta_42; - } - get _Title_43():import3.Title { - if ((this.__Title_43 == null)) { (this.__Title_43 = new import3.Title(this.parent.get(import3.DOCUMENT))); } - return this.__Title_43; - } - get _ɵi_44():import4.ɵi { - if ((this.__ɵi_44 == null)) { (this.__ɵi_44 = new import4.ɵi()); } - return this.__ɵi_44; - } - get _FormBuilder_45():import4.FormBuilder { - if ((this.__FormBuilder_45 == null)) { (this.__FormBuilder_45 = new import4.FormBuilder()); } - return this.__FormBuilder_45; - } - get _LAZY_LOADED_TOKEN_46():any { - if ((this.__LAZY_LOADED_TOKEN_46 == null)) { (this.__LAZY_LOADED_TOKEN_46 = import35.HomePage); } - return this.__LAZY_LOADED_TOKEN_46; - } - get _AppRootToken_47():any { - if ((this.__AppRootToken_47 == null)) { (this.__AppRootToken_47 = import36.MyApp); } - return this.__AppRootToken_47; - } - get _APP_BASE_HREF_48():any { - if ((this.__APP_BASE_HREF_48 == null)) { (this.__APP_BASE_HREF_48 = '/'); } - return this.__APP_BASE_HREF_48; - } - get _ActionSheetController_49():import12.ActionSheetController { - if ((this.__ActionSheetController_49 == null)) { (this.__ActionSheetController_49 = new import12.ActionSheetController(this._App_19,this._Config_16)); } - return this.__ActionSheetController_49; - } - get _AlertController_50():import13.AlertController { - if ((this.__AlertController_50 == null)) { (this.__AlertController_50 = new import13.AlertController(this._App_19,this._Config_16)); } - return this.__AlertController_50; - } - get _Events_51():import14.Events { - if ((this.__Events_51 == null)) { (this.__Events_51 = new import14.Events()); } - return this.__Events_51; - } - get _Form_52():import15.Form { - if ((this.__Form_52 == null)) { (this.__Form_52 = new import15.Form()); } - return this.__Form_52; - } - get _Haptic_53():import16.Haptic { - if ((this.__Haptic_53 == null)) { (this.__Haptic_53 = new import16.Haptic(this._Platform_15)); } - return this.__Haptic_53; - } - get _Keyboard_54():import17.Keyboard { - if ((this.__Keyboard_54 == null)) { (this.__Keyboard_54 = new import17.Keyboard(this._Config_16,this._Platform_15,this.parent.get(import0.NgZone),this._DomController_17)); } - return this.__Keyboard_54; - } - get _LoadingController_55():import18.LoadingController { - if ((this.__LoadingController_55 == null)) { (this.__LoadingController_55 = new import18.LoadingController(this._App_19,this._Config_16)); } - return this.__LoadingController_55; - } - get _LocationStrategy_56():any { - if ((this.__LocationStrategy_56 == null)) { (this.__LocationStrategy_56 = import5.provideLocationStrategy(this.parent.get(import2.PlatformLocation),this._APP_BASE_HREF_48,this._Config_16)); } - return this.__LocationStrategy_56; - } - get _Location_57():import2.Location { - if ((this.__Location_57 == null)) { (this.__Location_57 = new import2.Location(this._LocationStrategy_56)); } - return this.__Location_57; - } - get _UrlSerializer_58():any { - if ((this.__UrlSerializer_58 == null)) { (this.__UrlSerializer_58 = import37.setupUrlSerializer(this._DeepLinkConfigToken_21)); } - return this.__UrlSerializer_58; - } - get _DeepLinker_59():any { - if ((this.__DeepLinker_59 == null)) { (this.__DeepLinker_59 = import38.setupDeepLinker(this._App_19,this._UrlSerializer_58,this._Location_57,this._ModuleLoader_24,this)); } - return this.__DeepLinker_59; - } - get _ModalController_60():import19.ModalController { - if ((this.__ModalController_60 == null)) { (this.__ModalController_60 = new import19.ModalController(this._App_19,this._Config_16,this._DeepLinker_59)); } - return this.__ModalController_60; - } - get _PickerController_61():import20.PickerController { - if ((this.__PickerController_61 == null)) { (this.__PickerController_61 = new import20.PickerController(this._App_19,this._Config_16)); } - return this.__PickerController_61; - } - get _PopoverController_62():import21.PopoverController { - if ((this.__PopoverController_62 == null)) { (this.__PopoverController_62 = new import21.PopoverController(this._App_19,this._Config_16,this._DeepLinker_59)); } - return this.__PopoverController_62; - } - get _TapClick_63():import22.TapClick { - if ((this.__TapClick_63 == null)) { (this.__TapClick_63 = new import22.TapClick(this._Config_16,this._Platform_15,this._DomController_17,this._App_19,this.parent.get(import0.NgZone),this._GestureController_20)); } - return this.__TapClick_63; - } - get _ToastController_64():import23.ToastController { - if ((this.__ToastController_64 == null)) { (this.__ToastController_64 = new import23.ToastController(this._App_19,this._Config_16)); } - return this.__ToastController_64; - } - get _TransitionController_65():import24.TransitionController { - if ((this.__TransitionController_65 == null)) { (this.__TransitionController_65 = new import24.TransitionController(this._Platform_15,this._Config_16)); } - return this.__TransitionController_65; - } - createInternal():import1.AppModule { - this._CommonModule_0 = new import2.CommonModule(); - this._ApplicationModule_1 = new import0.ApplicationModule(); - this._BrowserModule_2 = new import3.BrowserModule(this.parent.get(import3.BrowserModule,(null as any))); - this._ɵba_3 = new import4.ɵba(); - this._FormsModule_4 = new import4.FormsModule(); - this._ReactiveFormsModule_5 = new import4.ReactiveFormsModule(); - this._IonicModule_6 = new import5.IonicModule(); - this._IonicPageModule_7 = new import5.IonicPageModule(); - this._HomePageModule_8 = new import6.HomePageModule(); - this._AppModule_9 = new import1.AppModule(); - this._ErrorHandler_12 = import3.ɵa(); - this._ConfigToken_13 = {}; - this._PlatformConfigToken_14 = import39.providePlatformConfigs(); - this._Platform_15 = import40.setupPlatform(this.parent.get(import3.DOCUMENT),this._PlatformConfigToken_14,this.parent.get(import0.NgZone)); - this._Config_16 = import41.setupConfig(this._ConfigToken_13,this._Platform_15); - this._DomController_17 = new import7.DomController(this._Platform_15); - this._MenuController_18 = new import8.MenuController(); - this._App_19 = new import9.App(this._Config_16,this._Platform_15,this._MenuController_18); - this._GestureController_20 = new import10.GestureController(this._App_19); - this._DeepLinkConfigToken_21 = (null as any); - this._Compiler_22 = new import0.Compiler(); - this._NgModuleLoader_23 = new import11.NgModuleLoader(this._Compiler_22,this.parent.get(import11.NgModuleLoaderConfig,(null as any))); - this._ModuleLoader_24 = import42.provideModuleLoader(this._NgModuleLoader_23,this); - this._APP_INITIALIZER_25 = [ - import0.ɵp, - import3.ɵc(this.parent.get(import3.NgProbeToken,(null as any)),this.parent.get(import0.NgProbeToken,(null as any))), - import43.registerModeConfigs(this._Config_16), - import14.setupProvideEvents(this._Platform_15,this._DomController_17), - import22.setupTapClick(this._Config_16,this._Platform_15,this._DomController_17,this._App_19,this.parent.get(import0.NgZone),this._GestureController_20), - import42.setupPreloading(this._Config_16,this._DeepLinkConfigToken_21,this._ModuleLoader_24,this.parent.get(import0.NgZone)) - ] - ; - this._ApplicationInitStatus_26 = new import0.ApplicationInitStatus(this._APP_INITIALIZER_25); - this._ɵf_27 = new import0.ɵf(this.parent.get(import0.NgZone),this.parent.get(import0.ɵConsole),this,this._ErrorHandler_12,this,this._ApplicationInitStatus_26); - this._ɵDomSharedStylesHost_37 = new import3.ɵDomSharedStylesHost(this.parent.get(import3.DOCUMENT)); - return this._AppModule_9; - } - getInternal(token:any,notFoundResult:any):any { - if ((token === import2.CommonModule)) { return this._CommonModule_0; } - if ((token === import0.ApplicationModule)) { return this._ApplicationModule_1; } - if ((token === import3.BrowserModule)) { return this._BrowserModule_2; } - if ((token === import4.ɵba)) { return this._ɵba_3; } - if ((token === import4.FormsModule)) { return this._FormsModule_4; } - if ((token === import4.ReactiveFormsModule)) { return this._ReactiveFormsModule_5; } - if ((token === import5.IonicModule)) { return this._IonicModule_6; } - if ((token === import5.IonicPageModule)) { return this._IonicPageModule_7; } - if ((token === import6.HomePageModule)) { return this._HomePageModule_8; } - if ((token === import1.AppModule)) { return this._AppModule_9; } - if ((token === import0.LOCALE_ID)) { return this._LOCALE_ID_10; } - if ((token === import2.NgLocalization)) { return this._NgLocalization_11; } - if ((token === import0.ErrorHandler)) { return this._ErrorHandler_12; } - if ((token === import41.ConfigToken)) { return this._ConfigToken_13; } - if ((token === import39.PlatformConfigToken)) { return this._PlatformConfigToken_14; } - if ((token === import40.Platform)) { return this._Platform_15; } - if ((token === import41.Config)) { return this._Config_16; } - if ((token === import7.DomController)) { return this._DomController_17; } - if ((token === import8.MenuController)) { return this._MenuController_18; } - if ((token === import9.App)) { return this._App_19; } - if ((token === import10.GestureController)) { return this._GestureController_20; } - if ((token === import37.DeepLinkConfigToken)) { return this._DeepLinkConfigToken_21; } - if ((token === import0.Compiler)) { return this._Compiler_22; } - if ((token === import11.NgModuleLoader)) { return this._NgModuleLoader_23; } - if ((token === import42.ModuleLoader)) { return this._ModuleLoader_24; } - if ((token === import0.APP_INITIALIZER)) { return this._APP_INITIALIZER_25; } - if ((token === import0.ApplicationInitStatus)) { return this._ApplicationInitStatus_26; } - if ((token === import0.ɵf)) { return this._ɵf_27; } - if ((token === import0.ApplicationRef)) { return this._ApplicationRef_28; } - if ((token === import0.APP_ID)) { return this._APP_ID_29; } - if ((token === import0.IterableDiffers)) { return this._IterableDiffers_30; } - if ((token === import0.KeyValueDiffers)) { return this._KeyValueDiffers_31; } - if ((token === import3.DomSanitizer)) { return this._DomSanitizer_32; } - if ((token === import0.Sanitizer)) { return this._Sanitizer_33; } - if ((token === import3.HAMMER_GESTURE_CONFIG)) { return this._HAMMER_GESTURE_CONFIG_34; } - if ((token === import3.EVENT_MANAGER_PLUGINS)) { return this._EVENT_MANAGER_PLUGINS_35; } - if ((token === import3.EventManager)) { return this._EventManager_36; } - if ((token === import3.ɵDomSharedStylesHost)) { return this._ɵDomSharedStylesHost_37; } - if ((token === import3.ɵDomRendererFactoryV2)) { return this._ɵDomRendererFactoryV2_38; } - if ((token === import0.RendererFactoryV2)) { return this._RendererFactoryV2_39; } - if ((token === import3.ɵSharedStylesHost)) { return this._ɵSharedStylesHost_40; } - if ((token === import0.Testability)) { return this._Testability_41; } - if ((token === import3.Meta)) { return this._Meta_42; } - if ((token === import3.Title)) { return this._Title_43; } - if ((token === import4.ɵi)) { return this._ɵi_44; } - if ((token === import4.FormBuilder)) { return this._FormBuilder_45; } - if ((token === import42.LAZY_LOADED_TOKEN)) { return this._LAZY_LOADED_TOKEN_46; } - if ((token === import44.AppRootToken)) { return this._AppRootToken_47; } - if ((token === import2.APP_BASE_HREF)) { return this._APP_BASE_HREF_48; } - if ((token === import12.ActionSheetController)) { return this._ActionSheetController_49; } - if ((token === import13.AlertController)) { return this._AlertController_50; } - if ((token === import14.Events)) { return this._Events_51; } - if ((token === import15.Form)) { return this._Form_52; } - if ((token === import16.Haptic)) { return this._Haptic_53; } - if ((token === import17.Keyboard)) { return this._Keyboard_54; } - if ((token === import18.LoadingController)) { return this._LoadingController_55; } - if ((token === import2.LocationStrategy)) { return this._LocationStrategy_56; } - if ((token === import2.Location)) { return this._Location_57; } - if ((token === import37.UrlSerializer)) { return this._UrlSerializer_58; } - if ((token === import38.DeepLinker)) { return this._DeepLinker_59; } - if ((token === import19.ModalController)) { return this._ModalController_60; } - if ((token === import20.PickerController)) { return this._PickerController_61; } - if ((token === import21.PopoverController)) { return this._PopoverController_62; } - if ((token === import22.TapClick)) { return this._TapClick_63; } - if ((token === import23.ToastController)) { return this._ToastController_64; } - if ((token === import24.TransitionController)) { return this._TransitionController_65; } - return notFoundResult; - } - destroyInternal():void { - this._ɵf_27.ngOnDestroy(); - this._ɵDomSharedStylesHost_37.ngOnDestroy(); + constructor(public popoverCtrl: PopoverController) { } + + presentPopover(event: Event) { + let popover = this.popoverCtrl.create('PopoverPage'); + popover.present({ ev: event }); } } -export const AppModuleNgFactory:import0.NgModuleFactory = new import0.NgModuleFactory(AppModuleInjector,import1.AppModule); - `; - - const knownAppNgModulePath = join(process.cwd(), 'myApp', 'src', 'app.module.ts'); - const knownAppNgModuleFactoryPath = helpers.changeExtension(knownAppNgModulePath, '.ngfactory.ts'); - fileCache.set(knownAppNgModulePath, { path: knownAppNgModulePath, content: ngModuleContent}); - fileCache.set(knownAppNgModuleFactoryPath, { path: knownAppNgModuleFactoryPath, content: knownNgFactoryContent}); - spyOn(helpers, helpers.getStringPropertyValue.name).and.returnValue(knownAppNgModulePath); - spyOn(fileCache, 'get').and.callThrough(); - spyOn(transpile, transpile.transpileTsString.name).and.callFake((context: BuildContext, filePath: string, contentToTranspile: string) => { - return { - sourceMapText: 'sourceMapText', - outputText: contentToTranspile - }; - }); - - const changedFiles: ChangedFile[] = []; - util.updateAppNgModuleAndFactoryWithDeepLinkConfig(knownContext, knownDeepLinkString, changedFiles, true); - expect(fileCache.getAll().length).toEqual(6); - expect(fileCache.get(knownAppNgModulePath).content.indexOf(knownDeepLinkString)).toBeGreaterThanOrEqual(0); - expect(fileCache.get(helpers.changeExtension(knownAppNgModulePath, '.js')).content.indexOf(knownDeepLinkString)).toBeGreaterThanOrEqual(0); - expect(fileCache.get(helpers.changeExtension(knownAppNgModulePath, '.js.map')).content).toEqual('sourceMapText'); - expect(fileCache.get(knownAppNgModuleFactoryPath)).toBeTruthy(); - expect(fileCache.get(helpers.changeExtension(knownAppNgModuleFactoryPath, '.js'))).toBeTruthy(); - expect(fileCache.get(helpers.changeExtension(knownAppNgModuleFactoryPath, '.js.map'))).toBeTruthy(); - expect(changedFiles.length).toEqual(2); - expect(changedFiles[0].event).toEqual('change'); - expect(changedFiles[0].ext).toEqual('.ts'); - expect(changedFiles[0].filePath).toEqual(knownAppNgModulePath); - expect(changedFiles[1].event).toEqual('change'); - expect(changedFiles[1].ext).toEqual('.ts'); - expect(changedFiles[1].filePath).toEqual(knownAppNgModuleFactoryPath); +`; + const result = util.purgeDeepLinkDecorator(input); + expect(result).toEqual(expectedContent); }); }); }); diff --git a/src/deep-linking/util.ts b/src/deep-linking/util.ts index dbec69ca..46b6fb3f 100644 --- a/src/deep-linking/util.ts +++ b/src/deep-linking/util.ts @@ -3,24 +3,41 @@ import { basename, dirname, extname, relative } from 'path'; import { ArrayLiteralExpression, CallExpression, + ClassDeclaration, + createClassDeclaration, + createIdentifier, Decorator, Expression, Identifier, + ImportDeclaration, + ImportSpecifier, + NamedImports, Node, + NodeArray, ObjectLiteralExpression, PropertyAccessExpression, PropertyAssignment, SourceFile, - SyntaxKind + StringLiteral, + SyntaxKind, + TransformationContext, + TransformerFactory, + updateCall, + updateClassDeclaration, + updateSourceFile, + visitEachChild, + VisitResult } from 'typescript'; import { Logger } from '../logger/logger'; import * as Constants from '../util/constants'; import { FileCache } from '../util/file-cache'; -import { changeExtension, getStringPropertyValue, replaceAll, toUnixPath } from '../util/helpers'; +import { changeExtension, getParsedDeepLinkConfig, getStringPropertyValue, replaceAll, toUnixPath } from '../util/helpers'; import { BuildContext, ChangedFile, DeepLinkConfigEntry, DeepLinkDecoratorAndClass, DeepLinkPathInfo, File } from '../util/interfaces'; import { + NG_MODULE_DECORATOR_TEXT, appendAfter, + findNodes, getClassDeclarations, getNgModuleClassName, getNgModuleDecorator, @@ -32,10 +49,11 @@ import { import { transpileTsString } from '../transpile'; -export function getDeepLinkData(appNgModuleFilePath: string, fileCache: FileCache, isAot: boolean): DeepLinkConfigEntry[] { +export function getDeepLinkData(appNgModuleFilePath: string, fileCache: FileCache, isAot: boolean): Map { // we only care about analyzing a subset of typescript files, so do that for efficiency const typescriptFiles = filterTypescriptFilesForDeepLinks(fileCache); - const deepLinkConfigEntries: DeepLinkConfigEntry[] = []; + const deepLinkConfigEntries = new Map(); + const segmentSet = new Set(); typescriptFiles.forEach(file => { const sourceFile = getTypescriptSourceFile(file.path, file.content); const deepLinkDecoratorData = getDeepLinkDecoratorContentForSourceFile(sourceFile); @@ -44,7 +62,19 @@ export function getDeepLinkData(appNgModuleFilePath: string, fileCache: FileCach // sweet, the page has a DeepLinkDecorator, which means it meets the criteria to process that bad boy const pathInfo = getNgModuleDataFromPage(appNgModuleFilePath, file.path, deepLinkDecoratorData.className, fileCache, isAot); const deepLinkConfigEntry = Object.assign({}, deepLinkDecoratorData, pathInfo); - deepLinkConfigEntries.push(deepLinkConfigEntry); + + if (deepLinkConfigEntries.has(deepLinkConfigEntry.name)) { + // gadzooks, it's a duplicate name + throw new Error(`There are multiple entries in the deeplink config with the name of ${deepLinkConfigEntry.name}`); + } + + if (segmentSet.has(deepLinkConfigEntry.segment)) { + // gadzooks, it's a duplicate segment + throw new Error(`There are multiple entries in the deeplink config with the segment of ${deepLinkConfigEntry.segment}`); + } + + segmentSet.add(deepLinkConfigEntry.segment); + deepLinkConfigEntries.set(deepLinkConfigEntry.name, deepLinkConfigEntry); } }); return deepLinkConfigEntries; @@ -96,6 +126,7 @@ export function getDeepLinkDecoratorContentForSourceFile(sourceFile: SourceFile) classDeclaration.decorators.forEach(decorator => { const className = (classDeclaration.name as Identifier).text; if (decorator.expression && (decorator.expression as CallExpression).expression && ((decorator.expression as CallExpression).expression as Identifier).text === DEEPLINK_DECORATOR_TEXT) { + const deepLinkArgs = (decorator.expression as CallExpression).arguments; let deepLinkObject: ObjectLiteralExpression = null; if (deepLinkArgs && deepLinkArgs.length) { @@ -238,8 +269,11 @@ function getIonicModuleForRootCall(decorator: Decorator) { return ionicModuleFunctionCalls[0] as CallExpression; } -export function convertDeepLinkConfigEntriesToString(entries: DeepLinkConfigEntry[]) { - const individualLinks = entries.map(entry => convertDeepLinkEntryToJsObjectString(entry)); +export function convertDeepLinkConfigEntriesToString(entries: Map) { + const individualLinks: string[] = []; + entries.forEach(entry => { + individualLinks.push(convertDeepLinkEntryToJsObjectString(entry)); + }); const deepLinkConfigString = ` { @@ -256,7 +290,7 @@ export function convertDeepLinkEntryToJsObjectString(entry: DeepLinkConfigEntry) return `{ loadChildren: '${entry.userlandModulePath}${LOAD_CHILDREN_SEPARATOR}${entry.className}', name: '${entry.name}', segment: ${segmentString}, priority: '${entry.priority}', defaultHistory: [${defaultHistoryWithQuotes.join(', ')}] }`; } -export function updateAppNgModuleAndFactoryWithDeepLinkConfig(context: BuildContext, deepLinkString: string, changedFiles: ChangedFile[], isAot: boolean) { +export function updateAppNgModuleWithDeepLinkConfig(context: BuildContext, deepLinkString: string, changedFiles: ChangedFile[]) { const appNgModulePath = getStringPropertyValue(Constants.ENV_APP_NG_MODULE_PATH); const appNgModuleFile = context.fileCache.get(appNgModulePath); @@ -267,12 +301,6 @@ export function updateAppNgModuleAndFactoryWithDeepLinkConfig(context: BuildCont const updatedAppNgModuleContent = getUpdatedAppNgModuleContentWithDeepLinkConfig(appNgModulePath, appNgModuleFile.content, deepLinkString); context.fileCache.set(appNgModulePath, { path: appNgModulePath, content: updatedAppNgModuleContent}); - const appNgModuleOutput = transpileTsString(context, appNgModulePath, updatedAppNgModuleContent); - const appNgModuleSourceMapPath = changeExtension(appNgModulePath, '.js.map'); - const appNgModulePathJsFile = changeExtension(appNgModulePath, '.js'); - context.fileCache.set(appNgModuleSourceMapPath, { path: appNgModuleSourceMapPath, content: appNgModuleOutput.sourceMapText}); - context.fileCache.set(appNgModulePathJsFile, { path: appNgModulePathJsFile, content: appNgModuleOutput.outputText}); - if (changedFiles) { changedFiles.push({ event: 'change', @@ -280,30 +308,6 @@ export function updateAppNgModuleAndFactoryWithDeepLinkConfig(context: BuildCont ext: extname(appNgModulePath).toLowerCase() }); } - - if (isAot) { - const appNgModuleFactoryPath = changeExtension(appNgModulePath, '.ngfactory.ts'); - const appNgModuleFactoryFile = context.fileCache.get(appNgModuleFactoryPath); - if (!appNgModuleFactoryFile) { - throw new Error(`App NgModule Factory ${appNgModuleFactoryPath} not found in cache`); - } - const updatedAppNgModuleFactoryContent = getUpdatedAppNgModuleFactoryContentWithDeepLinksConfig(appNgModuleFactoryFile.content, deepLinkString); - context.fileCache.set(appNgModuleFactoryPath, { path: appNgModuleFactoryPath, content: updatedAppNgModuleFactoryContent}); - const appNgModuleFactoryOutput = transpileTsString(context, appNgModuleFactoryPath, updatedAppNgModuleFactoryContent); - - const appNgModuleFactorySourceMapPath = changeExtension(appNgModuleFactoryPath, '.js.map'); - const appNgModuleFactoryPathJsFile = changeExtension(appNgModuleFactoryPath, '.js'); - context.fileCache.set(appNgModuleFactorySourceMapPath, { path: appNgModuleFactorySourceMapPath, content: appNgModuleFactoryOutput.sourceMapText}); - context.fileCache.set(appNgModuleFactoryPathJsFile, { path: appNgModuleFactoryPathJsFile, content: appNgModuleFactoryOutput.outputText}); - - if (changedFiles) { - changedFiles.push({ - event: 'change', - filePath: appNgModuleFactoryPath, - ext: extname(appNgModuleFactoryPath).toLowerCase() - }); - } - } } export function getUpdatedAppNgModuleContentWithDeepLinkConfig(appNgModuleFilePath: string, appNgModuleFileContent: string, deepLinkStringContent: string) { @@ -373,7 +377,146 @@ export class ${className}Module {} `; } +export function purgeDeepLinkDecoratorTSTransform(): TransformerFactory { + return (transformContext: TransformationContext) => { + + function visitClassDeclaration(classDeclaration: ClassDeclaration) { + let hasDeepLinkDecorator = false; + const diffDecorators: Decorator[] = []; + for (const decorator of classDeclaration.decorators || []) { + if (decorator.expression && (decorator.expression as CallExpression).expression + && ((decorator.expression as CallExpression).expression as Identifier).escapedText === DEEPLINK_DECORATOR_TEXT) { + hasDeepLinkDecorator = true; + } else { + diffDecorators.push(decorator); + } + } + if (hasDeepLinkDecorator) { + return updateClassDeclaration( + classDeclaration, + diffDecorators, + classDeclaration.modifiers, + classDeclaration.name, + classDeclaration.typeParameters, + classDeclaration.heritageClauses, + classDeclaration.members + ); + } + + return classDeclaration; + } + + function visit(node: Node): VisitResult { + switch (node.kind) { + + case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(node as ClassDeclaration); + + default: + return visitEachChild(node, (node) => { + return visit(node); + }, transformContext); + } + } + + return (sourceFile: SourceFile) => { + return visit(sourceFile) as SourceFile; + }; + }; +} + +export function purgeDeepLinkDecorator(inputText: string): string { + const sourceFile = getTypescriptSourceFile('', inputText); + const classDeclarations = getClassDeclarations(sourceFile); + const toRemove: Node[] = []; + let toReturn: string = inputText; + for (const classDeclaration of classDeclarations) { + for (const decorator of classDeclaration.decorators || []) { + if (decorator.expression && (decorator.expression as CallExpression).expression + && ((decorator.expression as CallExpression).expression as Identifier).escapedText === DEEPLINK_DECORATOR_TEXT) { + toRemove.push(decorator); + } + } + } + toRemove.forEach(node => { + toReturn = replaceNode('', inputText, node, ''); + }); + return toReturn; +} + +export function getInjectDeepLinkConfigTypescriptTransform() { + const deepLinkString = convertDeepLinkConfigEntriesToString(getParsedDeepLinkConfig()); + const appNgModulePath = getStringPropertyValue(Constants.ENV_APP_NG_MODULE_PATH); + return injectDeepLinkConfigTypescriptTransform(deepLinkString, appNgModulePath); +} + +export function injectDeepLinkConfigTypescriptTransform(deepLinkString: string, appNgModuleFilePath: string): TransformerFactory { + + function visitDecoratorNode(decorator: Decorator, sourceFile: SourceFile): Decorator { + if (decorator.expression && (decorator.expression as CallExpression).expression && ((decorator.expression as CallExpression).expression as Identifier).escapedText === NG_MODULE_DECORATOR_TEXT) { + + // okay cool, we have the ng module + let functionCall = getIonicModuleForRootCall(decorator); + + const updatedArgs: any[] = functionCall.arguments as any as any[]; + + if (updatedArgs.length === 1) { + updatedArgs.push(createIdentifier('{ }')); + } + + if (updatedArgs.length === 2) { + updatedArgs.push(createIdentifier(deepLinkString)); + } + + functionCall = updateCall( + functionCall, + functionCall.expression, + functionCall.typeArguments, + updatedArgs + ); + + // loop over the parent elements and replace the IonicModule expression with ours' + + for (let i = 0; i < ((functionCall.parent as any).elements || []).length; i++) { + const element = (functionCall.parent as any).elements[i]; + if (element.king === SyntaxKind.CallExpression + && element.expression + && element.expression.expression + && element.expression.expression.escapedText === 'IonicModule' + ) { + (functionCall.parent as any).elements[i] = functionCall; + } + } + } + + return decorator; + } + + return (transformContext: TransformationContext) => { + + function visit(node: Node, sourceFile: SourceFile, sourceFilePath: string): VisitResult { + if (sourceFilePath !== appNgModuleFilePath) { + return node; + } + + switch (node.kind) { + case SyntaxKind.Decorator: + return visitDecoratorNode(node as Decorator, sourceFile); + + default: + return visitEachChild(node, (node) => { + return visit(node, sourceFile, sourceFilePath); + }, transformContext); + } + + } + + return (sourceFile: SourceFile) => { + return visit(sourceFile, sourceFile, sourceFile.fileName) as SourceFile; + }; + }; +} const DEEPLINK_DECORATOR_TEXT = 'IonicPage'; const DEEPLINK_DECORATOR_NAME_ATTRIBUTE = 'name'; diff --git a/src/ngc.ts b/src/ngc.ts index 161642e2..f3b9486c 100644 --- a/src/ngc.ts +++ b/src/ngc.ts @@ -1,14 +1,18 @@ import { RawSourceMap, SourceMapConsumer, SourceMapGenerator } from 'source-map'; import { buildOptimizer, purify } from '@angular-devkit/build-optimizer'; +import { AotCompiler } from './aot/aot-compiler'; +import { + convertDeepLinkConfigEntriesToString, + getUpdatedAppNgModuleContentWithDeepLinkConfig, + filterTypescriptFilesForDeepLinks, + purgeDeepLinkDecorator +} from './deep-linking/util'; import { Logger } from './logger/logger'; import { getUserConfigFile} from './util/config'; import * as Constants from './util/constants'; -import { changeExtension } from './util/helpers'; +import { changeExtension, getBooleanPropertyValue, getParsedDeepLinkConfig, getStringPropertyValue } from './util/helpers'; import { BuildContext, TaskInfo } from './util/interfaces'; -import { AotCompiler } from './aot/aot-compiler'; - - export function ngc(context: BuildContext, configFile?: string) { configFile = getUserConfigFile(context, taskInfo, configFile); @@ -25,7 +29,9 @@ export function ngc(context: BuildContext, configFile?: string) { } export function ngcWorker(context: BuildContext, configFile: string): Promise { - return runNgc(context, configFile); + return transformTsForDeepLinking(context).then(() => { + return runNgc(context, configFile); + }); } export function runNgc(context: BuildContext, configFile: string): Promise { @@ -38,6 +44,19 @@ export function runNgc(context: BuildContext, configFile: string): Promise return compiler.compile(); } +export function transformTsForDeepLinking(context: BuildContext) { + if (getBooleanPropertyValue(Constants.ENV_PARSE_DEEPLINKS)) { + const tsFiles = filterTypescriptFilesForDeepLinks(context.fileCache); + tsFiles.forEach(tsFile => { + tsFile.content = purgeDeepLinkDecorator(tsFile.content); + }); + const tsFile = context.fileCache.get(getStringPropertyValue(Constants.ENV_APP_NG_MODULE_PATH)); + const deepLinkString = convertDeepLinkConfigEntriesToString(getParsedDeepLinkConfig()); + tsFile.content = getUpdatedAppNgModuleContentWithDeepLinkConfig(tsFile.path, tsFile.content, deepLinkString); + } + return Promise.resolve(); +} + const taskInfo: TaskInfo = { fullArg: '--ngc', shortArg: '-n', diff --git a/src/preprocess.ts b/src/preprocess.ts index 84af97b5..76dd8559 100644 --- a/src/preprocess.ts +++ b/src/preprocess.ts @@ -6,7 +6,6 @@ import { BuildError } from './util/errors'; import { GlobResult, globAll } from './util/glob-util'; import { getBooleanPropertyValue, getStringPropertyValue } from './util/helpers'; import { BuildContext, ChangedFile } from './util/interfaces'; -import { deepLinking, deepLinkingUpdate } from './deep-linking'; import { bundleCoreComponents } from './core/bundle-components'; @@ -24,9 +23,9 @@ export function preprocess(context: BuildContext) { function preprocessWorker(context: BuildContext) { const bundlePromise = bundleCoreComponents(context); - const deepLinksPromise = getBooleanPropertyValue(Constants.ENV_PARSE_DEEPLINKS) ? deepLinking(context) : Promise.resolve(); + const componentSassPromise = lookUpDefaultIonicComponentPaths(context); - return Promise.all([bundlePromise, deepLinksPromise, componentSassPromise]); + return Promise.all([bundlePromise, componentSassPromise]); } export function preprocessUpdate(changedFiles: ChangedFile[], context: BuildContext) { @@ -36,10 +35,6 @@ export function preprocessUpdate(changedFiles: ChangedFile[], context: BuildCont promises.push(bundleCoreComponents(context)); } - if (getBooleanPropertyValue(Constants.ENV_PARSE_DEEPLINKS)) { - promises.push(deepLinkingUpdate(changedFiles, context)); - } - return Promise.all(promises); } diff --git a/src/transpile.ts b/src/transpile.ts index 2dcbe1fa..cd205a9a 100644 --- a/src/transpile.ts +++ b/src/transpile.ts @@ -1,19 +1,26 @@ -import * as Constants from './util/constants'; -import { FileCache } from './util/file-cache'; -import { BuildContext, BuildState, ChangedFile } from './util/interfaces'; -import { BuildError } from './util/errors'; -import { buildJsSourceMaps } from './bundle'; -import { changeExtension } from './util/helpers'; -import { EventEmitter } from 'events'; import { fork, ChildProcess } from 'child_process'; -import { inlineTemplate } from './template'; -import { Logger } from './logger/logger'; +import { EventEmitter } from 'events'; import { readFileSync } from 'fs'; -import { runTypeScriptDiagnostics } from './logger/logger-typescript'; -import { printDiagnostics, clearDiagnostics, DiagnosticsType } from './logger/logger-diagnostics'; import * as path from 'path'; + import * as ts from 'typescript'; +import { getInMemoryCompilerHostInstance } from './aot/compiler-host-factory'; +import { buildJsSourceMaps } from './bundle'; +import { + getInjectDeepLinkConfigTypescriptTransform, + purgeDeepLinkDecoratorTSTransform } +from './deep-linking/util'; + +import { Logger } from './logger/logger'; +import { printDiagnostics, clearDiagnostics, DiagnosticsType } from './logger/logger-diagnostics'; +import { runTypeScriptDiagnostics } from './logger/logger-typescript'; +import { inlineTemplate } from './template'; +import * as Constants from './util/constants'; +import { BuildError } from './util/errors'; +import { FileCache } from './util/file-cache'; +import { changeExtension, getBooleanPropertyValue } from './util/helpers'; +import { BuildContext, BuildState, ChangedFile } from './util/interfaces'; export function transpile(context: BuildContext) { @@ -99,14 +106,25 @@ export function transpileWorker(context: BuildContext, workerConfig: TranspileWo tsConfig.options.declaration = undefined; // let's start a new tsFiles object to cache all the transpiled files in - const host = ts.createCompilerHost(tsConfig.options); + const host = getInMemoryCompilerHostInstance(tsConfig.options); + // ts.createCompilerHost(tsConfig.options); const program = ts.createProgram(tsFileNames, tsConfig.options, host, cachedProgram); + + const beforeArray: ts.TransformerFactory[] = []; + + if (getBooleanPropertyValue(Constants.ENV_PARSE_DEEPLINKS)) { + beforeArray.push(purgeDeepLinkDecoratorTSTransform()); + beforeArray.push(getInjectDeepLinkConfigTypescriptTransform()); + } + program.emit(undefined, (path: string, data: string, writeByteOrderMark: boolean, onError: Function, sourceFiles: ts.SourceFile[]) => { if (workerConfig.writeInMemory) { writeSourceFiles(context.fileCache, sourceFiles); writeTranspiledFilesCallback(context.fileCache, path, data, workerConfig.inlineTemplate); } + }, null, false, { + before: beforeArray }); // cache the typescript program for later use @@ -159,10 +177,20 @@ function transpileUpdateWorker(event: string, filePath: string, context: BuildCo // build the ts source maps if the bundler is going to use source maps cachedTsConfig.options.sourceMap = buildJsSourceMaps(context); + const beforeArray: ts.TransformerFactory[] = []; + + if (getBooleanPropertyValue(Constants.ENV_PARSE_DEEPLINKS)) { + beforeArray.push(purgeDeepLinkDecoratorTSTransform()); + beforeArray.push(getInjectDeepLinkConfigTypescriptTransform()); + } + const transpileOptions: ts.TranspileOptions = { compilerOptions: cachedTsConfig.options, fileName: filePath, - reportDiagnostics: true + reportDiagnostics: true, + transformers: { + before: beforeArray + } }; // let's manually transpile just this one ts file diff --git a/src/util/helpers.ts b/src/util/helpers.ts index 54411406..60fad2c8 100644 --- a/src/util/helpers.ts +++ b/src/util/helpers.ts @@ -12,7 +12,7 @@ import { CAMEL_CASE_UPPER_REGEXP } from './helpers/camel-case-upper-regexp'; import { NON_WORD_REGEXP } from './helpers/non-word-regexp'; let _context: BuildContext; -let _parsedDeepLinkConfig: DeepLinkConfigEntry[]; +let _deepLinkConfigEntriesMap: Map; let cachedAppScriptsPackageJson: any; export function getAppScriptsPackageJson() { @@ -219,15 +219,6 @@ export function readDirAsync(pathToDir: string) { }); } -export function createFileObject(filePath: string): File { - const content = readFileSync(filePath).toString(); - return { - content: content, - path: filePath, - timestamp: Date.now() - }; -} - export function setContext(context: BuildContext) { _context = context; } @@ -236,12 +227,12 @@ export function getContext() { return _context; } -export function setParsedDeepLinkConfig(parsedDeepLinkConfig: DeepLinkConfigEntry[]) { - _parsedDeepLinkConfig = parsedDeepLinkConfig; +export function setParsedDeepLinkConfig(map: Map) { + _deepLinkConfigEntriesMap = map; } -export function getParsedDeepLinkConfig(): DeepLinkConfigEntry[] { - return _parsedDeepLinkConfig; +export function getParsedDeepLinkConfig(): Map { + return _deepLinkConfigEntriesMap; } export function transformSrcPathToTmpPath(originalPath: string, context: BuildContext) { diff --git a/src/util/typescript-utils.ts b/src/util/typescript-utils.ts index 426efba3..4c66f485 100644 --- a/src/util/typescript-utils.ts +++ b/src/util/typescript-utils.ts @@ -273,4 +273,4 @@ export function appendNgModuleExports(filePath: string, fileContent: string, dec } -const NG_MODULE_DECORATOR_TEXT = 'NgModule'; +export const NG_MODULE_DECORATOR_TEXT = 'NgModule'; diff --git a/src/webpack/ionic-environment-plugin.ts b/src/webpack/ionic-environment-plugin.ts index 202fe586..82a3abc1 100644 --- a/src/webpack/ionic-environment-plugin.ts +++ b/src/webpack/ionic-environment-plugin.ts @@ -105,10 +105,10 @@ export class IonicEnvironmentPlugin { } -export function convertDeepLinkConfigToWebpackFormat(parsedDeepLinkConfigs: DeepLinkConfigEntry[]) { +export function convertDeepLinkConfigToWebpackFormat(parsedDeepLinkConfigs: Map) { const dictionary: { [index: string]: string} = { }; if (!parsedDeepLinkConfigs) { - parsedDeepLinkConfigs = []; + parsedDeepLinkConfigs = new Map(); } parsedDeepLinkConfigs.forEach(parsedDeepLinkConfig => { if (parsedDeepLinkConfig.userlandModulePath && parsedDeepLinkConfig.absolutePath) { diff --git a/src/webpack/ionic-webpack-factory.ts b/src/webpack/ionic-webpack-factory.ts index cc469a0a..418546a6 100644 --- a/src/webpack/ionic-webpack-factory.ts +++ b/src/webpack/ionic-webpack-factory.ts @@ -8,11 +8,6 @@ export function getIonicEnvironmentPlugin() { return new IonicEnvironmentPlugin(context, true); } -export function getIonicOptimizationEnvironmentPlugin() { - const context = getContext(); - return new IonicEnvironmentPlugin(context, false); -} - export function getSourceMapperFunction(): Function { return provideCorrectSourcePath; }