Skip to content

Commit 4aa3720

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

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-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: 30 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,32 @@ 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("WebpackMakeOutputExecutablePLugin", async (compilation) => {
20+
const promises: Promise<void>[] = [];
21+
22+
for (const chunk of compilation.chunks) {
23+
if (!chunk.canBeInitial()) {
24+
continue;
25+
}
26+
27+
for (const file of chunk.files) {
28+
promises.push(
29+
chmod(
30+
path.join(compilation.outputOptions.path ?? ".", file),
31+
0o755,
32+
),
33+
);
34+
}
35+
}
36+
37+
await Promise.all(promises);
38+
});
39+
}
40+
}
41+
1442
const config: Configuration = {
1543
target: "node",
1644
entry: path.resolve(__dirname, packageJson.exports),
@@ -58,6 +86,8 @@ const config: Configuration = {
5886
raw: true,
5987
entryOnly: true,
6088
}),
89+
90+
new WebpackMakeOutputExecutablePLugin(),
6191
],
6292
};
6393

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)