1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const tslib_1 = require("tslib");
|
4 | const fs_1 = tslib_1.__importDefault(require("fs"));
|
5 | const stream_1 = tslib_1.__importDefault(require("stream"));
|
6 | const elfy_1 = tslib_1.__importDefault(require("elfy"));
|
7 | const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
8 | const plugin_error_1 = tslib_1.__importDefault(require("plugin-error"));
|
9 | const vinyl_1 = tslib_1.__importDefault(require("vinyl"));
|
10 | const PLUGIN_NAME = 'nativeComponents';
|
11 | function checkBufferLength(buffer, expectedLength, name) {
|
12 | if (buffer.length !== expectedLength) {
|
13 | throw new plugin_error_1.default(PLUGIN_NAME, `${name} must be ${expectedLength} bytes, found ${buffer.length} in ${buffer}`);
|
14 | }
|
15 | }
|
16 | function formatUUID(buffer) {
|
17 | const uuid = buffer.toString('hex');
|
18 | return [
|
19 | uuid.substr(0, 8),
|
20 | uuid.substr(8, 4),
|
21 | uuid.substr(12, 4),
|
22 | uuid.substr(16, 4),
|
23 | uuid.substr(20, 12),
|
24 | ].join('-');
|
25 | }
|
26 | function readMetadata(elfPath) {
|
27 | const elfData = fs_1.default.readFileSync(elfPath);
|
28 | const elf = elfy_1.default.parse(elfData);
|
29 | const elfSections = lodash_1.default.groupBy(elf.body.sections, (s) => s.name);
|
30 | function findSection(name) {
|
31 | const sectionName = `.${name}`;
|
32 | const sections = elfSections[sectionName];
|
33 | if (!sections) {
|
34 | throw new plugin_error_1.default(PLUGIN_NAME, `ELF section '${name}' is missing`, {
|
35 | fileName: elfPath,
|
36 | });
|
37 | }
|
38 | if (sections.length > 1) {
|
39 | throw new plugin_error_1.default(PLUGIN_NAME, `ELF section '${name}' is present more than once`, { fileName: elfPath });
|
40 | }
|
41 | return sections[0].data;
|
42 | }
|
43 | const buildIDData = findSection('buildid');
|
44 | checkBufferLength(buildIDData, 8, 'Build ID');
|
45 | const appIDData = findSection('appuuid');
|
46 | checkBufferLength(appIDData, 16, 'App UUID');
|
47 | const platformJSON = findSection('appplatform').toString();
|
48 | let platform;
|
49 | try {
|
50 | platform = JSON.parse(platformJSON);
|
51 | }
|
52 | catch (ex) {
|
53 | throw new plugin_error_1.default(PLUGIN_NAME, `Could not parse platform specification in .appplatform section: ${ex.message}`, { fileName: elfPath });
|
54 | }
|
55 | if (!Array.isArray(platform)) {
|
56 | throw new plugin_error_1.default(PLUGIN_NAME, `Platform specification should be an array, but found a ${typeof platform}`, { fileName: elfPath });
|
57 | }
|
58 | return {
|
59 | platform,
|
60 | path: elfPath,
|
61 | data: elfData,
|
62 | appID: formatUUID(appIDData),
|
63 | buildID: `0x${Buffer.from(buildIDData).swap64().toString('hex')}`,
|
64 | family: findSection('appfamily').toString(),
|
65 | };
|
66 | }
|
67 | function nativeComponents(appID, componentPaths) {
|
68 | const components = componentPaths.map(readMetadata);
|
69 | const buildId = components[0].buildID;
|
70 | const divergentAppIDComponents = components.filter((c) => c.appID.toLowerCase() !== appID.toLowerCase());
|
71 | if (divergentAppIDComponents.length > 0) {
|
72 | const divergentAppIDsList = divergentAppIDComponents
|
73 | .map(({ path, family, appID }) => `${path} (${family}) has appID ${appID}`)
|
74 | .join('\n ');
|
75 | throw new plugin_error_1.default(PLUGIN_NAME, `App IDs of native components do not match package.json.
|
76 | Expected appID ${appID}.
|
77 | ${divergentAppIDsList}`);
|
78 | }
|
79 | if (lodash_1.default.uniqBy(components, (c) => c.buildID).length > 1) {
|
80 | const mismatchedBuildIDs = components
|
81 | .map(({ path, family, buildID }) => `${path} (${family}) has buildID ${buildID}`)
|
82 | .join('\n ');
|
83 | throw new plugin_error_1.default(PLUGIN_NAME, `Build IDs of native components do not match.
|
84 | ${mismatchedBuildIDs}`);
|
85 | }
|
86 | const componentStream = new stream_1.default.PassThrough({ objectMode: true });
|
87 | components.forEach(({ family, platform, data }) => componentStream.push(new vinyl_1.default({
|
88 | contents: data,
|
89 | path: `${family}.bundle`,
|
90 | componentBundle: { family, platform, type: 'device', isNative: true },
|
91 | })));
|
92 | componentStream.push(null);
|
93 | return {
|
94 | buildId,
|
95 | existingDeviceComponents: componentStream,
|
96 | };
|
97 | }
|
98 | exports.default = nativeComponents;
|