1
- import { GenericAngularEnv } from '@bitdev/angular.dev-services.common' ;
2
- import { AngularPreview , BundlerProvider , DevServerProvider } from '@bitdev/angular.dev-services.preview.preview' ;
1
+ import { VERSION } from '@angular/cli' ;
2
+ import {
3
+ ApplicationOptions ,
4
+ GenericAngularEnv ,
5
+ normalizePath
6
+ } from '@bitdev/angular.dev-services.common' ;
7
+ import {
8
+ AngularPreview ,
9
+ BundlerProvider ,
10
+ DevServerProvider
11
+ } from '@bitdev/angular.dev-services.preview.preview' ;
3
12
import { AppBuildContext , AppContext , Application } from '@teambit/application' ;
4
13
import { Bundler , BundlerContext , DevServer , DevServerContext } from '@teambit/bundler' ;
5
14
import { Component } from '@teambit/component' ;
6
15
import { DependencyResolverMain } from '@teambit/dependency-resolver' ;
7
16
import { EnvContext , EnvHandler } from '@teambit/envs' ;
8
17
import { CACHE_ROOT } from '@teambit/legacy/dist/constants' ;
9
- import { pathNormalizeToLinux } from '@teambit/legacy/dist/utils ' ;
18
+ import { Logger } from '@teambit/logger ' ;
10
19
import { Preview } from '@teambit/preview' ;
11
20
import { Port } from '@teambit/toolbox.network.get-port' ;
12
21
import { Workspace } from '@teambit/workspace' ;
13
- import { existsSync , mkdirSync , writeFileSync } from 'fs-extra' ;
22
+ import assert from 'assert' ;
23
+ import { existsSync , mkdirSync , outputJsonSync } from 'fs-extra' ;
14
24
import { cloneDeep } from 'lodash' ;
15
25
import objectHash from 'object-hash' ;
16
26
import { join } from 'path' ;
17
27
import { readConfigFile , sys } from 'typescript' ;
18
28
import { AngularAppOptions } from './angular-app-options' ;
19
29
import { AngularAppBuildResult } from './angular-build-result' ;
20
- import { expandIncludeExclude } from './utils' ;
30
+ import { buildApplication } from './application.bundler' ;
31
+ import { serveApplication } from './application.dev-server' ;
32
+ import { expandIncludeExclude , JsonObject } from './utils' ;
21
33
22
34
const writeHash = new Map < string , string > ( ) ;
23
35
@@ -35,17 +47,18 @@ export class AngularApp implements Application {
35
47
private envContext : EnvContext ,
36
48
readonly options : AngularAppOptions ,
37
49
private depsResolver : DependencyResolverMain ,
50
+ private logger : Logger ,
38
51
private workspace ?: Workspace
39
52
) {
40
53
this . name = options . name ;
41
54
42
- const idName = `bitdev.angular/${ this . name } ` ;
55
+ const idName = `bitdev.angular/${ this . name } ` ;
43
56
this . tempFolder = workspace ?. getTempDir ( idName ) || join ( CACHE_ROOT , idName ) ;
44
57
if ( ! existsSync ( this . tempFolder ) ) {
45
58
mkdirSync ( this . tempFolder , { recursive : true } ) ;
46
59
}
47
60
48
- this . tsconfigPath = pathNormalizeToLinux ( join ( this . tempFolder , `__tsconfig -${ Date . now ( ) } .json` ) ) ;
61
+ this . tsconfigPath = normalizePath ( join ( this . tempFolder , `tsconfig/tsconfig -${ Date . now ( ) } .json` ) ) ;
49
62
this . preview = this . getPreview ( ) ;
50
63
}
51
64
@@ -55,19 +68,20 @@ export class AngularApp implements Application {
55
68
return join ( artifactsDir , this . name ) ;
56
69
}
57
70
58
- private getDevServerContext ( context : AppContext ) : DevServerContext {
71
+ private getDevServerContext ( context : AppContext , appRootPath : string ) : DevServerContext {
72
+ // const ngEnvOptions = this.angularEnv.getNgEnvOptions();
59
73
return Object . assign ( cloneDeep ( context ) , {
60
74
entry : [ ] ,
61
- rootPath : '' ,
62
- publicPath : `${ this . publicDir } /${ this . options . name } ` ,
75
+ rootPath : /*ngEnvOptions.devServer === 'vite' ? appRootPath : */ '' ,
76
+ publicPath : `${ this . publicDir } /${ this . options . name } ` ,
63
77
title : this . options . name
64
78
} ) ;
65
79
}
66
80
67
81
private getBundlerContext ( context : AppBuildContext ) : BundlerContext {
68
82
const { capsule, artifactsDir } = context ;
69
83
const publicDir = this . getPublicDir ( artifactsDir ) ;
70
- const outputPath = pathNormalizeToLinux ( join ( capsule . path , publicDir ) ) ;
84
+ const outputPath = normalizePath ( join ( capsule . path , publicDir ) ) ;
71
85
72
86
return Object . assign ( cloneDeep ( context ) , {
73
87
targets : [ {
@@ -76,7 +90,7 @@ export class AngularApp implements Application {
76
90
outputPath
77
91
} ] ,
78
92
entry : [ ] ,
79
- rootPath : '/ ' ,
93
+ rootPath : '. ' ,
80
94
appName : this . options . name
81
95
} ) ;
82
96
}
@@ -97,84 +111,115 @@ export class AngularApp implements Application {
97
111
} ) ;
98
112
}
99
113
100
- private generateTsConfig ( bitCmps : Component [ ] , appRootPath : string , tsconfigPath : string ) : string {
101
- const tsconfigJSON = readConfigFile ( tsconfigPath , sys . readFile ) . config ;
114
+ private generateTsConfig ( bitCmps : Component [ ] , appRootPath : string , tsconfigPath : string , serverEntry ? : string ) : void {
115
+ const tsconfigJSON : JsonObject = readConfigFile ( tsconfigPath , sys . readFile ) . config ;
102
116
103
117
// Add the paths to tsconfig to remap bit components to local folders
104
118
tsconfigJSON . compilerOptions . paths = tsconfigJSON . compilerOptions . paths || { } ;
105
119
bitCmps . forEach ( ( dep : Component ) => {
106
- let componentDir = this . workspace ?. componentDir ( dep . id , {
107
- ignoreVersion : true
108
- } ) ;
109
- if ( componentDir ) {
110
- componentDir = pathNormalizeToLinux ( componentDir ) ;
111
- const pkgName = this . depsResolver . getPackageName ( dep ) ;
112
- // TODO we should find a way to use the real entry file based on the component config because people can change it
113
- tsconfigJSON . compilerOptions . paths [ pkgName ] = [ `${ componentDir } /public-api.ts` , `${ componentDir } ` ] ;
114
- tsconfigJSON . compilerOptions . paths [ `${ pkgName } /*` ] = [ `${ componentDir } /*` ] ;
115
- }
120
+ let componentDir = this . workspace ?. componentDir ( dep . id , {
121
+ ignoreVersion : true
122
+ } ) ;
123
+ if ( componentDir ) {
124
+ componentDir = normalizePath ( componentDir ) ;
125
+ const pkgName = this . depsResolver . getPackageName ( dep ) ;
126
+ // TODO we should find a way to use the real entry file based on the component config because people can change it
127
+ tsconfigJSON . compilerOptions . paths [ pkgName ] = [ `${ componentDir } /public-api.ts` , `${ componentDir } ` ] ;
128
+ tsconfigJSON . compilerOptions . paths [ `${ pkgName } /*` ] = [ `${ componentDir } /*` ] ;
129
+ }
116
130
} ) ;
117
131
132
+ if ( serverEntry ) {
133
+ tsconfigJSON . files . push ( serverEntry ) ;
134
+ }
135
+
118
136
const tsconfigContent = expandIncludeExclude ( tsconfigJSON , this . tsconfigPath , [ appRootPath ] ) ;
119
137
const hash = objectHash ( tsconfigContent ) ;
120
-
121
138
// write only if link has changed (prevents triggering fs watches)
122
139
if ( writeHash . get ( this . tsconfigPath ) !== hash ) {
123
- writeFileSync ( this . tsconfigPath , tsconfigContent ) ;
140
+ outputJsonSync ( this . tsconfigPath , tsconfigContent , { spaces : 2 } ) ;
124
141
writeHash . set ( this . tsconfigPath , hash ) ;
125
142
}
143
+ }
144
+
145
+ async getDevServer ( context : AppContext , appRootPath : string ) : Promise < DevServer > {
146
+ const devServerContext = this . getDevServerContext ( context , appRootPath ) ;
147
+ const preview = this . preview ( this . envContext ) ;
126
148
127
- return tsconfigContent ;
149
+ return preview . getDevServer ( devServerContext ) ( this . envContext ) ;
128
150
}
129
151
130
- async getDevServer ( context : AppContext ) : Promise < DevServer > {
131
- if ( ! this . workspace ) {
132
- throw new Error ( ' workspace is not defined') ;
133
- }
152
+ // TODO: fix return type once bit has a new stable version
153
+ async run ( context : AppContext ) : Promise < any > {
154
+ assert ( this . workspace , 'Workspace is not defined') ;
155
+ const port = context . port || ( await Port . getPortFromRange ( this . options . portRange || [ 3000 , 4000 ] ) ) ;
134
156
const appRootPath = this . workspace . componentDir ( context . appComponent . id , {
135
157
ignoreVersion : true
136
- } ) || '' ;
158
+ } ) ;
137
159
const tsconfigPath = join ( appRootPath , this . options . angularServeOptions . tsConfig ) ;
138
160
const workspaceCmpsIDs = await this . workspace . listIds ( ) ;
139
161
const bitCmps = await this . workspace . getMany ( workspaceCmpsIDs ) ;
140
162
this . generateTsConfig ( bitCmps , appRootPath , tsconfigPath ) ;
141
- const devServerContext = this . getDevServerContext ( context ) ;
142
- const preview = this . preview ( this . envContext ) ;
143
163
144
- return preview . getDevServer ( devServerContext ) ( this . envContext ) ;
145
- }
164
+ if ( Number ( VERSION . major ) >= 16 ) {
165
+ await serveApplication ( {
166
+ angularOptions : {
167
+ ...this . options . angularBuildOptions as ApplicationOptions ,
168
+ tsConfig : this . tsconfigPath
169
+ } ,
170
+ sourceRoot : this . options . sourceRoot || 'src' ,
171
+ workspaceRoot : appRootPath ,
172
+ port,
173
+ logger : this . logger ,
174
+ tempFolder : this . tempFolder
175
+ } ) ;
176
+ return port ;
177
+ }
146
178
147
- async run ( context : AppContext ) : Promise < number > {
148
- const port = context . port || ( await Port . getPortFromRange ( this . options . portRange || [ 3000 , 4000 ] ) ) ;
149
- const devServer = await this . getDevServer ( context ) ;
179
+ const devServer = await this . getDevServer ( context , appRootPath ) ;
150
180
await devServer . listen ( port ) ;
151
181
return port ;
152
182
}
153
183
154
184
async getBundler ( context : AppBuildContext ) : Promise < Bundler > {
155
- if ( this . options . bundler && typeof this . options . bundler !== 'string' ) {
156
- return this . options . bundler as Bundler ;
185
+ if ( this . options . bundler ) {
186
+ return this . options . bundler ;
157
187
}
158
188
159
- if ( this . options . bundler === 'vite' ) {
160
- throw new Error ( 'implement vite bundler' ) ;
161
- }
162
-
163
- const { capsule } = context ;
164
- const appRootPath = capsule . path ;
165
- const tsconfigPath = join ( appRootPath , this . options . angularBuildOptions . tsConfig ) ;
166
- this . generateTsConfig ( [ capsule . component ] , appRootPath , tsconfigPath ) ;
167
189
const bundlerContext = this . getBundlerContext ( context ) ;
168
190
const preview = this . preview ( this . envContext ) ;
169
191
170
192
return preview . getBundler ( bundlerContext ) ( this . envContext ) ;
171
193
}
172
194
173
195
async build ( context : AppBuildContext ) : Promise < AngularAppBuildResult > {
174
- const bundler = await this . getBundler ( context ) ;
175
- await bundler . run ( ) ;
196
+ const { capsule } = context ;
197
+ const outputPath = this . getPublicDir ( context . artifactsDir ) ;
198
+ const appRootPath = capsule . path ;
199
+ const tsconfigPath = join ( appRootPath , this . options . angularBuildOptions . tsConfig ) ;
200
+ const appOptions = this . options . angularBuildOptions as ApplicationOptions ;
201
+ const entryServer = appOptions . ssr && Number ( VERSION . major ) >= 17 ? './entry.server.ts' : undefined ;
202
+ this . generateTsConfig ( [ capsule . component ] , appRootPath , tsconfigPath , entryServer ) ;
203
+
204
+ if ( ! this . options . bundler && Number ( VERSION . major ) >= 16 ) {
205
+ await buildApplication ( {
206
+ angularOptions : {
207
+ ...appOptions ,
208
+ tsConfig : this . tsconfigPath
209
+ } ,
210
+ outputPath,
211
+ sourceRoot : this . options . sourceRoot || 'src' ,
212
+ workspaceRoot : context . capsule . path ,
213
+ logger : this . logger ,
214
+ tempFolder : this . tempFolder ,
215
+ entryServer
216
+ } ) ;
217
+ } else {
218
+ const bundler = await this . getBundler ( context ) ;
219
+ await bundler . run ( ) ;
220
+ }
176
221
return {
177
- publicDir : ` ${ this . getPublicDir ( context . artifactsDir ) } / ${ this . publicDir } `
222
+ publicDir : outputPath
178
223
} ;
179
224
}
180
225
}
0 commit comments