{"version":3,"sources":["../../src/lib/worker.ts"],"sourcesContent":["import type { ChildProcess } from 'child_process'\nimport { Worker as JestWorker } from 'next/dist/compiled/jest-worker'\nimport { Transform } from 'stream'\nimport {\n  formatDebugAddress,\n  formatNodeOptions,\n  getNodeDebugType,\n  getParsedDebugAddress,\n  getParsedNodeOptionsWithoutInspect,\n} from '../server/lib/utils'\n\ntype FarmOptions = NonNullable<ConstructorParameters<typeof JestWorker>[1]>\n\nconst RESTARTED = Symbol('restarted')\n\nconst cleanupWorkers = (worker: JestWorker) => {\n  for (const curWorker of ((worker as any)._workerPool?._workers || []) as {\n    _child?: ChildProcess\n  }[]) {\n    curWorker._child?.kill('SIGINT')\n  }\n}\n\nexport function getNextBuildDebuggerPortOffset(_: {\n  kind: 'export-page'\n}): number {\n  // 0: export worker\n  return 0\n}\n\nexport class Worker {\n  private _worker: JestWorker | undefined\n\n  constructor(\n    workerPath: string,\n    options: Omit<FarmOptions, 'forkOptions'> & {\n      forkOptions?:\n        | (Omit<NonNullable<FarmOptions['forkOptions']>, 'env'> & {\n            env?: Partial<NodeJS.ProcessEnv> | undefined\n          })\n        | undefined\n      /**\n       * `-1` if not inspectable\n       */\n      debuggerPortOffset: number\n      enableSourceMaps?: boolean\n      /**\n       * True if `--max-old-space-size` should not be forwarded to the worker.\n       */\n      isolatedMemory: boolean\n      timeout?: number\n      onActivity?: () => void\n      onActivityAbort?: () => void\n      onRestart?: (method: string, args: any[], attempts: number) => void\n      logger?: Pick<typeof console, 'error' | 'info' | 'warn'>\n      exposedMethods: ReadonlyArray<string>\n      enableWorkerThreads?: boolean\n    }\n  ) {\n    let {\n      enableSourceMaps,\n      timeout,\n      onRestart,\n      logger = console,\n      debuggerPortOffset,\n      isolatedMemory,\n      ...farmOptions\n    } = options\n\n    let restartPromise: Promise<typeof RESTARTED>\n    let resolveRestartPromise: (arg: typeof RESTARTED) => void\n    let activeTasks = 0\n\n    this._worker = undefined\n\n    // ensure we end workers if they weren't before exit\n    process.on('exit', () => {\n      this.close()\n    })\n\n    const nodeOptions = getParsedNodeOptionsWithoutInspect()\n\n    if (debuggerPortOffset !== -1) {\n      const nodeDebugType = getNodeDebugType()\n      if (nodeDebugType) {\n        const address = getParsedDebugAddress()\n        address.port =\n          address.port +\n          // current process runs on `address.port`\n          1 +\n          debuggerPortOffset\n        nodeOptions[nodeDebugType] = formatDebugAddress(address)\n      }\n    }\n\n    if (enableSourceMaps) {\n      nodeOptions['enable-source-maps'] = true\n    }\n\n    if (isolatedMemory) {\n      delete nodeOptions['max-old-space-size']\n      delete nodeOptions['max_old_space_size']\n    }\n\n    const createWorker = () => {\n      this._worker = new JestWorker(workerPath, {\n        ...farmOptions,\n        forkOptions: {\n          ...farmOptions.forkOptions,\n          env: {\n            ...process.env,\n            ...((farmOptions.forkOptions?.env || {}) as any),\n            IS_NEXT_WORKER: 'true',\n            NODE_OPTIONS: formatNodeOptions(nodeOptions),\n          } as any,\n        },\n        maxRetries: 0,\n      }) as JestWorker\n      restartPromise = new Promise(\n        (resolve) => (resolveRestartPromise = resolve)\n      )\n\n      /**\n       * Jest Worker has two worker types, ChildProcessWorker (uses child_process) and NodeThreadWorker (uses worker_threads)\n       * Next.js uses ChildProcessWorker by default, but it can be switched to NodeThreadWorker with an experimental flag\n       *\n       * We only want to handle ChildProcessWorker's orphan process issue, so we access the private property \"_child\":\n       * https://github.com/facebook/jest/blob/b38d7d345a81d97d1dc3b68b8458b1837fbf19be/packages/jest-worker/src/workers/ChildProcessWorker.ts\n       *\n       * But this property is not available in NodeThreadWorker, so we need to check if we are using ChildProcessWorker\n       */\n      if (!farmOptions.enableWorkerThreads) {\n        for (const worker of ((this._worker as any)._workerPool?._workers ||\n          []) as {\n          _child?: ChildProcess\n        }[]) {\n          worker._child?.on('exit', (code, signal) => {\n            if ((code || (signal && signal !== 'SIGINT')) && this._worker) {\n              logger.error(\n                `Next.js build worker exited with code: ${code} and signal: ${signal}`\n              )\n\n              // if a child process doesn't exit gracefully, we want to bubble up the exit code to the parent process\n              process.exit(code ?? 1)\n            }\n          })\n\n          // if a child process emits a particular message, we track that as activity\n          // so the parent process can keep track of progress\n          worker._child?.on('message', ([, data]: [number, unknown]) => {\n            if (\n              data &&\n              typeof data === 'object' &&\n              'type' in data &&\n              data.type === 'activity'\n            ) {\n              onActivity()\n            }\n          })\n        }\n      }\n\n      let aborted = false\n      const onActivityAbort = () => {\n        if (!aborted) {\n          options.onActivityAbort?.()\n          aborted = true\n        }\n      }\n\n      // Listen to the worker's stdout and stderr, if there's any thing logged, abort the activity first\n      const abortActivityStreamOnLog = new Transform({\n        transform(_chunk, _encoding, callback) {\n          onActivityAbort()\n          callback()\n        },\n      })\n      // Stop the activity if there's any output from the worker\n      this._worker.getStdout().pipe(abortActivityStreamOnLog)\n      this._worker.getStderr().pipe(abortActivityStreamOnLog)\n\n      // Pipe the worker's stdout and stderr to the parent process\n      this._worker.getStdout().pipe(process.stdout)\n      this._worker.getStderr().pipe(process.stderr)\n    }\n    createWorker()\n\n    const onHanging = () => {\n      const worker = this._worker\n      if (!worker) return\n      const resolve = resolveRestartPromise\n      createWorker()\n      logger.warn(\n        `Sending SIGTERM signal to static worker due to timeout${\n          timeout ? ` of ${timeout / 1000} seconds` : ''\n        }. Subsequent errors may be a result of the worker exiting.`\n      )\n      worker.end().then(() => {\n        resolve(RESTARTED)\n      })\n    }\n\n    let hangingTimer: NodeJS.Timeout | false = false\n\n    const onActivity = () => {\n      if (hangingTimer) clearTimeout(hangingTimer)\n      if (options.onActivity) options.onActivity()\n\n      hangingTimer = activeTasks > 0 && setTimeout(onHanging, timeout)\n    }\n\n    for (const method of farmOptions.exposedMethods) {\n      if (method.startsWith('_')) continue\n      ;(this as any)[method] = timeout\n        ? // eslint-disable-next-line no-loop-func\n          async (...args: any[]) => {\n            activeTasks++\n            try {\n              let attempts = 0\n              for (;;) {\n                onActivity()\n                const result = await Promise.race([\n                  (this._worker as any)[method](...args),\n                  restartPromise,\n                ])\n                if (result !== RESTARTED) return result\n                if (onRestart) onRestart(method, args, ++attempts)\n              }\n            } finally {\n              activeTasks--\n              onActivity()\n            }\n          }\n        : (this._worker as any)[method].bind(this._worker)\n    }\n  }\n\n  end(): ReturnType<JestWorker['end']> {\n    const worker = this._worker\n    if (!worker) {\n      throw new Error('Farm is ended, no more calls can be done to it')\n    }\n    cleanupWorkers(worker)\n    this._worker = undefined\n    return worker.end()\n  }\n\n  /**\n   * Quietly end the worker if it exists\n   */\n  close(): void {\n    if (this._worker) {\n      cleanupWorkers(this._worker)\n      this._worker.end()\n    }\n  }\n}\n"],"names":["Worker","JestWorker","Transform","formatDebugAddress","formatNodeOptions","getNodeDebugType","getParsedDebugAddress","getParsedNodeOptionsWithoutInspect","RESTARTED","Symbol","cleanupWorkers","worker","curWorker","_workerPool","_workers","_child","kill","getNextBuildDebuggerPortOffset","_","constructor","workerPath","options","enableSourceMaps","timeout","onRestart","logger","console","debuggerPortOffset","isolatedMemory","farmOptions","restartPromise","resolveRestartPromise","activeTasks","_worker","undefined","process","on","close","nodeOptions","nodeDebugType","address","port","createWorker","forkOptions","env","IS_NEXT_WORKER","NODE_OPTIONS","maxRetries","Promise","resolve","enableWorkerThreads","code","signal","error","exit","data","type","onActivity","aborted","onActivityAbort","abortActivityStreamOnLog","transform","_chunk","_encoding","callback","getStdout","pipe","getStderr","stdout","stderr","onHanging","warn","end","then","hangingTimer","clearTimeout","setTimeout","method","exposedMethods","startsWith","args","attempts","result","race","bind","Error"],"mappings":"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","ignoreList":[0]}