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

Code before:
      if (
        Object.values(builder.environments).every(
          (environment) => !environment.isBuilt,
        )
      ) {
        for (const environment of Object.values(builder.environments)) {
          await builder.build(environment)
        }
      }
    },
    async build(
      environment: BuildEnvironment,
    ): Promise<RollupOutput | RollupOutput[] | RollupWatcher> {
      const output = await buildEnvironment(environment)
      environment.isBuilt = true
      return output
    },
  }

  async function setupEnvironment(name: string, config: ResolvedConfig) {
    const environment = await config.build.createEnvironment(name, config)
    await environment.init()
    environments[name] = environment
  }

  if (useLegacyBuilder) {
    await setupEnvironment(config.build.ssr ? 'ssr' : 'client', config)
  } else {
    const environmentConfigs: [string, ResolvedConfig][] = []
    for (const environmentName of Object.keys(config.environments)) {
      // We need to resolve the config again so we can properly merge options
      // and get a new set of plugins for each build environment. The ecosystem
      // expects plugins to be run for the same environment once they are created
      // and to process a single bundle at a time (contrary to dev mode where
      // plugins are built to handle multiple environments concurrently).
      let environmentConfig = config
      if (!configBuilder.sharedConfigBuild) {
        const patchConfig = (resolved: ResolvedConfig) => {
          // Until the ecosystem updates to use `environment.config.build` instead of `config.build`,
          // we need to make override `config.build` for the current environment.
          // We can deprecate `config.build` in ResolvedConfig and push everyone to upgrade, and later
          // remove the default values that shouldn't be used at all once the config is resolved
          ;(resolved.build as ResolvedBuildOptions) = {
            ...resolved.environments[environmentName].build,
          }
        }
        const patchPlugins = (resolvedPlugins: Plugin[]) => {
          // Force opt-in shared plugins
          let j = 0
          for (let i = 0; i < resolvedPlugins.length; i++) {
            const environmentPlugin = resolvedPlugins[i]

Review comment (bug):
I have to admit I have no idea why this condition was written this way before. I can see it was initially added in https://github.com/vitejs/vite/pull/8762 but couldn't figure out why it was there. Changing this also doesn't seem to fail any test, so I suppose it isn't very critical.

I think it makes more sense for SSR to always use relative path for building. While client code can use either absolute path or relative path, server code almost definitely need relative path, since files have to be accessed through filesystem rather than network request, and the current directory the process starts from is often not reliable for resolving paths.

Code after:
    hostType: string,
  ) => string | { runtime: string },
): string | { runtime: string } {
  const { experimental, base, decodedBase } = environment.config
  const ssr = environment.config.consumer === 'server' // was !!environment.config.build.ssr
  const { renderBuiltUrl } = experimental
  let relative = base === '' || base === './'
  if (renderBuiltUrl) {
    const result = renderBuiltUrl(filename, {
      hostId,
      hostType,
      type,
      ssr,
    })
    if (typeof result === 'object') {
      if (result.runtime) {
        return { runtime: result.runtime }
      }
      if (typeof result.relative === 'boolean') {
        relative = result.relative
      }
    } else if (result) {
      return result
    }
  }
  if (relative && !ssr) {
    return toRelative(filename, hostId)
  }
  return joinUrlSegments(decodedBase, filename)
}

export function createToImportMetaURLBasedRelativeRuntime(
  format: InternalModuleFormat,
  isWorker: boolean,
): (filename: string, importer: string) => { runtime: string } {
  const formatLong = isWorker && format === 'iife' ? 'worker-iife' : format
  const toRelativePath = customRelativeUrlMechanisms[formatLong]
  return (filename, importer) => ({
    runtime: toRelativePath(
      path.posix.relative(path.dirname(importer), filename),
    ),
  })
}

export function toOutputFilePathWithoutRuntime(
  filename: string,
  type: 'asset' | 'public',
  hostId: string,
  hostType: 'js' | 'css' | 'html',
  config: ResolvedConfig,
  toRelative: (filename: string, hostId: string) => string,