Skip to content

Commit

Permalink
Add Logging API (#5026)
Browse files Browse the repository at this point in the history
* Add onLog to input options in JavaScript API

* Clean up tests

* Test string logging

* Test type fixes

* Add basic this.log support without position

* Add positions to logs in "transform" hook

* Separate level from logs

* Display logs in CLI

* Unify log handling to use onLog

* Introduce constants for log levels

* Replace RollupWarning with RollupLog type

* Replace warn with log in AST context

* Allow to turn logs into errors in default handler

* Add onLog plugin hook

* Clean up

* Add log levels

* Ensure "silent" also silences logs

* Do not augment onLog errors, only allow pos in transform via types

* Fix test

* Add docs

* Fix anchors

* Rename errors to logs

* Clarify that onLog option can be used for logging

* Start work on eixsting logs

* Use logging API for experimental features

* Improve coverage

* Allow logs to be functions

* Finalise docs

* Refine docs
  • Loading branch information
lukastaegert committed Jun 11, 2023
1 parent de2d6ff commit 23c111c
Show file tree
Hide file tree
Showing 189 changed files with 3,234 additions and 1,716 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ module.exports = {
'unicorn/prefer-math-trunc': 'off',
'unicorn/prefer-number-properties': 'off',
'unicorn/prefer-string-replace-all': 'off',
'unicorn/prefer-top-level-await': 'off'
'unicorn/prefer-top-level-await': 'off',
'unicorn/prevent-abbreviations': ['error', { replacements: { dir: false } }]
}
};
4 changes: 2 additions & 2 deletions browser/src/error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { error, errorNoFileSystemInBrowser } from '../../src/utils/error';
import { error, logNoFileSystemInBrowser } from '../../src/utils/logs';

