Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions adminforth/commands/createApp/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Handlebars from 'handlebars';
import { promisify } from 'util';
import { resolveAdminforthVersionRange } from '../cli.js';
import { checkNodeVersion } from '../nodeVersion.js';
import { isNodeScriptBin } from '../utils.js';

import { URL } from 'url'
import net from 'net'
Expand Down Expand Up @@ -684,9 +685,11 @@ async function installDependenciesPnpm(ctx, cwd) {
execAsync(`pnpm install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
]);
} else {
// pnpm 12 bin is a native executable, only older JS entrypoints can be passed to node
const pnpmCommand = isNodeScriptBin(pnpmPath) ? `${nodeBinary} ${pnpmPath}` : pnpmPath;
const res = await Promise.all([
execAsync(`${nodeBinary} ${pnpmPath} install`, { cwd, env: { PATH: process.env.PATH } }),
execAsync(`${nodeBinary} ${pnpmPath} install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
execAsync(`${pnpmCommand} install`, { cwd, env: { PATH: process.env.PATH } }),
execAsync(`${pnpmCommand} install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
]);
}
}
Expand All @@ -703,9 +706,10 @@ async function installDependenciesNpm(ctx, cwd) {
execAsync(`npm install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
]);
} else {
const npmCommand = isNodeScriptBin(npmPath) ? `${nodeBinary} ${npmPath}` : npmPath;
const res = await Promise.all([
execAsync(`${nodeBinary} ${npmPath} install`, { cwd, env: { PATH: process.env.PATH } }),
execAsync(`${nodeBinary} ${npmPath} install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
execAsync(`${npmCommand} install`, { cwd, env: { PATH: process.env.PATH } }),
execAsync(`${npmCommand} install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
]);
}
}
Expand Down
23 changes: 23 additions & 0 deletions adminforth/commands/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,26 @@ export async function getInstance(file, currentDirectory) {
}
return null;
}

/**
* Tells whether a package manager binary is a JS script which has to be launched with node,
* or a self-contained native executable which has to be launched directly.
* pnpm up to 11 shipped a JS entrypoint (bin/pnpm.mjs), pnpm 12 ships a native binary instead,
* and passing a native binary to node fails with "SyntaxError: Invalid or unexpected token".
*/
export function isNodeScriptBin(binPath) {
try {
const fd = fs.openSync(binPath, "r");
try {
const header = Buffer.alloc(2);
// JS entrypoints of npm/pnpm always start with a "#!/usr/bin/env node" shebang,
// native executables start with a binary magic number (0x7f "ELF" on Linux)
return fs.readSync(fd, header, 0, 2, 0) === 2 && header.toString("latin1") === "#!";
} finally {
fs.closeSync(fd);
}
} catch (e) {
// unreadable bin: keep launching it with node like we always did, so the error stays the same
return true;
}
}
10 changes: 7 additions & 3 deletions adminforth/modules/codeInjector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import pLimit from 'p-limit';
import { promisify } from 'util';
import yaml from 'yaml';
import AdminForth, { AdminForthConfigMenuItem } from '../index.js';
import { ADMIN_FORTH_ABSOLUTE_PATH, getComponentNameFromPath, transformObject, deepMerge, md5hash, slugifyString } from './utils.js';
import { ADMIN_FORTH_ABSOLUTE_PATH, getComponentNameFromPath, transformObject, deepMerge, md5hash, slugifyString, isNodeScriptBin } from './utils.js';
import { ICodeInjector } from '../types/Back.js';
import { StylesGenerator } from './styleGenerator.js';
import { afLogger } from '../modules/logger.js';
Expand Down Expand Up @@ -393,7 +393,9 @@ class CodeInjector implements ICodeInjector {
// Quote paths that contain spaces (for Unix systems)
const quotedNodeBinary = nodeBinary.includes(' ') ? `"${nodeBinary}"` : nodeBinary;
const quotedPackagePath = packagePath.includes(' ') ? `"${packagePath}"` : packagePath;
execCommand = `${quotedNodeBinary} ${quotedPackagePath} ${command}`;
execCommand = isNodeScriptBin(packagePath) ?
`${quotedNodeBinary} ${quotedPackagePath} ${command}` :
`${quotedPackagePath} ${command}`;
}

const execOptions: any = {
Expand Down Expand Up @@ -1464,7 +1466,9 @@ class CodeInjector implements ICodeInjector {
if (process.platform === 'win32') {
devServer = spawn(usersPackageManager, command.split(' '), { cwd, env, shell: true });
} else {
devServer = spawn(`${nodeBinary}`, [`${packageManagerPath}`, ...command.split(' ')], { cwd, env });
devServer = isNodeScriptBin(packageManagerPath) ?
spawn(`${nodeBinary}`, [`${packageManagerPath}`, ...command.split(' ')], { cwd, env }) :
spawn(`${packageManagerPath}`, command.split(' '), { cwd, env });
}
devServer.stdout.on('data', (data) => {
if (data.includes('➜')) {
Expand Down
24 changes: 23 additions & 1 deletion adminforth/modules/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,4 +671,26 @@ export function checkIfLinkInAllowedHosts(url: string, allowedHosts: string[]) {
if (!allowed) {
throw new Error(`Attachment host "${hostname}" is not in attachImagesAllowedHosts`);
}
}
}
/**
* Tells whether a package manager binary is a JS script which has to be launched with node,
* or a self-contained native executable which has to be launched directly.
* pnpm up to 11 shipped a JS entrypoint (bin/pnpm.mjs), pnpm 12 ships a native binary instead,
* and passing a native binary to node fails with "SyntaxError: Invalid or unexpected token".
*/
export function isNodeScriptBin(binPath: string): boolean {
try {
const fd = fs.openSync(binPath, 'r');
try {
const header = Buffer.alloc(2);
// JS entrypoints of npm/pnpm always start with a "#!/usr/bin/env node" shebang,
// native executables start with a binary magic number (0x7f "ELF" on Linux)
return fs.readSync(fd, header, 0, 2, 0) === 2 && header.toString('latin1') === '#!';
} finally {
fs.closeSync(fd);
}
} catch (e) {
// unreadable bin: keep launching it with node like we always did, so the error stays the same
return true;
}
}