diff --git a/vite-plugin-ruby/src/index.ts b/vite-plugin-ruby/src/index.ts index 8faa71aa..8515b2de 100644 --- a/vite-plugin-ruby/src/index.ts +++ b/vite-plugin-ruby/src/index.ts @@ -1,9 +1,10 @@ -import { basename, posix, resolve } from 'path' +import { basename, posix, relative, resolve } from 'path' import { existsSync, readFileSync } from 'fs' -import type { ConfigEnv, PluginOption, UserConfig, ViteDevServer } from 'vite' +import { createLogger } from 'vite' +import type { ConfigEnv, Logger, PluginOption, UserConfig, ViteDevServer } from 'vite' import { createDebug } from 'obug' -import { cleanConfig, configOptionFromEnv } from './utils' +import { cleanConfig, configOptionFromEnv, slash } from './utils' import { filterEntrypointsForRollup, loadConfiguration, resolveGlobs } from './config' import { assetsManifestPlugin } from './manifest' import { bindDevServerCleanup, resolveDevServerMeta, writeDevServerMeta } from './dev-server' @@ -47,6 +48,9 @@ function config (userConfig: UserConfig, env: ConfigEnv): UserConfig { const { assetsDir, base, outDir, server, root, entrypoints, ssrBuild } = config const isLocal = config.mode === 'development' || config.mode === 'test' + const customLogger = env.command === 'build' + ? buildOutputLogger(userConfig, root, outDir) + : userConfig.customLogger const rollupOptions = userConfig.build?.rollupOptions let rollupInput = rollupOptions?.input @@ -93,6 +97,7 @@ function config (userConfig: UserConfig, env: ConfigEnv): UserConfig { return cleanConfig({ resolve: { alias }, base, + customLogger, envDir, root, server, @@ -101,6 +106,31 @@ function config (userConfig: UserConfig, env: ConfigEnv): UserConfig { }) } +// Internal: Displays build output paths relative to the Ruby project root instead of Vite's nested root. +function buildOutputLogger (userConfig: UserConfig, root: string, outDir: string): Logger { + const logger = userConfig.customLogger || createLogger(userConfig.logLevel, { allowClearScreen: userConfig.clearScreen }) + const viteOutputDir = slash(outDir) + const projectOutputDir = slash(relative(projectRoot, resolve(root, outDir))) + const outputPathPattern = new RegExp(`^((?:\\x1B\\[[0-?]*[ -/]*[@-~])*)${escapeRegExp(viteOutputDir)}(?=/|$)`) + + return new Proxy(logger, { + get (target, property) { + if (property === 'info') { + return (message: string, options?: Parameters[1]) => { + target.info(message.replace(outputPathPattern, `$1${projectOutputDir}`), options) + } + } + + const value = Reflect.get(target, property, target) + return typeof value === 'function' ? value.bind(target) : value + }, + }) +} + +function escapeRegExp (value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +} + // Internal: Allows to watch additional paths outside the source code dir. function configureServer (server: ViteDevServer) { server.watcher.add(watchAdditionalPaths) diff --git a/vite-plugin-ruby/tests/index.spec.ts b/vite-plugin-ruby/tests/index.spec.ts index 10f3c572..903bbd0b 100644 --- a/vite-plugin-ruby/tests/index.spec.ts +++ b/vite-plugin-ruby/tests/index.spec.ts @@ -1,4 +1,6 @@ import { describe, test, expect } from 'vitest' +import { createLogger } from 'vite' +import type { LogOptions } from 'vite' import { defaultConfig } from '@plugin/config' import ViteRuby from '@plugin/index' @@ -29,6 +31,25 @@ describe('config', () => { }).toThrow('No SSR entrypoint available') }) + test('prints build output paths relative to the Ruby project root', () => { + const messages: Array<[string, LogOptions | undefined]> = [] + const logger = createLogger() + logger.info = (message, options) => messages.push([message, options]) + + const plugin = ViteRuby() + const pluginConfig = plugin[0].config + defaultConfig.configPath = './default.vite.json' + const result = pluginConfig({ ...defaultConfig, customLogger: logger }, { command: 'build', mode: 'production' }) + + result.customLogger.info('\u001B[2m../../public/vite/assets/application.js\u001B[22m 1.23 kB', { timestamp: true }) + result.customLogger.info('unrelated ../../public/vite message') + + expect(messages).toEqual([ + ['\u001B[2mpublic/vite/assets/application.js\u001B[22m 1.23 kB', { timestamp: true }], + ['unrelated ../../public/vite message', undefined], + ]) + }) + describe('outputFileName (assetFileNames)', () => { function getAssetFileNames () { const plugin = ViteRuby()