Skip to content

Commit fb8aa61

Browse files
committed
Prototype a webpack plugin that makes the output files executable
1 parent d8ea470 commit fb8aa61

File tree

3 files changed

+53
-4
lines changed

3 files changed

+53
-4
lines changed

workspaces/fetch-leetcode-problem-list/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"type": "module",
1616
"exports": "./src/main.ts",
1717
"scripts": {
18-
"build": "cross-env NODE_OPTIONS=\"--import tsx\" webpack && chmod +x dist/fetch-leetcode-problem-list.cjs",
18+
"build": "cross-env NODE_OPTIONS=\"--import tsx\" webpack",
1919
"format": "prettier --color --write .",
2020
"lint": "eslint --color --max-warnings=0 .",
2121
"start": "tsx src/main.ts",

workspaces/fetch-leetcode-problem-list/webpack.config.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import { chmod } from "node:fs/promises";
12
import { builtinModules } from "node:module";
23
import path from "node:path";
34

45
import webpack, {
6+
type Compiler,
57
type Configuration,
68
type ExternalItemFunctionData,
79
} from "webpack";
@@ -11,6 +13,35 @@ import { stripPrefixOrThrow } from "@code-chronicles/util/stripPrefixOrThrow";
1113

1214
import packageJson from "./package.json" with { type: "json" };
1315

16+
class WebpackMakeOutputExecutablePLugin {
17+
// eslint-disable-next-line class-methods-use-this -- This is the interface expected by webpack.
18+
apply(compiler: Compiler): void {
19+
compiler.hooks.afterEmit.tapAsync(
20+
"WebpackMakeOutputExecutablePLugin",
21+
async (compilation) => {
22+
const promises: Promise<void>[] = [];
23+
24+
for (const chunk of compilation.chunks) {
25+
if (!chunk.canBeInitial()) {
26+
continue;
27+
}
28+
29+
for (const file of chunk.files) {
30+
promises.push(
31+
chmod(
32+
path.join(compilation.outputOptions.path ?? ".", file),
33+
0o755,
34+
),
35+
);
36+
}
37+
}
38+
39+
await Promise.all(promises);
40+
},
41+
);
42+
}
43+
}
44+
1445
const config: Configuration = {
1546
target: "node",
1647
entry: path.resolve(__dirname, packageJson.exports),
@@ -58,6 +89,8 @@ const config: Configuration = {
5889
raw: true,
5990
entryOnly: true,
6091
}),
92+
93+
new WebpackMakeOutputExecutablePLugin(),
6194
],
6295
};
6396

workspaces/util/src/only.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
import invariant from "invariant";
2+
import nullthrows from "nullthrows";
23

3-
export function only<T>(array: readonly T[]): T {
4-
invariant(array.length === 1, "Expected a single element array!");
5-
return array[0];
4+
export function only<T>(iterable: Iterable<T>): T {
5+
if (Array.isArray(iterable)) {
6+
const { length } = iterable;
7+
invariant(
8+
length === 1,
9+
"Given array has length %d, not 1 as expected!",
10+
length,
11+
);
12+
return iterable[0];
13+
}
14+
15+
let elementBox: { element: T } | null = null;
16+
for (const element of iterable) {
17+
invariant(elementBox == null, "Iterable had multiple elements!");
18+
elementBox = { element };
19+
}
20+
21+
return nullthrows(elementBox, "Empty iterable!").element;
622
}

0 commit comments

Comments
 (0)