Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/analyze/core-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,17 @@ export async function runCoreJsAnalysis(
messages.push({
severity: 'warning',
score: 0,
message: `Broad core-js import "${specifier}" in ${filePath} loads all polyfills at once. Import only the specific modules you need.`
file: filePath,
message: `Broad core-js import "${specifier}" loads all polyfills at once. Import only the specific modules you need.`
});
} else if (specifier.startsWith('core-js/modules/')) {
const moduleName = specifier.slice('core-js/modules/'.length);
if (unnecessarySet.has(moduleName)) {
messages.push({
severity: 'suggestion',
score: 0,
message: `core-js polyfill "${moduleName}" imported in ${filePath} is unnecessary - your target engines already support this natively.`
file: filePath,
message: `core-js polyfill "${moduleName}" is unnecessary - your target engines already support this natively.`
});
}
}
Expand Down
1 change: 1 addition & 0 deletions src/analyze/publint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export async function runPublint(
result.messages.push({
severity: problem.type,
score: 0,
file: 'package.json',
message: formatMessage(problem, publintResult.pkg) ?? ''
});
}
Expand Down
1 change: 1 addition & 0 deletions src/analyze/replacements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ export async function runReplacements(
result.messages.push({
severity: 'warning',
score: 0,
file: 'package.json',
message,
...(fixableBy && {fixableBy})
});
Expand Down
3 changes: 2 additions & 1 deletion src/analyze/web-features-codemods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export async function runWebFeaturesCodemodsAnalysis(
messages.push({
severity: 'suggestion',
score: 0,
message: `File "${filePath}" can use newer web features: ${matches.join(', ')}.`
file: filePath,
message: `Can use newer web features: ${matches.join(', ')}.`
});
}
}
Expand Down
83 changes: 50 additions & 33 deletions src/commands/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,31 @@ export async function run(ctx: CommandContext<typeof meta>) {
const width = process.stdout?.columns ?? 80;
const maxContentWidth = Math.max(20, width - 4);

const formatBulletMessage = (text: string, bullet: string) =>
wrapAnsi(text, maxContentWidth)
const labels = {
error: {text: 'error', color: 'red'},
warning: {text: 'warning', color: 'yellow'},
suggestion: {text: 'suggestion', color: 'blue'}
} as const;

const labelWidth = Math.max(
...Object.values(labels).map((l) => l.text.length)
);
// Two leading spaces, then the severity column, then two spaces before text.
const gutter = 2 + labelWidth + 2;

const formatBulletMessage = (
text: string,
label: (typeof labels)[keyof typeof labels]
) => {
const severity = styleText(label.color, label.text.padEnd(labelWidth));
const indent = ' '.repeat(gutter);
return wrapAnsi(text, maxContentWidth)
.split('\n')
.map((line, i) => (i === 0 ? ` ${bullet} ${line}` : ` ${line}`))
.map((line, i) =>
i === 0 ? ` ${severity} ${line}` : `${indent}${line}`
)
.join('\n');
};

const errorMessages = visibleMessages.filter((m) => m.severity === 'error');
const warningMessages = visibleMessages.filter(
Expand All @@ -211,38 +231,35 @@ export async function run(ctx: CommandContext<typeof meta>) {
(m) => m.severity === 'suggestion'
);

// Display errors
if (errorMessages.length > 0) {
prompts.log.message(styleText('red', 'Errors:'), {spacing: 0});
for (const msg of errorMessages) {
const bullet = styleText('red', '•');
prompts.log.message(formatBulletMessage(msg.message, bullet), {
spacing: 0
});
}
prompts.log.message('', {spacing: 0});
}

// Display warnings
if (warningMessages.length > 0) {
prompts.log.message(styleText('yellow', 'Warnings:'), {spacing: 0});
for (const msg of warningMessages) {
const bullet = styleText('yellow', '•');
prompts.log.message(formatBulletMessage(msg.message, bullet), {
spacing: 0
});
const groups = new Map<string | undefined, Message[]>();
for (const msg of visibleMessages) {
const existing = groups.get(msg.file);
if (existing) {
existing.push(msg);
} else {
groups.set(msg.file, [msg]);
}
prompts.log.message('', {spacing: 0});
}

// Display suggestions
if (suggestionMessages.length > 0) {
prompts.log.message(styleText('blue', 'Suggestions:'), {spacing: 0});
for (const msg of suggestionMessages) {
const bullet = styleText('blue', '•');
prompts.log.message(formatBulletMessage(msg.message, bullet), {
spacing: 0
});
const sortedGroups = [...groups.entries()].sort(([a], [b]) => {
if (a === undefined) return 1;
if (b === undefined) return -1;
return a.localeCompare(b);
});

const severityOrder = {error: 0, warning: 1, suggestion: 2} as const;

for (const [file, fileMessages] of sortedGroups) {
const group = fileMessages
.slice()
.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
prompts.log.message(styleText('underline', file ?? 'general'), {
spacing: 0
});
for (const msg of group) {
prompts.log.message(
formatBulletMessage(msg.message, labels[msg.severity]),
{spacing: 0}
);
}
prompts.log.message('', {spacing: 0});
}
Expand Down
17 changes: 17 additions & 0 deletions src/test/__snapshots__/custom-manifests.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,31 @@ exports[`Custom Manifests > should handle invalid manifest files gracefully 1`]
exports[`Custom Manifests > should load and use custom manifest files 1`] = `
[
{
"file": "package.json",
"message": "Module "@e18e/fake-0" can be replaced with inline native syntax. Use picocolors, kleur, or native console styling. See more at https://github.com/e18e/module-replacements/blob/main/docs/modules/fake-0.md.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-1" can be replaced with inline native syntax. Use native JavaScript methods or specific lodash functions.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-2" can be replaced with native functionality. Required Node >= 12.0.0. You can read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-3" can be replaced with a more performant alternative. See more at https://github.com/e18e/module-replacements/blob/main/docs/modules/request-alternatives.md.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-4" can be removed, and native functionality used instead",
"score": 0,
"severity": "warning",
Expand All @@ -35,36 +40,43 @@ exports[`Custom Manifests > should load and use custom manifest files 1`] = `
exports[`Custom Manifests > should load multiple manifest files 1`] = `
[
{
"file": "package.json",
"message": "Module "@e18e/fake-0" can be replaced with inline native syntax. Use picocolors, kleur, or native console styling. See more at https://github.com/e18e/module-replacements/blob/main/docs/modules/fake-0.md.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-1" can be replaced with inline native syntax. Use native JavaScript methods or specific lodash functions.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-2" can be replaced with native functionality. Required Node >= 12.0.0. You can read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-3" can be replaced with a more performant alternative. See more at https://github.com/e18e/module-replacements/blob/main/docs/modules/request-alternatives.md.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-4" can be removed, and native functionality used instead",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-5" can be replaced with inline native syntax. Use Fastify, Koa, or native Node.js http module.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-6" can be removed, and native functionality used instead",
"score": 0,
"severity": "warning",
Expand All @@ -76,26 +88,31 @@ exports[`Custom Manifests > should prioritize custom replacements over built-in
{
"withCustom": [
{
"file": "package.json",
"message": "Module "@e18e/fake-0" can be replaced with inline native syntax. Use picocolors, kleur, or native console styling. See more at https://github.com/e18e/module-replacements/blob/main/docs/modules/fake-0.md.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-1" can be replaced with inline native syntax. Use native JavaScript methods or specific lodash functions.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-2" can be replaced with native functionality. Required Node >= 12.0.0. You can read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-3" can be replaced with a more performant alternative. See more at https://github.com/e18e/module-replacements/blob/main/docs/modules/request-alternatives.md.",
"score": 0,
"severity": "warning",
},
{
"file": "package.json",
"message": "Module "@e18e/fake-4" can be removed, and native functionality used instead",
"score": 0,
"severity": "warning",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
exports[`runWebFeaturesCodemodsAnalysis > handles a file with multiple matches 1`] = `
[
{
"message": "File "index.js" can use newer web features: arrayAt, exponentiation.",
"file": "index.js",
"message": "Can use newer web features: arrayAt, exponentiation.",
"score": 0,
"severity": "suggestion",
},
Expand All @@ -15,7 +16,8 @@ exports[`runWebFeaturesCodemodsAnalysis > handles a file with no matches 1`] = `
exports[`runWebFeaturesCodemodsAnalysis > handles a file with one match 1`] = `
[
{
"message": "File "index.js" can use newer web features: arrayAt.",
"file": "index.js",
"message": "Can use newer web features: arrayAt.",
"score": 0,
"severity": "suggestion",
},
Expand All @@ -25,7 +27,8 @@ exports[`runWebFeaturesCodemodsAnalysis > handles a file with one match 1`] = `
exports[`runWebFeaturesCodemodsAnalysis > handles multiple occurrences of the same match in one file 1`] = `
[
{
"message": "File "index.js" can use newer web features: arrayAt.",
"file": "index.js",
"message": "Can use newer web features: arrayAt.",
"score": 0,
"severity": "suggestion",
},
Expand All @@ -37,7 +40,8 @@ exports[`runWebFeaturesCodemodsAnalysis > ignores a file path outside of the roo
exports[`runWebFeaturesCodemodsAnalysis > respects the src option 1`] = `
[
{
"message": "File "src/index.js" can use newer web features: arrayAt.",
"file": "src/index.js",
"message": "Can use newer web features: arrayAt.",
"score": 0,
"severity": "suggestion",
},
Expand Down
2 changes: 1 addition & 1 deletion src/test/analyze/core-js.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ describe('runCoreJsAnalysis', () => {
const result = await runCoreJsAnalysis(context);

expect(result.messages).toHaveLength(1);
expect(result.messages[0]?.message).toContain('src');
expect(result.messages[0]?.file).toContain('src');
});

it('scans multiple src globs when options.src has more than one entry', async () => {
Expand Down
8 changes: 4 additions & 4 deletions src/test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ describe('analyze exit codes', () => {
);
expect(code).toBe(0);
const output = stdout + stderr;
expect(output).not.toContain('Warnings:');
expect(output).not.toContain('warning');
expect(output).toMatch(/below --report-level error/);
});

Expand All @@ -174,8 +174,8 @@ describe('analyze exit codes', () => {
);
expect(code).toBe(1);
const output = stdout + stderr;
expect(output).toContain('Warnings:');
expect(output).not.toContain('Suggestions:');
expect(output).toContain('warning');
expect(output).not.toContain('suggestion');
});

it('--quiet hides non-errors like ESLint (default log-level still fails on warnings)', async () => {
Expand All @@ -185,7 +185,7 @@ describe('analyze exit codes', () => {
);
expect(code).toBe(1);
const output = stdout + stderr;
expect(output).not.toContain('Warnings:');
expect(output).not.toContain('warning');
expect(output).toContain('hidden by --quiet');
});
});
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export interface Message {
severity: 'error' | 'warning' | 'suggestion';
score: number;
message: string;
/** File the message relates to, relative to the project root. */
file?: string;
/** Command that can fix this message (e.g. 'migrate'). */
fixableBy?: string;
}
Expand Down
Loading