#!/usr/bin/env node /* Copyright 2026 Element Creations Ltd. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ // Utility script to mimic yarn classic `link` behaviour // to enable rapid development of libraries like matrix-js-sdk using symlinks/directory junctions // reads .link-config file for DEPENDENCY=PATH values and removes those dependencies from node_modules, // replacing them with a symlink/directory junction. // This tool is a helpful substitute to `pnpm link` as that modifies the package.json & pnpm-lock.yaml files. import * as fs from "node:fs/promises"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { execSync } from "node:child_process"; const __dirname = dirname(fileURLToPath(import.meta.url)); const configPath = join(__dirname, "..", ".link-config"); const nodeModulesPath = join(__dirname, "..", "node_modules"); try { if (process.env.PLAYWRIGHT_COMMON_DOCKER) process.exit(0); // Skip in docker env const configFile = await fs.readFile(configPath, "utf-8"); for (const line of configFile.trim().split("\n")) { if (!line || line.startsWith("#")) continue; const [dependency, path] = line.split("="); const dependencyPath = join(nodeModulesPath, dependency); try { try { const stat = await fs.lstat(dependencyPath); console.log(`Existing is ${stat.isSymbolicLink() ? "symlink" : "directory"}`); if (stat.isSymbolicLink()) { const linkPath = await fs.readlink(dependencyPath); if (linkPath === path) { // already done continue; } else { await fs.unlink(dependencyPath); } } else { await fs.rm(dependencyPath, { recursive: true }); } } catch (e: any) { // fs.lstat throws ENOENT if the path doesn't exist (on Windows) if (e.code === "ENOENT") { console.log("Received ENOENT error on dependency path - assuming it doesn't exist"); } else { throw e; } } console.log(`Linking ${dependency} to ${path}`); await fs.symlink(path, dependencyPath, "junction"); // use a junction type to avoid EPERM errors on Windows const pkgJson = await fs.readFile(join(path, "package.json"), "utf-8"); const pkgManager = JSON.parse(pkgJson)["packageManager"]?.split("@").at(0) ?? "yarn"; // pnpm install may have wiped out the `node_modules` dir so we have to restore it execSync(`${pkgManager} install --ignore-scripts --frozen-lockfile`, { cwd: dependencyPath, }); } catch (e) { console.error(`Failed to link ${dependency}`, e); } } } catch { // Ignore config file not existing }