Skip to content

Commit 47e55a0

Browse files
committed
feat: Added definition for npm package
Closes yoheimuta#339
1 parent ad00a18 commit 47e55a0

File tree

9 files changed

+1141
-0
lines changed

9 files changed

+1141
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,10 @@ atlassian-ide-plugin.xml
2121
# Gradle files
2222
/.gradle
2323
/build
24+
25+
# JS files
26+
/bdist/js/bin/protolint
27+
/bdist/js/bin/protoc-gen-protolint
28+
/bdist/js/node_modules/
29+
/bdist/js/node_modules/*.tgz
30+

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,28 @@ However, I recommend using one of the pre-built binaries instead because it does
6969
go install github.com/yoheimuta/protolint/cmd/protolint@latest
7070
```
7171

72+
### Within JavaScript / TypeScript
73+
74+
You can use `protolint` using your nodejs package manager like `npm` or `yarn`.
75+
76+
```sh
77+
$ npm install protolint --save-dev
78+
```
79+
80+
This will add a reference to a development dependency to your local `package.json`.
81+
82+
During install, the [install.mjs](bdist/js/install.mjs) script will be called. It will download the matching `protolint` from github. Just like [@electron/get](https://github.com/electron/get/), you can bypass the download using the following environment variables:
83+
84+
| Environment Variable | Default value | Description |
85+
|-------------------------------|---------------------------------------|-----------------------------------------------|
86+
| PROTOLINT_MIRROR_HOST | https://github.com | HTTP/Web server base url hosting the binaries |
87+
| PROTOLINT_MIRROR_REMOTE_PATH | yoheimuta/protolint/download/releases | Path to the archives on the remote host |
88+
| PROTOLINT_MIRROR_USERNAME | | HTTP Basic auth user name |
89+
| PROTOLINT_MIRROR_PASSWORD | | HTTP Basic auth password |
90+
91+
Within the remote path, the archives from the [releases](https://github.com/yoheimuta/protolint/releases/latest/) page must be
92+
mirrored.
93+
7294
## Usage
7395

7496
```sh

bdist/js/bin/protoc-gen-protolint.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env node
2+
3+
'use strict';
4+
5+
var path = require('path');
6+
var execFile = require('child_process').execFile;
7+
8+
var exe_ext = process.platform === 'win32' ? '.exe' : '';
9+
10+
var protoc = path.resolve(__dirname, 'protoc-gen-protolint' + exe_ext);
11+
12+
var args = process.argv.slice(2);
13+
14+
var child_process = execFile(protoc, args, null);
15+
16+
child_process.stdout.pipe(process.stdout);
17+
child_process.stderr.pipe(process.stderr);
18+
19+
child_process.on("exit", (exit_code, _) => {
20+
process.exit(exit_code);
21+
});

bdist/js/bin/protolint.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env node
2+
3+
'use strict';
4+
5+
var path = require('path');
6+
var execFile = require('child_process').execFile;
7+
8+
var exe_ext = process.platform === 'win32' ? '.exe' : '';
9+
10+
var protoc = path.resolve(__dirname, 'protolint' + exe_ext);
11+
12+
var args = process.argv.slice(2);
13+
14+
var child_process = execFile(protoc, args, null);
15+
16+
child_process.stdout.pipe(process.stdout);
17+
child_process.stderr.pipe(process.stderr);
18+
19+
child_process.on("exit", (exit_code, _) => {
20+
process.exit(exit_code);
21+
});
22+

bdist/js/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict';
2+
3+
/**
4+
* package.json requires this file to be present. In the future, this can
5+
* export useful information about the included tools.
6+
*/
7+
8+
module.exports = {};

