bootstrap-node.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import * as path from "path"
  6. import * as fs from "fs"
  7. import { fileURLToPath } from "url"
  8. import { createRequire } from "node:module"
  9. import type { IProductConfiguration } from "./deps/vscode/vs/base/common/product.js"
  10. const require = createRequire(import.meta.url)
  11. const __dirname = path.dirname(fileURLToPath(import.meta.url))
  12. const isWindows = process.platform === "win32"
  13. // increase number of stack frames(from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
  14. Error.stackTraceLimit = 100
  15. if (!process.env["VSCODE_HANDLES_SIGPIPE"]) {
  16. // Workaround for Electron not installing a handler to ignore SIGPIPE
  17. // (https://github.com/electron/electron/issues/13254)
  18. let didLogAboutSIGPIPE = false
  19. process.on("SIGPIPE", () => {
  20. // See https://github.com/microsoft/vscode-remote-release/issues/6543
  21. // In certain situations, the console itself can be in a broken pipe state
  22. // so logging SIGPIPE to the console will cause an infinite async loop
  23. if (!didLogAboutSIGPIPE) {
  24. didLogAboutSIGPIPE = true
  25. console.error(new Error(`Unexpected SIGPIPE`))
  26. }
  27. })
  28. }
  29. // Setup current working directory in all our node & electron processes
  30. // - Windows: call `process.chdir()` to always set application folder as cwd
  31. // - all OS: store the `process.cwd()` inside `VSCODE_CWD` for consistent lookups
  32. function setupCurrentWorkingDirectory(): void {
  33. try {
  34. // Store the `process.cwd()` inside `VSCODE_CWD`
  35. // for consistent lookups, but make sure to only
  36. // do this once unless defined already from e.g.
  37. // a parent process.
  38. if (typeof process.env["VSCODE_CWD"] !== "string") {
  39. process.env["VSCODE_CWD"] = process.cwd()
  40. }
  41. // Windows: always set application folder as current working dir
  42. if (process.platform === "win32") {
  43. process.chdir(path.dirname(process.execPath))
  44. }
  45. } catch (err) {
  46. console.error(err)
  47. }
  48. }
  49. setupCurrentWorkingDirectory()
  50. /**
  51. * Add support for redirecting the loading of node modules
  52. *
  53. * Note: only applies when running out of sources.
  54. */
  55. export function devInjectNodeModuleLookupPath(injectPath: string): void {
  56. if (!process.env["VSCODE_DEV"]) {
  57. return // only applies running out of sources
  58. }
  59. if (!injectPath) {
  60. throw new Error("Missing injectPath")
  61. }
  62. // register a loader hook
  63. const Module = require("node:module")
  64. Module.register("./bootstrap-import.js", { parentURL: import.meta.url, data: injectPath })
  65. }
  66. export function removeGlobalNodeJsModuleLookupPaths(): void {
  67. if (typeof process?.versions?.electron === "string") {
  68. return // Electron disables global search paths in https://github.com/electron/electron/blob/3186c2f0efa92d275dc3d57b5a14a60ed3846b0e/shell/common/node_bindings.cc#L653
  69. }
  70. const Module = require("module")
  71. const globalPaths = Module.globalPaths
  72. const originalResolveLookupPaths = Module._resolveLookupPaths
  73. Module._resolveLookupPaths = function (moduleName: string, parent: any): string[] {
  74. const paths = originalResolveLookupPaths(moduleName, parent)
  75. if (Array.isArray(paths)) {
  76. let commonSuffixLength = 0
  77. while (
  78. commonSuffixLength < paths.length &&
  79. paths[paths.length - 1 - commonSuffixLength] ===
  80. globalPaths[globalPaths.length - 1 - commonSuffixLength]
  81. ) {
  82. commonSuffixLength++
  83. }
  84. return paths.slice(0, paths.length - commonSuffixLength)
  85. }
  86. return paths
  87. }
  88. const originalNodeModulePaths = Module._nodeModulePaths
  89. Module._nodeModulePaths = function (from: string): string[] {
  90. let paths: string[] = originalNodeModulePaths(from)
  91. if (!isWindows) {
  92. return paths
  93. }
  94. // On Windows, remove drive(s) and users' home directory from search paths,
  95. // UNLESS 'from' is explicitly set to one of those.
  96. const isDrive = (p: string) => p.length >= 3 && p.endsWith(":\\")
  97. if (!isDrive(from)) {
  98. paths = paths.filter((p) => !isDrive(path.dirname(p)))
  99. }
  100. if (process.env.HOMEDRIVE && process.env.HOMEPATH) {
  101. const userDir = path.dirname(path.join(process.env.HOMEDRIVE, process.env.HOMEPATH))
  102. const isUsersDir = (p: string) => path.relative(p, userDir).length === 0
  103. // Check if 'from' is the same as 'userDir'
  104. if (!isUsersDir(from)) {
  105. paths = paths.filter((p) => !isUsersDir(path.dirname(p)))
  106. }
  107. }
  108. return paths
  109. }
  110. }
  111. /**
  112. * Helper to enable portable mode.
  113. */
  114. export function configurePortable(product: Partial<IProductConfiguration>): {
  115. portableDataPath: string
  116. isPortable: boolean
  117. } {
  118. const appRoot = path.dirname(__dirname)
  119. function getApplicationPath(): string {
  120. if (process.env["VSCODE_DEV"]) {
  121. return appRoot
  122. }
  123. if (process.platform === "darwin") {
  124. return path.dirname(path.dirname(path.dirname(appRoot)))
  125. }
  126. return path.dirname(path.dirname(appRoot))
  127. }
  128. function getPortableDataPath(): string {
  129. if (process.env["VSCODE_PORTABLE"]) {
  130. return process.env["VSCODE_PORTABLE"]
  131. }
  132. if (process.platform === "win32" || process.platform === "linux") {
  133. return path.join(getApplicationPath(), "data")
  134. }
  135. const portableDataName = product.portable || `${product.applicationName}-portable-data`
  136. return path.join(path.dirname(getApplicationPath()), portableDataName)
  137. }
  138. const portableDataPath = getPortableDataPath()
  139. const isPortable = !("target" in product) && fs.existsSync(portableDataPath)
  140. const portableTempPath = path.join(portableDataPath, "tmp")
  141. const isTempPortable = isPortable && fs.existsSync(portableTempPath)
  142. if (isPortable) {
  143. process.env["VSCODE_PORTABLE"] = portableDataPath
  144. } else {
  145. delete process.env["VSCODE_PORTABLE"]
  146. }
  147. if (isTempPortable) {
  148. if (process.platform === "win32") {
  149. process.env["TMP"] = portableTempPath
  150. process.env["TEMP"] = portableTempPath
  151. } else {
  152. process.env["TMPDIR"] = portableTempPath
  153. }
  154. }
  155. return {
  156. portableDataPath,
  157. isPortable,
  158. }
  159. }