Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Hard fail on invalid bundle names #136

Merged
merged 10 commits into from
May 27, 2024
10 changes: 10 additions & 0 deletions .changeset/two-spies-talk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@codecov/bundler-plugin-core": patch
"@codecov/sveltekit-plugin": patch
"@codecov/webpack-plugin": patch
"@codecov/rollup-plugin": patch
"@codecov/nuxt-plugin": patch
"@codecov/vite-plugin": patch
---

When a user submits a invalid bundle name, we will hard fail and exit the bundle process now.
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,50 @@ describe("Generating nuxt stats", () => {
{ timeout: 25_000 },
);
});

describe("invalid bundle name is passed", () => {
beforeEach(async () => {
const config = new GenerateConfig({
// nuxt uses vite under the hood
plugin: "nuxt",
configFileName: "nuxt",
format: "esm",
detectFormat: "esm",
version: `v3`,
detectVersion: "v3",
file_format: "ts",
enableSourceMaps: false,
overrideOutputPath: `${nuxtApp}/nuxt.config.ts`,
});

await config.createConfig();
config.removeBundleName(`test-nuxt-v${version}`);
await config.writeConfig();
});

afterEach(async () => {
await $`rm -rf ${nuxtApp}/nuxt.config.ts`;
await $`rm -rf ${nuxtApp}/distV${version}`;
});

it(
"warns users and exits process with a code 1",
async () => {
const id = `nuxt-v${version}-${Date.now()}`;
const API_URL = `http://localhost:8000/test-url/${id}/200/false`;

// prepare and build the app
const { exitCode } =
await $`cd test-apps/nuxt && API_URL=${API_URL} pnpm run build`.nothrow();

expect(exitCode).toBe(1);
// for some reason this isn't being outputted in the test env
// expect(stdout.toString()).toContain(
// "[codecov] bundleName: `` does not match format: `/^[wd_:/@.{}[]$-]+$/`.",
// );
},
{ timeout: 25_000 },
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,45 @@ describe("Generating rollup stats", () => {
});
});
});

describe("passing invalid bundle name", () => {
beforeEach(async () => {
const config = new GenerateConfig({
plugin: "rollup",
configFileName: "rollup",
format: "esm",
detectFormat: "esm",
version: `v${version}`,
detectVersion: "v3",
file_format: "cjs",
enableSourceMaps: true,
});

await config.createConfig();
config.removeBundleName(`test-rollup-v${version}`);
await config.writeConfig();
});

afterEach(async () => {
await $`rm -rf ${rollupConfig(version, "esm")}`;
await $`rm -rf ${rollupApp}/distV${version}`;
});

it("warns users and exits process with a code 1", async () => {
const id = `rollup-v${version}-sourcemaps-${Date.now()}`;
const rollup = rollupPath(version);
const configFile = rollupConfig(version, "esm");
const API_URL = `http://localhost:8000/test-url/${id}/200/false`;

// build the app
const { exitCode, stdout } =
await $`API_URL=${API_URL} node ${rollup} -c ${configFile}`.nothrow();

expect(exitCode).toBe(1);
expect(stdout.toString()).toContain(
"[codecov] bundleName: `` does not match format: `/^[wd_:/@.{}[]$-]+$/`.",
);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,50 @@ describe("Generating sveltekit stats", () => {
{ timeout: 25_000 },
);
});

describe("invalid bundle name is passed", () => {
beforeEach(async () => {
const config = new GenerateConfig({
plugin: "sveltekit",
configFileName: "vite",
format: "esm",
detectFormat: "esm",
version: `v2`,
detectVersion: "v2",
file_format: "ts",
enableSourceMaps: false,
overrideOutputPath: `${sveltekitApp}/vite.config.ts`,
});

await config.createConfig();
config.removeBundleName(`test-sveltekit-v${version}`);
await config.writeConfig();
});

afterEach(async () => {
await $`rm -rf ${sveltekitApp}/vite.config.ts`;
await $`rm -rf ${sveltekitApp}/.svelte-kit`;
await $`rm -rf ./fixtures/generate-bundle-stats/sveltekit/.svelte-kit`;
});

it(
"warns users and exits process with a code 1",
async () => {
const id = `sveltekit-v${version}-${Date.now()}`;
const API_URL = `http://localhost:8000/test-url/${id}/200/false`;

// prepare and build the app
const { exitCode, stdout } =
await $`cd test-apps/sveltekit && API_URL=${API_URL} pnpm run build`.nothrow();

expect(exitCode).toBe(1);
// for some reason this isn't being outputted in the test env
expect(stdout.toString()).toContain(
"[codecov] bundleName: `` does not match format: `/^[wd_:/@.{}[]$-]+$/`.",
);
},
{ timeout: 25_000 },
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { GenerateConfig } from "../../../scripts/gen-config";
const vitePath = (version: number) =>
`node_modules/viteV${version}/bin/vite.js`;
const viteConfig = (version: number, format: string) =>
`fixtures/generate-bundle-stats/vite/vite-v${version}-${format}.config.ts`;
`fixtures/generate-bundle-stats/vite/vite-v${version}-${format}.config.*`;
const viteApp = "test-apps/vite";

const VERSIONS = [4, 5];
Expand Down Expand Up @@ -124,5 +124,45 @@ describe("Generating vite stats", () => {
});
});
});

describe("invalid bundle name is passed", () => {
beforeEach(async () => {
const config = new GenerateConfig({
plugin: "vite",
configFileName: "vite",
format: "esm",
detectFormat: "esm",
version: `v${version}`,
detectVersion: "v5",
file_format: "ts",
enableSourceMaps: true,
});

await config.createConfig();
config.removeBundleName(`test-vite-v${version}`);
await config.writeConfig();
});

afterEach(async () => {
await $`rm -rf ${viteConfig(version, "esm")}`;
await $`rm -rf ${viteApp}/distV${version}`;
});

it("warns users and exits process with a code 1", async () => {
const id = `vite-v${version}-sourcemaps-${Date.now()}`;
const vite = vitePath(version);
const configFile = viteConfig(version, "esm");
const API_URL = `http://localhost:8000/test-url/${id}/200/false`;

// build the app
const { exitCode, stdout } =
await $`API_URL=${API_URL} node ${vite} build -c ${configFile}`.nothrow();

expect(exitCode).toBe(1);
expect(stdout.toString()).toContain(
"[codecov] bundleName: `` does not match format: `/^[wd_:/@.{}[]$-]+$/`.",
);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { GenerateConfig } from "../../../scripts/gen-config";
const webpackPath = (version: number) =>
`node_modules/webpackV${version}/bin/webpack.js`;
const webpackConfig = (version: number, format: string) =>
`fixtures/generate-bundle-stats/webpack/webpack-v${version}-${format}.config.cjs`;
`fixtures/generate-bundle-stats/webpack/webpack-v${version}-${format}.config.*`;
const webpackApp = "test-apps/webpack";

const VERSIONS = [5];
Expand Down Expand Up @@ -116,5 +116,45 @@ describe("Generating webpack stats", () => {
});
});
});

describe("invalid bundle name is passed", () => {
beforeEach(async () => {
const config = new GenerateConfig({
plugin: "webpack",
configFileName: "webpack",
format: "module",
detectFormat: "commonjs",
version: `v${version}`,
detectVersion: "v5",
file_format: "cjs",
enableSourceMaps: false,
});

await config.createConfig();
config.removeBundleName(`test-webpack-v${version}`);
await config.writeConfig();
});

afterEach(async () => {
await $`rm -rf ${webpackConfig(version, "module")}`;
await $`rm -rf ${webpackApp}/distV${version}`;
});

it("warns users and exits process with a code 1", async () => {
const id = `webpack-v${version}-sourcemaps-${Date.now()}`;
const webpack = webpackPath(version);
const configFile = webpackConfig(version, "module");
const API_URL = `http://localhost:8000/test-url/${id}/200/false`;

// build the app
const { exitCode, stdout } =
await $`API_URL=${API_URL} node ${webpack} --config ${configFile}`.nothrow();

expect(exitCode).toBe(1);
expect(stdout.toString()).toContain(
"[codecov] bundleName: `` does not match format: `/^[wd_:/@.{}[]$-]+$/`.",
);
});
});
});
});
9 changes: 9 additions & 0 deletions integration-tests/scripts/gen-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ export class GenerateConfig {
return this.newConfigContents;
}

