Skip to content

Commit ce14cae

Browse files
committed
fix(create-rezi): run cli when launched via symlinked bin
1 parent 13e58c4 commit ce14cae

File tree

4 files changed

+64
-2
lines changed

4 files changed

+64
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on Keep a Changelog and the project follows Semantic Version
66

77
## [Unreleased]
88

9+
### Bug Fixes
10+
11+
- **create-rezi/cli**: Fixed `npm create rezi` / `bun create rezi` no-op scaffolding by making CLI main-entry detection symlink-safe.
12+
913
## [0.1.0-alpha.58] - 2026-03-06
1014
### Breaking Changes
1115

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { mkdtemp, symlink, writeFile } from "node:fs/promises";
2+
import { tmpdir } from "node:os";
3+
import { join } from "node:path";
4+
import { pathToFileURL } from "node:url";
5+
import { assert, test } from "@rezi-ui/testkit";
6+
import { isMainModuleEntry } from "../mainEntry.js";
7+
8+
test("isMainModuleEntry returns true for direct script execution", async () => {
9+
const root = await mkdtemp(join(tmpdir(), "rezi-main-entry-"));
10+
const scriptPath = join(root, "create-rezi.mjs");
11+
await writeFile(scriptPath, "export {};\n", "utf8");
12+
13+
const moduleUrl = pathToFileURL(scriptPath).href;
14+
assert.equal(isMainModuleEntry(scriptPath, moduleUrl), true);
15+
});
16+
17+
test("isMainModuleEntry resolves symlinked launchers", async () => {
18+
const root = await mkdtemp(join(tmpdir(), "rezi-main-entry-"));
19+
const scriptPath = join(root, "dist-index.mjs");
20+
const launcherPath = join(root, "launcher.mjs");
21+
await writeFile(scriptPath, "export {};\n", "utf8");
22+
await symlink(scriptPath, launcherPath);
23+
24+
const moduleUrl = pathToFileURL(scriptPath).href;
25+
assert.equal(isMainModuleEntry(launcherPath, moduleUrl), true);
26+
});
27+
28+
test("isMainModuleEntry rejects non-matching entry paths", async () => {
29+
const root = await mkdtemp(join(tmpdir(), "rezi-main-entry-"));
30+
const scriptPath = join(root, "dist-index.mjs");
31+
const otherPath = join(root, "other.mjs");
32+
await writeFile(scriptPath, "export {};\n", "utf8");
33+
await writeFile(otherPath, "export {};\n", "utf8");
34+
35+
const moduleUrl = pathToFileURL(scriptPath).href;
36+
assert.equal(isMainModuleEntry(otherPath, moduleUrl), false);
37+
assert.equal(isMainModuleEntry(undefined, moduleUrl), false);
38+
});

packages/create-rezi/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { spawnSync } from "node:child_process";
33
import { relative, resolve } from "node:path";
44
import { cwd, exit, stdin, stdout } from "node:process";
55
import { createInterface } from "node:readline/promises";
6-
import { fileURLToPath } from "node:url";
6+
import { isMainModuleEntry } from "./mainEntry.js";
77
import {
88
TEMPLATE_DEFINITIONS,
99
createProject,
@@ -276,7 +276,7 @@ async function main(): Promise<void> {
276276
}
277277
}
278278

279-
const isMain = process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1]);
279+
const isMain = isMainModuleEntry(process.argv[1], import.meta.url);
280280
if (isMain) {
281281
main().catch((err) => {
282282
stdout.write(`\ncreate-rezi error: ${err instanceof Error ? err.message : String(err)}\n`);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { realpathSync } from "node:fs";
2+
import { resolve } from "node:path";
3+
import { fileURLToPath } from "node:url";
4+
5+
function resolveCanonicalPath(pathValue: string): string {
6+
try {
7+
return realpathSync(pathValue);
8+
} catch {
9+
return pathValue;
10+
}
11+
}
12+
13+
export function isMainModuleEntry(argvPath: string | undefined, moduleUrl: string): boolean {
14+
if (!argvPath) {
15+
return false;
16+
}
17+
const modulePath = resolveCanonicalPath(fileURLToPath(moduleUrl));
18+
const entryPath = resolveCanonicalPath(resolve(argvPath));
19+
return modulePath === entryPath;
20+
}

0 commit comments

Comments
 (0)