fix(javascript): keep paths after __dirname inside the source directory (#2366)

Node's path.join does not reset at an absolute segment, so
path.join(__dirname, '/preload.js') is <dir>/preload.js. The static path
projection skipped the __dirname anchor and joined the remaining parts,
which turned that common spelling into the root path /preload.js. In an
app whose main script lives in src/, the BrowserWindow preload was then
reported not-found, or resolved to an unrelated preload.js at the artifact
root, and the utilityProcess.fork target was never resolved. String
concatenation had the same defect: path.dirname(__filename) +
'/preload.js' projected to /preload.js, and __dirname + '/preload.js'
stayed unknown.

A source directory now contributes "." to a join, so path.join(__dirname)
projects to "." like path.resolve(__dirname). A concatenation that starts
with __dirname or the source file's dirname keeps text that begins with a
separator inside that directory; other text extends the directory's own
name and stays unknown. The empty-string projection of
path.dirname(__filename), which produced the wrong concatenation, is
removed, so a bare path.dirname(__filename) is unknown like a bare
__dirname.
This commit is contained in:
Chaoqi
2026-10-13 08:10:10 -04:00
committed by GitHub
parent cd2ad1f240
commit f974353c32
2 changed files with 60 additions and 17 deletions
@@ -181,18 +181,37 @@ export const staticPath = (node: t.Node): string | undefined => {
if (t.isStringLiteral(node)) return node.value;
if (t.isTemplateLiteral(node) && node.expressions.length === 0)
return stringValue(node);
if (t.isBinaryExpression(node, { operator: "+" })) {
const left = staticPath(node.left);
const right = staticPath(node.right);
return left === undefined || right === undefined
? undefined
: `${left}${right}`;
}
if (t.isBinaryExpression(node, { operator: "+" }))
return staticConcatenation(node);
if (t.isCallExpression(node) || t.isNewExpression(node))
return staticCallPath(node);
return undefined;
};
/**
* Concatenate inert text. A leading source directory contributes no text:
* `__dirname + "/preload.js"` names a file inside it, while text without a
* leading separator extends the directory's own name and stays unknown.
*/
const staticConcatenation = (node: t.BinaryExpression): string | undefined => {
const operands: t.Node[] = [];
let current: t.Node = node;
while (t.isBinaryExpression(current, { operator: "+" })) {
operands.push(current.right);
current = current.left;
}
const anchored = isDirectoryPath(current);
if (!anchored) operands.push(current);
let text = "";
for (const operand of operands.reverse()) {
const value = staticPath(operand);
if (value === undefined) return undefined;
text += value;
}
if (!anchored) return text;
return text.startsWith("/") ? posix.join(".", text) : undefined;
};
const staticCallPath = (
node: t.CallExpression | t.NewExpression,
): string | undefined => {
@@ -204,11 +223,6 @@ const staticCallPath = (
if (name === "fileURLToPath" || name.endsWith(".fileURLToPath")) {
return staticFileUrlPath(argumentNode(node.arguments[0]));
}
if (
(name === "dirname" || name.endsWith(".dirname")) &&
isFileIdentity(argumentNode(node.arguments[0]))
)
return "";
if (name.endsWith(".resolve"))
return name === "win32.resolve" || name.endsWith(".win32.resolve")
? undefined
@@ -216,7 +230,12 @@ const staticCallPath = (
if (!name.endsWith(".join") && name !== "join") return undefined;
const parts: string[] = [];
for (const argument of node.arguments) {
if (isDirectoryIdentity(argumentNode(argument))) continue;
if (isDirectoryIdentity(argumentNode(argument))) {
// join never resets at an absolute segment, so `/preload.js` after the
// source directory stays inside it.
parts.push(".");
continue;
}
const value = t.isNode(argument) ? staticPath(argument) : undefined;
if (value === undefined) return undefined;
parts.push(value);
@@ -331,11 +350,20 @@ const isFilesystemPathExpression = (node: t.Node): boolean => {
};
const isDirectoryIdentity = (node: t.Node | undefined): boolean => {
if (t.isIdentifier(node, { name: "__dirname" })) return true;
if (isDirectoryPath(node)) return true;
if (!t.isCallExpression(node) && !t.isNewExpression(node)) return false;
const name = calleeName(node.callee);
if (name === "URL" || name.endsWith(".URL"))
return isFileIdentity(argumentNode(node.arguments[1]));
return (
(name === "URL" || name.endsWith(".URL")) &&
isFileIdentity(argumentNode(node.arguments[1]))
);
};
/** `__dirname` or the dirname of the source file, as a string. */
const isDirectoryPath = (node: t.Node | undefined): boolean => {
if (t.isIdentifier(node, { name: "__dirname" })) return true;
if (!t.isCallExpression(node)) return false;
const name = calleeName(node.callee);
return (
(name === "dirname" || name.endsWith(".dirname")) &&
isFileIdentity(argumentNode(node.arguments[0]))