Skip to content

Commit a0065ca

Browse files
committed
feat: add esbuild, ssr and interactive ng-app template
1 parent c90c789 commit a0065ca

File tree

104 files changed

+2113
-984
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+2113
-984
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ dist-legacy/
6161
flow-typed/
6262
eslint-report.html
6363
types/*
64+
.nitro
6465

6566
#distrubution
6667
distribution/

angular/app-types/angular-app-type/angular-app-options.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import { BrowserOptions, DevServerOptions } from '@bitdev/angular.dev-services.common';
1+
import {
2+
ApplicationOptions,
3+
BrowserOptions,
4+
DevServerOptions
5+
} from '@bitdev/angular.dev-services.common';
26
import { Bundler } from '@teambit/bundler';
37
import { WebpackConfigTransformer } from '@teambit/webpack';
48
import { AngularDeployContext } from './deploy-context';
59

6-
710
export type AngularAppOptions = {
811
/**
912
* Name of the application.
@@ -16,9 +19,9 @@ export type AngularAppOptions = {
1619
sourceRoot: string;
1720

1821
/**
19-
* Instance of bundler to use. default is Webpack.
22+
* Instance of bundler to use, default is esbuild after v17 and webpack before that.
2023
*/
21-
bundler?: Bundler | string;
24+
bundler?: Bundler;
2225

2326
/**
2427
* Set webpack build transformers
@@ -43,10 +46,10 @@ export type AngularAppOptions = {
4346
/**
4447
* Angular options for `bit build`
4548
*/
46-
angularBuildOptions: BrowserOptions;
49+
angularBuildOptions: BrowserOptions | ApplicationOptions;
4750

4851
/**
4952
* Angular options for `bit run`
5053
*/
51-
angularServeOptions: BrowserOptions & DevServerOptions;
54+
angularServeOptions: (BrowserOptions & DevServerOptions) | (ApplicationOptions & DevServerOptions);
5255
};

angular/app-types/angular-app-type/angular.app-type.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { GenericAngularEnv, getWorkspace, NG_APP_NAME } from '@bitdev/angular.de
22
import { Application, ApplicationType } from '@teambit/application';
33
import { DependencyResolverAspect, DependencyResolverMain } from '@teambit/dependency-resolver';
44
import { EnvContext, EnvHandler } from '@teambit/envs';
5+
import { Logger } from '@teambit/logger';
56
import { Workspace } from '@teambit/workspace';
67
import { AngularAppOptions } from './angular-app-options';
78
import { AngularApp } from './angular.application';
@@ -12,15 +13,17 @@ interface AngularAppTypeOptions {
1213
}
1314

1415
export class AngularAppType implements ApplicationType<AngularAppOptions> {
15-
constructor(readonly name: string, private angularEnv: GenericAngularEnv, private context: EnvContext, private depsResolver: DependencyResolverMain, private workspace?: Workspace) {}
16+
constructor(readonly name: string, private angularEnv: GenericAngularEnv, private context: EnvContext, private depsResolver: DependencyResolverMain, private logger: Logger, private workspace?: Workspace) {
17+
}
1618

1719
createApp(options: AngularAppOptions): Application {
1820
return new AngularApp(
1921
this.angularEnv,
2022
this.context,
2123
options,
2224
this.depsResolver,
23-
this.workspace,
25+
this.logger,
26+
this.workspace
2427
);
2528
}
2629

@@ -29,7 +32,8 @@ export class AngularAppType implements ApplicationType<AngularAppOptions> {
2932
const name = options.name || NG_APP_NAME;
3033
const depsResolver = context.getAspect<DependencyResolverMain>(DependencyResolverAspect.id);
3134
const workspace = getWorkspace(context);
32-
return new AngularAppType(name, options.angularEnv, context, depsResolver, workspace);
35+
const logger = context.createLogger(name);
36+
return new AngularAppType(name, options.angularEnv, context, depsResolver, logger, workspace);
3337
};
3438
}
3539
}
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,35 @@
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';
312
import { AppBuildContext, AppContext, Application } from '@teambit/application';
413
import { Bundler, BundlerContext, DevServer, DevServerContext } from '@teambit/bundler';
514
import { Component } from '@teambit/component';
615
import { DependencyResolverMain } from '@teambit/dependency-resolver';
716
import { EnvContext, EnvHandler } from '@teambit/envs';
817
import { CACHE_ROOT } from '@teambit/legacy/dist/constants';
9-
import { pathNormalizeToLinux } from '@teambit/legacy/dist/utils';
18+
import { Logger } from '@teambit/logger';
1019
import { Preview } from '@teambit/preview';
1120
import { Port } from '@teambit/toolbox.network.get-port';
1221
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';
1424
import { cloneDeep } from 'lodash';
1525
import objectHash from 'object-hash';
1626
import { join } from 'path';
1727
import { readConfigFile, sys } from 'typescript';
1828
import { AngularAppOptions } from './angular-app-options';
1929
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';
2133

