Repository: vitejs/vite
File: packages/vite/src/node/plugins/terser.ts (TypeScript)

Code before:
      throw message
    }
  }
  return terserPath
}

export function terserPlugin(config: ResolvedConfig): Plugin {
  const { maxWorkers, ...terserOptions } = config.build.terserOptions

  const makeWorker = () =>
    new WorkerWithFallback(
      () =>
        async (
          terserPath: string,
          code: string,
          options: TerserMinifyOptions,
        ) => {
          try {
            // test fails when using `import`. maybe related: https://github.com/nodejs/node/issues/43205
            // eslint-disable-next-line no-restricted-globals -- this function runs inside cjs
            const terser = require(terserPath)
            return (await terser.minify(code, options)) as TerserMinifyOutput
          } catch (e) {
            // convert to a plain object as additional properties of Error instances are not
            // sent back to the main thread
            throw { ...e }
          }
        },
      {
        shouldUseFake(_terserPath, _code, options) {
          return !!(
            (typeof options.mangle === 'object' &&
              (options.mangle.nth_identifier?.get ||
                (typeof options.mangle.properties === 'object' &&
                  options.mangle.properties.nth_identifier?.get))) ||
            typeof options.format?.comments === 'function' ||
            typeof options.output?.comments === 'function'
          )
        },
        max: maxWorkers,
      },
    )

  let worker: ReturnType<typeof makeWorker>

  return {
    name: 'vite:terser',

    applyToEnvironment(environment) {
      // We also need the plugin even if minify isn't 'terser' as we force
      // terser in plugin-legacy

Review comment (suggestion):
Throwing a plain object instead of an Error instance loses the error's prototype chain and stack trace. Consider using `throw new Error(e.message, { cause: e })` or creating a proper Error object with the necessary properties.
```suggestion
            // Wrap the error in an Error instance to preserve stack trace and prototype chain
            const error = new Error(e && e.message ? e.message : String(e));
            if (e && e.stack) error.stack = e.stack;
            // Optionally copy other properties if needed
            throw error;
```

Code after:
      message.stack = e.stack + '\n' + message.stack
      throw message
    }
  }
  return terserPath
}

export function terserPlugin(config: ResolvedConfig): Plugin {
  const { maxWorkers, ...terserOptions } = config.build.terserOptions

  const makeWorker = () =>
    new WorkerWithFallback(
      () =>
        async (
          terserPath: string,
          code: string,
          options: TerserMinifyOptions,
        ) => {
          const terser: typeof import('terser') = (await import(terserPath))
            .default
          try {
            return (await terser.minify(code, options)) as TerserMinifyOutput
          } catch (e) {
            // convert to a plain object as additional properties of Error instances are not
            // sent back to the main thread
            throw { stack: e.stack /* stack is non-enumerable */, ...e }
          }
        },
      {
        shouldUseFake(_terserPath, _code, options) {
          return !!(
            (typeof options.mangle === 'object' &&
              (options.mangle.nth_identifier?.get ||
                (typeof options.mangle.properties === 'object' &&
                  options.mangle.properties.nth_identifier?.get))) ||
            typeof options.format?.comments === 'function' ||
            typeof options.output?.comments === 'function'
          )
        },
        max: maxWorkers,
      },
    )

  let worker: ReturnType<typeof makeWorker>

  return {
    name: 'vite:terser',

    applyToEnvironment(environment) {
      // We also need the plugin even if minify isn't 'terser' as we force
      // terser in plugin-legacy