UNPKG

14.1 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.build = exports.buildProject = exports.buildAppPackage = exports.buildCompanion = exports.buildDeviceComponents = exports.buildDeviceResources = exports.buildComponent = exports.loadProjectConfig = exports.generateBuildId = exports.DiagnosticCategory = void 0;
4const tslib_1 = require("tslib");
5const fs_1 = tslib_1.__importDefault(require("fs"));
6const stream_1 = require("stream");
7const drop_stream_1 = tslib_1.__importDefault(require("drop-stream"));
8const lazystream_1 = tslib_1.__importDefault(require("lazystream"));
9const merge_stream_1 = tslib_1.__importDefault(require("merge-stream"));
10const multistream_1 = tslib_1.__importDefault(require("multistream"));
11const playback_stream_1 = tslib_1.__importDefault(require("playback-stream"));
12const plugin_error_1 = tslib_1.__importDefault(require("plugin-error"));
13const pumpify_1 = tslib_1.__importDefault(require("pumpify"));
14const simple_random_1 = tslib_1.__importDefault(require("simple-random"));
15const vinyl_fs_1 = tslib_1.__importDefault(require("vinyl-fs"));
16const appPackageManifest_1 = tslib_1.__importDefault(require("./appPackageManifest"));
17const BuildError_1 = tslib_1.__importDefault(require("./util/BuildError"));
18const buildTargets_1 = tslib_1.__importDefault(require("./buildTargets"));
19const collectComponentSourceMaps_1 = tslib_1.__importDefault(require("./collectComponentSourceMaps"));
20const compile_1 = tslib_1.__importDefault(require("./compile"));
21const compileTranslations_1 = tslib_1.__importDefault(require("./compileTranslations"));
22const componentManifest_1 = require("./componentManifest");
23const componentTargets_1 = tslib_1.__importStar(require("./componentTargets"));
24const convertImageToTXI_1 = tslib_1.__importStar(require("./convertImageToTXI"));
25const eventsIntercept_1 = tslib_1.__importDefault(require("./util/eventsIntercept"));
26const gulpAssertFiles_1 = tslib_1.__importDefault(require("./gulpAssertFiles"));
27const gulpSetProperty_1 = tslib_1.__importDefault(require("./gulpSetProperty"));
28const diagnostics_1 = require("./diagnostics");
29Object.defineProperty(exports, "DiagnosticCategory", { enumerable: true, get: function () { return diagnostics_1.DiagnosticCategory; } });
30const filterResourceTag_1 = tslib_1.__importDefault(require("./filterResourceTag"));
31const findEntryPoint_1 = tslib_1.__importDefault(require("./findEntryPoint"));
32const nativeComponents_1 = tslib_1.__importDefault(require("./nativeComponents"));
33const pluginError_1 = tslib_1.__importDefault(require("./util/pluginError"));
34const ProjectConfiguration_1 = require("./ProjectConfiguration");
35const resources = tslib_1.__importStar(require("./resources"));
36const validateIcon_1 = tslib_1.__importDefault(require("./validateIcon"));
37const validateFileSizes_1 = tslib_1.__importDefault(require("./validateFileSizes"));
38const zip_1 = tslib_1.__importDefault(require("./zip"));
39function generateBuildId() {
40 return `0x0${simple_random_1.default({
41 secure: simple_random_1.default.isSecureSupported,
42 chars: '0123456789abcdef',
43 length: 15,
44 })}`;
45}
46exports.generateBuildId = generateBuildId;
47function addDiagnosticTarget(target, onDiagnostic) {
48 return (diagnostic) => onDiagnostic(Object.assign({ target }, diagnostic));
49}
50function 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}
66function 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}
78function emptyReadable() {
79 return new stream_1.Readable({
80 objectMode: true,
81 read() {
82 this.push(null);
83 },
84 });
85}
86function transformIf(condition, plugin) {
87 return condition ? plugin : new stream_1.Stream.PassThrough({ objectMode: true });
88}
89function 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}
109exports.loadProjectConfig = loadProjectConfig;
110function 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}
130exports.buildComponent = buildComponent;
131function 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}
136exports.buildDeviceResources = buildDeviceResources;
137function 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}
172exports.buildDeviceComponents = buildDeviceComponents;
173function 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}
215exports.buildCompanion = buildCompanion;
216function 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}
244exports.buildAppPackage = buildAppPackage;
245function 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}
270exports.buildProject = buildProject;
271function 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}
288exports.build = build;