2234
const writeHash = new Map<string, string>();
2335

@@ -35,17 +47,18 @@ export class AngularApp implements Application {
3547
private envContext: EnvContext,
3648
readonly options: AngularAppOptions,
3749
private depsResolver: DependencyResolverMain,
50+
private logger: Logger,
3851
private workspace?: Workspace
3952
) {
4053
this.name = options.name;
4154

42-
const idName = `bitdev.angular/${this.name}`;
55+
const idName = `bitdev.angular/${ this.name }`;
4356
this.tempFolder = workspace?.getTempDir(idName) || join(CACHE_ROOT, idName);
4457
if (!existsSync(this.tempFolder)) {
4558
mkdirSync(this.tempFolder, { recursive: true });
4659
}
4760

48-
this.tsconfigPath = pathNormalizeToLinux(join(this.tempFolder, `__tsconfig-${Date.now()}.json`));
61+
this.tsconfigPath = normalizePath(join(this.tempFolder, `tsconfig/tsconfig-${ Date.now() }.json`));
4962
this.preview = this.getPreview();
5063
}
5164

@@ -55,19 +68,20 @@ export class AngularApp implements Application {
5568
return join(artifactsDir, this.name);
5669
}
5770

58-
private getDevServerContext(context: AppContext): DevServerContext {
71+
private getDevServerContext(context: AppContext, appRootPath: string): DevServerContext {
72+
// const ngEnvOptions = this.angularEnv.getNgEnvOptions();
5973
return Object.assign(cloneDeep(context), {
6074
entry: [],
61-
rootPath: '',
62-
publicPath: `${this.publicDir}/${this.options.name}`,
75+
rootPath: /*ngEnvOptions.devServer === 'vite' ? appRootPath : */'',
76+
publicPath: `${ this.publicDir }/${ this.options.name }`,
6377
title: this.options.name
6478
});
6579
}
6680

6781
private getBundlerContext(context: AppBuildContext): BundlerContext {
6882
const { capsule, artifactsDir } = context;
6983
const publicDir = this.getPublicDir(artifactsDir);
70-
const outputPath = pathNormalizeToLinux(join(capsule.path, publicDir));
84+
const outputPath = normalizePath(join(capsule.path, publicDir));
7185

7286
return Object.assign(cloneDeep(context), {
7387
targets: [{
@@ -76,7 +90,7 @@ export class AngularApp implements Application {
7690
outputPath
7791
}],
7892
entry: [],
79-
rootPath: '/',
93+
rootPath: '.',
8094
appName: this.options.name
8195
});
8296
}
@@ -97,84 +111,115 @@ export class AngularApp implements Application {
97111
});
98112
}
99113

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;
102116

103117
// Add the paths to tsconfig to remap bit components to local folders
104118
tsconfigJSON.compilerOptions.paths = tsconfigJSON.compilerOptions.paths || {};
105119
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+
}
116130
});
117131

132+
if (serverEntry) {
133+
tsconfigJSON.files.push(serverEntry);
134+
}
135+
118136
const tsconfigContent = expandIncludeExclude(tsconfigJSON, this.tsconfigPath, [appRootPath]);
119137
const hash = objectHash(tsconfigContent);
120-
121138
// write only if link has changed (prevents triggering fs watches)
122139
if (writeHash.get(this.tsconfigPath) !== hash) {
123-
writeFileSync(this.tsconfigPath, tsconfigContent);
140+
outputJsonSync(this.tsconfigPath, tsconfigContent, { spaces: 2 });
124141
writeHash.set(this.tsconfigPath, hash);
125142
}
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);
126148

127-
return tsconfigContent;
149+
return preview.getDevServer(devServerContext)(this.envContext);
128150
}
129151

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]));
134156
const appRootPath = this.workspace.componentDir(context.appComponent.id, {
135157
ignoreVersion: true
136-
}) || '';
158+
});
137159
const tsconfigPath = join(appRootPath, this.options.angularServeOptions.tsConfig);
138160
const workspaceCmpsIDs = await this.workspace.listIds();
139161
const bitCmps = await this.workspace.getMany(workspaceCmpsIDs);
140162
this.generateTsConfig(bitCmps, appRootPath, tsconfigPath);
141-
const devServerContext = this.getDevServerContext(context);
142-
const preview = this.preview(this.envContext);
143163

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+
}
146178

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);
150180
await devServer.listen(port);
151181
return port;
152182
}
153183

154184
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;
157187
}
158188

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);
167189
const bundlerContext = this.getBundlerContext(context);
168190
const preview = this.preview(this.envContext);
169191

170192
return preview.getBundler(bundlerContext)(this.envContext);
171193
}
172194

173195
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+
}
176221
return {
177-
publicDir: `${this.getPublicDir(context.artifactsDir)}/${this.publicDir}`
222+
publicDir: outputPath
178223
};
179224
}
180225
}

0 commit comments

Comments
 (0)