aboutsummaryrefslogtreecommitdiff
path: root/pw_protobuf_compiler/ts/build.ts
blob: 9ae903612faf825d918f998e24f495d586c4dec4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

import { exec, ExecException } from 'child_process';
import fs from 'fs';
import path from 'path';
import generateTemplate from './codegen/template_replacement';
import * as argModule from 'arg';
const arg = argModule.default;

const googProtobufPath = require.resolve('google-protobuf');
const googProtobufModule = fs.readFileSync(googProtobufPath, 'utf-8');

const args = arg({
  // Types
  '--proto': [String],
  '--out': String,

  // Aliases
  '-p': '--proto',
});

const protos = args['--proto'];
const outDir = args['--out'] || 'protos';

fs.mkdirSync(outDir, { recursive: true });

const run = function (executable: string, args: string[]) {
  return new Promise<void>((resolve) => {
    exec(
      `${executable} ${args.join(' ')}`,
      { cwd: process.cwd() },
      (error: ExecException | null, stdout: string | Buffer) => {
        if (error) {
          throw error;
        }

        console.log(stdout);
        resolve();
      },
    );
  });
};

const protoc = async function (protos: string[], outDir: string) {
  const PROTOC_GEN_TS_PATH = path.resolve(
    path.dirname(require.resolve('ts-protoc-gen/generate.js')),
    '..',
    '.bin',
    'protoc-gen-ts',
  );

  await run('protoc', [
    `--plugin="protoc-gen-ts=${PROTOC_GEN_TS_PATH}"`,
    `--descriptor_set_out=${outDir}/descriptor.bin`,
    `--js_out=import_style=commonjs,binary:${outDir}`,
    `--ts_out=${outDir}`,
    `--proto_path=${process.cwd()}`,
    ...protos,
  ]);

  // ES6 workaround: Replace google-protobuf imports with entire library.
  protos.forEach((protoPath) => {
    const outPath = path.join(outDir, protoPath.replace('.proto', '_pb.js'));

    if (fs.existsSync(outPath)) {
      let data = fs.readFileSync(outPath, 'utf8');
      data = data.replace(
        "var jspb = require('google-protobuf');",
        googProtobufModule,
      );
      data = data.replace('var goog = jspb;', '');
      fs.writeFileSync(outPath, data);
    }
  });
};

const makeProtoCollection = function (
  descriptorBinPath: string,
  protoPath: string,
  importPath: string,
) {
  const outputCollectionName =
    path.extname(require.resolve('./ts_proto_collection.template')) === '.ts'
      ? 'collection.ts'
      : 'collection.js';
  generateTemplate(
    `${protoPath}/${outputCollectionName}`,
    descriptorBinPath,
    require.resolve('./ts_proto_collection.template'),
    importPath,
  );
};

protoc(protos, outDir).then(() => {
  makeProtoCollection(
    path.join(outDir, 'descriptor.bin'),
    outDir,
    'pigweedjs/protos',
  );
});