11import { VERSION } from '@angular/cli' ;
22import {
33 ApplicationOptions ,
4- GenericAngularEnv ,
4+ getWorkspace ,
5+ NG_APP_NAME ,
56 normalizePath
67} from '@bitdev/angular.dev-services.common' ;
8+ import { AngularPreview } from '@bitdev/angular.dev-services.preview.preview' ;
79import {
8- AngularPreview ,
9- BundlerProvider ,
10- DevServerProvider
11- } from '@bitdev/angular.dev-services.preview.preview' ;
12- import { AppBuildContext , AppContext , Application } from '@teambit/application' ;
13- import { Bundler , BundlerContext , DevServer , DevServerContext } from '@teambit/bundler' ;
10+ AppBuildContext ,
11+ AppContext ,
12+ Application ,
13+ ApplicationInstance
14+ } from '@teambit/application' ;
15+ import { Bundler , BundlerContext , DevServerContext } from '@teambit/bundler' ;
1416import { Component } from '@teambit/component' ;
15- import { DependencyResolverMain } from '@teambit/dependency-resolver' ;
17+ import { DependencyResolverAspect , DependencyResolverMain } from '@teambit/dependency-resolver' ;
1618import { EnvContext , EnvHandler } from '@teambit/envs' ;
1719import { CACHE_ROOT } from '@teambit/legacy/dist/constants' ;
18- import { Logger } from '@teambit/logger' ;
1920import { Preview } from '@teambit/preview' ;
2021import { Port } from '@teambit/toolbox.network.get-port' ;
2122import { Workspace } from '@teambit/workspace' ;
@@ -35,34 +36,28 @@ const writeHash = new Map<string, string>();
3536
3637export class AngularApp implements Application {
3738 readonly name : string ;
38-
39- readonly preview : EnvHandler < Preview > ;
40-
41- readonly tempFolder : string ;
42-
43- readonly tsconfigPath : string ;
39+ readonly idName : string ;
4440
4541 constructor (
46- private angularEnv : GenericAngularEnv ,
47- private envContext : EnvContext ,
48- readonly options : AngularAppOptions ,
49- private depsResolver : DependencyResolverMain ,
50- private logger : Logger ,
51- private workspace ?: Workspace
42+ readonly options : AngularAppOptions
5243 ) {
53- this . name = options . name ;
44+ this . name = options . name || NG_APP_NAME ;
45+ this . idName = `bitdev.angular/${ this . name } ` ;
46+ }
5447
55- const idName = `bitdev.angular/${ this . name } ` ;
56- this . tempFolder = workspace ?. getTempDir ( idName ) || join ( CACHE_ROOT , idName ) ;
57- if ( ! existsSync ( this . tempFolder ) ) {
58- mkdirSync ( this . tempFolder , { recursive : true } ) ;
59- }
48+ readonly publicDir = 'public' ;
6049
61- this . tsconfigPath = normalizePath ( join ( this . tempFolder , `tsconfig/tsconfig-${ Date . now ( ) } .json` ) ) ;
62- this . preview = this . getPreview ( ) ;
50+ private getTempFolder ( workspace ?: Workspace ) : string {
51+ const tempFolder = workspace ?. getTempDir ( this . idName ) || join ( CACHE_ROOT , this . idName ) ;
52+ if ( ! existsSync ( tempFolder ) ) {
53+ mkdirSync ( tempFolder , { recursive : true } ) ;
54+ }
55+ return tempFolder ;
6356 }
6457
65- readonly publicDir = 'public' ;
58+ private getTsconfigPath ( tempFolder : string ) : string {
59+ return normalizePath ( join ( tempFolder , `tsconfig/tsconfig-${ Date . now ( ) } .json` ) ) ;
60+ }
6661
6762 private getPublicDir ( artifactsDir : string ) {
6863 return join ( artifactsDir , this . name ) ;
@@ -95,35 +90,33 @@ export class AngularApp implements Application {
9590 } ) ;
9691 }
9792
98- private getPreview ( ) : EnvHandler < Preview > {
99- const ngEnvOptions = this . angularEnv . getNgEnvOptions ( ) ;
100-
101- const angularServeOptions : any = Object . assign ( cloneDeep ( this . options . angularServeOptions ) , { tsConfig : this . tsconfigPath } ) ;
102- const angularBuildOptions : any = Object . assign ( cloneDeep ( this . options . angularBuildOptions ) , { tsConfig : this . tsconfigPath } ) ;
93+ private getPreview ( tsconfigPath : string ) : EnvHandler < Preview > {
94+ const angularServeOptions : any = Object . assign ( cloneDeep ( this . options . angularServeOptions ) , { tsConfig : tsconfigPath } ) ;
95+ const angularBuildOptions : any = Object . assign ( cloneDeep ( this . options . angularBuildOptions ) , { tsConfig : tsconfigPath } ) ;
10396
10497 return AngularPreview . from ( {
10598 webpackServeTransformers : this . options . webpackServeTransformers ,
10699 webpackBuildTransformers : this . options . webpackBuildTransformers ,
107100 angularServeOptions,
108101 angularBuildOptions,
109- ngEnvOptions,
102+ ngEnvOptions : this . options . ngEnvOptions ,
110103 sourceRoot : this . options . sourceRoot ,
111104 } ) ;
112105
113106 }
114107
115- private generateTsConfig ( bitCmps : Component [ ] , appRootPath : string , tsconfigPath : string , serverEntry ?: string ) : void {
116- const tsconfigJSON : JsonObject = readConfigFile ( tsconfigPath , sys . readFile ) . config ;
108+ private generateTsConfig ( bitCmps : Component [ ] , appRootPath : string , appTsconfigPath : string , tsconfigPath : string , depsResolver : DependencyResolverMain , workspace ?: Workspace , serverEntry ?: string ) : void {
109+ const tsconfigJSON : JsonObject = readConfigFile ( appTsconfigPath , sys . readFile ) . config ;
117110
118111 // Add the paths to tsconfig to remap bit components to local folders
119112 tsconfigJSON . compilerOptions . paths = tsconfigJSON . compilerOptions . paths || { } ;
120113 bitCmps . forEach ( ( dep : Component ) => {
121- let componentDir = this . workspace ?. componentDir ( dep . id , {
114+ let componentDir = workspace ?. componentDir ( dep . id , {
122115 ignoreVersion : true
123116 } ) ;
124117 if ( componentDir ) {
125118 componentDir = normalizePath ( componentDir ) ;
126- const pkgName = this . depsResolver . getPackageName ( dep ) ;
119+ const pkgName = depsResolver . getPackageName ( dep ) ;
127120 // TODO we should find a way to use the real entry file based on the component config because people can change it
128121 if ( existsSync ( join ( componentDir , 'public-api.ts' ) ) ) {
129122 tsconfigJSON . compilerOptions . paths [ pkgName ] = [ `${ componentDir } /public-api.ts` , `${ componentDir } ` ] ;
@@ -136,93 +129,114 @@ export class AngularApp implements Application {
136129 tsconfigJSON . files . push ( serverEntry ) ;
137130 }
138131
139- const tsconfigContent = expandIncludeExclude ( tsconfigJSON , this . tsconfigPath , [ appRootPath ] ) ;
132+ const tsconfigContent = expandIncludeExclude ( tsconfigJSON , tsconfigPath , [ appRootPath ] ) ;
140133 const hash = objectHash ( tsconfigContent ) ;
141134 // write only if link has changed (prevents triggering fs watches)
142- if ( writeHash . get ( this . tsconfigPath ) !== hash ) {
143- outputJsonSync ( this . tsconfigPath , tsconfigContent , { spaces : 2 } ) ;
144- writeHash . set ( this . tsconfigPath , hash ) ;
135+ if ( writeHash . get ( tsconfigPath ) !== hash ) {
136+ outputJsonSync ( tsconfigPath , tsconfigContent , { spaces : 2 } ) ;
137+ writeHash . set ( tsconfigPath , hash ) ;
145138 }
146139 }
147140
148- async getDevServer ( context : AppContext , appRootPath : string ) : Promise < DevServer > {
149- const devServerContext = this . getDevServerContext ( context , appRootPath ) ;
150- const preview = this . preview ( this . envContext ) ;
151-
152- return preview . getDevServer ( devServerContext ) ( this . envContext ) ;
141+ /**
142+ * Transform the app context into env context to make typescript happy.
143+ * Technically, we only use methods that exist in both interfaces, so it's fine.
144+ */
145+ private getEnvContext ( context : AppContext | AppBuildContext ) : EnvContext {
146+ return context as any as EnvContext ;
153147 }
154148
155149 // TODO: fix return type once bit has a new stable version
156- async run ( context : AppContext ) : Promise < any > {
157- assert ( this . workspace , 'Workspace is not defined' ) ;
150+ async run ( context : AppContext ) : Promise < ApplicationInstance > {
151+ const depsResolver = context . getAspect < DependencyResolverMain > ( DependencyResolverAspect . id ) ;
152+ assert ( depsResolver , 'Dependency resolver is not defined' ) ;
153+ const workspace = getWorkspace ( context ) ;
154+ assert ( workspace , 'Workspace is not defined' ) ;
155+ const logger = context . createLogger ( this . name ) ;
158156 const port = context . port || ( await Port . getPortFromRange ( this . options . portRange || [ 3000 , 4000 ] ) ) ;
159- const appRootPath = this . workspace . componentDir ( context . appComponent . id , {
157+ const appRootPath = workspace . componentDir ( context . appComponent . id , {
160158 ignoreVersion : true
161159 } ) ;
162- const tsconfigPath = join ( appRootPath , this . options . angularServeOptions . tsConfig ) ;
163- const workspaceCmpsIDs = await this . workspace . listIds ( ) ;
164- const bitCmps = await this . workspace . getMany ( workspaceCmpsIDs ) ;
165- this . generateTsConfig ( bitCmps , appRootPath , tsconfigPath ) ;
160+ const appTsconfigPath = join ( appRootPath , this . options . angularServeOptions . tsConfig ) ;
161+ const workspaceCmpsIDs = await workspace . listIds ( ) ;
162+ const bitCmps = await workspace . getMany ( workspaceCmpsIDs ) ;
163+ const tempFolder = this . getTempFolder ( workspace ) ;
164+ const tsconfigPath = this . getTsconfigPath ( tempFolder ) ;
165+ this . generateTsConfig ( bitCmps , appRootPath , appTsconfigPath , tsconfigPath , depsResolver , workspace ) ;
166166
167167 if ( Number ( VERSION . major ) >= 16 ) {
168168 await serveApplication ( {
169169 angularOptions : {
170170 ...this . options . angularBuildOptions as ApplicationOptions ,
171- tsConfig : this . tsconfigPath
171+ tsConfig : tsconfigPath
172172 } ,
173173 sourceRoot : this . options . sourceRoot || 'src' ,
174174 workspaceRoot : appRootPath ,
175175 port,
176- logger : this . logger ,
177- tempFolder : this . tempFolder
176+ logger : logger ,
177+ tempFolder : tempFolder
178178 } ) ;
179- return port ;
180- }
181-
182- const devServer = await this . getDevServer ( context , appRootPath ) ;
183- await devServer . listen ( port ) ;
184- return port ;
185- }
179+ } else {
180+ const devServerContext = this . getDevServerContext ( context , appRootPath ) ;
181+ const envContext = this . getEnvContext ( context ) ;
182+ const preview = this . getPreview ( tsconfigPath ) ( envContext ) ;
186183
187- async getBundler ( context : AppBuildContext ) : Promise < Bundler > {
188- if ( this . options . bundler ) {
189- return this . options . bundler ;
184+ const devServer = await preview . getDevServer ( devServerContext ) ( envContext ) ;
185+ await devServer . listen ( port ) ;
190186 }
191187
192- const bundlerContext = this . getBundlerContext ( context ) ;
193- const preview = this . preview ( this . envContext ) ;
194-
195- return preview . getBundler ( bundlerContext ) ( this . envContext ) ;
188+ return {
189+ appName : this . name ,
190+ port
191+ } ;
196192 }
197193
198194 async build ( context : AppBuildContext ) : Promise < AngularAppBuildResult > {
199195 const { capsule } = context ;
196+ const depsResolver = context . getAspect < DependencyResolverMain > ( DependencyResolverAspect . id ) ;
197+ assert ( depsResolver , 'Dependency resolver is not defined' ) ;
198+ const logger = context . createLogger ( this . name ) ;
200199 const outputPath = this . getPublicDir ( context . artifactsDir ) ;
201200 const appRootPath = capsule . path ;
202- const tsconfigPath = join ( appRootPath , this . options . angularBuildOptions . tsConfig ) ;
201+ const appTsconfigPath = join ( appRootPath , this . options . angularBuildOptions . tsConfig ) ;
203202 const appOptions = this . options . angularBuildOptions as ApplicationOptions ;
204203 const entryServer = appOptions . ssr && Number ( VERSION . major ) >= 17 ? './entry.server.ts' : undefined ;
205- this . generateTsConfig ( [ capsule . component ] , appRootPath , tsconfigPath , entryServer ) ;
204+ const tempFolder = this . getTempFolder ( ) ;
205+ const tsconfigPath = this . getTsconfigPath ( tempFolder ) ;
206+ this . generateTsConfig ( [ capsule . component ] , appRootPath , appTsconfigPath , tsconfigPath , depsResolver , undefined , entryServer ) ;
206207
207208 if ( ! this . options . bundler && Number ( VERSION . major ) >= 16 ) {
208209 await buildApplication ( {
209210 angularOptions : {
210211 ...appOptions ,
211- tsConfig : this . tsconfigPath
212+ tsConfig : tsconfigPath
212213 } ,
213214 outputPath,
214215 sourceRoot : this . options . sourceRoot || 'src' ,
215216 workspaceRoot : context . capsule . path ,
216- logger : this . logger ,
217- tempFolder : this . tempFolder ,
217+ logger : logger ,
218+ tempFolder : tempFolder ,
218219 entryServer
219220 } ) ;
220221 } else {
221- const bundler = await this . getBundler ( context ) ;
222+ let bundler : Bundler ;
223+ if ( this . options . bundler ) {
224+ bundler = this . options . bundler ;
225+ } else {
226+ const bundlerContext = this . getBundlerContext ( context ) ;
227+ const envContext = this . getEnvContext ( context ) ;
228+ const preview = this . getPreview ( tsconfigPath ) ( envContext ) ;
229+
230+ bundler = await preview . getBundler ( bundlerContext ) ( envContext ) ;
231+ }
222232 await bundler . run ( ) ;
223233 }
224234 return {
225235 publicDir : outputPath
226236 } ;
227237 }
238+
239+ static from ( options : AngularAppOptions ) : Application {
240+ return new AngularApp ( options ) ;
241+ }
228242}
0 commit comments