bdist/js/install.mjs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { got } from 'got';
2+
import { createFetch } from 'got-fetch';
3+
import npmlog from 'npmlog';
4+
import { HttpProxyAgent } from 'http-proxy-agent';
5+
import * as fs from 'fs';
6+
import { temporaryFile } from 'tempy';
7+
import * as tar from 'tar';
8+
import * as pipeline from 'stream';
9+
10+
const _arch_mapping = { "x64": "amd64" };
11+
const _platform_mapping = { "win32": "windows" };
12+
13+
const _platform = process.platform;
14+
const _arch = process.arch;
15+
16+
// TODO FIND correct paths -> "./bin" goes to node_modules
17+
18+
const script_name = "protolint-install";
19+
20+
const module_name = "protolint";
21+
const protolint_host = process.env.PROTOLINT_MIRROR_HOST ?? "https://github.com";
22+
const protolint_path = process.env.PROTOLINT_MIRROR_REMOTE_PATH ?? `yoheimuta/${module_name}/releases/download/`;
23+
const protolint_version = process.env.npm_package_version;
24+
const platform = _platform_mapping[_platform] ?? _platform;
25+
const arch = _arch_mapping[_arch] ?? _arch;
26+
27+
const url = `${protolint_host}/${protolint_path}/v${protolint_version}/${module_name}_${protolint_version}_${platform}_${arch}.tar.gz`;
28+
29+
var agent = null;
30+
if (process.env.PROTOLINT_PROXY) {
31+
agent = HttpProxyAgent(process.env.PROTOLINT_PROXY);
32+
}
33+
34+
const instance = got.extend({
35+
followRedirect: true,
36+
maxRedirects: 3,
37+
username: process.env.PROTOLINT_MIRROR_USERNAME ?? '',
38+
password: process.env.PROTOLINT_MIRROR_PASSWORD ?? '',
39+
agent: {
40+
http: agent
41+
}
42+
});
43+
44+
function get_filename_with_extension(fileName) {
45+
const ext = process.platform == "win32" ? ".exe" : "";
46+
return `${fileName}${ext}`;
47+
}
48+
49+
npmlog.info(script_name, "Fetching protolint executable from %s", url);
50+
51+
const fetch = createFetch(instance);
52+
53+
fetch(url).then(
54+
async response => {
55+
if (response.ok)
56+
{
57+
const targetFile = temporaryFile({ name: "_protolint.tar.gz"});
58+
const out = fs.createWriteStream(targetFile, {
59+
flags: "w+"
60+
});
61+
var success = undefined;
62+
const streaming = pipeline.pipeline(
63+
response.body,
64+
out,
65+
(err) => {
66+
if (err)
67+
{
68+
npmlog.error(script_name, "Failed to save downloaded file: %s", err);
69+
success = false;
70+
}
71+
else
72+
{
73+
npmlog.info(script_name, "Protolint saved to %s", targetFile);
74+
success = true;
75+
}
76+
}
77+
);
78+
79+
while (success === undefined)
80+
{
81+
await new Promise(resolve => setTimeout(resolve, 1000));
82+
}
83+
84+
if (success)
85+
{
86+
return targetFile;
87+
}
88+
89+
return null;
90+
}
91+
else
92+
{
93+
npmlog.error(script_name, "Failed to download %s. Got status: %i", response.url, response.status);
94+
return null;
95+
}
96+
}
97+
).then(
98+
previous => {
99+
if (!fs.existsSync("./bin"))
100+
{
101+
fs.mkdirSync("./bin");
102+
}
103+
104+
return previous;
105+
}
106+
).then(
107+
async file => {
108+
if (file)
109+
{
110+
const result = await tar.x(
111+
{
112+
"keep-existing": false,
113+
cwd: "./bin/",
114+
sync: false,
115+
file: file,
116+
strict: true,
117+
},
118+
[
119+
get_filename_with_extension("protolint"),
120+
get_filename_with_extension("protoc-gen-protolint"),
121+
],
122+
(err) => {
123+
if (err) {
124+
npmlog.error(script_name, "Failed to extract protlint executables: %s");
125+
}
126+
},
127+
)
128+
.then(
129+
() => {
130+
return {
131+
protolint: `./bin/${get_filename_with_extension("protolint")}`,
132+
protoc_gen_protolint: `./bin/${get_filename_with_extension("protoc-gen-protolint")}`,
133+
};
134+
}
135+
).catch(
136+
(err) => {
137+
npmlog.error(script_name, "Failed to extract files from downloaded tar file: %s", err);
138+
return {
139+
protolint: undefined,
140+
protoc_gen_protolint: undefined,
141+
};
142+
}
143+
);
144+
145+
return result;
146+
}
147+
else
148+
{
149+
npmlog.warn(script_name, "Could not find downloaded protolint archive.");
150+
return {
151+
protolint: undefined,
152+
protoc_gen_protolint: undefined,
153+
};
154+
}
155+
}
156+
).then(
157+
(protolint_obj) => {
158+
return (protolint_obj != null && protolint_obj.protolint != null && protolint_obj.protoc_gen_protolint != null);
159+
}
160+
).then(
161+
(result) => {
162+
if (result){
163+
npmlog.info(script_name, "Protolint installed successfully.");
164+
}
165+
else {
166+
npmlog.warn(script_name, "Failed to download protolint. See previous messages for details");
167+
}
168+
}
169+
).catch(
170+
reason => {
171+
npmlog.error(script_name, "Failed to install protolint: %s", reason);
172+
process.exit(1);
173+
}
174+
);

0 commit comments

Comments
 (0)