Ctrl K
ComunidadGuías…

scripts/build-npm-package.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node

import { execFile } from "node:child_process";
import {
  copyFile,
  cp,
  mkdir,
  mkdtemp,
  readFile,
  rm,
  writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { basename, dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { collectSiwcRuntimeAssets } from "../src/services/siwc-runtime-assets.js";

const execFileAsync = promisify(execFile);
const sourceRoot = fileURLToPath(new URL("..", import.meta.url));
const defaultOutputDirectory = join(sourceRoot, "dist", "npm");

export function resolveNpmInvocation({
  env = process.env,
  execPath = process.execPath,
  platform = process.platform,
} = {}) {
  if (platform !== "win32") {
    return { command: "npm", prefixArgs: [] };
  }

  const npmExecPathKey = Object.keys(env ?? {}).find(
    (key) => key.toLowerCase() === "npm_execpath",
  );
  const npmExecPath =
    npmExecPathKey === undefined ? undefined : env[npmExecPathKey];
  let npmCliPath;

  if (typeof npmExecPath === "string") {
    if (/(?:^|[\\/])npm-cli\.js$/iu.test(npmExecPath)) {
      npmCliPath = resolve(npmExecPath);
    } else if (/(?:^|[\\/])npx-cli\.js$/iu.test(npmExecPath)) {
      npmCliPath = resolve(dirname(npmExecPath), "npm-cli.js");
    }
  }

  return {
    command: execPath,
    // Windows cannot execute npm.cmd directly with shell:false.
    prefixArgs: [
      npmCliPath ??
        resolve(dirname(execPath), "node_modules", "npm", "bin", "npm-cli.js"),
    ],
  };
}

// Two published files come from a differently named source. npm never publishes
// package-lock.json; shipped as npm-shrinkwrap.json, the same file makes every install use the
// reviewed versions of the packages below the direct dependencies.
const publishSources = new Map([
  ["README.md", join("npm", "README.md")],
  ["npm-shrinkwrap.json", "package-lock.json"],
]);

async function copyPublishEntry({ entry, stagingDirectory, unpublished }) {
  const source = join(sourceRoot, entry);
  const target = join(stagingDirectory, entry);

  if (publishSources.has(entry)) {
    await copyFile(join(sourceRoot, publishSources.get(entry)), target);
    return;
  }

  await cp(source, target, {
    recursive: true,
    filter: (path) => !unpublished.has(path),
  });
}

export async function stageNpmPackage(stagingDirectory) {
  const packageJson = JSON.parse(
    await readFile(join(sourceRoot, "package.json"), "utf8"),
  );
  const files = packageJson.files ?? [];
  // A `!path` entry in `files` stays in the repository but is not published. npm leaves it
  // out of the tarball; it is not copied into the staging folder either.
  const unpublished = new Set(
    files
      .filter((entry) => entry.startsWith("!"))
      .map((entry) => join(sourceRoot, entry.slice(1))),
  );
  const publishEntries = new Set([
    "package.json",
    ...files.filter((entry) => !entry.startsWith("!")),
  ]);

  await mkdir(stagingDirectory, { recursive: true });
  for (const entry of publishEntries) {
    await copyPublishEntry({ entry, stagingDirectory, unpublished });
  }
  const { packageLock } = await collectSiwcRuntimeAssets();
  await writeFile(join(stagingDirectory, "src", "services", "siwc-runtime-lock.json"), packageLock, {
    mode: 0o644,
  });

  return packageJson;
}

export async function buildNpmPackage({
  outputDirectory = defaultOutputDirectory,
} = {}) {
  const stagingDirectory = await mkdtemp(join(tmpdir(), "relmio-package-"));

  try {
    const packageJson = await stageNpmPackage(stagingDirectory);
    await mkdir(outputDirectory, { recursive: true });

    const expectedFilename = `${packageJson.name}-${packageJson.version}.tgz`;
    const expectedPath = join(outputDirectory, expectedFilename);
    await rm(expectedPath, { force: true });

    const npmInvocation = resolveNpmInvocation();
    const { stdout } = await execFileAsync(
      npmInvocation.command,
      [
        ...npmInvocation.prefixArgs,
        "pack",
        stagingDirectory,
        "--json",
        "--ignore-scripts",
        "--pack-destination",
        outputDirectory,
      ],
      {
        cwd: sourceRoot,
        env: {
          ...process.env,
          npm_config_cache: join(stagingDirectory, ".npm-cache"),
        },
      },
    );
    const [packedPackage] = JSON.parse(stdout);
    const tarballPath = resolve(outputDirectory, packedPackage.filename);

    if (basename(tarballPath) !== expectedFilename) {
      throw new Error(
        `npm produced ${basename(tarballPath)} instead of ${expectedFilename}.`,
      );
    }

    return { packageJson, packedPackage, tarballPath };
  } finally {
    await rm(stagingDirectory, { recursive: true, force: true });
  }
}

async function main() {
  const outputArgument = process.argv[2];
  const outputDirectory =
    outputArgument === undefined
      ? defaultOutputDirectory
      : resolve(process.cwd(), outputArgument);
  const result = await buildNpmPackage({ outputDirectory });
  console.log(result.tarballPath);
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
  main().catch((error) => {
    console.error(error.message);
    process.exitCode = 1;
  });
}