export const throwNoFileSystem = (method: string) => (): never =>
error(errorNoFileSystemInBrowser(method));
error(logNoFileSystemInBrowser(method));
1 change: 1 addition & 0 deletions cli/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Basic options:
--inlineDynamicImports Create single bundle when using dynamic imports
--no-interop Do not include interop block
--intro <text> Code to insert at top of bundle (inside wrapper)
--logLevel <level> Which kind of logs to display
--no-makeAbsoluteExternalsRelative Prevent normalization of external imports
--maxParallelFileOps <value> How many files to read in parallel
--minifyInternalExports Force or disable minification of internal exports
Expand Down
93 changes: 61 additions & 32 deletions cli/run/batchWarnings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { RollupWarning } from '../../src/rollup/types';
import { blue, cyan } from 'colorette';
import type { RollupLog } from '../../src/rollup/types';
import { bold, gray, yellow } from '../../src/utils/colors';
import { getNewArray, getOrCreate } from '../../src/utils/getOrCreate';
import { LOGLEVEL_DEBUG, LOGLEVEL_WARN } from '../../src/utils/logging';
import { printQuotedStringList } from '../../src/utils/printStringList';
import relativeId from '../../src/utils/relativeId';
import { getRollupUrl } from '../../src/utils/url';
Expand All @@ -16,44 +18,35 @@ import {
import { stderr } from '../logging';
import type { BatchWarnings } from './loadConfigFileType';

export default function batchWarnings(): BatchWarnings {
export default function batchWarnings(silent: boolean): BatchWarnings {
let count = 0;
const deferredWarnings = new Map<keyof typeof deferredHandlers, RollupWarning[]>();
const deferredWarnings = new Map<keyof typeof deferredHandlers, RollupLog[]>();
let warningOccurred = false;

return {
add(warning: RollupWarning) {
count += 1;
warningOccurred = true;

if (warning.code! in deferredHandlers) {
getOrCreate(deferredWarnings, warning.code!, getNewArray).push(warning);
} else if (warning.code! in immediateHandlers) {
immediateHandlers[warning.code!](warning);
} else {
title(warning.message);

if (warning.url) info(warning.url);

const id = warning.loc?.file || warning.id;
if (id) {
const loc = warning.loc
? `${relativeId(id)} (${warning.loc.line}:${warning.loc.column})`
: relativeId(id);

stderr(bold(relativeId(loc)));
}
const add = (warning: RollupLog) => {
count += 1;
warningOccurred = true;

if (silent) return;
if (warning.code! in deferredHandlers) {
getOrCreate(deferredWarnings, warning.code!, getNewArray).push(warning);
} else if (warning.code! in immediateHandlers) {
immediateHandlers[warning.code!](warning);
} else {
title(warning.message);
defaultBody(warning);
}
};

if (warning.frame) info(warning.frame);
}
},
return {
add,

get count() {
return count;
},

flush() {
if (count === 0) return;
if (count === 0 || silent) return;

const codes = [...deferredWarnings.keys()].sort(
(a, b) => deferredWarnings.get(b)!.length - deferredWarnings.get(a)!.length
Expand All @@ -67,14 +60,35 @@ export default function batchWarnings(): BatchWarnings {
count = 0;
},

log(level, log) {
switch (level) {
case LOGLEVEL_WARN: {
return add(log);
}
case LOGLEVEL_DEBUG: {
if (!silent) {
stderr(bold(blue(log.message)));
defaultBody(log);
}
return;
}
default: {
if (!silent) {
stderr(bold(cyan(log.message)));
defaultBody(log);
}
}
}
},

get warningOccurred() {
return warningOccurred;
}
};
}

const immediateHandlers: {
[code: string]: (warning: RollupWarning) => void;
[code: string]: (warning: RollupLog) => void;
} = {
MISSING_NODE_BUILTINS(warning) {
title(`Missing shims for Node.js built-ins`);
Expand All @@ -93,7 +107,7 @@ const immediateHandlers: {
};

const deferredHandlers: {
[code: string]: (warnings: RollupWarning[]) => void;
[code: string]: (warnings: RollupLog[]) => void;
} = {
CIRCULAR_DEPENDENCY(warnings) {
title(`Circular dependenc${warnings.length > 1 ? 'ies' : 'y'}`);
Expand Down Expand Up @@ -249,6 +263,21 @@ const deferredHandlers: {
}
};

function defaultBody(log: RollupLog): void {
if (log.url) {
info(getRollupUrl(log.url));
}

const id = log.loc?.file || log.id;
if (id) {
const loc = log.loc ? `${relativeId(id)} (${log.loc.line}:${log.loc.column})` : relativeId(id);

stderr(bold(relativeId(loc)));
}

if (log.frame) info(log.frame);
}

function title(string_: string): void {
stderr(bold(yellow(`(!) ${string_}`)));
}
Expand Down Expand Up @@ -281,7 +310,7 @@ function nest<T extends Record<string, any>>(array: readonly T[], property: stri
return nested;
}

function showTruncatedWarnings(warnings: readonly RollupWarning[]): void {
function showTruncatedWarnings(warnings: readonly RollupLog[]): void {
const nestedByModule = nest(warnings, 'id');

const displayedByModule = nestedByModule.length > 5 ? nestedByModule.slice(0, 3) : nestedByModule;
Expand Down
4 changes: 2 additions & 2 deletions cli/run/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ms from 'pretty-ms';
import { rollup } from '../../src/node-entry';
import type { MergedRollupOptions } from '../../src/rollup/types';
import { bold, cyan, green } from '../../src/utils/colors';
import { errorOnlyInlineSourcemapsForStdout } from '../../src/utils/error';
import { logOnlyInlineSourcemapsForStdout } from '../../src/utils/logs';
import relativeId from '../../src/utils/relativeId';
import { handleError, stderr } from '../logging';
import type { BatchWarnings } from './loadConfigFileType';
Expand Down Expand Up @@ -34,7 +34,7 @@ export default async function build(
if (useStdout) {
const output = outputOptions[0];
if (output.sourcemap && output.sourcemap !== 'inline') {
handleError(errorOnlyInlineSourcemapsForStdout());
handleError(logOnlyInlineSourcemapsForStdout());
}
const { output: outputs } = await bundle.generate(output);
for (const file of outputs) {
Expand Down
4 changes: 2 additions & 2 deletions cli/run/getConfigPath.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { readdir } from 'node:fs/promises';
import { resolve } from 'node:path';
import { cwd } from 'node:process';
import { errorMissingExternalConfig } from '../../src/utils/error';
import { logMissingExternalConfig } from '../../src/utils/logs';
import { handleError } from '../logging';

const DEFAULT_CONFIG_BASE = 'rollup.config';
Expand All @@ -21,7 +21,7 @@ export async function getConfigPath(commandConfig: string | true): Promise<strin
return require.resolve(packageName, { paths: [cwd()] });
} catch (error: any) {
if (error.code === 'MODULE_NOT_FOUND') {
handleError(errorMissingExternalConfig(commandConfig));
handleError(logMissingExternalConfig(commandConfig));
}
throw error;
}
Expand Down
10 changes: 5 additions & 5 deletions cli/run/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { env } from 'node:process';
import type { MergedRollupOptions } from '../../src/rollup/types';
import { errorDuplicateImportOptions, errorFailAfterWarnings } from '../../src/utils/error';
import { logDuplicateImportOptions, logFailAfterWarnings } from '../../src/utils/logs';
import { isWatchEnabled } from '../../src/utils/options/mergeOptions';
import { getAliasName } from '../../src/utils/relativeId';
import { loadFsEvents } from '../../src/watch/fsevents-importer';
Expand All @@ -15,7 +15,7 @@ export default async function runRollup(command: Record<string, any>): Promise<v
let inputSource;
if (command._.length > 0) {
if (command.input) {
handleError(errorDuplicateImportOptions());
handleError(logDuplicateImportOptions());
}
inputSource = command._;
} else if (typeof command.input === 'string') {
Expand Down Expand Up @@ -65,7 +65,7 @@ export default async function runRollup(command: Record<string, any>): Promise<v
}
if (command.failAfterWarnings && warnings.warningOccurred) {
warnings.flush();
handleError(errorFailAfterWarnings());
handleError(logFailAfterWarnings());
}
} catch (error: any) {
warnings.flush();
Expand All @@ -82,8 +82,8 @@ async function getConfigs(
): Promise<{ options: MergedRollupOptions[]; warnings: BatchWarnings }> {
if (command.config) {
const configFile = await getConfigPath(command.config);
const { options, warnings } = await loadConfigFile(configFile, command);
const { options, warnings } = await loadConfigFile(configFile, command, false);
return { options, warnings };
}
return await loadConfigFromCommand(command);
return await loadConfigFromCommand(command, false);
}
48 changes: 28 additions & 20 deletions cli/run/loadConfigFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import type { MergedRollupOptions } from '../../src/rollup/types';
import { bold } from '../../src/utils/colors';
import {
error,
errorCannotBundleConfigAsEsm,
errorCannotLoadConfigAsCjs,
errorCannotLoadConfigAsEsm,
errorMissingConfig
} from '../../src/utils/error';
logCannotBundleConfigAsEsm,
logCannotLoadConfigAsCjs,
logCannotLoadConfigAsEsm,
logMissingConfig
} from '../../src/utils/logs';
import { mergeOptions } from '../../src/utils/options/mergeOptions';
import type { GenericConfigObject } from '../../src/utils/options/options';
import relativeId from '../../src/utils/relativeId';
Expand All @@ -20,16 +20,20 @@ import batchWarnings from './batchWarnings';
import { addCommandPluginsToInputOptions, addPluginsFromCommandOption } from './commandPlugins';
import type { LoadConfigFile } from './loadConfigFileType';

export const loadConfigFile: LoadConfigFile = async (fileName, commandOptions = {}) => {
export const loadConfigFile: LoadConfigFile = async (
fileName,
commandOptions = {},
watchMode = false
) => {
const configs = await getConfigList(
getDefaultFromCjs(await getConfigFileExport(fileName, commandOptions)),
getDefaultFromCjs(await getConfigFileExport(fileName, commandOptions, watchMode)),
commandOptions
);
const warnings = batchWarnings();
const warnings = batchWarnings(commandOptions.silent);
try {
const normalizedConfigs: MergedRollupOptions[] = [];
for (const config of configs) {
const options = await mergeOptions(config, commandOptions, warnings.add);
const options = await mergeOptions(config, watchMode, commandOptions, warnings.log);
await addCommandPluginsToInputOptions(options, commandOptions);
normalizedConfigs.push(options);
}
Expand All @@ -40,13 +44,17 @@ export const loadConfigFile: LoadConfigFile = async (fileName, commandOptions =
}
};

async function getConfigFileExport(fileName: string, commandOptions: Record<string, unknown>) {
async function getConfigFileExport(
fileName: string,
commandOptions: Record<string, unknown>,
watchMode: boolean
) {
if (commandOptions.configPlugin || commandOptions.bundleConfigAsCjs) {
try {
return await loadTranspiledConfigFile(fileName, commandOptions);
} catch (error_: any) {
if (error_.message.includes('not defined in ES module scope')) {
return error(errorCannotBundleConfigAsEsm(error_));
return error(logCannotBundleConfigAsEsm(error_));
}
throw error_;
}
Expand All @@ -60,17 +68,17 @@ async function getConfigFileExport(fileName: string, commandOptions: Record<stri
process.on('warning', handleWarning);
try {
const fileUrl = pathToFileURL(fileName);
if (process.env.ROLLUP_WATCH) {
if (watchMode) {
// We are adding the current date to allow reloads in watch mode
fileUrl.search = `?${Date.now()}`;
}
return (await import(fileUrl.href)).default;
} catch (error_: any) {
if (cannotLoadEsm) {
return error(errorCannotLoadConfigAsCjs(error_));
return error(logCannotLoadConfigAsCjs(error_));
}
if (error_.message.includes('not defined in ES module scope')) {
return error(errorCannotLoadConfigAsEsm(error_));
return error(logCannotLoadConfigAsEsm(error_));
}
throw error_;
} finally {
Expand All @@ -86,7 +94,7 @@ async function loadTranspiledConfigFile(
fileName: string,
{ bundleConfigAsCjs, configPlugin, silent }: Record<string, unknown>
): Promise<unknown> {
const warnings = batchWarnings();
const warnings = batchWarnings(!!silent);
const inputOptions = {
external: (id: string) =>
(id[0] !== '.' && !isAbsolute(id)) || id.slice(-5, id.length) === '.json',
Expand All @@ -97,10 +105,6 @@ async function loadTranspiledConfigFile(
};
await addPluginsFromCommandOption(configPlugin, inputOptions);
const bundle = await rollup.rollup(inputOptions);
if (!silent && warnings.count > 0) {
stderr(bold(`loaded ${relativeId(fileName)} with warnings`));
warnings.flush();
}
const {
output: [{ code }]
} = await bundle.generate({
Expand All @@ -120,6 +124,10 @@ async function loadTranspiledConfigFile(
}
]
});
if (!silent && warnings.count > 0) {
stderr(bold(`loaded ${relativeId(fileName)} with warnings`));
warnings.flush();
}
return loadConfigFromWrittenFile(
join(dirname(fileName), `rollup.config-${Date.now()}.${bundleConfigAsCjs ? 'cjs' : 'mjs'}`),
code
Expand All @@ -144,7 +152,7 @@ async function getConfigList(configFileExport: any, commandOptions: any): Promis
? configFileExport(commandOptions)
: configFileExport);
if (Object.keys(config).length === 0) {
return error(errorMissingConfig());
return error(logMissingConfig());
}
return Array.isArray(config) ? config : [config];
}
8 changes: 5 additions & 3 deletions cli/run/loadConfigFileType.d.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import type { MergedRollupOptions, RollupWarning } from '../../src/rollup/types';
import type { LogHandler, MergedRollupOptions, RollupLog } from '../../src/rollup/types';

export interface BatchWarnings {
add: (warning: RollupWarning) => void;
add: (warning: RollupLog) => void;
readonly count: number;
flush: () => void;
log: LogHandler;
readonly warningOccurred: boolean;
}

export type LoadConfigFile = typeof loadConfigFile;

export function loadConfigFile(
fileName: string,
commandOptions: any
commandOptions: any,
watchMode?: boolean
): Promise<{
options: MergedRollupOptions[];
warnings: BatchWarnings;
Expand Down
Loading

0 comments on commit 23c111c

Please sign in to comment.