{"version":3,"file":"registry.mjs","names":[],"sources":["../src/registry.ts"],"sourcesContent":["/* -------------------------------------------------------------------\n\n                       ⚡ Storm Software - Stryke\n\n This code was released as part of the Stryke project. Stryke\n is maintained by Storm Software under the Apache-2.0 license, and is\n free for commercial and private use. For more information, please visit\n our licensing page at https://stormsoftware.com/licenses/projects/stryke.\n\n Website:                  https://stormsoftware.com\n Repository:               https://github.com/storm-software/stryke\n Documentation:            https://docs.stormsoftware.com/projects/stryke\n Contact:                  https://stormsoftware.com/contact\n\n SPDX-License-Identifier:  Apache-2.0\n\n ------------------------------------------------------------------- */\n\nimport { detectPackageManager } from \"@antfu/install-pkg\";\nimport { execSync } from \"node:child_process\";\nimport { parseArgs } from \"node:util\";\nimport { getWorkspaceRoot } from \"./get-workspace-root\";\n\nexport interface NodeOptionsToken {\n  kind: \"option\";\n  index: number;\n  name: string;\n  rawName: string;\n  value: undefined;\n  inlineValue: undefined;\n}\n\nconst parseNodeArgs = (args: string[]) => {\n  const { values, tokens } = parseArgs({ args, strict: false, tokens: true });\n\n  // For the `NODE_OPTIONS`, we support arguments with values without the `=`\n  // sign. We need to parse them manually.\n  let orphan: NodeOptionsToken | null | undefined = null;\n  for (let i = 0; i < tokens.length; i++) {\n    const token = tokens[i]!;\n\n    if (token.kind === \"option-terminator\") {\n      break;\n    }\n\n    // When we encounter an option, if it's value is undefined, we should check\n    // to see if the following tokens are positional parameters. If they are,\n    // then the option is orphaned, and we can assign it.\n    if (token.kind === \"option\") {\n      orphan = typeof token.value === \"undefined\" ? token : null;\n      continue;\n    }\n\n    // If the token isn't a positional one, then we can't assign it to the found\n    // orphaned option.\n    if (token.kind !== \"positional\") {\n      orphan = null;\n      continue;\n    }\n\n    // If we don't have an orphan, then we can skip this token.\n    if (!orphan) {\n      continue;\n    }\n\n    // If the token is a positional one, and it has a value, so add it to the\n    // values object. If it already exists, append it with a space.\n    if (orphan.name in values && typeof values[orphan.name] === \"string\") {\n      values[orphan.name] += ` ${token.value}`;\n    } else {\n      values[orphan.name] = token.value;\n    }\n  }\n\n  return values;\n};\n\n/**\n * Tokenizes the arguments string into an array of strings, supporting quoted\n * values and escaped characters.\n * Converted from: https://github.com/nodejs/node/blob/c29d53c5cfc63c5a876084e788d70c9e87bed880/src/node_options.cc#L1401\n *\n * @param input - The arguments string to be tokenized.\n * @returns An array of strings with the tokenized arguments.\n */\nexport const tokenizeArgs = (input: string): string[] => {\n  const args: string[] = [];\n  let isInString = false;\n  let willStartNewArg = true;\n\n  for (let i = 0; i < input.length; i++) {\n    let char = input[i]!;\n\n    // Skip any escaped characters in strings.\n    if (char === \"\\\\\" && isInString) {\n      // Ensure we don't have an escape character at the end.\n      if (input.length === i + 1) {\n        throw new Error(\"Invalid escape character at the end.\");\n      }\n\n      // Skip the next character.\n      char = input[++i]!;\n    }\n    // If we find a space outside of a string, we should start a new argument.\n    else if (char === \" \" && !isInString) {\n      willStartNewArg = true;\n      continue;\n    }\n\n    // If we find a quote, we should toggle the string flag.\n    else if (char === '\"') {\n      isInString = !isInString;\n      continue;\n    }\n\n    // If we're starting a new argument, we should add it to the array.\n    if (willStartNewArg) {\n      args.push(char);\n      willStartNewArg = false;\n    }\n    // Otherwise, add it to the last argument.\n    else {\n      args[args.length - 1] += char;\n    }\n  }\n\n  if (isInString) {\n    throw new Error(\"Unterminated string\");\n  }\n\n  return args;\n};\n\n/**\n * Get the node options from the environment variable `NODE_OPTIONS` and returns\n * them as an array of strings.\n *\n * @returns An array of strings with the node options.\n */\nconst getNodeOptionsArgs = () => {\n  if (!process.env.NODE_OPTIONS) return [];\n\n  return tokenizeArgs(process.env.NODE_OPTIONS);\n};\n\n/**\n * Stringify the arguments to be used in a command line. It will ignore any\n * argument that has a value of `undefined`.\n *\n * @param args - The arguments to be stringified.\n * @returns A string with the arguments.\n */\nexport function formatNodeOptions(\n  args: Record<string, string | boolean | undefined>\n): string {\n  return Object.entries(args)\n    .map(([key, value]) => {\n      if (value === true) {\n        return `--${key}`;\n      }\n\n      if (value) {\n        return `--${key}=${\n          // Values with spaces need to be quoted. We use JSON.stringify to\n          // also escape any nested quotes.\n          value.includes(\" \") && !value.startsWith('\"')\n            ? JSON.stringify(value)\n            : value\n        }`;\n      }\n\n      return null;\n    })\n    .filter(arg => arg !== null)\n    .join(\" \");\n}\n\n/**\n * Get the node options from the `NODE_OPTIONS` environment variable and parse\n * them into an object without the inspect options.\n *\n * @returns An object with the parsed node options.\n */\nexport function getParsedNodeOptionsWithoutInspect() {\n  const args = getNodeOptionsArgs();\n  if (args.length === 0) return {};\n\n  const parsed = parseNodeArgs(args);\n\n  // Remove inspect options.\n  delete parsed.inspect;\n  delete parsed[\"inspect-brk\"];\n  delete parsed.inspect_brk;\n\n  return parsed;\n}\n\n/**\n * Get the node options from the `NODE_OPTIONS` environment variable and format\n * them into a string without the inspect options.\n *\n * @returns A string with the formatted node options.\n */\nexport function getFormattedNodeOptionsWithoutInspect() {\n  const args = getParsedNodeOptionsWithoutInspect();\n  if (Object.keys(args).length === 0) return \"\";\n\n  return formatNodeOptions(args);\n}\n\n/**\n * Returns the package registry using the user's package manager. The URL will have a trailing slash.\n *\n * @param baseDir - The base directory to detect the package manager from.\n * @returns The package registry URL with a trailing slash.\n * @throws Will throw an error if the package manager cannot be detected or if the registry cannot be retrieved.\n */\nexport async function getRegistry(baseDir?: string) {\n  const workspaceRoot = getWorkspaceRoot(baseDir);\n  const pkgManager = await detectPackageManager(workspaceRoot);\n\n  // Since `npm config` command fails in npm workspace to prevent workspace config conflicts,\n  // add `--no-workspaces` flag to run under the context of the root project only.\n  // Safe for non-workspace projects as it's equivalent to default `--workspaces=false`.\n  // x-ref: https://github.com/vercel/next.js/issues/47121#issuecomment-1499044345\n  // x-ref: https://github.com/npm/statusboard/issues/371#issue-920669998\n  const resolvedFlags = pkgManager === \"npm\" ? \"--no-workspaces\" : \"\";\n  let registry = `https://registry.npmjs.org/`;\n\n  try {\n    const output = execSync(\n      `${pkgManager} config get registry ${resolvedFlags}`,\n      {\n        env: {\n          ...process.env,\n          NODE_OPTIONS: getFormattedNodeOptionsWithoutInspect()\n        }\n      }\n    )\n      .toString()\n      .trim();\n\n    if (output.startsWith(\"http\")) {\n      registry = output.endsWith(\"/\") ? output : `${output}/`;\n    }\n  } catch (err) {\n    throw new Error(`Failed to get registry from \"${pkgManager}\".`, {\n      cause: err\n    });\n  }\n\n  return registry;\n}\n"],"mappings":";;;;;;AAgCA,MAAM,iBAAiB,SAAmB;CACxC,MAAM,EAAE,QAAQ,WAAW,UAAU;EAAE;EAAM,QAAQ;EAAO,QAAQ;EAAM,CAAC;CAI3E,IAAI,SAA8C;AAClD,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;EACtC,MAAM,QAAQ,OAAO;AAErB,MAAI,MAAM,SAAS,oBACjB;AAMF,MAAI,MAAM,SAAS,UAAU;AAC3B,YAAS,OAAO,MAAM,UAAU,cAAc,QAAQ;AACtD;;AAKF,MAAI,MAAM,SAAS,cAAc;AAC/B,YAAS;AACT;;AAIF,MAAI,CAAC,OACH;AAKF,MAAI,OAAO,QAAQ,UAAU,OAAO,OAAO,OAAO,UAAU,SAC1D,QAAO,OAAO,SAAS,IAAI,MAAM;MAEjC,QAAO,OAAO,QAAQ,MAAM;;AAIhC,QAAO;;;;;;;;;;AAWT,MAAa,gBAAgB,UAA4B;CACvD,MAAM,OAAiB,EAAE;CACzB,IAAI,aAAa;CACjB,IAAI,kBAAkB;AAEtB,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,IAAI,OAAO,MAAM;AAGjB,MAAI,SAAS,QAAQ,YAAY;AAE/B,OAAI,MAAM,WAAW,IAAI,EACvB,OAAM,IAAI,MAAM,uCAAuC;AAIzD,UAAO,MAAM,EAAE;aAGR,SAAS,OAAO,CAAC,YAAY;AACpC,qBAAkB;AAClB;aAIO,SAAS,MAAK;AACrB,gBAAa,CAAC;AACd;;AAIF,MAAI,iBAAiB;AACnB,QAAK,KAAK,KAAK;AACf,qBAAkB;QAIlB,MAAK,KAAK,SAAS,MAAM;;AAI7B,KAAI,WACF,OAAM,IAAI,MAAM,sBAAsB;AAGxC,QAAO;;;;;;;;AAST,MAAM,2BAA2B;AAC/B,KAAI,CAAC,QAAQ,IAAI,aAAc,QAAO,EAAE;AAExC,QAAO,aAAa,QAAQ,IAAI,aAAa;;;;;;;;;AAU/C,SAAgB,kBACd,MACQ;AACR,QAAO,OAAO,QAAQ,KAAK,CACxB,KAAK,CAAC,KAAK,WAAW;AACrB,MAAI,UAAU,KACZ,QAAO,KAAK;AAGd,MAAI,MACF,QAAO,KAAK,IAAI,GAGd,MAAM,SAAS,IAAI,IAAI,CAAC,MAAM,WAAW,KAAI,GACzC,KAAK,UAAU,MAAM,GACrB;AAIR,SAAO;GACP,CACD,QAAO,QAAO,QAAQ,KAAK,CAC3B,KAAK,IAAI;;;;;;;;AASd,SAAgB,qCAAqC;CACnD,MAAM,OAAO,oBAAoB;AACjC,KAAI,KAAK,WAAW,EAAG,QAAO,EAAE;CAEhC,MAAM,SAAS,cAAc,KAAK;AAGlC,QAAO,OAAO;AACd,QAAO,OAAO;AACd,QAAO,OAAO;AAEd,QAAO;;;;;;;;AAST,SAAgB,wCAAwC;CACtD,MAAM,OAAO,oCAAoC;AACjD,KAAI,OAAO,KAAK,KAAK,CAAC,WAAW,EAAG,QAAO;AAE3C,QAAO,kBAAkB,KAAK;;;;;;;;;AAUhC,eAAsB,YAAY,SAAkB;CAElD,MAAM,aAAa,MAAM,qBADH,iBAAiB,QACoB,CAAC;CAO5D,MAAM,gBAAgB,eAAe,QAAQ,oBAAoB;CACjE,IAAI,WAAW;AAEf,KAAI;EACF,MAAM,SAAS,SACb,GAAG,WAAW,uBAAuB,iBACrC,EACE,KAAK;GACH,GAAG,QAAQ;GACX,cAAc,uCAAuC;GACtD,EACF,CACF,CACE,UAAU,CACV,MAAM;AAET,MAAI,OAAO,WAAW,OAAO,CAC3B,YAAW,OAAO,SAAS,IAAI,GAAG,SAAS,GAAG,OAAO;UAEhD,KAAK;AACZ,QAAM,IAAI,MAAM,gCAAgC,WAAW,KAAK,EAC9D,OAAO,KACR,CAAC;;AAGJ,QAAO"}