Skip to content

Commit a188ae2

Browse files
committed
fix: add support for AngularV11
1 parent 0f3de87 commit a188ae2

14 files changed

+478
-192
lines changed

src/deploy/actions.spec.ts

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,42 @@ import {
55
ScheduleOptions,
66
Target
77
} from '@angular-devkit/architect/src/index';
8+
89
import deploy from './actions';
10+
import { BuildTarget } from 'interfaces';
11+
import * as path from 'path';
912

10-
let context: BuilderContext;
11-
const mockEngine = { run: (_: string, __: any, __2: any) => Promise.resolve() };
13+
import { readFileAsync } from '../utils';
14+
jest.mock('../utils');
1215

16+
const mockEngine = {
17+
run: (_: string, __: any, __2: any) => Promise.resolve()
18+
};
1319
const PROJECT = 'pirojok-project';
1420

21+
let context: BuilderContext;
22+
let mockedReadFileAsync: jest.Mock<ReturnType<typeof readFileAsync>>;
23+
let ngPackageContent: JsonObject;
24+
1525
describe('Deploy Angular apps', () => {
16-
beforeEach(() => initMocks());
26+
beforeEach(() => {
27+
ngPackageContent = {
28+
dest: `../../dist/randomness/${PROJECT}`
29+
};
30+
31+
mockedReadFileAsync = readFileAsync as jest.Mock<
32+
ReturnType<typeof readFileAsync>
33+
>;
34+
mockedReadFileAsync.mockImplementation(() =>
35+
Promise.resolve(JSON.stringify(ngPackageContent, null, 2))
36+
);
37+
38+
initMocks();
39+
});
40+
41+
afterEach(() => {
42+
jest.clearAllMocks();
43+
});
1744

1845
describe('Builder', () => {
1946
let spy: jest.SpyInstance;
@@ -23,7 +50,7 @@ describe('Deploy Angular apps', () => {
2350
});
2451

2552
it('should invoke the builder', async () => {
26-
await deploy(mockEngine, context, 'host', {});
53+
await deploy(mockEngine, context, getBuildTarget(), {});
2754

2855
expect(spy).toHaveBeenCalledWith({
2956
target: 'build',
@@ -34,7 +61,7 @@ describe('Deploy Angular apps', () => {
3461
it('should invoke the builder with the right configuration', async () => {
3562
const customConf = 'my-custom-conf';
3663

37-
await deploy(mockEngine, context, 'host', {
64+
await deploy(mockEngine, context, getBuildTarget(), {
3865
configuration: customConf
3966
});
4067

@@ -47,27 +74,45 @@ describe('Deploy Angular apps', () => {
4774
});
4875

4976
it('should invoke engine.run', async () => {
50-
const spy = spyOn(mockEngine, 'run').and.callThrough();
51-
await deploy(mockEngine, context, 'host', {});
77+
const expectedOutputDir = path.join(
78+
context.workspaceRoot,
79+
`dist/randomness/${PROJECT}`
80+
);
81+
const runSpy = spyOn(mockEngine, 'run').and.callThrough();
82+
83+
await deploy(mockEngine, context, getBuildTarget(), {});
5284

53-
expect(spy).toHaveBeenCalledWith('host', {}, context.logger);
85+
expect(runSpy).toHaveBeenCalledWith(expectedOutputDir, {}, context.logger);
5486
});
5587

5688
describe('error handling', () => {
57-
it('throws if there is no target project', async () => {
89+
it('should throw if there is no target project', async () => {
5890
context.target = undefined;
5991
try {
60-
await deploy(mockEngine, context, 'host', {});
92+
await deploy(mockEngine, context, getBuildTarget(), {});
6193
fail();
6294
} catch (e) {
6395
expect(e.message).toMatch(/Cannot execute the build target/);
6496
}
6597
});
98+
99+
it('should throw if there is not project on build options', async () => {
100+
context.getTargetOptions = () => Promise.resolve({});
101+
102+
try {
103+
await deploy(mockEngine, context, getBuildTarget(), {});
104+
fail();
105+
} catch (e) {
106+
expect(e.message).toMatch(
107+
/Cannot read the project path option of the Angular library '.*' in angular.json/
108+
);
109+
}
110+
});
66111
});
67112
});
68113

69114
const initMocks = () => {
70-
context = {
115+
context = ({
71116
target: {
72117
configuration: 'production',
73118
project: PROJECT,
@@ -78,21 +123,18 @@ const initMocks = () => {
78123
description: 'mock',
79124
optionSchema: false
80125
},
81-
currentDirectory: 'cwd',
82-
id: 1,
126+
workspaceRoot: 'my/workspace/root',
83127
logger: new logging.NullLogger() as any,
84-
workspaceRoot: 'cwd',
85-
addTeardown: _ => {},
86-
validateOptions: _ => Promise.resolve({} as any),
87-
getBuilderNameForTarget: () => Promise.resolve(''),
88-
analytics: null as any,
89-
getTargetOptions: (_: Target) => Promise.resolve({}),
90-
reportProgress: (_: number, __?: number, ___?: string) => {},
91-
reportStatus: (_: string) => {},
92-
reportRunning: () => {},
93-
scheduleBuilder: (_: string, __?: JsonObject, ___?: ScheduleOptions) =>
94-
Promise.resolve({} as BuilderRun),
95128
scheduleTarget: (_: Target, __?: JsonObject, ___?: ScheduleOptions) =>
96-
Promise.resolve({} as BuilderRun)
97-
};
129+
Promise.resolve({} as BuilderRun),
130+
getTargetOptions: (t: Target) =>
131+
Promise.resolve({
132+
project: `projects/${t.project}/some-file.json`,
133+
target: t.target
134+
})
135+
} as unknown) as BuilderContext;
98136
};
137+
138+
const getBuildTarget = (): BuildTarget => ({
139+
name: `${PROJECT}:build:production`
140+
});

src/deploy/actions.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
import { BuilderContext, Target } from '@angular-devkit/architect';
1+
import {
2+
BuilderContext,
3+
Target,
4+
targetFromTargetString
5+
} from '@angular-devkit/architect';
26
import { logging } from '@angular-devkit/core';
37

8+
import * as path from 'path';
9+
410
import { Schema } from './schema';
11+
import { readFileAsync } from '../utils';
12+
import { BuildTarget } from 'interfaces';
513

614
export default async function deploy(
715
engine: {
@@ -12,7 +20,7 @@ export default async function deploy(
1220
) => Promise<void>;
1321
},
1422
context: BuilderContext,
15-
projectRoot: string,
23+
buildTarget: BuildTarget,
1624
options: Schema
1725
) {
1826
if (options.noBuild) {
@@ -44,9 +52,55 @@ export default async function deploy(
4452
await build.result;
4553
}
4654

55+
const targetFromStr = targetFromTargetString(buildTarget.name);
56+
const buildOptions = await context.getTargetOptions(targetFromStr);
57+
58+
if (!buildOptions.project || typeof buildOptions.project !== 'string') {
59+
throw new Error(
60+
`Cannot read the project path option of the Angular library '${buildTarget.name}' in angular.json`
61+
);
62+
}
63+
64+
const outputPath = await getOutPutPath(
65+
context.workspaceRoot,
66+
buildOptions.project
67+
);
68+
4769
await engine.run(
48-
projectRoot,
70+
outputPath,
4971
options,
5072
(context.logger as unknown) as logging.LoggerApi
5173
);
5274
}
75+
76+
async function getOutPutPath(
77+
projectRoot: string,
78+
relativeNgPackagePath: string
79+
): Promise<string> {
80+
const ngPackagePath = path.join(projectRoot, relativeNgPackagePath);
81+
82+
let ngPackageContentStr: string;
83+
84+
try {
85+
ngPackageContentStr = await readFileAsync(ngPackagePath, {
86+
encoding: 'utf8'
87+
});
88+
} catch (error) {
89+
throw new Error(`Error reading the ng-package.json`);
90+
}
91+
92+
const ngPackageContent = JSON.parse(ngPackageContentStr);
93+
94+
if (!ngPackageContent.dest || typeof ngPackageContent.dest !== 'string') {
95+
throw new Error(
96+
`Cannot read the project 'dest' option of the ng-package.json`
97+
);
98+
}
99+
100+
const outputPath = path.join(
101+
path.dirname(ngPackagePath),
102+
ngPackageContent.dest
103+
);
104+
105+
return outputPath;
106+
}

src/deploy/builder.ts

Lines changed: 8 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,28 @@ import {
33
BuilderOutput,
44
createBuilder
55
} from '@angular-devkit/architect';
6-
import { asWindowsPath, experimental, normalize } from '@angular-devkit/core';
7-
import { NodeJsSyncHost } from '@angular-devkit/core/node';
8-
import os from 'os';
9-
import * as path from 'path';
106

117
import * as engine from '../engine/engine';
128
import deploy from './actions';
139
import { Schema } from './schema';
1410

1511
// Call the createBuilder() function to create a builder. This mirrors
1612
// createJobHandler() but add typings specific to Architect Builders.
17-
export default createBuilder<any>(
13+
export default createBuilder(
1814
async (options: Schema, context: BuilderContext): Promise<BuilderOutput> => {
19-
// The project root is added to a BuilderContext.
20-
const root = normalize(context.workspaceRoot);
21-
const workspace = new experimental.workspace.Workspace(
22-
root,
23-
new NodeJsSyncHost()
24-
);
25-
await workspace
26-
.loadWorkspaceFromHost(normalize('angular.json'))
27-
.toPromise();
28-
2915
if (!context.target) {
3016
throw new Error('Cannot deploy the application without a target');
3117
}
3218

33-
const targets = workspace.getProjectTargets(context.target.project);
34-
35-
const outputPath = await getLibraryOutputPath(targets);
36-
37-
// normalizes pathes don't work with all native functions
38-
// as a workaround, you can use the following 2 lines
39-
const isWin = os.platform() === 'win32';
40-
const workspaceRoot = !isWin
41-
? workspace.root
42-
: asWindowsPath(workspace.root);
43-
// if this is not necessary, use this:
44-
// const workspaceRoot = workspace.root;
19+
const configuration = options.configuration
20+
? `:${options.configuration}`
21+
: '';
22+
const buildTarget = {
23+
name: `${context.target.project}:build${configuration}`
24+
};
4525

4626
try {
47-
await deploy(
48-
engine,
49-
context,
50-
path.join(workspaceRoot, outputPath),
51-
options
52-
);
27+
await deploy(engine, context, buildTarget, options);
5328
} catch (e) {
5429
context.logger.error('Error when trying to deploy:', e);
5530
console.error(e);
@@ -59,31 +34,3 @@ export default createBuilder<any>(
5934
return { success: true };
6035
}
6136
);
62-
63-
const fs = require('fs');
64-
function readFileAsync<T>(path: string): Promise<T> {
65-
return new Promise((res, rej) => {
66-
fs.readFile(path, 'utf8', function(err, contents) {
67-
if (err) rej(err);
68-
69-
res(contents);
70-
});
71-
});
72-
}
73-
74-
async function getLibraryOutputPath(
75-
targets: experimental.workspace.WorkspaceTool
76-
) {
77-
const ngPackagePath = targets.build.options.project;
78-
79-
try {
80-
const dataStr = await readFileAsync<string>(ngPackagePath);
81-
const data = JSON.parse(dataStr);
82-
83-
const fullPath = (data.dest as string).replace(/\.\.\//g, '');
84-
85-
return fullPath;
86-
} catch (err) {
87-
throw new Error('An error occurs reading the ng-package.json');
88-
}
89-
}

src/engine/engine.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import * as engine from './engine';
55
import { Schema } from '../deploy/schema';
66
import { npmAccess } from './defaults';
77

8-
import * as exec from './utils/exec-async';
8+
import * as exec from '../utils/exec-async';
99

10-
import * as fs from './utils/fs-async';
10+
import * as fs from '../utils/fs-async';
1111
import { PromiseWithChild } from 'child_process';
1212

1313
describe('engine', () => {

src/engine/engine.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { logging } from '@angular-devkit/core';
1+
import { JsonObject, logging } from '@angular-devkit/core';
22
import * as path from 'path';
33

4-
import * as fs from './utils/fs-async';
5-
import { execAsync } from './utils/exec-async';
4+
import * as fs from '../utils/fs-async';
5+
import { execAsync } from '../utils/exec-async';
66

77
import { Schema } from '../deploy/schema';
88
import { defaults } from './defaults';
@@ -17,7 +17,7 @@ export async function run(
1717

1818
// If we are not on dry run
1919
if (options.packageVersion && !options.dryRun) {
20-
await setPackageVersion(dir, options);
20+
await setPackageVersion(dir, options.packageVersion);
2121
}
2222

2323
const npmOptions = extractOnlyNPMOptions(options);
@@ -44,15 +44,15 @@ export async function run(
4444
}
4545
}
4646

47-
async function setPackageVersion(dir: string, options: Schema) {
48-
let packageContent: string = await fs.readFileAsync(
47+
async function setPackageVersion(dir: string, packageVersion: string) {
48+
const packageContent: string = await fs.readFileAsync(
4949
path.join(dir, 'package.json'),
5050
{ encoding: 'utf8' }
5151
);
5252

53-
let packageObj: any = JSON.parse(packageContent);
53+
const packageObj: JsonObject = JSON.parse(packageContent);
5454

55-
packageObj.version = options.packageVersion;
55+
packageObj.version = packageVersion;
5656

5757
await fs.writeFileAsync(
5858
path.join(dir, 'package.json'),

src/interfaces.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export interface WorkspaceProject {
2+
projectType?: string;
3+
architect?: Record<
4+
string,
5+
{ builder: string; options?: Record<string, any> }
6+
>;
7+
}
8+
9+
export interface Workspace {
10+
defaultProject?: string;
11+
projects: Record<string, WorkspaceProject>;
12+
}
13+
14+
export interface BuildTarget {
15+
name: string;
16+
options?: { [name: string]: any };
17+
}

0 commit comments

Comments
 (0)