UNPKG

@electron-forge/core

Version:

A complete tool for building modern Electron applications

127 lines (113 loc) 4.38 kB
import { spawnSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import { PMDetails, spawnPackageManager } from '@electron-forge/core-utils'; import { ForgeListrTask } from '@electron-forge/shared-types'; import debug from 'debug'; import { readRawPackageJson } from '../../util/read-package-json'; const d = debug('electron-forge:init:link'); /** * Link local forge dependencies * * This allows developers working on forge itself to easily init * a local template and have it use their local plugins / core / cli packages. * * Uses yarn link to create portal: resolutions that point to local workspace paths. */ export async function initLink<T>( pm: PMDetails, dir: string, task?: ForgeListrTask<T>, ) { const shouldLink = process.env.LINK_FORGE_DEPENDENCIES_ON_INIT; if (shouldLink) { d('Linking forge dependencies'); const packageJson = await readRawPackageJson(dir); const forgeRoot = path.resolve( __dirname, '..', '..', '..', '..', '..', '..', ); const getWorkspacePath = (packageName: string): string => { const result = spawnSync( 'yarn', ['workspace', packageName, 'exec', 'pwd'], { cwd: forgeRoot, encoding: 'utf-8', shell: process.platform === 'win32', }, ); if (result.status !== 0) { d(`Failed to get workspace path for ${packageName}: ${result.stderr}`); throw new Error( `Unable to determine workspace path for ${packageName}`, ); } const workspacePath = result.stdout.trim(); return workspacePath; }; // Collect all @electron-forge packages and their workspace paths const packagesToLink: Record<string, string> = {}; for (const packageName of Object.keys(packageJson.devDependencies)) { if (packageName.startsWith('@electron-forge/')) { const workspacePath = getWorkspacePath(packageName); packagesToLink[packageName] = workspacePath; d(`Found ${packageName}, will link to ${workspacePath}`); } } // Use yarn link to create portal: resolutions for local packages if (Object.keys(packagesToLink).length > 0) { // Copy the root .yarnrc.yml to the target directory before linking // This ensures settings like npmMinimalAgeGate are preserved if (pm.executable === 'yarn') { const rootYarnrc = path.join(forgeRoot, '.yarnrc.yml'); const targetYarnrc = path.join(dir, '.yarnrc.yml'); if (fs.existsSync(rootYarnrc)) { const yarnrcContent = await fs.promises.readFile(rootYarnrc, 'utf-8'); // we create a new yarnrc.yml (without yarnPath and enableScripts) and yarn.lock to mark as separate project // this avoids issues with yarnPath and enableScripts in CI const filteredContent = yarnrcContent .split('\n') .filter( (line) => !line.trim().startsWith('yarnPath:') && !line.trim().startsWith('enableScripts:'), ) .join('\n'); await fs.promises.writeFile(targetYarnrc, filteredContent); d('Copied .yarnrc.yml (without yarnPath/enableScripts)'); const targetYarnLock = path.join(dir, 'yarn.lock'); if (!fs.existsSync(targetYarnLock)) { await fs.promises.writeFile(targetYarnLock, ''); d('Created empty yarn.lock to mark as separate project'); } } } const paths = Object.values(packagesToLink); if (task) task.output = `${pm.executable} link ${paths.length} packages`; d(`Linking ${paths.length} packages`); await spawnPackageManager(pm, ['link', ...paths], { cwd: dir, }); d('Linking completed successfully'); // an additional install is needed to resolve any remaining dependencies if (task) task.output = `${pm.executable} install`; d(`Running: ${pm.executable} install (cwd: ${dir})`); await spawnPackageManager(pm, ['install'], { cwd: dir, }); d('Install completed successfully'); } await fs.promises.chmod( path.resolve(dir, 'node_modules', '.bin', 'electron-forge'), 0o755, ); } else { d('LINK_FORGE_DEPENDENCIES_ON_INIT is falsy. Skipping.'); } }