Skip to content

Commit b36b67c

Browse files
filipesilvahansl
authored andcommitted
feat(@angular-devkit/core): add default project
Partially address #10352
1 parent 8f9e242 commit b36b67c

File tree

7 files changed

+90
-88
lines changed

7 files changed

+90
-88
lines changed

packages/angular_devkit/architect/src/architect.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export class Architect {
188188
}
189189

190190
const builderConfiguration: BuilderConfiguration<OptionsT> = {
191-
root: project.root,
191+
root: project.root as Path,
192192
projectType: project.projectType,
193193
builder: target.builder,
194194
options: {

packages/angular_devkit/core/src/workspace/workspace-schema.json

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
"description": "New project root.",
1818
"default": "./"
1919
},
20+
"defaultProject": {
21+
"type": "string",
22+
"description": "The default project."
23+
},
2024
"cli": {
2125
"$ref": "#/definitions/tool",
2226
"default": {}

packages/angular_devkit/core/src/workspace/workspace-schema.ts

+13-39
Original file line numberDiff line numberDiff line change
@@ -19,47 +19,33 @@ export interface WorkspaceSchema {
1919
* New project root.
2020
*/
2121
newProjectRoot?: string;
22+
/**
23+
* The default project.
24+
*/
25+
defaultProject?: string;
2226
/**
2327
* Tool options.
2428
*/
25-
cli?: {
26-
/**
27-
* Link to schema.
28-
*/
29-
$schema?: string;
30-
[k: string]: any;
31-
};
29+
cli?: WorkspaceTool;
3230
/**
3331
* Tool options.
3432
*/
35-
schematics?: {
36-
/**
37-
* Link to schema.
38-
*/
39-
$schema?: string;
40-
[k: string]: any;
41-
};
33+
schematics?: WorkspaceTool;
4234
/**
4335
* Tool options.
4436
*/
45-
architect?: {
46-
/**
47-
* Link to schema.
48-
*/
49-
$schema?: string;
50-
[k: string]: any;
51-
};
37+
architect?: WorkspaceTool;
5238
/**
5339
* A map of project names to project options.
5440
*/
5541
projects: {
56-
[k: string]: Project;
42+
[k: string]: WorkspaceProject;
5743
};
5844
}
5945
/**
6046
* Project options.
6147
*/
62-
export interface Project {
48+
export interface WorkspaceProject {
6349
/**
6450
* Project type.
6551
*/
@@ -75,32 +61,20 @@ export interface Project {
7561
/**
7662
* Tool options.
7763
*/
78-
cli?: {
79-
/**
80-
* Link to schema.
81-
*/
82-
$schema?: string;
83-
[k: string]: any;
84-
};
64+
cli?: WorkspaceTool;
8565
/**
8666
* Tool options.
8767
*/
88-
schematics?: {
89-
/**
90-
* Link to schema.
91-
*/
92-
$schema?: string;
93-
[k: string]: any;
94-
};
68+
schematics?: WorkspaceTool;
9569
/**
9670
* Tool options.
9771
*/
98-
architect?: Architect;
72+
architect?: WorkspaceTool;
9973
}
10074
/**
10175
* Architect options.
10276
*/
103-
export interface Architect {
77+
export interface WorkspaceTool {
10478
/**
10579
* Link to schema.
10680
*/

packages/angular_devkit/core/src/workspace/workspace.ts

+18-24
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ import {
1919
virtualFs,
2020
} from '..';
2121
import { BaseException } from '../exception/exception';
22-
// Note: importing BaseException from '..' seems to lead to odd circular dependency errors.
23-
// TypeError: Class extends value undefined is not a constructor or null
24-
// at Object.<anonymous> (<path>\packages\angular_devkit\core\src\workspace\workspace.ts:19:44)
22+
import { WorkspaceProject, WorkspaceSchema, WorkspaceTool } from './workspace-schema';
2523

2624

2725
export class ProjectNotFoundException extends BaseException {
@@ -46,30 +44,11 @@ export class WorkspaceNotYetLoadedException extends BaseException {
4644
constructor() { super(`Workspace needs to be loaded before it is used.`); }
4745
}
4846

49-
export interface WorkspaceJson {
50-
version: number;
51-
// TODO: figure out if newProjectRoot should stay here.
52-
newProjectRoot: Path;
53-
cli: WorkspaceTool;
54-
schematics: WorkspaceTool;
55-
architect: WorkspaceTool;
56-
projects: { [k: string]: WorkspaceProject };
57-
}
58-
59-
export interface WorkspaceProject {
60-
projectType: 'application' | 'library';
61-
root: Path;
62-
cli: WorkspaceTool;
63-
schematics: WorkspaceTool;
64-
architect: WorkspaceTool;
65-
}
66-
67-
export interface WorkspaceTool extends JsonObject { }
6847

6948
export class Workspace {
7049
private readonly _workspaceSchemaPath = join(normalize(__dirname), 'workspace-schema.json');
7150
private _workspaceSchema: JsonObject;
72-
private _workspace: WorkspaceJson;
51+
private _workspace: WorkspaceSchema;
7352
private _registry: schema.CoreSchemaRegistry;
7453

7554
constructor(private _root: Path, private _host: virtualFs.Host<{}>) {
@@ -79,7 +58,7 @@ export class Workspace {
7958
loadWorkspaceFromJson(json: {}) {
8059
return this._loadWorkspaceSchema().pipe(
8160
concatMap((workspaceSchema) => this.validateAgainstSchema(json, workspaceSchema)),
82-
tap((validatedWorkspace: WorkspaceJson) => this._workspace = validatedWorkspace),
61+
tap((validatedWorkspace: WorkspaceSchema) => this._workspace = validatedWorkspace),
8362
map(() => this),
8463
);
8564
}
@@ -149,6 +128,21 @@ export class Workspace {
149128
};
150129
}
151130

131+
getDefaultProject() {
132+
this._assertLoaded();
133+
134+
if (this._workspace.defaultProject) {
135+
// If there is a default project name, return it.
136+
return this.getProject(this._workspace.defaultProject);
137+
} else if (this.listProjectNames().length === 1) {
138+
// If there is only one project, return that one.
139+
return this.getProject(this.listProjectNames()[0]);
140+
}
141+
142+
// Otherwise return null.
143+
return null;
144+
}
145+
152146
getCli() {
153147
return this._getTool('cli');
154148
}

packages/angular_devkit/core/src/workspace/workspace_spec.ts

+41-15
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@ import {
1414
ProjectNotFoundException,
1515
Workspace,
1616
WorkspaceNotYetLoadedException,
17-
WorkspaceProject,
1817
} from './workspace';
18+
import { WorkspaceProject, WorkspaceSchema, WorkspaceTool } from './workspace-schema';
1919

2020

2121
describe('Workspace', () => {
2222
const host = new NodeJsSyncHost();
2323
const root = normalize(__dirname);
2424
// The content of this JSON object should be kept in sync with the path below:
2525
// tests/@angular_devkit/workspace/angular-workspace.json
26-
const workspaceJson = {
26+
const workspaceJson: WorkspaceSchema = {
2727
version: 1,
2828
newProjectRoot: './projects',
29+
defaultProject: 'app',
2930
cli: {
3031
'$globalOverride': '${HOME}/.angular-cli.json',
3132
'schematics': {
@@ -98,6 +99,13 @@ describe('Workspace', () => {
9899
},
99100
},
100101
};
102+
const appProject = {
103+
...workspaceJson.projects['app'],
104+
// Tools should not be returned when getting a project.
105+
cli: {},
106+
schematics: {},
107+
architect: {},
108+
} as {} as WorkspaceProject;
101109

102110
it('loads workspace from json', (done) => {
103111
const workspace = new Workspace(root, host);
@@ -168,13 +176,7 @@ describe('Workspace', () => {
168176
it('gets project by name', (done) => {
169177
const workspace = new Workspace(root, host);
170178
workspace.loadWorkspaceFromJson(workspaceJson).pipe(
171-
tap((ws) => expect(ws.getProject('app')).toEqual({
172-
...workspaceJson.projects['app'],
173-
// Tools should not be returned when getting a project.
174-
cli: {},
175-
schematics: {},
176-
architect: {},
177-
} as {} as WorkspaceProject)),
179+
tap((ws) => expect(ws.getProject('app')).toEqual(appProject)),
178180
).subscribe(undefined, done.fail, done);
179181
});
180182

@@ -185,47 +187,71 @@ describe('Workspace', () => {
185187
).subscribe(undefined, done.fail, done);
186188
});
187189

190+
it('gets default project', (done) => {
191+
const workspace = new Workspace(root, host);
192+
workspace.loadWorkspaceFromJson(workspaceJson).pipe(
193+
tap((ws) => expect(ws.getDefaultProject()).toEqual(appProject)),
194+
).subscribe(undefined, done.fail, done);
195+
});
196+
197+
it('gets default project when there is a single one', (done) => {
198+
const customWorkspaceJson = { ...workspaceJson, defaultProject: undefined };
199+
const workspace = new Workspace(root, host);
200+
workspace.loadWorkspaceFromJson(customWorkspaceJson).pipe(
201+
tap((ws) => expect(ws.getDefaultProject()).toEqual(appProject)),
202+
).subscribe(undefined, done.fail, done);
203+
});
204+
205+
it('gets default project when there is a single one', (done) => {
206+
const customWorkspaceJson = { ...workspaceJson, defaultProject: undefined, projects: {} };
207+
const workspace = new Workspace(root, host);
208+
workspace.loadWorkspaceFromJson(customWorkspaceJson).pipe(
209+
tap((ws) => expect(ws.getDefaultProject()).toEqual(null)),
210+
).subscribe(undefined, done.fail, done);
211+
});
212+
188213
it('gets workspace cli', (done) => {
189214
const workspace = new Workspace(root, host);
190215
workspace.loadWorkspaceFromJson(workspaceJson).pipe(
191-
tap((ws) => expect(ws.getCli()).toEqual(workspaceJson.cli)),
216+
tap((ws) => expect(ws.getCli()).toEqual(workspaceJson.cli as WorkspaceTool)),
192217
).subscribe(undefined, done.fail, done);
193218
});
194219

195220
it('gets workspace schematics', (done) => {
196221
const workspace = new Workspace(root, host);
197222
workspace.loadWorkspaceFromJson(workspaceJson).pipe(
198-
tap((ws) => expect(ws.getSchematics()).toEqual(workspaceJson.schematics)),
223+
tap((ws) => expect(ws.getSchematics()).toEqual(workspaceJson.schematics as WorkspaceTool)),
199224
).subscribe(undefined, done.fail, done);
200225
});
201226

202227
it('gets workspace architect', (done) => {
203228
const workspace = new Workspace(root, host);
204229
workspace.loadWorkspaceFromJson(workspaceJson).pipe(
205-
tap((ws) => expect(ws.getArchitect()).toEqual(workspaceJson.architect)),
230+
tap((ws) => expect(ws.getArchitect()).toEqual(workspaceJson.architect as WorkspaceTool)),
206231
).subscribe(undefined, done.fail, done);
207232
});
208233

209234
it('gets project cli', (done) => {
210235
const workspace = new Workspace(root, host);
211236
workspace.loadWorkspaceFromJson(workspaceJson).pipe(
212-
tap((ws) => expect(ws.getProjectCli('app')).toEqual(workspaceJson.projects.app.cli)),
237+
tap((ws) => expect(ws.getProjectCli('app'))
238+
.toEqual(workspaceJson.projects.app.cli as WorkspaceTool)),
213239
).subscribe(undefined, done.fail, done);
214240
});
215241

216242
it('gets project schematics', (done) => {
217243
const workspace = new Workspace(root, host);
218244
workspace.loadWorkspaceFromJson(workspaceJson).pipe(
219245
tap((ws) => expect(ws.getProjectSchematics('app'))
220-
.toEqual(workspaceJson.projects.app.schematics)),
246+
.toEqual(workspaceJson.projects.app.schematics as WorkspaceTool)),
221247
).subscribe(undefined, done.fail, done);
222248
});
223249

224250
it('gets project architect', (done) => {
225251
const workspace = new Workspace(root, host);
226252
workspace.loadWorkspaceFromJson(workspaceJson).pipe(
227253
tap((ws) => expect(ws.getProjectArchitect('app'))
228-
.toEqual(workspaceJson.projects.app.architect)),
254+
.toEqual(workspaceJson.projects.app.architect as WorkspaceTool)),
229255
).subscribe(undefined, done.fail, done);
230256
});
231257

packages/schematics/angular/app-shell/index.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import {
1616
} from '@angular-devkit/schematics';
1717
import * as ts from 'typescript';
1818
import {
19-
Architect,
20-
Project,
19+
WorkspaceProject,
2120
WorkspaceSchema,
21+
WorkspaceTool,
2222
} from '../../../angular_devkit/core/src/workspace/workspace-schema';
2323
import { Schema as ComponentOptions } from '../component/schema';
2424
import {
@@ -54,7 +54,9 @@ function getSourceFile(host: Tree, path: string): ts.SourceFile {
5454
return source;
5555
}
5656

57-
function getServerModulePath(host: Tree, project: Project, architect: Architect): string | null {
57+
function getServerModulePath(
58+
host: Tree, project: WorkspaceProject, architect: WorkspaceTool,
59+
): string | null {
5860
const mainPath = architect.server.options.main;
5961
const mainSource = getSourceFile(host, mainPath);
6062
const allNodes = getSourceNodes(mainSource);
@@ -102,7 +104,7 @@ function getComponentTemplate(host: Tree, compPath: string, tmplInfo: TemplateIn
102104
return template;
103105
}
104106

105-
function getBootstrapComponentPath(host: Tree, project: Project): string {
107+
function getBootstrapComponentPath(host: Tree, project: WorkspaceProject): string {
106108
if (!project.architect) {
107109
throw new Error('Project architect not found.');
108110
}
@@ -317,7 +319,7 @@ function addShellComponent(options: AppShellOptions): Rule {
317319
};
318320
}
319321

320-
function getClientProject(host: Tree, options: AppShellOptions): Project {
322+
function getClientProject(host: Tree, options: AppShellOptions): WorkspaceProject {
321323
const workspace = getWorkspace(host);
322324
const clientProject = workspace.projects[options.clientProject];
323325
if (!clientProject) {
@@ -327,7 +329,7 @@ function getClientProject(host: Tree, options: AppShellOptions): Project {
327329
return clientProject;
328330
}
329331

330-
function getClientArchitect(host: Tree, options: AppShellOptions): Architect {
332+
function getClientArchitect(host: Tree, options: AppShellOptions): WorkspaceTool {
331333
const clientArchitect = getClientProject(host, options).architect;
332334

333335
if (!clientArchitect) {

packages/schematics/angular/universal/index.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ function getWorkspacePath(host: Tree): string {
3939
return possibleFiles.filter(path => host.exists(path))[0];
4040
}
4141

42-
function getClientProject(host: Tree, options: UniversalOptions): experimental.workspace.Project {
42+
function getClientProject(
43+
host: Tree, options: UniversalOptions,
44+
): experimental.workspace.WorkspaceProject {
4345
const workspace = getWorkspace(host);
4446
const clientProject = workspace.projects[options.clientProject];
4547
if (!clientProject) {
@@ -52,7 +54,7 @@ function getClientProject(host: Tree, options: UniversalOptions): experimental.w
5254
function getClientArchitect(
5355
host: Tree,
5456
options: UniversalOptions,
55-
): experimental.workspace.Architect {
57+
): experimental.workspace.WorkspaceTool {
5658
const clientArchitect = getClientProject(host, options).architect;
5759

5860
if (!clientArchitect) {
@@ -181,7 +183,7 @@ function addDependencies(): Rule {
181183
};
182184
}
183185

184-
function getTsConfigOutDir(host: Tree, architect: experimental.workspace.Architect): string {
186+
function getTsConfigOutDir(host: Tree, architect: experimental.workspace.WorkspaceTool): string {
185187
const tsConfigPath = architect.build.options.tsConfig;
186188
const tsConfigBuffer = host.read(tsConfigPath);
187189
if (!tsConfigBuffer) {

0 commit comments

Comments
 (0)