UNPKG

9.52 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright Google LLC All Rights Reserved.
5 *
6 * Use of this source code is governed by an MIT-style license that can be
7 * found in the LICENSE file at https://angular.io/license
8 */
9Object.defineProperty(exports, "__esModule", { value: true });
10exports.createBuilder = void 0;
11const core_1 = require("@angular-devkit/core");
12const rxjs_1 = require("rxjs");
13const operators_1 = require("rxjs/operators");
14const api_1 = require("./api");
15const internal_1 = require("./internal");
16const jobs_1 = require("./jobs");
17const schedule_by_name_1 = require("./schedule-by-name");
18// eslint-disable-next-line max-lines-per-function
19function createBuilder(fn) {
20 const cjh = jobs_1.createJobHandler;
21 // eslint-disable-next-line max-lines-per-function
22 const handler = cjh((options, context) => {
23 const scheduler = context.scheduler;
24 const progressChannel = context.createChannel('progress');
25 const logChannel = context.createChannel('log');
26 let currentState = api_1.BuilderProgressState.Stopped;
27 const teardownLogics = [];
28 let tearingDown = false;
29 let current = 0;
30 let status = '';
31 let total = 1;
32 function log(entry) {
33 logChannel.next(entry);
34 }
35 function progress(progress, context) {
36 currentState = progress.state;
37 if (progress.state === api_1.BuilderProgressState.Running) {
38 current = progress.current;
39 total = progress.total !== undefined ? progress.total : total;
40 if (progress.status === undefined) {
41 progress.status = status;
42 }
43 else {
44 status = progress.status;
45 }
46 }
47 progressChannel.next({
48 ...progress,
49 ...(context.target && { target: context.target }),
50 ...(context.builder && { builder: context.builder }),
51 id: context.id,
52 });
53 }
54 return new rxjs_1.Observable((observer) => {
55 const subscriptions = [];
56 const inputSubscription = context.inboundBus.subscribe((i) => {
57 switch (i.kind) {
58 case jobs_1.JobInboundMessageKind.Stop:
59 // Run teardown logic then complete.
60 tearingDown = true;
61 Promise.all(teardownLogics.map((fn) => fn() || Promise.resolve())).then(() => observer.complete(), (err) => observer.error(err));
62 break;
63 case jobs_1.JobInboundMessageKind.Input:
64 if (!tearingDown) {
65 onInput(i.value);
66 }
67 break;
68 }
69 });
70 function onInput(i) {
71 const builder = i.info;
72 const loggerName = i.target
73 ? (0, api_1.targetStringFromTarget)(i.target)
74 : builder.builderName;
75 const logger = new core_1.logging.Logger(loggerName);
76 subscriptions.push(logger.subscribe((entry) => log(entry)));
77 const context = {
78 builder,
79 workspaceRoot: i.workspaceRoot,
80 currentDirectory: i.currentDirectory,
81 target: i.target,
82 logger: logger,
83 id: i.id,
84 async scheduleTarget(target, overrides = {}, scheduleOptions = {}) {
85 const run = await (0, schedule_by_name_1.scheduleByTarget)(target, overrides, {
86 scheduler,
87 logger: scheduleOptions.logger || logger.createChild(''),
88 workspaceRoot: i.workspaceRoot,
89 currentDirectory: i.currentDirectory,
90 });
91 // We don't want to subscribe errors and complete.
92 subscriptions.push(run.progress.subscribe((event) => progressChannel.next(event)));
93 return run;
94 },
95 async scheduleBuilder(builderName, options = {}, scheduleOptions = {}) {
96 const run = await (0, schedule_by_name_1.scheduleByName)(builderName, options, {
97 scheduler,
98 target: scheduleOptions.target,
99 logger: scheduleOptions.logger || logger.createChild(''),
100 workspaceRoot: i.workspaceRoot,
101 currentDirectory: i.currentDirectory,
102 });
103 // We don't want to subscribe errors and complete.
104 subscriptions.push(run.progress.subscribe((event) => progressChannel.next(event)));
105 return run;
106 },
107 async getTargetOptions(target) {
108 return scheduler
109 .schedule('..getTargetOptions', target)
110 .output.toPromise();
111 },
112 async getProjectMetadata(target) {
113 return scheduler
114 .schedule('..getProjectMetadata', target)
115 .output.toPromise();
116 },
117 async getBuilderNameForTarget(target) {
118 return scheduler
119 .schedule('..getBuilderNameForTarget', target)
120 .output.toPromise();
121 },
122 async validateOptions(options, builderName) {
123 return scheduler
124 .schedule('..validateOptions', [
125 builderName,
126 options,
127 ])
128 .output.toPromise();
129 },
130 reportRunning() {
131 switch (currentState) {
132 case api_1.BuilderProgressState.Waiting:
133 case api_1.BuilderProgressState.Stopped:
134 progress({ state: api_1.BuilderProgressState.Running, current: 0, total }, context);
135 break;
136 }
137 },
138 reportStatus(status) {
139 switch (currentState) {
140 case api_1.BuilderProgressState.Running:
141 progress({ state: currentState, status, current, total }, context);
142 break;
143 case api_1.BuilderProgressState.Waiting:
144 progress({ state: currentState, status }, context);
145 break;
146 }
147 },
148 reportProgress(current, total, status) {
149 switch (currentState) {
150 case api_1.BuilderProgressState.Running:
151 progress({ state: currentState, current, total, status }, context);
152 }
153 },
154 addTeardown(teardown) {
155 teardownLogics.push(teardown);
156 },
157 };
158 context.reportRunning();
159 let result;
160 try {
161 result = fn(i.options, context);
162 if ((0, api_1.isBuilderOutput)(result)) {
163 result = (0, rxjs_1.of)(result);
164 }
165 else if (!(0, rxjs_1.isObservable)(result) && isAsyncIterable(result)) {
166 result = (0, api_1.fromAsyncIterable)(result);
167 }
168 else {
169 result = (0, rxjs_1.from)(result);
170 }
171 }
172 catch (e) {
173 result = (0, rxjs_1.throwError)(e);
174 }
175 // Manage some state automatically.
176 progress({ state: api_1.BuilderProgressState.Running, current: 0, total: 1 }, context);
177 subscriptions.push(result
178 .pipe((0, operators_1.defaultIfEmpty)({ success: false }), (0, operators_1.tap)(() => {
179 progress({ state: api_1.BuilderProgressState.Running, current: total }, context);
180 progress({ state: api_1.BuilderProgressState.Stopped }, context);
181 }), (0, operators_1.mergeMap)(async (value) => {
182 // Allow the log queue to flush
183 await new Promise(setImmediate);
184 return value;
185 }))
186 .subscribe((message) => observer.next(message), (error) => observer.error(error), () => observer.complete()));
187 }
188 return () => {
189 subscriptions.forEach((x) => x.unsubscribe());
190 inputSubscription.unsubscribe();
191 };
192 });
193 });
194 return {
195 handler,
196 [internal_1.BuilderSymbol]: true,
197 [internal_1.BuilderVersionSymbol]: require('../package.json').version,
198 };
199}
200exports.createBuilder = createBuilder;
201function isAsyncIterable(obj) {
202 return !!obj && typeof obj[Symbol.asyncIterator] === 'function';
203}