jam 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -232
- package/bin/jam.js +31 -0
- package/install.js +108 -0
- package/lib/platform.js +72 -0
- package/lib/resolve-binary.js +37 -0
- package/package.json +24 -27
- package/.npmignore +0 -6
- package/.travis.yml +0 -3
- package/Makefile +0 -67
- package/example/file1.txt +0 -1
- package/example/file2.txt +0 -1
- package/example/file3.txt +0 -1
- package/example/map.js +0 -29
- package/index.js +0 -6
- package/lib/jam.js +0 -295
- package/test/helpers.js +0 -271
- package/test/jam.js +0 -121
package/README.md
CHANGED
|
@@ -1,249 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
# jam
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Install and run the Jam server binary from npm.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
$ npm install jam --save
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
JAM is another kind of async framework that tries to have as minimum boilerplate code as
|
|
10
|
-
possible with sensible defaults. (Or as sensible as I can make it; PR and ideas welcome.)
|
|
11
|
-
|
|
12
|
-
JAM wants you to get right in to building your `async` chain as soon as possible.
|
|
13
|
-
|
|
14
|
-
JAM also aims to bundle with itself some "combinators" (or just "helpers") which helps you
|
|
15
|
-
manipulate arguments and functions that are being passed around in the chain with ease.
|
|
16
|
-
There is only a handful of them right now, but I will add more whenever I see a good use
|
|
17
|
-
for one.
|
|
18
|
-
|
|
19
|
-
# HOW TO
|
|
20
|
-
|
|
21
|
-
JAM functions must accept a `next` argument first thing which you should call as soon as
|
|
22
|
-
your asynchronous processing is done:
|
|
23
|
-
|
|
24
|
-
Let's start with the simplest possible invocation of jam:
|
|
25
|
-
|
|
26
|
-
```js
|
|
27
|
-
var chain = jam( function(next) { next(); } );
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
**JAM will starts executing your chain as soon as `nextTick`.** So, in the event loop time
|
|
31
|
-
you have been allocated, you can add as many methods as you like and the chain will start
|
|
32
|
-
executing as soon your loop finishes. No more steps necessary!
|
|
33
|
-
|
|
34
|
-
You may think that this poses a problem but I find that most (if not all) of the cases
|
|
35
|
-
where you want to do multiple asynchronous calls, you will build all your calls in a
|
|
36
|
-
single run loop. So this is a non-issue.
|
|
37
|
-
|
|
38
|
-
To add a method to the chain, simply invoke the result from the last JAM invocation as a
|
|
39
|
-
function like this:
|
|
40
|
-
|
|
41
|
-
```js
|
|
42
|
-
chain = chain( function secondStep(next) { next(); } );
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
JAM expects most asynchronous method calls to be executed serially so that is what the
|
|
46
|
-
chain does by default when you start adding methods to the chain.
|
|
47
|
-
|
|
48
|
-
Since JAM return values are just functions, you don't even need to hold it in a variable
|
|
49
|
-
if you like extra brevity of code:
|
|
50
|
-
|
|
51
|
-
```js
|
|
52
|
-
jam( function firstStep(next) { next(); } )
|
|
53
|
-
( function secondStep(next) { next(); } )
|
|
54
|
-
( function lastStep() { } );
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
JAM also handles `Error`s for you. Note:
|
|
58
|
-
|
|
59
|
-
* The convention here is that the last function in the chain is often the one that will
|
|
60
|
-
handle all errors in the chain.
|
|
61
|
-
* The last function does not need any more `next()` since it's the last one.
|
|
62
|
-
|
|
63
|
-
JAM convention utilizes the two facts above to pass any `Error` that happens in the chain
|
|
64
|
-
to the last function as first argument.
|
|
65
|
-
|
|
66
|
-
So if you need error handling, write the last function as a standard node.js callback:
|
|
67
|
-
|
|
68
|
-
```js
|
|
69
|
-
jam(function erroneous(next) {
|
|
70
|
-
next(new Error('naw!');
|
|
71
|
-
})
|
|
72
|
-
(function handler(err) {
|
|
73
|
-
if (err) { console.log(err.stack); }
|
|
74
|
-
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Additionally, JAM also passes anything else given to the `next()` function to the next one
|
|
78
|
-
as arguments as well so you can do this:
|
|
79
|
-
|
|
80
|
-
```js
|
|
81
|
-
jam(function(next) { fs.readFile('filename.txt', next); })
|
|
82
|
-
(function(next, data) {
|
|
83
|
-
console.log("FILE DATA:\r\n" + data);
|
|
84
|
-
});
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
This is much better than if JAM put `next()` as the last argument since some functions
|
|
88
|
-
calls your `callback` with more arguments than you need (or aware of) thus making your
|
|
89
|
-
code dependent on the number of arguments given.
|
|
90
|
-
|
|
91
|
-
Passing `next` as first argument eliminates the dependency since you can bind as many
|
|
92
|
-
arguments as you want and the `next()` is still passed as first argument always.
|
|
93
|
-
|
|
94
|
-
Since this pattern allows you to pass functions verbatim, JAM also helps you binds the
|
|
95
|
-
funciton context as well if you supply the context object as the second argument:
|
|
96
|
-
|
|
97
|
-
```js
|
|
98
|
-
var myObj =
|
|
99
|
-
{ text: 'HELLO'
|
|
100
|
-
, echo: function() { console.log(this.text); }
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
jam(myObj.echo, myObj); // executes myObj.echo with this === myObj
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Additionally, there are helpers available that lets you build JAM chains more easily.
|
|
5
|
+
## Usage
|
|
107
6
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
available with with the [annotated source of jam.js](http://gh.chakrit.net/jam/).
|
|
112
|
-
|
|
113
|
-
Or feel free to ping me [@chakrit](http://twitter.com/chakrit) on Twitter or open a GH
|
|
114
|
-
issue for questions.
|
|
115
|
-
|
|
116
|
-
#### identity( )
|
|
117
|
-
|
|
118
|
-
```js
|
|
119
|
-
jam(function first(next) { next('one'); })
|
|
120
|
-
(jam.identity)
|
|
121
|
-
(function second(err, arg) {
|
|
122
|
-
assert(arg === 'one'); // passese
|
|
123
|
-
});
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Passes arguments it receives to the next function in the chain without any modification.
|
|
127
|
-
Also useful as a starting point when building a complex jam chain (i.e. in a for loops
|
|
128
|
-
that re-uses the jam return values.)
|
|
129
|
-
|
|
130
|
-
See `nextTick()` below.
|
|
131
|
-
|
|
132
|
-
#### nextTick( )
|
|
133
|
-
|
|
134
|
-
```js
|
|
135
|
-
jam( function firstStep(next) { next(); } )
|
|
136
|
-
( jam.nextTick )
|
|
137
|
-
( function badSecondStep(next) { next(); } );
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Except for the first invocation, JAM chains are executed synchronously one after another
|
|
141
|
-
as soon as you call `next()`. This may pose a problem for some code that does not expect
|
|
142
|
-
asynchronous functions to execute immeditaely.
|
|
143
|
-
|
|
144
|
-
This function fixes this case by inserting a nextTick() in-between the call chain to make
|
|
145
|
-
sure it executes on `process.nextTick`.
|
|
146
|
-
|
|
147
|
-
This function is actually just an alias for `.identity`
|
|
148
|
-
|
|
149
|
-
#### return( [args...] )
|
|
150
|
-
|
|
151
|
-
```js
|
|
152
|
-
function handleFileContent(e, file) {
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
// parallel handleFileContent jam
|
|
156
|
-
['file1.txt', 'file2.txt', 'file3.txt'].forEach(function(file) {
|
|
157
|
-
jam(jam.return(file))
|
|
158
|
-
(jam.call(fs.readFile)) // no function() needed!
|
|
159
|
-
(handleFileContent);
|
|
160
|
-
});
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
This allows you to provide arguments to the next function in the chain (or for starting
|
|
164
|
-
it) without modifying or wrapping code for the rest of the chains.
|
|
165
|
-
|
|
166
|
-
#### call( func, [args...] )
|
|
167
|
-
|
|
168
|
-
```js
|
|
169
|
-
jam(jam.call(findTheRightFile))
|
|
170
|
-
(jam.call(fs.readFile))
|
|
171
|
-
(function(e, fileContent) {
|
|
172
|
-
// fileContent is the content of the right file
|
|
173
|
-
});
|
|
7
|
+
```sh
|
|
8
|
+
npm install -g jam
|
|
9
|
+
jam --help
|
|
174
10
|
```
|
|
175
11
|
|
|
176
|
-
|
|
177
|
-
Additionally, any arguments that would normally be given to the chain function would be
|
|
178
|
-
used to call the function instead (`next()` is then added at the end of the arguments
|
|
179
|
-
list).
|
|
180
|
-
|
|
181
|
-
#### each and map( array, iterator( next, element, index ) )
|
|
182
|
-
|
|
183
|
-
```js
|
|
184
|
-
var FILES = 'file1.txt,file2.txt,file3.txt'.split(',');
|
|
12
|
+
You can also use it without a global install:
|
|
185
13
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
})(function(e, result) {
|
|
190
|
-
var cat = result.join('');
|
|
191
|
-
console.log(cat);
|
|
192
|
-
|
|
193
|
-
});
|
|
14
|
+
```sh
|
|
15
|
+
npx jam --help
|
|
194
16
|
```
|
|
195
17
|
|
|
196
|
-
|
|
197
|
-
of `next()` and the element to process. If no array is given, the method assumes that the
|
|
198
|
-
previous step in the chain produce something that looks like an array.
|
|
199
|
-
|
|
200
|
-
Internally a new JAM chain is built and a chain step is added for each element.
|
|
201
|
-
|
|
202
|
-
The next step in the JAM chain will receive the original array verbatim, or the
|
|
203
|
-
transformed result in case of `map`.
|
|
204
|
-
|
|
205
|
-
See the `example/map.js` file for more information.
|
|
206
|
-
|
|
207
|
-
# LICENSE
|
|
208
|
-
|
|
209
|
-
BSD
|
|
210
|
-
|
|
211
|
-
# SUPPORT / CONTRIBUTE
|
|
212
|
-
|
|
213
|
-
Pull requests and/or ideas welcome.
|
|
214
|
-
|
|
215
|
-
Please open a [new GitHub Issue](https://github.com/chakrit/jam/issues/new) for any bugs
|
|
216
|
-
you find or if you just had a question.
|
|
217
|
-
|
|
218
|
-
#### TODOs
|
|
18
|
+
## How it works
|
|
219
19
|
|
|
220
|
-
|
|
221
|
-
* Nullify calls, in case you don't want any arguments passed.
|
|
222
|
-
* Parellel map() ?
|
|
20
|
+
On install, this package detects your OS/CPU target and downloads the matching Jam binary from GitHub Releases.
|
|
223
21
|
|
|
224
|
-
|
|
22
|
+
Expected release asset names:
|
|
225
23
|
|
|
226
|
-
|
|
227
|
-
|
|
24
|
+
- `jam-darwin-arm64`
|
|
25
|
+
- `jam-darwin-x64`
|
|
26
|
+
- `jam-linux-x64`
|
|
27
|
+
- `jam-linux-arm64`
|
|
228
28
|
|
|
229
|
-
|
|
230
|
-
really isn't one where you could quickly just type-in the list of stuff to do and be done
|
|
231
|
-
with it without worrying about forgetting to close the list with that final parenthesis or
|
|
232
|
-
forgetting to add a comma. And yeah, IMO it is wayyy easier to just add a
|
|
233
|
-
`(function() { })` block at the end because that's what you're usually doing all the time
|
|
234
|
-
anyway taking care of all those JS variable scopes. Plus it is easier to
|
|
235
|
-
copy/paste/reorder the steps as well.
|
|
29
|
+
Current support is macOS (`darwin`) and Linux glibc builds. Linux musl and Windows targets are not published yet.
|
|
236
30
|
|
|
237
|
-
|
|
238
|
-
to run asynchronous functions where most of the time you just want to reduce the amount of
|
|
239
|
-
nesting in your code.
|
|
31
|
+
The binary is stored inside this package at `downloaded/jam`, and the `jam` npm bin shim executes it.
|
|
240
32
|
|
|
241
|
-
|
|
242
|
-
sane defaults and then provide helpers for bringing edge cases into this minimal interface
|
|
243
|
-
neatly so you can just get your stuff done without worrying about wether you are using the
|
|
244
|
-
right async call or if you have the right number of arguments.
|
|
33
|
+
## Environment overrides
|
|
245
34
|
|
|
246
|
-
|
|
35
|
+
- `JAM_SKIP_DOWNLOAD=1`: skip binary download during install.
|
|
36
|
+
- `JAM_FORCE_DOWNLOAD=1`: force download even in the Jam source checkout.
|
|
37
|
+
- `JAM_BINARY_VERSION=<version>`: download a different Jam release version.
|
|
38
|
+
- `JAM_BINARY_BASE_URL=<url>`: override the release URL prefix.
|
|
39
|
+
- `JAM_LIBC=glibc|musl`: override Linux libc detection.
|
|
40
|
+
- `JAM_BINARY_PATH=<path>`: run a specific local binary instead of the downloaded one.
|
|
247
41
|
|
|
248
|
-
|
|
42
|
+
## Local development
|
|
249
43
|
|
|
44
|
+
When installing from the Jam source checkout (`packages/jam` workspace), postinstall download is skipped automatically. Set `JAM_FORCE_DOWNLOAD=1` to force a download during local testing.
|
package/bin/jam.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
let { spawnSync } = require("node:child_process");
|
|
5
|
+
|
|
6
|
+
let { resolveBinaryPath } = require("../lib/resolve-binary");
|
|
7
|
+
|
|
8
|
+
function main() {
|
|
9
|
+
try {
|
|
10
|
+
let binaryPath = resolveBinaryPath();
|
|
11
|
+
let result = spawnSync(binaryPath, process.argv.slice(2), {
|
|
12
|
+
stdio: "inherit",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (result.error) throw result.error;
|
|
16
|
+
|
|
17
|
+
if (typeof result.status === "number") {
|
|
18
|
+
process.exit(result.status);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (result.signal) {
|
|
22
|
+
process.kill(process.pid, result.signal);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error(`[jam] ${error.message}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main();
|
package/install.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
let fs = require("node:fs");
|
|
5
|
+
let path = require("node:path");
|
|
6
|
+
let { Readable } = require("node:stream");
|
|
7
|
+
let { pipeline } = require("node:stream/promises");
|
|
8
|
+
|
|
9
|
+
let { getRuntimeDescriptor } = require("./lib/platform");
|
|
10
|
+
let { downloadedBinaryPath } = require("./lib/resolve-binary");
|
|
11
|
+
|
|
12
|
+
let MAX_REDIRECTS = 5;
|
|
13
|
+
let REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
|
|
14
|
+
|
|
15
|
+
function loadPackageVersion() {
|
|
16
|
+
let pkgPath = path.join(__dirname, "package.json");
|
|
17
|
+
let pkgJson = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
18
|
+
return pkgJson.version;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function shouldSkipInstall(version) {
|
|
22
|
+
if (process.env.JAM_SKIP_DOWNLOAD === "1") return true;
|
|
23
|
+
if (isJamSourceCheckout() && process.env.JAM_FORCE_DOWNLOAD !== "1") return true;
|
|
24
|
+
return version.startsWith("0.0.0-");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isJamSourceCheckout() {
|
|
28
|
+
let repoRoot = path.join(__dirname, "..", "..");
|
|
29
|
+
return (
|
|
30
|
+
fs.existsSync(path.join(repoRoot, "go.mod")) &&
|
|
31
|
+
fs.existsSync(path.join(repoRoot, "cmd", "jam", "main.go")) &&
|
|
32
|
+
fs.existsSync(path.join(repoRoot, "pnpm-workspace.yaml"))
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function buildDownloadUrl(target, binaryName, version) {
|
|
37
|
+
let releaseVersion = process.env.JAM_BINARY_VERSION || version;
|
|
38
|
+
let explicitBaseUrl = process.env.JAM_BINARY_BASE_URL;
|
|
39
|
+
let baseUrl =
|
|
40
|
+
explicitBaseUrl ||
|
|
41
|
+
`https://github.com/mjackson/jam/releases/download/v${releaseVersion}`;
|
|
42
|
+
return `${baseUrl.replace(/\/+$/, "")}/jam-${target}${binaryName.endsWith(".exe") ? ".exe" : ""}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function fetchToFile(url, filePath, redirects) {
|
|
46
|
+
if (redirects > MAX_REDIRECTS) {
|
|
47
|
+
throw new Error(`Too many redirects while downloading ${url}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let response = await fetch(url, {
|
|
51
|
+
headers: {
|
|
52
|
+
"user-agent": "jam-npm-installer",
|
|
53
|
+
},
|
|
54
|
+
redirect: "manual",
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (REDIRECT_STATUSES.has(response.status)) {
|
|
58
|
+
let location = response.headers.get("location");
|
|
59
|
+
if (!location) {
|
|
60
|
+
throw new Error(`Redirect missing location header: ${response.status} ${url}`);
|
|
61
|
+
}
|
|
62
|
+
let redirectUrl = new URL(location, url).toString();
|
|
63
|
+
await fetchToFile(redirectUrl, filePath, redirects + 1);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (response.status !== 200) {
|
|
68
|
+
throw new Error(`Download failed: ${response.status} ${url}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!response.body) {
|
|
72
|
+
throw new Error(`Download failed: empty response body for ${url}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let out = fs.createWriteStream(filePath, { mode: 0o755 });
|
|
76
|
+
await pipeline(Readable.fromWeb(response.body), out);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function install() {
|
|
80
|
+
let version = loadPackageVersion();
|
|
81
|
+
if (shouldSkipInstall(version)) {
|
|
82
|
+
if (isJamSourceCheckout()) {
|
|
83
|
+
console.log("[jam] Skipping binary download in Jam source checkout.");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
console.log("[jam] Skipping binary download.");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let descriptor = getRuntimeDescriptor();
|
|
91
|
+
let downloadUrl = buildDownloadUrl(descriptor.target, descriptor.binaryName, version);
|
|
92
|
+
let outputPath = downloadedBinaryPath(descriptor);
|
|
93
|
+
|
|
94
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
95
|
+
console.log(`[jam] Downloading ${descriptor.target} binary from ${downloadUrl}`);
|
|
96
|
+
await fetchToFile(downloadUrl, outputPath, 0);
|
|
97
|
+
|
|
98
|
+
if (descriptor.platform !== "win32") {
|
|
99
|
+
fs.chmodSync(outputPath, 0o755);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(`[jam] Installed binary to ${outputPath}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
install().catch((error) => {
|
|
106
|
+
console.error(`[jam] ${error.message}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
});
|
package/lib/platform.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function parseNodeLibcFromReport(report) {
|
|
4
|
+
if (!report || !report.header) return null;
|
|
5
|
+
if (report.header.glibcVersionRuntime) return "glibc";
|
|
6
|
+
|
|
7
|
+
let reportText = JSON.stringify(report).toLowerCase();
|
|
8
|
+
if (reportText.includes("musl")) return "musl";
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function detectLibc(runtime) {
|
|
13
|
+
if (runtime.platform !== "linux") return null;
|
|
14
|
+
|
|
15
|
+
let forced = runtime.env.JAM_LIBC;
|
|
16
|
+
if (forced === "glibc" || forced === "musl") return forced;
|
|
17
|
+
|
|
18
|
+
let fromReport = parseNodeLibcFromReport(runtime.report);
|
|
19
|
+
if (fromReport) return fromReport;
|
|
20
|
+
|
|
21
|
+
return "glibc";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveTarget(platform, arch, libc) {
|
|
25
|
+
if (platform === "darwin" && arch === "arm64") return "darwin-arm64";
|
|
26
|
+
if (platform === "darwin" && arch === "x64") return "darwin-x64";
|
|
27
|
+
|
|
28
|
+
if (platform === "linux" && arch === "x64" && libc === "glibc") return "linux-x64";
|
|
29
|
+
if (platform === "linux" && arch === "arm64" && libc === "glibc") return "linux-arm64";
|
|
30
|
+
if (platform === "linux" && libc === "musl") {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Linux musl is not supported yet: platform=${platform} arch=${arch} libc=${libc}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Unsupported platform for Jam npm package: platform=${platform} arch=${arch} libc=${libc || "n/a"}`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function binaryFileName(platform) {
|
|
42
|
+
return platform === "win32" ? "jam.exe" : "jam";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getRuntimeDescriptor(overrides) {
|
|
46
|
+
let platform = overrides?.platform ?? process.platform;
|
|
47
|
+
let arch = overrides?.arch ?? process.arch;
|
|
48
|
+
let report =
|
|
49
|
+
overrides?.report ??
|
|
50
|
+
(process.report && typeof process.report.getReport === "function"
|
|
51
|
+
? process.report.getReport()
|
|
52
|
+
: null);
|
|
53
|
+
let env = overrides?.env ?? process.env;
|
|
54
|
+
|
|
55
|
+
let libc = detectLibc({ platform, env, report });
|
|
56
|
+
let target = resolveTarget(platform, arch, libc);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
arch,
|
|
60
|
+
libc,
|
|
61
|
+
platform,
|
|
62
|
+
target,
|
|
63
|
+
binaryName: binaryFileName(platform),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
binaryFileName,
|
|
69
|
+
detectLibc,
|
|
70
|
+
getRuntimeDescriptor,
|
|
71
|
+
resolveTarget,
|
|
72
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
let fs = require("node:fs");
|
|
4
|
+
let path = require("node:path");
|
|
5
|
+
|
|
6
|
+
let { getRuntimeDescriptor } = require("./platform");
|
|
7
|
+
|
|
8
|
+
function packageRoot() {
|
|
9
|
+
return path.join(__dirname, "..");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function downloadedBinaryPath(runtimeDescriptor) {
|
|
13
|
+
let descriptor = runtimeDescriptor || getRuntimeDescriptor();
|
|
14
|
+
return path.join(packageRoot(), "downloaded", descriptor.binaryName);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveBinaryPath() {
|
|
18
|
+
let fromEnv = process.env.JAM_BINARY_PATH;
|
|
19
|
+
if (fromEnv) return fromEnv;
|
|
20
|
+
|
|
21
|
+
let descriptor = getRuntimeDescriptor();
|
|
22
|
+
let targetPath = downloadedBinaryPath(descriptor);
|
|
23
|
+
if (fs.existsSync(targetPath)) return targetPath;
|
|
24
|
+
|
|
25
|
+
throw new Error(
|
|
26
|
+
[
|
|
27
|
+
`Jam binary not found for ${descriptor.target}.`,
|
|
28
|
+
`Expected: ${targetPath}`,
|
|
29
|
+
"Try reinstalling the package or run `npm rebuild jam`.",
|
|
30
|
+
].join("\n")
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
downloadedBinaryPath,
|
|
36
|
+
resolveBinaryPath,
|
|
37
|
+
};
|
package/package.json
CHANGED
|
@@ -1,36 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jam",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"test": "make test"
|
|
8
|
-
},
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "An application server for isolated JavaScript",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
9
7
|
"repository": {
|
|
10
8
|
"type": "git",
|
|
11
|
-
"url": "git://github.com/
|
|
9
|
+
"url": "git+https://github.com/mjackson/jam.git",
|
|
10
|
+
"directory": "packages/jam"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/mjackson/jam",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/mjackson/jam/issues"
|
|
12
15
|
},
|
|
13
|
-
"
|
|
14
|
-
"jam"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
16
|
+
"bin": {
|
|
17
|
+
"jam": "bin/jam.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"bin",
|
|
21
|
+
"lib",
|
|
22
|
+
"install.js",
|
|
23
|
+
"README.md"
|
|
19
24
|
],
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"test": "test",
|
|
25
|
-
"lib": "lib"
|
|
25
|
+
"scripts": {
|
|
26
|
+
"postinstall": "node install.js",
|
|
27
|
+
"test": "node test/platform.test.js",
|
|
28
|
+
"prepublishOnly": "node test/platform.test.js"
|
|
26
29
|
},
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"mocha-istanbul": "~0.2.0",
|
|
30
|
-
"mocha": "~1.12.0",
|
|
31
|
-
"plato": "~0.6.1",
|
|
32
|
-
"chai": "~1.7.2",
|
|
33
|
-
"sinon": "~1.7.3",
|
|
34
|
-
"groc": "~0.4.0"
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
35
32
|
}
|
|
36
33
|
}
|
package/.npmignore
DELETED
package/.travis.yml
DELETED
package/Makefile
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
BIN := $(shell pwd)/node_modules/.bin
|
|
3
|
-
|
|
4
|
-
GLOBALS := __coverage__,buffertools,SlowBuffer,events,util,task
|
|
5
|
-
TEST_ENV := test
|
|
6
|
-
|
|
7
|
-
# Project files definition
|
|
8
|
-
TEST_FILES := $(wildcard test/**/*.js) $(wildcard test/*.js)
|
|
9
|
-
LIB_FILES := $(wildcard lib/**/*.js) $(wildcard lib/*.js)
|
|
10
|
-
COV_FILES := $(LIB_FILES:lib/%.js=lib-cov/%.js)
|
|
11
|
-
|
|
12
|
-
INDEX_FILE = index.js
|
|
13
|
-
MAIN_FILE = lib/jam.js
|
|
14
|
-
|
|
15
|
-
# Test parameters so we can configure these via make
|
|
16
|
-
TEST_TIMEOUT = 100
|
|
17
|
-
TEST_REPORTER = list
|
|
18
|
-
TDD_REPORTER = min
|
|
19
|
-
COVER_REPORTER = mocha-istanbul
|
|
20
|
-
|
|
21
|
-
# Command-line tools options
|
|
22
|
-
MOCHA_OPTS = --bail --timeout $(TEST_TIMEOUT) --reporter $(TEST_REPORTER) --globals $(GLOBALS)
|
|
23
|
-
MOCHA_TDD_OPTS = $(MOCHA_OPTS) --watch --reporter $(TDD_REPORTER)
|
|
24
|
-
MOCHA_COVER_OPTS = $(MOCHA_OPTS) --reporter $(COVER_REPORTER)
|
|
25
|
-
ISTANBUL_OPTS = instrument --variable global.__coverage__ --no-compact
|
|
26
|
-
PLATO_OPTS = -d html-report/
|
|
27
|
-
GROC_OPTS = -t lib/ -o doc/ --no-whitespace-after-token false --index $(MAIN_FILE)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
default: node_modules
|
|
31
|
-
|
|
32
|
-
node_modules:
|
|
33
|
-
npm install
|
|
34
|
-
|
|
35
|
-
# File transformations
|
|
36
|
-
lib-cov/%.js: lib/%.js
|
|
37
|
-
@mkdir -p $(@D)
|
|
38
|
-
$(BIN)/istanbul $(ISTANBUL_OPTS) --output $@ $<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# Testing
|
|
42
|
-
test: node_modules
|
|
43
|
-
NODE_ENV=$(TEST_ENV) $(BIN)/mocha $(MOCHA_OPTS) $(TEST_FILES)
|
|
44
|
-
tdd: node_modules
|
|
45
|
-
NODE_ENV=$(TEST_ENV) $(BIN)/mocha $(MOCHA_TDD_OPTS) $(TEST_FILES)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# Code instrumentation
|
|
49
|
-
instrument: node_modules $(COV_FILES)
|
|
50
|
-
cover: instrument
|
|
51
|
-
NODE_ENV=$(TEST_ENV) JAM_COVER=1 $(BIN)/mocha $(MOCHA_COVER_OPTS) $(TEST_FILES)
|
|
52
|
-
complex:
|
|
53
|
-
$(BIN)/plato $(PLATO_OPTS) $(LIB_FILES)
|
|
54
|
-
|
|
55
|
-
doc:
|
|
56
|
-
$(BIN)/groc $(GROC_OPTS) $(LIB_FILES)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Cleans
|
|
60
|
-
clean:
|
|
61
|
-
-rm -Rf lib-cov/
|
|
62
|
-
-rm -Rf html-report/
|
|
63
|
-
-rm -Rf doc/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.PHONY: debug default test tdd clean doc doc-gh instrument cover complex
|
|
67
|
-
|
package/example/file1.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a - First! I am the very very first file.
|
package/example/file2.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
b - I am the second file, residing in B. 2nd's not a bad place, ain't it?
|
package/example/file3.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
c - I'm the last one. I get to write the ending!
|