Skip to content

Commit 2719fb5

Browse files
committed
feat: replace ts-node shebangs with node ones in published bins
1 parent 02f4295 commit 2719fb5

File tree

3 files changed

+100
-2
lines changed

3 files changed

+100
-2
lines changed

lib/content/update-shebang.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env ts-node
2+
3+
import { spawnSync } from "node:child_process";
4+
import { readFile, writeFile } from "node:fs/promises";
5+
import { dirname, resolve } from "node:path";
6+
7+
const ROOT = dirname(__dirname);
8+
const tsShebang = "#!/usr/bin/env ts-node";
9+
const jsShebang = "#!/usr/bin/env node";
10+
11+
async function updateShebang (path: string) {
12+
const originalContent = await readFile(path, { encoding: "utf8" });
13+
const updatedContent = originalContent.replace(tsShebang, jsShebang);
14+
await writeFile(path, updatedContent, { encoding: "utf8" });
15+
}
16+
17+
async function main () {
18+
const npmResult = spawnSync("npm", ["show", ".", "bin", "--json"], {
19+
cwd: ROOT,
20+
shell: true,
21+
encoding: "utf8",
22+
});
23+
24+
if (npmResult.stdout) {
25+
const binEntries = JSON.parse(npmResult.stdout) as Record<string, string>;
26+
const binPaths = Object.values(binEntries);
27+
28+
for (const binPath of binPaths) {
29+
await updateShebang(resolve(ROOT, binPath));
30+
}
31+
}
32+
}
33+
34+
main().catch((err: Error) => {
35+
process.exitCode = 1;
36+
console.error(err.stack);
37+
});

lib/index.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,27 @@ interface Variables {
1414
ci?: object;
1515
}
1616

17+
type PkgContents = {
18+
// we don't use these fields directly, we just need to know if they have a value
19+
bin?: unknown;
20+
directories?: { bin?: unknown };
21+
22+
// these ones we inspect
23+
name: string;
24+
devDependencies?: Record<string, string>;
25+
};
26+
1727
export default async function (root: string, variables: Variables) {
1828
const actualContent = await readFile(join(root, "package.json"), { encoding: "utf8" });
19-
const actualPkg = JSON.parse(actualContent) as { name: string; devDependencies?: Record<string, string> };
29+
const actualPkg = JSON.parse(actualContent) as PkgContents;
2030
if (actualPkg.name !== ownPkg.name && actualPkg.devDependencies?.[ownPkg.name] !== ownPkg.version) {
2131
console.log(`ERROR! The devDependency "${ownPkg.name}" must be set to the exact version "${ownPkg.version}"`);
2232
console.log(`Try running \`npm install --save-exact -D ${ownPkg.name}\``);
2333
process.exit(1);
2434
}
2535

36+
const hasBin = !!(actualPkg.bin || actualPkg.directories?.bin);
37+
2638
const csBin = variables.dogfood ? "./bin/code-skeleton.ts" : "code-skeleton";
2739
const skeleton: Skeleton = {
2840
"package.json": pkg({
@@ -43,9 +55,17 @@ export default async function (root: string, variables: Variables) {
4355
postlint: "npm run skeleton:verify",
4456
test: "tap",
4557
posttest: "npm run lint",
46-
prepack: "tsc --project tsconfig.build.json",
4758
"skeleton:apply": `${csBin} apply`,
4859
"skeleton:verify": `${csBin} verify`,
60+
...(hasBin
61+
? {
62+
"update-shebang": "./scripts/update-shebang.ts",
63+
prepack: "tsc --project tsconfig.build.json && npm run update-shebang",
64+
}
65+
: {
66+
prepack: "tsc --project tsconfig.build.json",
67+
}
68+
),
4969
},
5070
tap: {
5171
coverage: true,
@@ -108,5 +128,9 @@ export default async function (root: string, variables: Variables) {
108128
skeleton[".npmrc"] = copy(join(__dirname, "content", "npmrc"));
109129
}
110130

131+
if (hasBin) {
132+
skeleton["scripts/update-shebang.ts"] = copy(join(__dirname, "content", "update-shebang.ts"));
133+
}
134+
111135
return skeleton;
112136
}

scripts/update-shebang.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env ts-node
2+
3+
import { spawnSync } from "node:child_process";
4+
import { readFile, writeFile } from "node:fs/promises";
5+
import { dirname, resolve } from "node:path";
6+
7+
const ROOT = dirname(__dirname);
8+
const tsShebang = "#!/usr/bin/env ts-node";
9+
const jsShebang = "#!/usr/bin/env node";
10+
11+
async function updateShebang (path: string) {
12+
const originalContent = await readFile(path, { encoding: "utf8" });
13+
const updatedContent = originalContent.replace(tsShebang, jsShebang);
14+
await writeFile(path, updatedContent, { encoding: "utf8" });
15+
}
16+
17+
async function main () {
18+
const npmResult = spawnSync("npm", ["show", ".", "bin", "--json"], {
19+
cwd: ROOT,
20+
shell: true,
21+
encoding: "utf8",
22+
});
23+
24+
if (npmResult.stdout) {
25+
const binEntries = JSON.parse(npmResult.stdout) as Record<string, string>;
26+
const binPaths = Object.values(binEntries);
27+
28+
for (const binPath of binPaths) {
29+
await updateShebang(resolve(ROOT, binPath));
30+
}
31+
}
32+
}
33+
34+
main().catch((err: Error) => {
35+
process.exitCode = 1;
36+
console.error(err.stack);
37+
});

0 commit comments

Comments
 (0)