• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2022 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15import { exec, ExecException } from 'child_process';
16import fs from 'fs';
17import path from 'path';
18import generateTemplate from './codegen/template_replacement';
19import * as argModule from 'arg';
20const arg = argModule.default;
21
22const googProtobufPath = require.resolve('google-protobuf');
23const googProtobufModule = fs.readFileSync(googProtobufPath, 'utf-8');
24
25const args = arg({
26  // Types
27  '--proto': [String],
28  '--out': String,
29
30  // Aliases
31  '-p': '--proto',
32});
33
34const protos = args['--proto'];
35const outDir = args['--out'] || 'protos';
36
37fs.mkdirSync(outDir, { recursive: true });
38
39const run = function (executable: string, args: string[]) {
40  return new Promise<void>((resolve) => {
41    exec(
42      `${executable} ${args.join(' ')}`,
43      { cwd: process.cwd() },
44      (error: ExecException | null, stdout: string | Buffer) => {
45        if (error) {
46          throw error;
47        }
48
49        console.log(stdout);
50        resolve();
51      },
52    );
53  });
54};
55
56const protoc = async function (protos: string[], outDir: string) {
57  const PROTOC_GEN_TS_PATH = path.resolve(
58    path.dirname(require.resolve('ts-protoc-gen/generate.js')),
59    '..',
60    '.bin',
61    'protoc-gen-ts',
62  );
63
64  await run('protoc', [
65    `--plugin="protoc-gen-ts=${PROTOC_GEN_TS_PATH}"`,
66    `--descriptor_set_out=${outDir}/descriptor.bin`,
67    `--js_out=import_style=commonjs,binary:${outDir}`,
68    `--ts_out=${outDir}`,
69    `--proto_path=${process.cwd()}`,
70    ...protos,
71  ]);
72
73  // ES6 workaround: Replace google-protobuf imports with entire library.
74  protos.forEach((protoPath) => {
75    const outPath = path.join(outDir, protoPath.replace('.proto', '_pb.js'));
76
77    if (fs.existsSync(outPath)) {
78      let data = fs.readFileSync(outPath, 'utf8');
79      data = data.replace(
80        "var jspb = require('google-protobuf');",
81        googProtobufModule,
82      );
83      data = data.replace('var goog = jspb;', '');
84      fs.writeFileSync(outPath, data);
85    }
86  });
87};
88
89const makeProtoCollection = function (
90  descriptorBinPath: string,
91  protoPath: string,
92  importPath: string,
93) {
94  const outputCollectionName =
95    path.extname(require.resolve('./ts_proto_collection.template')) === '.ts'
96      ? 'collection.ts'
97      : 'collection.js';
98  generateTemplate(
99    `${protoPath}/${outputCollectionName}`,
100    descriptorBinPath,
101    require.resolve('./ts_proto_collection.template'),
102    importPath,
103  );
104};
105
106protoc(protos, outDir).then(() => {
107  makeProtoCollection(
108    path.join(outDir, 'descriptor.bin'),
109    outDir,
110    'pigweedjs/protos',
111  );
112});
113