From e546be5344e5fddbd49c00647d723d3c403b1e4d Mon Sep 17 00:00:00 2001 From: Yoann Moinet Date: Wed, 13 Nov 2024 18:16:16 +0100 Subject: [PATCH 1/6] Better integrate logger into the ecosystem --- CONTRIBUTING.md | 3 +- README.md | 1 + packages/core/src/log.ts | 45 ---------- packages/core/src/types.ts | 17 +++- packages/factory/README.md | 27 ++++++ packages/factory/package.json | 1 + packages/factory/src/helpers.ts | 83 +++++++++++++++++-- packages/factory/src/index.ts | 9 +- packages/plugins/build-report/src/esbuild.ts | 7 +- packages/plugins/build-report/src/helpers.ts | 2 + packages/plugins/build-report/src/index.ts | 7 +- packages/plugins/build-report/src/rollup.ts | 3 +- packages/plugins/build-report/src/webpack.ts | 6 +- packages/plugins/injection/src/helpers.ts | 3 +- packages/plugins/injection/src/index.ts | 6 +- packages/plugins/rum/src/index.ts | 3 +- packages/plugins/rum/src/sourcemaps/index.ts | 3 +- packages/plugins/rum/src/sourcemaps/sender.ts | 3 +- packages/plugins/rum/src/validate.ts | 2 +- .../telemetry/src/common/output/files.ts | 2 +- .../telemetry/src/common/output/text.ts | 3 +- .../plugins/telemetry/src/common/sender.ts | 2 +- .../telemetry/src/esbuild-plugin/index.ts | 3 +- packages/plugins/telemetry/src/index.ts | 3 +- packages/tests/src/_jest/helpers/mocks.ts | 23 ++++- packages/tests/src/factory/index.test.ts | 2 +- .../src/plugins/injection/helpers.test.ts | 3 +- .../src/plugins/rum/sourcemaps/files.test.ts | 1 + .../src/plugins/rum/sourcemaps/sender.test.ts | 20 ++--- .../tests/src/plugins/rum/validate.test.ts | 5 +- .../plugins/telemetry/common/helpers.test.ts | 5 +- .../telemetry/common/output/files.test.ts | 3 +- .../src/plugins/telemetry/testHelpers.ts | 12 +-- .../src/commands/create-plugin/templates.ts | 16 ++-- packages/tools/src/helpers.ts | 8 +- yarn.lock | 1 + 36 files changed, 206 insertions(+), 137 deletions(-) delete mode 100644 packages/core/src/log.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 90ad6e2a..c925e646 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -114,7 +114,6 @@ stateDiagram-v2 } state core { - getLogger() sharedHelpers sharedConstants types @@ -163,7 +162,7 @@ Bootstrapping all the files you'll need to start coding. yarn cli create-plugin ``` -Then learn more about what you can use from [the ecosystem](/packages/core). +Then learn more about what you can use from [the ecosystem](/packages/factory). ## Tests diff --git a/README.md b/README.md index 47b5990c..217eeab3 100644 --- a/README.md +++ b/README.md @@ -372,6 +372,7 @@ type GlobalContext = { writeDuration?: number; }; cwd: string; + getLogger: (name: string) => [Logger](/packages/factory/src/helpers.ts); // Added in `buildStart`. git?: { hash: string; diff --git a/packages/core/src/log.ts b/packages/core/src/log.ts deleted file mode 100644 index 4ac31f21..00000000 --- a/packages/core/src/log.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2019-Present Datadog, Inc. - -import c from 'chalk'; - -import type { LogLevel } from './types'; - -export type Logger = (text: any, type?: LogLevel) => void; - -// TODO Move this into a plugin and better integrate with the bundlers. -// TODO Warnings/errors should be added to the build report. -const log = (text: any, level: LogLevel, type: LogLevel = 'debug', name?: string) => { - // By default (debug) we print dimmed. - let color = c.dim; - let logFn = console.log; - - if (type === 'error') { - color = c.red; - logFn = console.error; - } else if (type === 'warn') { - color = c.yellow; - logFn = console.warn; - } else if (type === 'info') { - color = c.cyan; - logFn = console.log; - } - - const prefix = name ? `[${type}|${name}] ` : ''; - - if ( - level === 'debug' || - (level === 'info' && ['info', 'error', 'warn'].includes(type)) || - (level === 'warn' && ['error', 'warn'].includes(type)) || - (level === 'error' && type === 'error') - ) { - const content = typeof text === 'string' ? text : JSON.stringify(text, null, 2); - logFn(`${color(prefix)}${content}`); - } -}; - -export const getLogger = - (level: LogLevel = 'warn', name?: string): Logger => - (text: any, type: LogLevel = 'debug') => - log(text, level, type, name); diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 9a58bb5b..8b1cf3e3 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -41,6 +41,7 @@ export type SerializedOutput = Assign; export type BuildReport = { errors: string[]; warnings: string[]; + logs: { pluginName: string; type: LogLevel; message: string; time: number }[]; entries?: Entry[]; inputs?: Input[]; outputs?: Output[]; @@ -73,12 +74,21 @@ export type BundlerReport = { export type ToInjectItem = { type: 'file' | 'code'; value: string; fallback?: ToInjectItem }; +export type Logger = { + (text: any, type?: LogLevel): void; + error: (text: any) => void; + warn: (text: any) => void; + info: (text: any) => void; + debug: (text: any) => void; +}; +export type GetLogger = (name: string) => Logger; export type GlobalContext = { auth?: AuthOptions; inject: (item: ToInjectItem) => void; bundler: BundlerReport; build: BuildReport; cwd: string; + getLogger: GetLogger; git?: RepositoryData; pluginNames: string[]; start: number; @@ -103,13 +113,13 @@ export type AuthOptions = { apiKey?: string; }; -export interface GetPluginsOptions { +export interface BaseOptions { auth?: AuthOptions; disableGit?: boolean; logLevel?: LogLevel; } -export interface Options extends GetPluginsOptions { +export interface Options extends BaseOptions { // Each product should have a unique entry. // #types-injection-marker [rum.CONFIG_KEY]?: RumOptions; @@ -118,6 +128,9 @@ export interface Options extends GetPluginsOptions { customPlugins?: GetCustomPlugins; } +export type GetPluginsOptions = Required; +export type OptionsWithDefaults = Assign; + export type PluginName = `datadog-${Lowercase}-plugin`; type Data = { data: BodyInit; headers?: Record }; diff --git a/packages/factory/README.md b/packages/factory/README.md index e7d6c980..e9abdbb7 100644 --- a/packages/factory/README.md +++ b/packages/factory/README.md @@ -49,6 +49,32 @@ Most of the time they will interact via the global context. [📝 Full documentation ➡️](/packages/plugins/injection#readme) +## Logger + +If you need to log anything into the console you'll have to use the global Logger. +Simply instantiate your logger in your plugin's initialization. + +```typescript +// ./packages/plugins/my-plugin/index.ts +[...] + +export const getMyPlugins = (context: GlobalContext) => { + const logger = context.getLogger('my-plugin'); +}; +``` + +Then you can either use one of the helpers or programmatically use a specific level: + +```typescript +const logLevel = 'warn'; +logger('This will be a warning', logLevel); + +logger.warn('This is also a warning'); +logger.error('This is an error'); +logger.info('This is an info'); +logger.debug('This is a debug message'); +``` + ## Global Context A global, shared context within the build plugins ecosystem.
@@ -106,6 +132,7 @@ type GlobalContext = { writeDuration?: number; }; cwd: string; + getLogger: (name: string) => [Logger](/packages/factory/src/helpers.ts); // Added in `buildStart`. git?: { hash: string; diff --git a/packages/factory/package.json b/packages/factory/package.json index 3ca66755..8b63c6de 100644 --- a/packages/factory/package.json +++ b/packages/factory/package.json @@ -23,6 +23,7 @@ "@dd/internal-injection-plugin": "workspace:*", "@dd/rum-plugin": "workspace:*", "@dd/telemetry-plugin": "workspace:*", + "chalk": "2.3.1", "unplugin": "1.16.0" } } diff --git a/packages/factory/src/helpers.ts b/packages/factory/src/helpers.ts index 6bcfb52e..f927973d 100644 --- a/packages/factory/src/helpers.ts +++ b/packages/factory/src/helpers.ts @@ -3,22 +3,85 @@ // Copyright 2019-Present Datadog, Inc. import type { + BuildReport, BundlerFullName, BundlerName, FactoryMeta, + GetLogger, GlobalContext, + LogLevel, Options, + OptionsWithDefaults, ToInjectItem, } from '@dd/core/types'; +import c from 'chalk'; + +const logPriority: Record = { + debug: 0, + info: 1, + warn: 2, + error: 3, + none: 4, +}; + +const getLoggerFactory = + (build: BuildReport, logLevel: LogLevel = 'warn'): GetLogger => + (name) => { + const log = (text: any, type: LogLevel = 'debug') => { + // By default (debug) we print dimmed. + let color = c.dim; + let logFn = console.log; + + if (type === 'error') { + color = c.red; + logFn = console.error; + } else if (type === 'warn') { + color = c.yellow; + logFn = console.warn; + } else if (type === 'info') { + color = c.cyan; + logFn = console.log; + } + + const prefix = `[${type}|${name}]`; + + // Keep a trace of the log in the build report. + const content = typeof text === 'string' ? text : JSON.stringify(text, null, 2); + build.logs.push({ pluginName: name, type, message: content, time: Date.now() }); + if (type === 'error') { + build.errors.push(content); + } + if (type === 'warn') { + build.warnings.push(content); + } + + // Only log if the log level is high enough. + if (logPriority[type] >= logPriority[logLevel]) { + logFn(`${color(prefix)} ${content}`); + } + }; + + const logFn = (text: any, type: LogLevel = 'debug') => { + log(text, type); + }; + + // Add shortcuts for the other log levels. + logFn.error = (text: any) => log(text, 'error'); + logFn.warn = (text: any) => log(text, 'warn'); + logFn.info = (text: any) => log(text, 'info'); + logFn.debug = (text: any) => log(text, 'debug'); + + return logFn; + }; export const getContext = ({ - auth, + options, bundlerName, bundlerVersion, injections, version, }: { - auth: Options['auth']; + options: OptionsWithDefaults; bundlerName: BundlerName; bundlerVersion: string; injections: ToInjectItem[]; @@ -26,9 +89,13 @@ export const getContext = ({ }): GlobalContext => { const cwd = process.cwd(); const variant = bundlerName === 'webpack' ? bundlerVersion.split('.')[0] : ''; - + const build: BuildReport = { + errors: [], + warnings: [], + logs: [], + }; const context: GlobalContext = { - auth, + auth: options.auth, pluginNames: [], bundler: { name: bundlerName, @@ -37,14 +104,12 @@ export const getContext = ({ outDir: cwd, version: bundlerVersion, }, - build: { - errors: [], - warnings: [], - }, + build, cwd, inject: (item: ToInjectItem) => { injections.push(item); }, + getLogger: getLoggerFactory(build, options.logLevel), start: Date.now(), version, }; @@ -52,7 +117,7 @@ export const getContext = ({ return context; }; -export const validateOptions = (options: Options = {}): Options => { +export const validateOptions = (options: Options = {}): OptionsWithDefaults => { return { auth: {}, disableGit: false, diff --git a/packages/factory/src/index.ts b/packages/factory/src/index.ts index 80c76d93..6a143331 100644 --- a/packages/factory/src/index.ts +++ b/packages/factory/src/index.ts @@ -16,6 +16,7 @@ import type { FactoryMeta, GlobalContext, Options, + OptionsWithDefaults, PluginOptions, ToInjectItem, } from '@dd/core/types'; @@ -59,7 +60,7 @@ export const buildPluginFactory = ({ // TODO: Validate API Key and endpoint. // TODO: Inject a metric logger into the global context. - const options = validateOptions(opts); + const options: OptionsWithDefaults = validateOptions(opts); // Set the host name for the esbuild plugin. if (unpluginMetaContext.framework === 'esbuild') { @@ -69,7 +70,7 @@ export const buildPluginFactory = ({ // Create the global context. const injections: ToInjectItem[] = []; const context: GlobalContext = getContext({ - auth: options.auth, + options, bundlerVersion: bundler.version || bundler.VERSION, bundlerName: unpluginMetaContext.framework as BundlerName, injections, @@ -83,10 +84,10 @@ export const buildPluginFactory = ({ const plugins: (PluginOptions | UnpluginOptions)[] = [ // Prefill with our internal plugins. // #internal-plugins-injection-marker - ...getBuildReportPlugins(options, context), + ...getBuildReportPlugins(context), ...getBundlerReportPlugins(context), ...getGitPlugins(options, context), - ...getInjectionPlugins(bundler, options, context, injections), + ...getInjectionPlugins(bundler, context, injections), // #internal-plugins-injection-marker ]; diff --git a/packages/plugins/build-report/src/esbuild.ts b/packages/plugins/build-report/src/esbuild.ts index 885a5c2a..77019046 100644 --- a/packages/plugins/build-report/src/esbuild.ts +++ b/packages/plugins/build-report/src/esbuild.ts @@ -3,8 +3,7 @@ // Copyright 2019-Present Datadog, Inc. import { getResolvedPath, isInjectionFile } from '@dd/core/helpers'; -import type { Logger } from '@dd/core/log'; -import type { Entry, GlobalContext, Input, Output, PluginOptions } from '@dd/core/types'; +import type { Logger, Entry, GlobalContext, Input, Output, PluginOptions } from '@dd/core/types'; import { glob } from 'glob'; import { cleanName, getAbsolutePath, getType } from './helpers'; @@ -68,8 +67,8 @@ export const getEsbuildPlugin = (context: GlobalContext, log: Logger): PluginOpt const entryNames = getEntryNames(entrypoints, context); build.onEnd((result) => { - context.build.errors = result.errors.map((err) => err.text); - context.build.warnings = result.warnings.map((err) => err.text); + context.build.errors.push(...result.errors.map((err) => err.text)); + context.build.warnings.push(...result.warnings.map((err) => err.text)); const warn = (warning: string) => { context.build.warnings.push(warning); diff --git a/packages/plugins/build-report/src/helpers.ts b/packages/plugins/build-report/src/helpers.ts index fa421bae..080fd5c3 100644 --- a/packages/plugins/build-report/src/helpers.ts +++ b/packages/plugins/build-report/src/helpers.ts @@ -51,6 +51,7 @@ export const serializeBuildReport = (report: BuildReport): SerializedBuildReport const jsonReport: SerializedBuildReport = { errors: report.errors, warnings: report.warnings, + logs: report.logs, start: report.start, end: report.end, duration: report.duration, @@ -103,6 +104,7 @@ export const unserializeBuildReport = (report: SerializedBuildReport): BuildRepo const buildReport: BuildReport = { errors: report.errors, warnings: report.warnings, + logs: report.logs, start: report.start, end: report.end, duration: report.duration, diff --git a/packages/plugins/build-report/src/index.ts b/packages/plugins/build-report/src/index.ts index 5700946a..a8945e8a 100644 --- a/packages/plugins/build-report/src/index.ts +++ b/packages/plugins/build-report/src/index.ts @@ -2,8 +2,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import { getLogger } from '@dd/core/log'; -import type { GlobalContext, Options, PluginOptions } from '@dd/core/types'; +import type { GlobalContext, PluginOptions } from '@dd/core/types'; import { getEsbuildPlugin } from './esbuild'; import { getRollupPlugin } from './rollup'; @@ -11,8 +10,8 @@ import { getWebpackPlugin } from './webpack'; const PLUGIN_NAME = 'datadog-build-report-plugin'; -export const getBuildReportPlugins = (opts: Options, context: GlobalContext): PluginOptions[] => { - const log = getLogger(opts.logLevel, PLUGIN_NAME); +export const getBuildReportPlugins = (context: GlobalContext): PluginOptions[] => { + const log = context.getLogger(PLUGIN_NAME); return [ { name: PLUGIN_NAME, diff --git a/packages/plugins/build-report/src/rollup.ts b/packages/plugins/build-report/src/rollup.ts index 5b512874..5ccf56c0 100644 --- a/packages/plugins/build-report/src/rollup.ts +++ b/packages/plugins/build-report/src/rollup.ts @@ -2,8 +2,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import type { Logger } from '@dd/core/log'; -import type { Entry, GlobalContext, Input, Output, PluginOptions } from '@dd/core/types'; +import type { Logger, Entry, GlobalContext, Input, Output, PluginOptions } from '@dd/core/types'; import { cleanName, cleanPath, cleanReport, getAbsolutePath, getType } from './helpers'; diff --git a/packages/plugins/build-report/src/webpack.ts b/packages/plugins/build-report/src/webpack.ts index 85185bc9..bb4c7d2b 100644 --- a/packages/plugins/build-report/src/webpack.ts +++ b/packages/plugins/build-report/src/webpack.ts @@ -2,8 +2,8 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import type { Logger } from '@dd/core/log'; import type { + Logger, Entry, GlobalContext, Input, @@ -292,8 +292,8 @@ export const getWebpackPlugin = } // Save everything in the context. - context.build.errors = result.errors.map((err) => err.message); - context.build.warnings = [...warnings, ...result.warnings.map((err) => err.message)]; + context.build.errors.push(...result.errors.map((err) => err.message)); + context.build.warnings.push(...warnings, ...result.warnings.map((err) => err.message)); context.build.inputs = inputs; context.build.outputs = outputs; context.build.entries = entries; diff --git a/packages/plugins/injection/src/helpers.ts b/packages/plugins/injection/src/helpers.ts index 25a1c687..2e765b67 100644 --- a/packages/plugins/injection/src/helpers.ts +++ b/packages/plugins/injection/src/helpers.ts @@ -3,8 +3,7 @@ // Copyright 2019-Present Datadog, Inc. import { doRequest, truncateString } from '@dd/core/helpers'; -import type { Logger } from '@dd/core/log'; -import type { ToInjectItem } from '@dd/core/types'; +import type { Logger, ToInjectItem } from '@dd/core/types'; import { getAbsolutePath } from '@dd/internal-build-report-plugin/helpers'; import { readFile } from 'fs/promises'; diff --git a/packages/plugins/injection/src/index.ts b/packages/plugins/injection/src/index.ts index 850adab3..dae1f27d 100644 --- a/packages/plugins/injection/src/index.ts +++ b/packages/plugins/injection/src/index.ts @@ -4,8 +4,7 @@ import { INJECTED_FILE } from '@dd/core/constants'; import { outputFile, rm } from '@dd/core/helpers'; -import { getLogger } from '@dd/core/log'; -import type { GlobalContext, Options, PluginOptions, ToInjectItem } from '@dd/core/types'; +import type { GlobalContext, PluginOptions, ToInjectItem } from '@dd/core/types'; import path from 'path'; import { PLUGIN_NAME, PREPARATION_PLUGIN_NAME } from './constants'; @@ -13,11 +12,10 @@ import { processInjections } from './helpers'; export const getInjectionPlugins = ( bundler: any, - opts: Options, context: GlobalContext, toInject: ToInjectItem[], ): PluginOptions[] => { - const log = getLogger(opts.logLevel, PLUGIN_NAME); + const log = context.getLogger(PLUGIN_NAME); const contentToInject: string[] = []; const getContentToInject = () => { diff --git a/packages/plugins/rum/src/index.ts b/packages/plugins/rum/src/index.ts index 549fdf4a..01e71c46 100644 --- a/packages/plugins/rum/src/index.ts +++ b/packages/plugins/rum/src/index.ts @@ -2,7 +2,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import { getLogger } from '@dd/core/log'; import type { GlobalContext, GetPlugins } from '@dd/core/types'; import { PLUGIN_NAME } from './constants'; @@ -22,7 +21,7 @@ export const getPlugins: GetPlugins = ( opts: OptionsWithRum, context: GlobalContext, ) => { - const log = getLogger(opts.logLevel, PLUGIN_NAME); + const log = context.getLogger(PLUGIN_NAME); // Verify configuration. const rumOptions = validateOptions(opts, log); return [ diff --git a/packages/plugins/rum/src/sourcemaps/index.ts b/packages/plugins/rum/src/sourcemaps/index.ts index eb00b94d..6020f346 100644 --- a/packages/plugins/rum/src/sourcemaps/index.ts +++ b/packages/plugins/rum/src/sourcemaps/index.ts @@ -2,8 +2,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import type { Logger } from '@dd/core/log'; -import type { GlobalContext } from '@dd/core/types'; +import type { Logger, GlobalContext } from '@dd/core/types'; import chalk from 'chalk'; import { outdent } from 'outdent'; diff --git a/packages/plugins/rum/src/sourcemaps/sender.ts b/packages/plugins/rum/src/sourcemaps/sender.ts index a50626b6..f19c825f 100644 --- a/packages/plugins/rum/src/sourcemaps/sender.ts +++ b/packages/plugins/rum/src/sourcemaps/sender.ts @@ -3,8 +3,7 @@ // Copyright 2019-Present Datadog, Inc. import { NB_RETRIES, doRequest, formatDuration } from '@dd/core/helpers'; -import type { Logger } from '@dd/core/log'; -import type { GlobalContext } from '@dd/core/types'; +import type { Logger, GlobalContext } from '@dd/core/types'; import { File } from 'buffer'; import chalk from 'chalk'; import fs from 'fs'; diff --git a/packages/plugins/rum/src/validate.ts b/packages/plugins/rum/src/validate.ts index 42d69d90..add00334 100644 --- a/packages/plugins/rum/src/validate.ts +++ b/packages/plugins/rum/src/validate.ts @@ -2,7 +2,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import type { Logger } from '@dd/core/log'; +import type { Logger } from '@dd/core/types'; import chalk from 'chalk'; import { CONFIG_KEY, PLUGIN_NAME } from './constants'; diff --git a/packages/plugins/telemetry/src/common/output/files.ts b/packages/plugins/telemetry/src/common/output/files.ts index 3d2b9053..a1ba2f96 100644 --- a/packages/plugins/telemetry/src/common/output/files.ts +++ b/packages/plugins/telemetry/src/common/output/files.ts @@ -3,7 +3,7 @@ // Copyright 2019-Present Datadog, Inc. import { formatDuration, outputJson } from '@dd/core/helpers'; -import type { Logger } from '@dd/core/log'; +import type { Logger } from '@dd/core/types'; import type { MetricToSend, OutputOptions, Report } from '@dd/telemetry-plugin/types'; import path from 'path'; diff --git a/packages/plugins/telemetry/src/common/output/text.ts b/packages/plugins/telemetry/src/common/output/text.ts index 3172a040..87f1ac94 100644 --- a/packages/plugins/telemetry/src/common/output/text.ts +++ b/packages/plugins/telemetry/src/common/output/text.ts @@ -3,8 +3,7 @@ // Copyright 2019-Present Datadog, Inc. import { formatDuration, truncateString } from '@dd/core/helpers'; -import type { Logger } from '@dd/core/log'; -import type { Entry, GlobalContext, Output } from '@dd/core/types'; +import type { Logger, Entry, GlobalContext, Output } from '@dd/core/types'; import { serializeBuildReport } from '@dd/internal-build-report-plugin/helpers'; import chalk from 'chalk'; import prettyBytes from 'pretty-bytes'; diff --git a/packages/plugins/telemetry/src/common/sender.ts b/packages/plugins/telemetry/src/common/sender.ts index 9a5f634f..251028b9 100644 --- a/packages/plugins/telemetry/src/common/sender.ts +++ b/packages/plugins/telemetry/src/common/sender.ts @@ -3,7 +3,7 @@ // Copyright 2019-Present Datadog, Inc. import { doRequest, formatDuration } from '@dd/core/helpers'; -import type { Logger } from '@dd/core/log'; +import type { Logger } from '@dd/core/types'; import type { MetricToSend } from '@dd/telemetry-plugin/types'; export const sendMetrics = ( diff --git a/packages/plugins/telemetry/src/esbuild-plugin/index.ts b/packages/plugins/telemetry/src/esbuild-plugin/index.ts index d0f5bfd4..434788c8 100644 --- a/packages/plugins/telemetry/src/esbuild-plugin/index.ts +++ b/packages/plugins/telemetry/src/esbuild-plugin/index.ts @@ -2,8 +2,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import type { Logger } from '@dd/core/log'; -import type { GlobalContext, PluginOptions } from '@dd/core/types'; +import type { Logger, GlobalContext, PluginOptions } from '@dd/core/types'; import type { BundlerContext } from '@dd/telemetry-plugin/types'; import type { BuildResult } from 'esbuild'; diff --git a/packages/plugins/telemetry/src/index.ts b/packages/plugins/telemetry/src/index.ts index e059e51b..f1ee3932 100644 --- a/packages/plugins/telemetry/src/index.ts +++ b/packages/plugins/telemetry/src/index.ts @@ -2,7 +2,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import { getLogger } from '@dd/core/log'; import type { GlobalContext, GetPlugins, PluginOptions } from '@dd/core/types'; import { getMetrics } from './common/aggregator'; @@ -44,7 +43,7 @@ export const getPlugins: GetPlugins = ( }; const telemetryOptions = validateOptions(options); - const logger = getLogger(options.logLevel, PLUGIN_NAME); + const logger = context.getLogger(PLUGIN_NAME); const plugins: PluginOptions[] = []; // Webpack and Esbuild specific plugins. diff --git a/packages/tests/src/_jest/helpers/mocks.ts b/packages/tests/src/_jest/helpers/mocks.ts index 471d5eee..1aaf0a34 100644 --- a/packages/tests/src/_jest/helpers/mocks.ts +++ b/packages/tests/src/_jest/helpers/mocks.ts @@ -6,8 +6,11 @@ import { outputJsonSync } from '@dd/core/helpers'; import type { File, GetCustomPlugins, + GetPluginsOptions, GlobalContext, IterableElement, + Logger, + LogLevel, Options, } from '@dd/core/types'; import { serializeBuildReport } from '@dd/internal-build-report-plugin/helpers'; @@ -35,13 +38,29 @@ export const defaultEntries = { }; export const defaultDestination = path.resolve(ROOT, 'packages/tests/src/_jest/fixtures/dist'); -export const defaultPluginOptions: Options = { +export const defaultPluginOptions: GetPluginsOptions = { auth: { apiKey: '123', }, + disableGit: false, logLevel: 'debug', }; +const logFn: any = jest.fn((text: any, type?: LogLevel) => {}); +logFn.error = (text: any) => { + logFn(text, 'error'); +}; +logFn.warn = (text: any) => { + logFn(text, 'warn'); +}; +logFn.info = (text: any) => { + logFn(text, 'info'); +}; +logFn.debug = (text: any) => { + logFn(text, 'debug'); +}; +export const mockLogger: Logger = logFn; + export const getContextMock = (options: Partial = {}): GlobalContext => { return { auth: { apiKey: 'FAKE_API_KEY' }, @@ -54,9 +73,11 @@ export const getContextMock = (options: Partial = {}): GlobalCont build: { warnings: [], errors: [], + logs: [], }, cwd: '/cwd/path', inject: jest.fn(), + getLogger: jest.fn(() => mockLogger), pluginNames: [], start: Date.now(), version: 'FAKE_VERSION', diff --git a/packages/tests/src/factory/index.test.ts b/packages/tests/src/factory/index.test.ts index 12288eed..8f5348ad 100644 --- a/packages/tests/src/factory/index.test.ts +++ b/packages/tests/src/factory/index.test.ts @@ -27,7 +27,7 @@ describe('Factory', () => { const { buildPluginFactory } = await import('@dd/factory'); expect(() => { const factory = buildPluginFactory({ bundler: {}, version: '1.0.0' }); - // Vite can call the factory without options. + // Vite could call the factory without options. // @ts-expect-error - We are testing the factory without options. factory.vite(); }).not.toThrow(); diff --git a/packages/tests/src/plugins/injection/helpers.test.ts b/packages/tests/src/plugins/injection/helpers.test.ts index e6d2b30a..76ce4620 100644 --- a/packages/tests/src/plugins/injection/helpers.test.ts +++ b/packages/tests/src/plugins/injection/helpers.test.ts @@ -2,7 +2,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import type { Logger } from '@dd/core/log'; import type { ToInjectItem } from '@dd/core/types'; import { processInjections, @@ -10,6 +9,7 @@ import { processLocalFile, processDistantFile, } from '@dd/internal-injection-plugin/helpers'; +import { mockLogger } from '@dd/tests/_jest/helpers/mocks'; import { vol } from 'memfs'; import nock from 'nock'; import path from 'path'; @@ -37,7 +37,6 @@ const nonExistingDistantFile: ToInjectItem = { }; describe('Injection Plugin Helpers', () => { - const mockLogger: Logger = jest.fn(); let nockScope: nock.Scope; beforeEach(() => { diff --git a/packages/tests/src/plugins/rum/sourcemaps/files.test.ts b/packages/tests/src/plugins/rum/sourcemaps/files.test.ts index 9a77b192..7bb1e1aa 100644 --- a/packages/tests/src/plugins/rum/sourcemaps/files.test.ts +++ b/packages/tests/src/plugins/rum/sourcemaps/files.test.ts @@ -24,6 +24,7 @@ describe('RUM Plugin Sourcemaps Files', () => { build: { warnings: [], errors: [], + logs: [], outputs: [ 'fixtures/common.js', 'fixtures/common.min.js.map', diff --git a/packages/tests/src/plugins/rum/sourcemaps/sender.test.ts b/packages/tests/src/plugins/rum/sourcemaps/sender.test.ts index 7742670a..c3f8e821 100644 --- a/packages/tests/src/plugins/rum/sourcemaps/sender.test.ts +++ b/packages/tests/src/plugins/rum/sourcemaps/sender.test.ts @@ -4,7 +4,7 @@ import { doRequest } from '@dd/core/helpers'; import { getData, sendSourcemaps, upload } from '@dd/rum-plugin/sourcemaps/sender'; -import { getContextMock } from '@dd/tests/_jest/helpers/mocks'; +import { getContextMock, mockLogger } from '@dd/tests/_jest/helpers/mocks'; import { vol } from 'memfs'; import { type Stream } from 'stream'; import { unzipSync } from 'zlib'; @@ -89,7 +89,7 @@ describe('RUM Plugin Sourcemaps', () => { [getSourcemapMock()], getSourcemapsConfiguration(), getContextMock(), - () => {}, + mockLogger, ); expect(doRequestMock).toHaveBeenCalledTimes(1); @@ -105,17 +105,15 @@ describe('RUM Plugin Sourcemaps', () => { __dirname, ); - const logMock = jest.fn(); - await sendSourcemaps( [getSourcemapMock()], getSourcemapsConfiguration(), getContextMock(), - logMock, + mockLogger, ); - expect(logMock).toHaveBeenCalledTimes(1); - expect(logMock).toHaveBeenCalledWith( + expect(mockLogger).toHaveBeenCalledTimes(1); + expect(mockLogger).toHaveBeenCalledWith( expect.stringMatching('Failed to prepare payloads, aborting upload'), 'error', ); @@ -137,7 +135,7 @@ describe('RUM Plugin Sourcemaps', () => { [getSourcemapMock()], getSourcemapsConfiguration({ bailOnError: true }), getContextMock(), - () => {}, + mockLogger, ); }).rejects.toThrow('Failed to prepare payloads, aborting upload'); expect(doRequestMock).not.toHaveBeenCalled(); @@ -170,7 +168,7 @@ describe('RUM Plugin Sourcemaps', () => { payloads, getSourcemapsConfiguration(), getContextMock(), - () => {}, + mockLogger, ); expect(warnings).toHaveLength(0); @@ -186,7 +184,7 @@ describe('RUM Plugin Sourcemaps', () => { payloads, getSourcemapsConfiguration(), getContextMock(), - jest.fn(), + mockLogger, ); expect(errors).toHaveLength(1); @@ -209,7 +207,7 @@ describe('RUM Plugin Sourcemaps', () => { payloads, getSourcemapsConfiguration({ bailOnError: true }), getContextMock(), - () => {}, + mockLogger, ), ).rejects.toThrow('Fake Error'); }); diff --git a/packages/tests/src/plugins/rum/validate.test.ts b/packages/tests/src/plugins/rum/validate.test.ts index 02639188..c12f1277 100644 --- a/packages/tests/src/plugins/rum/validate.test.ts +++ b/packages/tests/src/plugins/rum/validate.test.ts @@ -4,6 +4,7 @@ import type { RumSourcemapsOptions } from '@dd/rum-plugin/types'; import { validateOptions, validateSourcemapsOptions } from '@dd/rum-plugin/validate'; +import { mockLogger } from '@dd/tests/_jest/helpers/mocks'; import stripAnsi from 'strip-ansi'; import { getMinimalSourcemapsConfiguration } from './testHelpers'; @@ -20,7 +21,7 @@ describe('RUM Plugins validate', () => { disabled: false, }, }, - jest.fn(), + mockLogger, ); expect(config).toEqual({ @@ -40,7 +41,7 @@ describe('RUM Plugins validate', () => { sourcemaps: {} as RumSourcemapsOptions, }, }, - jest.fn(), + mockLogger, ); }).toThrow(); }); diff --git a/packages/tests/src/plugins/telemetry/common/helpers.test.ts b/packages/tests/src/plugins/telemetry/common/helpers.test.ts index 19c043f6..7b252523 100644 --- a/packages/tests/src/plugins/telemetry/common/helpers.test.ts +++ b/packages/tests/src/plugins/telemetry/common/helpers.test.ts @@ -9,13 +9,14 @@ import { validateOptions, } from '@dd/telemetry-plugin/common/helpers'; import { CONFIG_KEY } from '@dd/telemetry-plugin'; +import { defaultPluginOptions } from '@dd/tests/_jest/helpers/mocks'; import { getMockCompilation, getMockModule, mockCompilation } from '../testHelpers'; describe('Telemetry Helpers', () => { describe('validateOptions', () => { test('Should return the default options', () => { - const options = { [CONFIG_KEY]: {} }; + const options = { ...defaultPluginOptions, [CONFIG_KEY]: {} }; expect(validateOptions(options)).toEqual({ disabled: false, enableTracing: false, @@ -30,6 +31,7 @@ describe('Telemetry Helpers', () => { test('Should return the options with the provided values', () => { const fakeFilter = jest.fn(); const options = { + ...defaultPluginOptions, [CONFIG_KEY]: { disabled: true, enableTracing: true, @@ -53,6 +55,7 @@ describe('Telemetry Helpers', () => { test('Should add https:// if the endpoint does not have one', () => { const options = { + ...defaultPluginOptions, [CONFIG_KEY]: { endPoint: 'app.datadoghq.eu', }, diff --git a/packages/tests/src/plugins/telemetry/common/output/files.test.ts b/packages/tests/src/plugins/telemetry/common/output/files.test.ts index f020d308..d4479ca9 100644 --- a/packages/tests/src/plugins/telemetry/common/output/files.test.ts +++ b/packages/tests/src/plugins/telemetry/common/output/files.test.ts @@ -4,7 +4,8 @@ import { outputFiles } from '@dd/telemetry-plugin/common/output/files'; import type { OutputOptions } from '@dd/telemetry-plugin/types'; -import { mockLogger, mockReport } from '@dd/tests/plugins/telemetry/testHelpers'; +import { mockLogger } from '@dd/tests/_jest/helpers/mocks'; +import { mockReport } from '@dd/tests/plugins/telemetry/testHelpers'; import { vol } from 'memfs'; import path from 'path'; diff --git a/packages/tests/src/plugins/telemetry/testHelpers.ts b/packages/tests/src/plugins/telemetry/testHelpers.ts index 340e3794..0252d74b 100644 --- a/packages/tests/src/plugins/telemetry/testHelpers.ts +++ b/packages/tests/src/plugins/telemetry/testHelpers.ts @@ -2,8 +2,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import type { Logger } from '@dd/core/log'; -import type { LogLevel, Options } from '@dd/core/types'; import type { Report, Compilation, @@ -13,7 +11,7 @@ import type { TelemetryOptions, Module, } from '@dd/telemetry-plugin/types'; -import { FAKE_URL } from '@dd/tests/_jest/helpers/mocks'; +import { FAKE_URL, defaultPluginOptions } from '@dd/tests/_jest/helpers/mocks'; import type { PluginBuild, Metafile } from 'esbuild'; import esbuild from 'esbuild'; @@ -113,12 +111,6 @@ export const mockReport: Report = { }, }; -export const mockOptions: Options = { - auth: { - apiKey: '', - }, -}; -export const mockLogger: Logger = jest.fn((text: any, type?: LogLevel) => {}); export const mockOutputOptions: OutputOptions = true; export const mockOptionsDD: OptionsDD = { tags: [], @@ -128,7 +120,7 @@ export const mockOptionsDD: OptionsDD = { }; export const mockTelemetryOptions: TelemetryOptions = {}; export const mockOptionsWithTelemetry: OptionsWithTelemetry = { - ...mockOptions, + ...defaultPluginOptions, telemetry: mockTelemetryOptions, }; diff --git a/packages/tools/src/commands/create-plugin/templates.ts b/packages/tools/src/commands/create-plugin/templates.ts index 4b27ff4a..043752b9 100644 --- a/packages/tools/src/commands/create-plugin/templates.ts +++ b/packages/tools/src/commands/create-plugin/templates.ts @@ -27,7 +27,6 @@ export const getFiles = (context: Context): File[] => { content: (ctx) => { const hooksContent = ctx.hooks.map((hook) => getHookTemplate(hook)).join('\n'); return outdent` - import { getLogger } from '@dd/core/log'; import type { GlobalContext, GetPlugins } from '@dd/core/types'; import { CONFIG_KEY, PLUGIN_NAME } from './constants'; @@ -58,7 +57,8 @@ export const getFiles = (context: Context): File[] => { opts: OptionsWith${pascalCase}, context: GlobalContext, ) => { - const log = getLogger(opts.logLevel, PLUGIN_NAME); + const log = context.getLogger(PLUGIN_NAME); + // Verify configuration. const options = validateOptions(opts); @@ -99,18 +99,12 @@ export const getFiles = (context: Context): File[] => { content: (ctx) => { const hooksContent = ctx.hooks.map((hook) => getHookTemplate(hook)).join('\n'); return outdent` - import { getLogger } from '@dd/core/log'; - import type { GlobalContext, Options, PluginOptions } from '@dd/core/types'; + import type { GlobalContext, PluginOptions } from '@dd/core/types'; import { PLUGIN_NAME } from './constants'; - // Feel free to change how you want to handle the plugin's creation. - // TODO Call this function from packages/factory/src/internalPlugins.ts - export const get${pascalCase}Plugins = ( - opts: Options, - context: GlobalContext, - ): PluginOptions[] => { - const log = getLogger(opts.logLevel, PLUGIN_NAME); + export const get${pascalCase}Plugins = (context: GlobalContext): PluginOptions[] => { + const log = context.getLogger(PLUGIN_NAME); return [ { diff --git a/packages/tools/src/helpers.ts b/packages/tools/src/helpers.ts index 57564770..98cd69fe 100644 --- a/packages/tools/src/helpers.ts +++ b/packages/tools/src/helpers.ts @@ -3,7 +3,7 @@ // Copyright 2019-Present Datadog, Inc. import { readJsonSync } from '@dd/core/helpers'; -import type { GetPlugins } from '@dd/core/types'; +import type { GetPlugins, Logger } from '@dd/core/types'; import chalk from 'chalk'; import { execFile, execFileSync } from 'child_process'; import path from 'path'; @@ -149,6 +149,12 @@ export const getSupportedBundlers = (getPlugins: GetPlugins) => { build: { warnings: [], errors: [], + logs: [], + }, + getLogger() { + const fn = () => {}; + // We don't care about the logger here. + return fn as unknown as Logger; }, inject() {}, pluginNames: [], diff --git a/yarn.lock b/yarn.lock index 6537592a..a0f2a785 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1601,6 +1601,7 @@ __metadata: "@dd/internal-injection-plugin": "workspace:*" "@dd/rum-plugin": "workspace:*" "@dd/telemetry-plugin": "workspace:*" + chalk: "npm:2.3.1" unplugin: "npm:1.16.0" languageName: unknown linkType: soft From 4bce5c4a057f15806dd08551b79066fe93f813c6 Mon Sep 17 00:00:00 2001 From: Yoann Moinet Date: Fri, 15 Nov 2024 10:07:55 +0100 Subject: [PATCH 2/6] Run integrity --- packages/factory/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/factory/README.md b/packages/factory/README.md index e9abdbb7..3af47770 100644 --- a/packages/factory/README.md +++ b/packages/factory/README.md @@ -13,6 +13,7 @@ This is used to aggregate all the plugins and expose them to the bundler. - [Bundler Report](#bundler-report) - [Git](#git) - [Injection](#injection) +- [Logger](#logger) - [Global Context](#global-context) From 04d7afa53aea1f538c816339cdced0c34ffbb213 Mon Sep 17 00:00:00 2001 From: Yoann Moinet Date: Fri, 15 Nov 2024 11:16:34 +0100 Subject: [PATCH 3/6] Add tests --- packages/factory/src/helpers.ts | 3 +- packages/tests/package.json | 1 + packages/tests/src/factory/helpers.test.ts | 194 ++++++++++++++++++--- yarn.lock | 3 +- 4 files changed, 177 insertions(+), 24 deletions(-) diff --git a/packages/factory/src/helpers.ts b/packages/factory/src/helpers.ts index f927973d..113b6586 100644 --- a/packages/factory/src/helpers.ts +++ b/packages/factory/src/helpers.ts @@ -24,7 +24,8 @@ const logPriority: Record = { none: 4, }; -const getLoggerFactory = +// Exported for testing. +export const getLoggerFactory = (build: BuildReport, logLevel: LogLevel = 'warn'): GetLogger => (name) => { const log = (text: any, type: LogLevel = 'debug') => { diff --git a/packages/tests/package.json b/packages/tests/package.json index f6c0a100..52874a66 100644 --- a/packages/tests/package.json +++ b/packages/tests/package.json @@ -52,6 +52,7 @@ "memfs": "4.9.2", "nock": "14.0.0-beta.7", "rollup": "4.24.2", + "strip-ansi": "6.0.1", "typescript": "5.4.3", "vite": "5.4.10", "webpack": "5.92.1", diff --git a/packages/tests/src/factory/helpers.test.ts b/packages/tests/src/factory/helpers.test.ts index cf70aa06..212be3b6 100644 --- a/packages/tests/src/factory/helpers.test.ts +++ b/packages/tests/src/factory/helpers.test.ts @@ -2,36 +2,56 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import type { GlobalContext, Options } from '@dd/core/types'; +import type { BuildReport, GlobalContext, Logger, Options, ToInjectItem } from '@dd/core/types'; +import { getContext, getLoggerFactory } from '@dd/factory/helpers'; import { BUNDLER_VERSIONS } from '@dd/tests/_jest/helpers/constants'; import { defaultPluginOptions } from '@dd/tests/_jest/helpers/mocks'; import { BUNDLERS, runBundlers } from '@dd/tests/_jest/helpers/runBundlers'; import type { CleanupFn } from '@dd/tests/_jest/helpers/types'; +import stripAnsi from 'strip-ansi'; + +// Keep a reference to console.log for debugging. +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const log = console.log; + +// Spy on console to avoid logs in the console and to assert. +jest.spyOn(console, 'log').mockImplementation(() => {}); +jest.spyOn(console, 'error').mockImplementation(() => {}); +jest.spyOn(console, 'warn').mockImplementation(() => {}); + +const logMock = jest.mocked(console.log); +const errorMock = jest.mocked(console.error); +const warnMock = jest.mocked(console.warn); describe('Factory Helpers', () => { - describe('getContext', () => { - // Intercept contexts to verify it at the moment they're used. - const initialContexts: Record = {}; - let cleanup: CleanupFn; - - beforeAll(async () => { - const pluginConfig: Options = { - ...defaultPluginOptions, - // Use a custom plugin to intercept contexts to verify it at initialization. - customPlugins: (opts, context) => { - const bundlerName = context.bundler.fullName; - initialContexts[bundlerName] = JSON.parse(JSON.stringify(context)); - return []; - }, - }; - - cleanup = await runBundlers(pluginConfig); - }); + // Intercept contexts to verify it at the moment they're used. + const initialContexts: Record = {}; + let cleanup: CleanupFn; - afterAll(async () => { - await cleanup(); - }); + beforeAll(async () => { + const pluginConfig: Options = { + ...defaultPluginOptions, + // Use a custom plugin to intercept contexts to verify it at initialization. + customPlugins: (opts, context) => { + const bundlerName = context.bundler.fullName; + initialContexts[bundlerName] = JSON.parse(JSON.stringify(context)); + + // These are functions, so they can't be serialized with parse/stringify. + initialContexts[bundlerName].getLogger = context.getLogger; + initialContexts[bundlerName].inject = context.inject; + + return []; + }, + }; + + cleanup = await runBundlers(pluginConfig); + }); + + afterAll(async () => { + await cleanup(); + }); + describe('getContext', () => { describe.each(BUNDLERS)('[$name|$version]', ({ name, version }) => { test('Should have the right initial context.', () => { const context = initialContexts[name]; @@ -44,7 +64,137 @@ describe('Factory Helpers', () => { expect(context.bundler.version).toBe(BUNDLER_VERSIONS[name]); expect(context.cwd).toBe(process.cwd()); expect(context.version).toBe(version); + expect(context.getLogger).toEqual(expect.any(Function)); + expect(context.inject).toEqual(expect.any(Function)); + }); + }); + + test('Should inject items for the injection plugin.', () => { + const injections: ToInjectItem[] = []; + const context = getContext({ + options: defaultPluginOptions, + bundlerName: 'webpack', + bundlerVersion: '1.0.0', + injections, + version: '1.0.0', + }); + const injectedItem: ToInjectItem = { type: 'code', value: 'injected' }; + context.inject(injectedItem); + expect(injections).toEqual([injectedItem]); + }); + + test('Should return a logger.', () => { + const context = getContext({ + options: defaultPluginOptions, + bundlerName: 'webpack', + bundlerVersion: '1.0.0', + injections: [], + version: '1.0.0', + }); + + const logger = context.getLogger('testLogger'); + + expect(logger).toEqual(expect.any(Function)); + expect(logger.error).toEqual(expect.any(Function)); + expect(logger.warn).toEqual(expect.any(Function)); + expect(logger.info).toEqual(expect.any(Function)); + expect(logger.debug).toEqual(expect.any(Function)); + }); + }); + + describe('getLoggerFactory', () => { + const setupLogger = (): [Logger, BuildReport] => { + const mockBuild = { errors: [], warnings: [], logs: [] }; + const loggerFactory = getLoggerFactory(mockBuild, 'debug'); + const logger = loggerFactory('testLogger'); + + return [logger, mockBuild]; + }; + + const useLogger = (logger: Logger) => { + logger('A message.'); + logger('Info message #1.', 'info'); + logger.error('An error occurred.'); + logger.warn('A warning message.'); + logger.info('Info message #2.'); + logger.debug('A debug message.'); + }; + + test('Should return a logger factory.', () => { + const [logger] = setupLogger(); + + expect(logger).toEqual(expect.any(Function)); + expect(logger.error).toEqual(expect.any(Function)); + expect(logger.warn).toEqual(expect.any(Function)); + expect(logger.info).toEqual(expect.any(Function)); + expect(logger.debug).toEqual(expect.any(Function)); + }); + + test('Should log as expected', () => { + const [logger] = setupLogger(); + useLogger(logger); + + // Access logs and strip colors. + const getOutput = (mock: jest.Mock, index: number) => + stripAnsi(mock.mock.calls[index][0]); + + expect(logMock).toHaveBeenCalledTimes(4); + expect(getOutput(logMock, 0)).toBe('[debug|testLogger] A message.'); + expect(getOutput(logMock, 1)).toBe('[info|testLogger] Info message #1.'); + expect(getOutput(logMock, 2)).toBe('[info|testLogger] Info message #2.'); + expect(getOutput(logMock, 3)).toBe('[debug|testLogger] A debug message.'); + + expect(errorMock).toHaveBeenCalledTimes(1); + expect(getOutput(errorMock, 0)).toBe('[error|testLogger] An error occurred.'); + + expect(warnMock).toHaveBeenCalledTimes(1); + expect(getOutput(warnMock, 0)).toBe('[warn|testLogger] A warning message.'); + }); + + test('Should store logs as expected.', () => { + const [logger, buildReport] = setupLogger(); + useLogger(logger); + + expect(buildReport.logs).toHaveLength(6); + expect(buildReport.logs[0]).toEqual({ + pluginName: 'testLogger', + type: 'debug', + message: 'A message.', + time: expect.any(Number), + }); + expect(buildReport.logs[1]).toEqual({ + pluginName: 'testLogger', + type: 'info', + message: 'Info message #1.', + time: expect.any(Number), }); + expect(buildReport.logs[2]).toEqual({ + pluginName: 'testLogger', + type: 'error', + message: 'An error occurred.', + time: expect.any(Number), + }); + expect(buildReport.logs[3]).toEqual({ + pluginName: 'testLogger', + type: 'warn', + message: 'A warning message.', + time: expect.any(Number), + }); + expect(buildReport.logs[4]).toEqual({ + pluginName: 'testLogger', + type: 'info', + message: 'Info message #2.', + time: expect.any(Number), + }); + expect(buildReport.logs[5]).toEqual({ + pluginName: 'testLogger', + type: 'debug', + message: 'A debug message.', + time: expect.any(Number), + }); + + expect(buildReport.errors).toEqual(['An error occurred.']); + expect(buildReport.warnings).toEqual(['A warning message.']); }); }); }); diff --git a/yarn.lock b/yarn.lock index a0f2a785..ede23cd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1701,6 +1701,7 @@ __metadata: memfs: "npm:4.9.2" nock: "npm:14.0.0-beta.7" rollup: "npm:4.24.2" + strip-ansi: "npm:6.0.1" ts-jest: "npm:29.1.2" typescript: "npm:5.4.3" vite: "npm:5.4.10" @@ -10846,7 +10847,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" dependencies: From a6e5eaf748a7a8ce75fd4559d56e30e5dd31045b Mon Sep 17 00:00:00 2001 From: Yoann Moinet Date: Fri, 15 Nov 2024 11:41:59 +0100 Subject: [PATCH 4/6] Add logs to catch race condition in tests for injection --- packages/plugins/injection/src/index.ts | 5 +++++ packages/tests/src/tools/src/rollupConfig.test.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/plugins/injection/src/index.ts b/packages/plugins/injection/src/index.ts index dae1f27d..07e7d5b2 100644 --- a/packages/plugins/injection/src/index.ts +++ b/packages/plugins/injection/src/index.ts @@ -5,6 +5,7 @@ import { INJECTED_FILE } from '@dd/core/constants'; import { outputFile, rm } from '@dd/core/helpers'; import type { GlobalContext, PluginOptions, ToInjectItem } from '@dd/core/types'; +import fs from 'fs'; import path from 'path'; import { PLUGIN_NAME, PREPARATION_PLUGIN_NAME } from './constants'; @@ -77,6 +78,10 @@ export const getInjectionPlugins = ( // Actually create the file to avoid any resolution errors. // It needs to be within cwd. try { + // Verify that the file doesn't already exist. + if (fs.existsSync(absolutePathInjectFile)) { + log(`Temporary file "${INJECTED_FILE_PATH}" already exists.`, 'warn'); + } await outputFile(absolutePathInjectFile, getContentToInject()); } catch (e: any) { log(`Could not create the file: ${e.message}`, 'error'); diff --git a/packages/tests/src/tools/src/rollupConfig.test.ts b/packages/tests/src/tools/src/rollupConfig.test.ts index 0cad44c4..de307763 100644 --- a/packages/tests/src/tools/src/rollupConfig.test.ts +++ b/packages/tests/src/tools/src/rollupConfig.test.ts @@ -198,6 +198,7 @@ describe('Bundling', () => { if (NO_CLEANUP) { return; } + console.log('[rollupConfig | Bundling] Cleaning up seeded folders.\n', seededFolders); await Promise.all(seededFolders.map((folder) => rm(folder))); }); @@ -314,13 +315,12 @@ describe('Bundling', () => { name: 'custom-build-loader', setup(build) { build.onLoad({ filter: /.*\/main1\.js/ }, async ({ path: filepath }) => { - const options = { ...build.initialOptions }; const outfile = path.resolve(esbuildOutdir, 'app1.2.js'); await runEsbuild( SEED, {}, { - ...options, + ...build.initialOptions, entryPoints: [filepath], outfile, outdir: undefined, From 2f762c9965ee594ffefb8057b06be44d72a5aa0b Mon Sep 17 00:00:00 2001 From: Yoann Moinet Date: Tue, 19 Nov 2024 16:18:29 -0500 Subject: [PATCH 5/6] Remove top level log function from logger --- packages/core/src/types.ts | 1 - packages/factory/README.md | 5 +-- packages/factory/src/helpers.ts | 15 +++----- packages/plugins/build-report/src/esbuild.ts | 2 +- packages/plugins/build-report/src/rollup.ts | 2 +- packages/plugins/build-report/src/webpack.ts | 7 ++-- packages/plugins/injection/src/helpers.ts | 4 +-- packages/plugins/injection/src/index.ts | 6 ++-- packages/plugins/rum/src/sourcemaps/index.ts | 2 +- packages/plugins/rum/src/sourcemaps/sender.ts | 17 +++++---- packages/plugins/rum/src/validate.ts | 2 +- .../telemetry/src/common/output/files.ts | 14 ++++---- .../telemetry/src/common/output/text.ts | 2 +- .../plugins/telemetry/src/common/sender.ts | 10 +++--- .../telemetry/src/esbuild-plugin/index.ts | 2 +- packages/tests/src/_jest/helpers/mocks.ts | 26 +++++++------- packages/tests/src/factory/helpers.test.ts | 36 +++++-------------- .../src/plugins/rum/sourcemaps/sender.test.ts | 6 ++-- 18 files changed, 65 insertions(+), 94 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 8b1cf3e3..393a52f2 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -75,7 +75,6 @@ export type BundlerReport = { export type ToInjectItem = { type: 'file' | 'code'; value: string; fallback?: ToInjectItem }; export type Logger = { - (text: any, type?: LogLevel): void; error: (text: any) => void; warn: (text: any) => void; info: (text: any) => void; diff --git a/packages/factory/README.md b/packages/factory/README.md index 3af47770..f1c27a60 100644 --- a/packages/factory/README.md +++ b/packages/factory/README.md @@ -64,12 +64,9 @@ export const getMyPlugins = (context: GlobalContext) => { }; ``` -Then you can either use one of the helpers or programmatically use a specific level: +Then you can either use one of the level logger methods: ```typescript -const logLevel = 'warn'; -logger('This will be a warning', logLevel); - logger.warn('This is also a warning'); logger.error('This is an error'); logger.info('This is an info'); diff --git a/packages/factory/src/helpers.ts b/packages/factory/src/helpers.ts index 113b6586..88076b8d 100644 --- a/packages/factory/src/helpers.ts +++ b/packages/factory/src/helpers.ts @@ -62,17 +62,12 @@ export const getLoggerFactory = } }; - const logFn = (text: any, type: LogLevel = 'debug') => { - log(text, type); + return { + error: (text: any) => log(text, 'error'), + warn: (text: any) => log(text, 'warn'), + info: (text: any) => log(text, 'info'), + debug: (text: any) => log(text, 'debug'), }; - - // Add shortcuts for the other log levels. - logFn.error = (text: any) => log(text, 'error'); - logFn.warn = (text: any) => log(text, 'warn'); - logFn.info = (text: any) => log(text, 'info'); - logFn.debug = (text: any) => log(text, 'debug'); - - return logFn; }; export const getContext = ({ diff --git a/packages/plugins/build-report/src/esbuild.ts b/packages/plugins/build-report/src/esbuild.ts index 77019046..4bd9c539 100644 --- a/packages/plugins/build-report/src/esbuild.ts +++ b/packages/plugins/build-report/src/esbuild.ts @@ -72,7 +72,7 @@ export const getEsbuildPlugin = (context: GlobalContext, log: Logger): PluginOpt const warn = (warning: string) => { context.build.warnings.push(warning); - log(warning, 'warn'); + log.warn(warning); }; if (!result.metafile) { diff --git a/packages/plugins/build-report/src/rollup.ts b/packages/plugins/build-report/src/rollup.ts index 5ccf56c0..4e070d81 100644 --- a/packages/plugins/build-report/src/rollup.ts +++ b/packages/plugins/build-report/src/rollup.ts @@ -66,7 +66,7 @@ export const getRollupPlugin = (context: GlobalContext, log: Logger): PluginOpti const warn = (warning: string) => { context.build.warnings.push(warning); - log(warning, 'warn'); + log.warn(warning); }; // Complete the importsReport with missing dependents and dependencies. diff --git a/packages/plugins/build-report/src/webpack.ts b/packages/plugins/build-report/src/webpack.ts index bb4c7d2b..b908cac7 100644 --- a/packages/plugins/build-report/src/webpack.ts +++ b/packages/plugins/build-report/src/webpack.ts @@ -20,7 +20,6 @@ export const getWebpackPlugin = const inputs: Input[] = []; const outputs: Output[] = []; const entries: Entry[] = []; - const warnings: string[] = []; // Some indexes to help with the report generation. const reportInputsIndexed: Map = new Map(); @@ -43,8 +42,8 @@ export const getWebpackPlugin = }; const warn = (warning: string) => { - warnings.push(warning); - log(warning, 'warn'); + context.build.warnings.push(warning); + log.warn(warning); }; /** @@ -293,7 +292,7 @@ export const getWebpackPlugin = // Save everything in the context. context.build.errors.push(...result.errors.map((err) => err.message)); - context.build.warnings.push(...warnings, ...result.warnings.map((err) => err.message)); + context.build.warnings.push(...result.warnings.map((err) => err.message)); context.build.inputs = inputs; context.build.outputs = outputs; context.build.entries = entries; diff --git a/packages/plugins/injection/src/helpers.ts b/packages/plugins/injection/src/helpers.ts index 2e765b67..d73871d1 100644 --- a/packages/plugins/injection/src/helpers.ts +++ b/packages/plugins/injection/src/helpers.ts @@ -58,11 +58,11 @@ export const processItem = async (item: ToInjectItem, log: Logger): Promise { @@ -131,10 +131,10 @@ export const upload = async ( onRetry: (error: Error, attempt: number) => { const warningMessage = `Failed to upload ${yellow(metadata.sourcemap)} | ${yellow(metadata.file)}:\n ${error.message}\nRetrying ${attempt}/${NB_RETRIES}`; warnings.push(warningMessage); - log(warningMessage, 'warn'); + log.warn(warningMessage); }, }); - log(`Sent ${green(metadata.sourcemap)} | ${green(metadata.file)}`); + log.debug(`Sent ${green(metadata.sourcemap)} | ${green(metadata.file)}`); } catch (e: any) { errors.push({ metadata, error: e }); // Depending on the configuration we throw or not. @@ -178,12 +178,12 @@ export const sendSourcemaps = async ( const warnings = payloads.map((payload) => payload.warnings).flat(); if (warnings.length > 0) { - log(`Warnings while preparing payloads:\n - ${warnings.join('\n - ')}`, 'warn'); + log.warn(`Warnings while preparing payloads:\n - ${warnings.join('\n - ')}`); } if (errors.length > 0) { const errorMsg = `Failed to prepare payloads, aborting upload :\n - ${errors.join('\n - ')}`; - log(errorMsg, 'error'); + log.error(errorMsg); // Depending on the configuration we throw or not. if (options.bailOnError === true) { throw new Error(errorMsg); @@ -198,9 +198,8 @@ export const sendSourcemaps = async ( log, ); - log( + log.info( `Done uploading ${green(sourcemaps.length.toString())} sourcemaps in ${green(formatDuration(Date.now() - start))}.`, - 'info', ); if (uploadErrors.length > 0) { @@ -214,7 +213,7 @@ export const sendSourcemaps = async ( .join('\n - ')}`; const errorMsg = `Failed to upload some sourcemaps:\n${listOfErrors}`; - log(errorMsg, 'error'); + log.error(errorMsg); // Depending on the configuration we throw or not. // This should not be reached as we'd have thrown earlier. if (options.bailOnError === true) { @@ -223,6 +222,6 @@ export const sendSourcemaps = async ( } if (uploadWarnings.length > 0) { - log(`Warnings while uploading sourcemaps:\n - ${warnings.join('\n - ')}`, 'warn'); + log.warn(`Warnings while uploading sourcemaps:\n - ${warnings.join('\n - ')}`); } }; diff --git a/packages/plugins/rum/src/validate.ts b/packages/plugins/rum/src/validate.ts index add00334..89448ab9 100644 --- a/packages/plugins/rum/src/validate.ts +++ b/packages/plugins/rum/src/validate.ts @@ -28,7 +28,7 @@ export const validateOptions = ( // Throw if there are any errors. if (errors.length) { - log(`\n - ${errors.join('\n - ')}`, 'error'); + log.error(`\n - ${errors.join('\n - ')}`); throw new Error(`Invalid configuration for ${PLUGIN_NAME}.`); } diff --git a/packages/plugins/telemetry/src/common/output/files.ts b/packages/plugins/telemetry/src/common/output/files.ts index a1ba2f96..cd8ef983 100644 --- a/packages/plugins/telemetry/src/common/output/files.ts +++ b/packages/plugins/telemetry/src/common/output/files.ts @@ -72,14 +72,13 @@ export const outputFiles = async ( const proms = Object.entries(filesToWrite).map(async ([filename, file]): Promise => { const start = Date.now(); - log(`Start writing ${filename}.json.`); + log.debug(`Start writing ${filename}.json.`); try { await outputJson(path.join(outputPath, `${filename}.json`), file.content); - log(`Wrote ${filename}.json in ${formatDuration(Date.now() - start)}`); + log.debug(`Wrote ${filename}.json in ${formatDuration(Date.now() - start)}`); } catch (e: any) { - log( + log.error( `Failed to write ${filename}.json in ${formatDuration(Date.now() - start)}`, - 'error', ); errors[filename] = e; } @@ -87,18 +86,17 @@ export const outputFiles = async ( // We can't use Promise.allSettled because we want to support NodeJS 10+ await Promise.all(proms); - log(`Wrote files in ${formatDuration(Date.now() - startWriting)}.`); + log.debug(`Wrote files in ${formatDuration(Date.now() - startWriting)}.`); // If we had some errors. const fileErrored = Object.keys(errors); if (fileErrored.length) { - log( + log.error( `Couldn't write files.\n${fileErrored.map( (file) => ` - ${file}: ${errors[file].toString()}`, )}`, - 'error', ); } } catch (e) { - log(`Couldn't write files. ${e}`, 'error'); + log.error(`Couldn't write files. ${e}`); } }; diff --git a/packages/plugins/telemetry/src/common/output/text.ts b/packages/plugins/telemetry/src/common/output/text.ts index 87f1ac94..1be2299e 100644 --- a/packages/plugins/telemetry/src/common/output/text.ts +++ b/packages/plugins/telemetry/src/common/output/text.ts @@ -384,5 +384,5 @@ export const outputTexts = (globalContext: GlobalContext, log: Logger, report?: const outputString = renderValues(valuesToPrint); - log(outputString, 'info'); + log.info(outputString); }; diff --git a/packages/plugins/telemetry/src/common/sender.ts b/packages/plugins/telemetry/src/common/sender.ts index 251028b9..243ec9c7 100644 --- a/packages/plugins/telemetry/src/common/sender.ts +++ b/packages/plugins/telemetry/src/common/sender.ts @@ -13,11 +13,11 @@ export const sendMetrics = ( ) => { const startSending = Date.now(); if (!auth.apiKey) { - log(`Won't send metrics to Datadog: missing API Key.`, 'warn'); + log.warn(`Won't send metrics to Datadog: missing API Key.`); return; } if (!metrics || metrics.length === 0) { - log(`No metrics to send.`, 'warn'); + log.warn(`No metrics to send.`); return; } @@ -25,7 +25,7 @@ export const sendMetrics = ( .sort() .map((name) => `${name} - ${metrics.filter((m) => m.metric === name).length}`); - log(` + log.debug(` Sending ${metrics.length} metrics. Metrics: - ${metricsNames.join('\n - ')}`); @@ -36,9 +36,9 @@ Metrics: getData: () => ({ data: JSON.stringify({ series: metrics }) }), }) .then(() => { - log(`Sent metrics in ${formatDuration(Date.now() - startSending)}.`); + log.debug(`Sent metrics in ${formatDuration(Date.now() - startSending)}.`); }) .catch((e) => { - log(`Error sending metrics ${e}`, 'error'); + log.error(`Error sending metrics ${e}`); }); }; diff --git a/packages/plugins/telemetry/src/esbuild-plugin/index.ts b/packages/plugins/telemetry/src/esbuild-plugin/index.ts index 434788c8..b617b80b 100644 --- a/packages/plugins/telemetry/src/esbuild-plugin/index.ts +++ b/packages/plugins/telemetry/src/esbuild-plugin/index.ts @@ -22,7 +22,7 @@ export const getEsbuildPlugin = ( wrapPlugins(build, globalContext.cwd); build.onEnd(async (result: BuildResult) => { if (!result.metafile) { - logger("Missing metafile, can't proceed with modules data.", 'warn'); + logger.warn("Missing metafile, can't proceed with modules data."); return; } diff --git a/packages/tests/src/_jest/helpers/mocks.ts b/packages/tests/src/_jest/helpers/mocks.ts index 1aaf0a34..20b320bc 100644 --- a/packages/tests/src/_jest/helpers/mocks.ts +++ b/packages/tests/src/_jest/helpers/mocks.ts @@ -46,18 +46,20 @@ export const defaultPluginOptions: GetPluginsOptions = { logLevel: 'debug', }; -const logFn: any = jest.fn((text: any, type?: LogLevel) => {}); -logFn.error = (text: any) => { - logFn(text, 'error'); -}; -logFn.warn = (text: any) => { - logFn(text, 'warn'); -}; -logFn.info = (text: any) => { - logFn(text, 'info'); -}; -logFn.debug = (text: any) => { - logFn(text, 'debug'); +export const mockLogFn = jest.fn((text: any, level: LogLevel) => {}); +const logFn: Logger = { + error: (text: any) => { + mockLogFn(text, 'error'); + }, + warn: (text: any) => { + mockLogFn(text, 'warn'); + }, + info: (text: any) => { + mockLogFn(text, 'info'); + }, + debug: (text: any) => { + mockLogFn(text, 'debug'); + }, }; export const mockLogger: Logger = logFn; diff --git a/packages/tests/src/factory/helpers.test.ts b/packages/tests/src/factory/helpers.test.ts index 212be3b6..8dd5224c 100644 --- a/packages/tests/src/factory/helpers.test.ts +++ b/packages/tests/src/factory/helpers.test.ts @@ -94,7 +94,6 @@ describe('Factory Helpers', () => { const logger = context.getLogger('testLogger'); - expect(logger).toEqual(expect.any(Function)); expect(logger.error).toEqual(expect.any(Function)); expect(logger.warn).toEqual(expect.any(Function)); expect(logger.info).toEqual(expect.any(Function)); @@ -112,18 +111,15 @@ describe('Factory Helpers', () => { }; const useLogger = (logger: Logger) => { - logger('A message.'); - logger('Info message #1.', 'info'); logger.error('An error occurred.'); logger.warn('A warning message.'); - logger.info('Info message #2.'); + logger.info('An info message.'); logger.debug('A debug message.'); }; test('Should return a logger factory.', () => { const [logger] = setupLogger(); - expect(logger).toEqual(expect.any(Function)); expect(logger.error).toEqual(expect.any(Function)); expect(logger.warn).toEqual(expect.any(Function)); expect(logger.info).toEqual(expect.any(Function)); @@ -138,11 +134,9 @@ describe('Factory Helpers', () => { const getOutput = (mock: jest.Mock, index: number) => stripAnsi(mock.mock.calls[index][0]); - expect(logMock).toHaveBeenCalledTimes(4); - expect(getOutput(logMock, 0)).toBe('[debug|testLogger] A message.'); - expect(getOutput(logMock, 1)).toBe('[info|testLogger] Info message #1.'); - expect(getOutput(logMock, 2)).toBe('[info|testLogger] Info message #2.'); - expect(getOutput(logMock, 3)).toBe('[debug|testLogger] A debug message.'); + expect(logMock).toHaveBeenCalledTimes(2); + expect(getOutput(logMock, 0)).toBe('[info|testLogger] An info message.'); + expect(getOutput(logMock, 1)).toBe('[debug|testLogger] A debug message.'); expect(errorMock).toHaveBeenCalledTimes(1); expect(getOutput(errorMock, 0)).toBe('[error|testLogger] An error occurred.'); @@ -155,38 +149,26 @@ describe('Factory Helpers', () => { const [logger, buildReport] = setupLogger(); useLogger(logger); - expect(buildReport.logs).toHaveLength(6); + expect(buildReport.logs).toHaveLength(4); expect(buildReport.logs[0]).toEqual({ - pluginName: 'testLogger', - type: 'debug', - message: 'A message.', - time: expect.any(Number), - }); - expect(buildReport.logs[1]).toEqual({ - pluginName: 'testLogger', - type: 'info', - message: 'Info message #1.', - time: expect.any(Number), - }); - expect(buildReport.logs[2]).toEqual({ pluginName: 'testLogger', type: 'error', message: 'An error occurred.', time: expect.any(Number), }); - expect(buildReport.logs[3]).toEqual({ + expect(buildReport.logs[1]).toEqual({ pluginName: 'testLogger', type: 'warn', message: 'A warning message.', time: expect.any(Number), }); - expect(buildReport.logs[4]).toEqual({ + expect(buildReport.logs[2]).toEqual({ pluginName: 'testLogger', type: 'info', - message: 'Info message #2.', + message: 'An info message.', time: expect.any(Number), }); - expect(buildReport.logs[5]).toEqual({ + expect(buildReport.logs[3]).toEqual({ pluginName: 'testLogger', type: 'debug', message: 'A debug message.', diff --git a/packages/tests/src/plugins/rum/sourcemaps/sender.test.ts b/packages/tests/src/plugins/rum/sourcemaps/sender.test.ts index c3f8e821..20660dfb 100644 --- a/packages/tests/src/plugins/rum/sourcemaps/sender.test.ts +++ b/packages/tests/src/plugins/rum/sourcemaps/sender.test.ts @@ -4,7 +4,7 @@ import { doRequest } from '@dd/core/helpers'; import { getData, sendSourcemaps, upload } from '@dd/rum-plugin/sourcemaps/sender'; -import { getContextMock, mockLogger } from '@dd/tests/_jest/helpers/mocks'; +import { getContextMock, mockLogFn, mockLogger } from '@dd/tests/_jest/helpers/mocks'; import { vol } from 'memfs'; import { type Stream } from 'stream'; import { unzipSync } from 'zlib'; @@ -112,8 +112,8 @@ describe('RUM Plugin Sourcemaps', () => { mockLogger, ); - expect(mockLogger).toHaveBeenCalledTimes(1); - expect(mockLogger).toHaveBeenCalledWith( + expect(mockLogFn).toHaveBeenCalledTimes(1); + expect(mockLogFn).toHaveBeenCalledWith( expect.stringMatching('Failed to prepare payloads, aborting upload'), 'error', ); From 0956b29ed9d9904f3327101d3f3b4a92c9a5960b Mon Sep 17 00:00:00 2001 From: Yoann Moinet Date: Tue, 19 Nov 2024 16:23:26 -0500 Subject: [PATCH 6/6] Do not destructure arrays that could be huge --- packages/plugins/build-report/src/esbuild.ts | 8 ++++++-- packages/plugins/build-report/src/webpack.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/plugins/build-report/src/esbuild.ts b/packages/plugins/build-report/src/esbuild.ts index 4bd9c539..709b5466 100644 --- a/packages/plugins/build-report/src/esbuild.ts +++ b/packages/plugins/build-report/src/esbuild.ts @@ -67,8 +67,12 @@ export const getEsbuildPlugin = (context: GlobalContext, log: Logger): PluginOpt const entryNames = getEntryNames(entrypoints, context); build.onEnd((result) => { - context.build.errors.push(...result.errors.map((err) => err.text)); - context.build.warnings.push(...result.warnings.map((err) => err.text)); + for (const error of result.errors) { + context.build.errors.push(error.text); + } + for (const warning of result.warnings) { + context.build.warnings.push(warning.text); + } const warn = (warning: string) => { context.build.warnings.push(warning); diff --git a/packages/plugins/build-report/src/webpack.ts b/packages/plugins/build-report/src/webpack.ts index b908cac7..15e8384f 100644 --- a/packages/plugins/build-report/src/webpack.ts +++ b/packages/plugins/build-report/src/webpack.ts @@ -291,8 +291,12 @@ export const getWebpackPlugin = } // Save everything in the context. - context.build.errors.push(...result.errors.map((err) => err.message)); - context.build.warnings.push(...result.warnings.map((err) => err.message)); + for (const error of result.errors) { + context.build.errors.push(error.message); + } + for (const warning of result.warnings) { + context.build.warnings.push(warning.message); + } context.build.inputs = inputs; context.build.outputs = outputs; context.build.entries = entries;