removeBundleName(bundleName: string) {
if (typeof this.newConfigContents === "string") {
this.newConfigContents = this.newConfigContents.replaceAll(
bundleName,
"",
);
}
}

async writeConfig() {
if (typeof this.newConfigContents === "string") {
await Bun.write(this.outFilePath, this.newConfigContents);
Expand Down
15 changes: 11 additions & 4 deletions packages/bundler-plugin-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
import {
type Asset,
type BundleAnalysisUploadPlugin,
type Chunk,
type Module,
type Options,
type ProviderUtilInputs,
type UploadOverrides,
type BundleAnalysisUploadPlugin,
} from "./types.ts";
import { checkNodeVersion } from "./utils/checkNodeVersion.ts";
import { red } from "./utils/logging.ts";
import { normalizeOptions } from "./utils/normalizeOptions.ts";
import { handleErrors, normalizeOptions } from "./utils/normalizeOptions.ts";
import { normalizePath } from "./utils/normalizePath.ts";
import { Output } from "./utils/Output.ts";

export type {
Asset,
BundleAnalysisUploadPlugin,
Chunk,
Module,
Options,
ProviderUtilInputs,
UploadOverrides,
BundleAnalysisUploadPlugin,
};

export { checkNodeVersion, normalizeOptions, normalizePath, red, Output };
export {
checkNodeVersion,
handleErrors,
normalizeOptions,
normalizePath,
Output,
red,
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { describe, it, expect } from "vitest";
import {
describe,
expect,
it,
vi,
beforeEach,
afterEach,
type MockInstance,
} from "vitest";
import { type Options } from "../../types.ts";
import {
normalizeOptions,
type NormalizedOptionsResult,
handleErrors,
} from "../normalizeOptions";

interface Test {
Expand Down Expand Up @@ -195,3 +204,46 @@ describe("normalizeOptions", () => {
expect(expectation).toEqual(expected);
});
});

describe("handleErrors", () => {
let consoleSpy: MockInstance;
let processExitSpy: MockInstance<[code?: number], never>;

beforeEach(() => {
consoleSpy = vi.spyOn(console, "log");
processExitSpy = vi
.spyOn(process, "exit")
.mockImplementation(() => undefined as never);
});

afterEach(() => {
processExitSpy.mockReset();
consoleSpy.mockReset();
});

describe("there is a bundleName error", () => {
it("logs out the error message", () => {
handleErrors({
success: false,
errors: [
"`bundleName` is required for uploading bundle analysis information.",
],
});

expect(consoleSpy).toHaveBeenCalled();
expect(consoleSpy).toHaveBeenCalledWith(
"[codecov] `bundleName` is required for uploading bundle analysis information.",
);
});

it("exits the process with status code 1", () => {
handleErrors({
success: false,
errors: [
"`bundleName` is required for uploading bundle analysis information.",
],
});
expect(processExitSpy).toHaveBeenCalledWith(1);
});
});
});
Loading
Loading