1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.build = exports.buildProject = exports.buildAppPackage = exports.buildCompanion = exports.buildDeviceComponents = exports.buildDeviceResources = exports.buildComponent = exports.loadProjectConfig = exports.generateBuildId = exports.DiagnosticCategory = void 0;
|
4 | const tslib_1 = require("tslib");
|
5 | const fs_1 = tslib_1.__importDefault(require("fs"));
|
6 | const stream_1 = require("stream");
|
7 | const drop_stream_1 = tslib_1.__importDefault(require("drop-stream"));
|
8 | const lazystream_1 = tslib_1.__importDefault(require("lazystream"));
|
9 | const merge_stream_1 = tslib_1.__importDefault(require("merge-stream"));
|
10 | const multistream_1 = tslib_1.__importDefault(require("multistream"));
|
11 | const playback_stream_1 = tslib_1.__importDefault(require("playback-stream"));
|
12 | const plugin_error_1 = tslib_1.__importDefault(require("plugin-error"));
|
13 | const pumpify_1 = tslib_1.__importDefault(require("pumpify"));
|
14 | const simple_random_1 = tslib_1.__importDefault(require("simple-random"));
|
15 | const vinyl_fs_1 = tslib_1.__importDefault(require("vinyl-fs"));
|
16 | const appPackageManifest_1 = tslib_1.__importDefault(require("./appPackageManifest"));
|
17 | const BuildError_1 = tslib_1.__importDefault(require("./util/BuildError"));
|
18 | const buildTargets_1 = tslib_1.__importDefault(require("./buildTargets"));
|
19 | const collectComponentSourceMaps_1 = tslib_1.__importDefault(require("./collectComponentSourceMaps"));
|
20 | const compile_1 = tslib_1.__importDefault(require("./compile"));
|
21 | const compileTranslations_1 = tslib_1.__importDefault(require("./compileTranslations"));
|
22 | const componentManifest_1 = require("./componentManifest");
|
23 | const componentTargets_1 = tslib_1.__importStar(require("./componentTargets"));
|
24 | const convertImageToTXI_1 = tslib_1.__importStar(require("./convertImageToTXI"));
|
25 | const eventsIntercept_1 = tslib_1.__importDefault(require("./util/eventsIntercept"));
|
26 | const gulpAssertFiles_1 = tslib_1.__importDefault(require("./gulpAssertFiles"));
|
27 | const gulpSetProperty_1 = tslib_1.__importDefault(require("./gulpSetProperty"));
|
28 | const diagnostics_1 = require("./diagnostics");
|
29 | Object.defineProperty(exports, "DiagnosticCategory", { enumerable: true, get: function () { return diagnostics_1.DiagnosticCategory; } });
|
30 | const filterResourceTag_1 = tslib_1.__importDefault(require("./filterResourceTag"));
|
31 | const findEntryPoint_1 = tslib_1.__importDefault(require("./findEntryPoint"));
|
32 | const nativeComponents_1 = tslib_1.__importDefault(require("./nativeComponents"));
|
33 | const pluginError_1 = tslib_1.__importDefault(require("./util/pluginError"));
|
34 | const ProjectConfiguration_1 = require("./ProjectConfiguration");
|
35 | const resources = tslib_1.__importStar(require("./resources"));
|
36 | const validateIcon_1 = tslib_1.__importDefault(require("./validateIcon"));
|
37 | const validateFileSizes_1 = tslib_1.__importDefault(require("./validateFileSizes"));
|
38 | const zip_1 = tslib_1.__importDefault(require("./zip"));
|
39 | function generateBuildId() {
|
40 | return `0x0${simple_random_1.default({
|
41 | secure: simple_random_1.default.isSecureSupported,
|
42 | chars: '0123456789abcdef',
|
43 | length: 15,
|
44 | })}`;
|
45 | }
|
46 | exports.generateBuildId = generateBuildId;
|
47 | function addDiagnosticTarget(target, onDiagnostic) {
|
48 | return (diagnostic) => onDiagnostic(Object.assign({ target }, diagnostic));
|
49 | }
|
50 | function addErrorTarget(target, fn) {
|
51 | function wrap(err) {
|
52 | if (BuildError_1.default.is(err) || pluginError_1.default.isPluginError(err)) {
|
53 | err.target = target;
|
54 | }
|
55 | return err;
|
56 | }
|
57 | try {
|
58 | const buildStream = eventsIntercept_1.default(fn());
|
59 | buildStream.intercept('error', (err, done) => done(null, wrap(err)));
|
60 | return buildStream;
|
61 | }
|
62 | catch (ex) {
|
63 | throw wrap(ex);
|
64 | }
|
65 | }
|
66 | function lazyObjectReadable(fn) {
|
67 | const lazyStream = new lazystream_1.default.Readable(() => {
|
68 | try {
|
69 | return fn();
|
70 | }
|
71 | catch (ex) {
|
72 | lazyStream.emit('error', ex);
|
73 | return emptyReadable();
|
74 | }
|
75 | }, { objectMode: true });
|
76 | return lazyStream;
|
77 | }
|
78 | function emptyReadable() {
|
79 | return new stream_1.Readable({
|
80 | objectMode: true,
|
81 | read() {
|
82 | this.push(null);
|
83 | },
|
84 | });
|
85 | }
|
86 | function transformIf(condition, plugin) {
|
87 | return condition ? plugin : new stream_1.Stream.PassThrough({ objectMode: true });
|
88 | }
|
89 | function loadProjectConfig({ hasNativeComponents = false, onDiagnostic = diagnostics_1.logDiagnosticToConsole, fileName = 'package.json', }) {
|
90 | try {
|
91 | const config = ProjectConfiguration_1.normalizeProjectConfig(JSON.parse(fs_1.default.readFileSync(fileName, 'utf-8')));
|
92 | const diagnostics = ProjectConfiguration_1.validate(config, { hasNativeComponents });
|
93 | diagnostics.diagnostics.forEach((diagnostic) => onDiagnostic(Object.assign({ file: { path: fileName } }, diagnostic)));
|
94 | if (diagnostics.fatalError) {
|
95 | throw new BuildError_1.default('Project configuration is invalid');
|
96 | }
|
97 | if (config.enableProposedAPI) {
|
98 | onDiagnostic({
|
99 | category: diagnostics_1.DiagnosticCategory.Warning,
|
100 | messageText: 'Targeting proposed API may cause your app to behave unexpectedly. Use only when needed for development or QA.',
|
101 | });
|
102 | }
|
103 | return config;
|
104 | }
|
105 | catch (err) {
|
106 | throw new plugin_error_1.default('projectConfig', err, { fileName });
|
107 | }
|
108 | }
|
109 | exports.loadProjectConfig = loadProjectConfig;
|
110 | function buildComponent({ projectConfig, component, onDiagnostic = diagnostics_1.logDiagnosticToConsole, }) {
|
111 | const { inputs, outputDir, notFoundIsFatal } = componentTargets_1.default[component];
|
112 | const entryPoint = findEntryPoint_1.default(inputs, {
|
113 | onDiagnostic,
|
114 | component,
|
115 | notFoundIsFatal,
|
116 | });
|
117 | if (!entryPoint)
|
118 | return;
|
119 | return lazyObjectReadable(() => new pumpify_1.default.obj(compile_1.default({
|
120 | component,
|
121 | onDiagnostic,
|
122 | entryPoint,
|
123 | outputDir,
|
124 | allowUnknownExternals: projectConfig.enableProposedAPI,
|
125 | defaultLanguage: projectConfig.defaultLanguage,
|
126 | }), gulpSetProperty_1.default({
|
127 | componentType: component,
|
128 | })));
|
129 | }
|
130 | exports.buildComponent = buildComponent;
|
131 | function buildDeviceResources(projectConfig, { resourceFilterTag }, onDiagnostic = diagnostics_1.logDiagnosticToConsole) {
|
132 | return new pumpify_1.default.obj(filterResourceTag_1.default(resourceFilterTag), validateIcon_1.default({ projectConfig, onDiagnostic }), convertImageToTXI_1.default({
|
133 | rgbaOutputFormat: convertImageToTXI_1.TXIOutputFormat.RGBA6666,
|
134 | }), gulpAssertFiles_1.default([resources.svgMain, resources.svgWidgets]));
|
135 | }
|
136 | exports.buildDeviceResources = buildDeviceResources;
|
137 | function buildDeviceComponents({ projectConfig, buildId, onDiagnostic = diagnostics_1.logDiagnosticToConsole, }) {
|
138 | const deviceJSPipeline = [
|
139 | buildComponent({
|
140 | projectConfig,
|
141 | onDiagnostic,
|
142 | component: componentTargets_1.ComponentType.DEVICE,
|
143 | }),
|
144 | ];
|
145 | const processedJS = new playback_stream_1.default({ objectMode: true });
|
146 | deviceJSPipeline.push(processedJS);
|
147 | return multistream_1.default.obj([
|
148 | new pumpify_1.default.obj(...deviceJSPipeline, drop_stream_1.default.obj()),
|
149 | ...projectConfig.buildTargets.map((family) => lazyObjectReadable(() => {
|
150 | const { platform, displayName, maxDeviceBundleSize } = buildTargets_1.default[family];
|
151 | onDiagnostic({
|
152 | messageText: `Building app for ${displayName}`,
|
153 | category: diagnostics_1.DiagnosticCategory.Message,
|
154 | });
|
155 | const bundleFilename = `device-${family}.zip`;
|
156 | const sourceMap = collectComponentSourceMaps_1.default();
|
157 | return new pumpify_1.default.obj(merge_stream_1.default(new pumpify_1.default.obj(processedJS.newReadableSide({ objectMode: true }), sourceMap.collector(componentTargets_1.ComponentType.DEVICE, family)), new pumpify_1.default.obj(vinyl_fs_1.default.src('./resources/**', { base: '.' }), buildDeviceResources(projectConfig, buildTargets_1.default[family], onDiagnostic)), new pumpify_1.default.obj(vinyl_fs_1.default.src(componentTargets_1.default.device.translationsGlob, {
|
158 | base: '.',
|
159 | }), compileTranslations_1.default(projectConfig.defaultLanguage))), componentManifest_1.makeDeviceManifest({ projectConfig, buildId, targetDevice: family }), zip_1.default(bundleFilename), transformIf(maxDeviceBundleSize !== undefined, validateFileSizes_1.default({
|
160 | onDiagnostic,
|
161 | maxSizes: { [bundleFilename]: maxDeviceBundleSize },
|
162 | })), gulpSetProperty_1.default({
|
163 | componentBundle: {
|
164 | family,
|
165 | platform,
|
166 | type: 'device',
|
167 | },
|
168 | }), sourceMap.emitter);
|
169 | })),
|
170 | ]);
|
171 | }
|
172 | exports.buildDeviceComponents = buildDeviceComponents;
|
173 | function buildCompanion({ projectConfig, buildId, onDiagnostic = diagnostics_1.logDiagnosticToConsole, }) {
|
174 | const sourceMaps = collectComponentSourceMaps_1.default();
|
175 | const diagnosticTargets = {
|
176 | [componentTargets_1.ComponentType.COMPANION]: diagnostics_1.DiagnosticTarget.Companion,
|
177 | [componentTargets_1.ComponentType.SETTINGS]: diagnostics_1.DiagnosticTarget.Settings,
|
178 | [componentTargets_1.ComponentType.DEVICE]: diagnostics_1.DiagnosticTarget.App,
|
179 | };
|
180 | const [companion, settings] = [
|
181 | componentTargets_1.ComponentType.COMPANION,
|
182 | componentTargets_1.ComponentType.SETTINGS,
|
183 | ].map((componentType) => {
|
184 | const targetedDiagnostic = addDiagnosticTarget(diagnosticTargets[componentType], onDiagnostic);
|
185 | const component = buildComponent({
|
186 | projectConfig,
|
187 | component: componentType,
|
188 | onDiagnostic: targetedDiagnostic,
|
189 | });
|
190 | if (component) {
|
191 | return lazyObjectReadable(() => {
|
192 | targetedDiagnostic({
|
193 | category: diagnostics_1.DiagnosticCategory.Message,
|
194 | messageText: `Building ${diagnosticTargets[componentType]}`,
|
195 | });
|
196 | return new pumpify_1.default.obj(addErrorTarget(diagnosticTargets[componentType], () => component), sourceMaps.collector(componentType));
|
197 | });
|
198 | }
|
199 | return component;
|
200 | });
|
201 | if (settings && !companion) {
|
202 | throw new BuildError_1.default('This project is being built with settings, but has no companion component.');
|
203 | }
|
204 | const components = [companion, settings].filter((component) => component !== undefined);
|
205 | if (components.length === 0)
|
206 | return;
|
207 | return lazyObjectReadable(() => new pumpify_1.default.obj(multistream_1.default.obj(components), componentManifest_1.makeCompanionManifest({
|
208 | projectConfig,
|
209 | buildId,
|
210 | hasSettings: !!settings,
|
211 | }), zip_1.default('companion.zip'), gulpSetProperty_1.default({
|
212 | componentBundle: { type: 'companion' },
|
213 | }), sourceMaps.emitter));
|
214 | }
|
215 | exports.buildCompanion = buildCompanion;
|
216 | function buildAppPackage({ projectConfig, buildId, existingDeviceComponents, onDiagnostic = diagnostics_1.logDiagnosticToConsole, }) {
|
217 | const components = [];
|
218 | if (existingDeviceComponents) {
|
219 | onDiagnostic({
|
220 | messageText: 'Bundling native device components, JS device app will not be built',
|
221 | category: diagnostics_1.DiagnosticCategory.Message,
|
222 | });
|
223 | components.push(existingDeviceComponents);
|
224 | }
|
225 | else {
|
226 | components.push(addErrorTarget(diagnostics_1.DiagnosticTarget.App, () => buildDeviceComponents({
|
227 | projectConfig,
|
228 | buildId,
|
229 | onDiagnostic: addDiagnosticTarget(diagnostics_1.DiagnosticTarget.App, onDiagnostic),
|
230 | })));
|
231 | }
|
232 | const companion = buildCompanion({
|
233 | projectConfig,
|
234 | buildId,
|
235 | onDiagnostic: addDiagnosticTarget(diagnostics_1.DiagnosticTarget.Companion, onDiagnostic),
|
236 | });
|
237 | if (companion)
|
238 | components.push(companion);
|
239 | return new pumpify_1.default.obj(multistream_1.default.obj(components), appPackageManifest_1.default({
|
240 | projectConfig,
|
241 | buildId,
|
242 | }), zip_1.default('app.fba'));
|
243 | }
|
244 | exports.buildAppPackage = buildAppPackage;
|
245 | function buildProject({ nativeDeviceComponentPaths = [], onDiagnostic = diagnostics_1.logDiagnosticToConsole, }) {
|
246 | let buildId;
|
247 | let existingDeviceComponents;
|
248 | const projectConfig = loadProjectConfig({
|
249 | onDiagnostic,
|
250 | hasNativeComponents: nativeDeviceComponentPaths && nativeDeviceComponentPaths.length > 0,
|
251 | });
|
252 | if (nativeDeviceComponentPaths.length > 0) {
|
253 | ({ buildId, existingDeviceComponents } = nativeComponents_1.default(projectConfig.appUUID, nativeDeviceComponentPaths));
|
254 | }
|
255 | else {
|
256 | buildId = generateBuildId();
|
257 | }
|
258 | return buildAppPackage({
|
259 | projectConfig,
|
260 | buildId,
|
261 | onDiagnostic,
|
262 | existingDeviceComponents,
|
263 | }).on('finish', () => {
|
264 | onDiagnostic({
|
265 | messageText: `App UUID: ${projectConfig.appUUID}, BuildID: ${buildId}`,
|
266 | category: diagnostics_1.DiagnosticCategory.Message,
|
267 | });
|
268 | });
|
269 | }
|
270 | exports.buildProject = buildProject;
|
271 | function build({ dest = vinyl_fs_1.default.dest('./build'), onDiagnostic = diagnostics_1.logDiagnosticToConsole, nativeDeviceComponentPaths, } = {}) {
|
272 | return new Promise((resolve, reject) => {
|
273 | new pumpify_1.default.obj(buildProject({ nativeDeviceComponentPaths, onDiagnostic }), dest)
|
274 | .on('error', reject)
|
275 | .on('finish', resolve);
|
276 | }).catch((e) => {
|
277 | if (pluginError_1.default.isPluginError(e) && pluginError_1.default.isProjectBuildError(e)) {
|
278 | onDiagnostic(pluginError_1.default.convertToDiagnostic(e));
|
279 | return Promise.reject();
|
280 | }
|
281 | if (BuildError_1.default.is(e)) {
|
282 | onDiagnostic(e.toDiagnostic());
|
283 | return Promise.reject();
|
284 | }
|
285 | return Promise.reject(e);
|
286 | });
|
287 | }
|
288 | exports.build = build;
|