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: Normalize asset file names #27

Merged
merged 12 commits into from
Dec 19, 2023
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
4 changes: 3 additions & 1 deletion examples/next-js/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ const { codecovWebpackPlugin } = require("@codecov/webpack-plugin");
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config, options) => {
config.plugins.push(codecovWebpackPlugin({ enableBundleAnalysis: true }));
config.plugins.push(
codecovWebpackPlugin({ enableBundleAnalysis: true, dryRun: true }),
);

return config;
},
Expand Down
2 changes: 1 addition & 1 deletion examples/rollup/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ export default defineConfig({
resolve(), // tells Rollup how to find date-fns in node_modules
commonjs(), // converts date-fns to ES modules
production && terser(), // minify, but only in production
codecovRollupPlugin({ enableBundleAnalysis: true }),
codecovRollupPlugin({ enableBundleAnalysis: true, dryRun: true }),
],
});
17 changes: 16 additions & 1 deletion examples/vite/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,20 @@ import { codecovVitePlugin } from "@codecov/vite-plugin";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), codecovVitePlugin({ enableBundleAnalysis: true })],
build: {
rollupOptions: {
output: {
assetFileNames: "[name].[hash].js",
chunkFileNames: "[name]-[hash].js",
},
},
},
plugins: [
react(),
codecovVitePlugin({
enableBundleAnalysis: true,
dryRun: true,
globalUploadToken: "super-cool-token",
}),
],
});
1 change: 1 addition & 0 deletions examples/webpack/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
plugins: [
codecovWebpackPlugin({
enableBundleAnalysis: true,
dryRun: true,
}),
],
};
6 changes: 3 additions & 3 deletions packages/bundler-plugin-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
type UploadOverrides,
type Output,
} from "./types.ts";

import { red } from "./utils/logging.ts";
import { normalizePath } from "./utils/normalizePath.ts";
import { bundleAnalysisPluginFactory } from "./bundle-analysis/bundleAnalysisPluginFactory.ts";

const NODE_VERSION_RANGE = ">=18.18.0";
Expand Down Expand Up @@ -48,8 +48,6 @@ function codecovUnpluginFactory({
});
}

export { red, codecovUnpluginFactory };

export type {
BundleAnalysisUploadPlugin,
Asset,
Expand All @@ -60,3 +58,5 @@ export type {
UploadOverrides,
Output,
};

export { normalizePath, codecovUnpluginFactory, red };
4 changes: 2 additions & 2 deletions packages/bundler-plugin-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ export interface Dependency {
export interface Asset {
name: string;
size: number;
normalized: string;
}

export interface Chunk {
id: string;
uniqueId: string;
entry: boolean;
initial: boolean;
files: string[];
names: string[];
files: string[];
}

export interface Module {
name: string;
size?: number;
chunks: (string | number)[];
chunkUniqueIds: string[];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { normalizePath } from "../normalizePath";

interface Test {
name: string;
input: {
path: string;
format: string;
};
expected: string;
}

const tests: Test[] = [
{
name: "should replace '[hash]' with '*'",
input: {
path: "test.123.chunk.js",
format: "[name].[hash].chunk.js",
},
expected: "test.*.chunk.js",
},
{
name: "should replace '[contenthash]' with '*'",
input: {
path: "test.123.chunk.js",
format: "[name].[contenthash].chunk.js",
},
expected: "test.*.chunk.js",
},
{
name: "should replace '[fullhash]' with '*'",
input: {
path: "test.123.chunk.js",
format: "[name].[fullhash].chunk.js",
},
expected: "test.*.chunk.js",
},
{
name: "should replace '[chunkhash]' with '*'",
input: {
path: "test.123.chunk.js",
format: "[name].[chunkhash].chunk.js",
},
expected: "test.*.chunk.js",
},
{
name: "should replace multiple hash format occurrences '*'",
input: {
path: "test.123.456.chunk.js",
format: "[name].[hash].[chunkhash].chunk.js",
},
expected: "test.*.*.chunk.js",
},
{
name: "should brute force wildcard if no hash format is found",
input: {
path: "test.12345678.chunk.js",
format: "[name].chunk.js",
},
expected: "test.*.chunk.js",
},
];

describe("normalizePath", () => {
it.each(tests)("$name", ({ input, expected }) => {
const expectation = normalizePath(input.path, input.format);
expect(expectation).toEqual(expected);
});
});
60 changes: 60 additions & 0 deletions packages/bundler-plugin-core/src/utils/normalizePath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const HASH_REGEX = /[a-f0-9]{8,}/i;
const POTENTIAL_HASHES = [
"[hash]",
"[contenthash]",
"[fullhash]",
"[chunkhash]",
];

const escapeRegex = (string: string): string =>
string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");

interface HashMatch {
hashString: string;
hashIndex: number;
}

export const normalizePath = (path: string, format: string): string => {
// grab all potential hashes in the format string
const matches: HashMatch[] = [];
for (const hash of POTENTIAL_HASHES) {
const index = format.indexOf(hash);
if (index !== -1) {
matches.push({ hashString: hash, hashIndex: index });
}
}

let normalizedPath = path;
// loop through all the matches and replace the hash with a wildcard
for (const match of matches) {
// grab the leading delimiter and create a regex group for it
const leadingDelimiter = format.at(match.hashIndex - 1) ?? "";
const leadingRegex = `(?<leadingDelimiter>${escapeRegex(
leadingDelimiter,
)})`;

// grab the ending delimiter and create a regex group for it
const endingDelimiter =
format.at(match.hashIndex + match.hashString.length) ?? "";
const endingRegex = `(?<endingDelimiter>${escapeRegex(endingDelimiter)})`;

// create a regex that will match the hash
const regexString = `(${leadingRegex}(?<hash>[0-9a-f]+)${endingRegex})`;
const HASH_REPLACE_REGEX = new RegExp(regexString, "i");

// replace the hash with a wildcard and the delimiters
normalizedPath = normalizedPath.replace(
HASH_REPLACE_REGEX,
"$<leadingDelimiter>*$<endingDelimiter>",
);
}

// if the path is the same as the normalized path, and the path contains a
// hash, then we can assume that something went wrong and we should just
// replace/brute force the hash with a wildcard
if (normalizedPath === path && HASH_REGEX.test(normalizedPath)) {
return normalizedPath.replace(HASH_REGEX, "*");
}

return normalizedPath;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
type Module,
type BundleAnalysisUploadPlugin,
red,
normalizePath,
} from "@codecov/bundler-plugin-core";

const PLUGIN_NAME = "codecov-rollup-bundle-analysis-plugin";
Expand All @@ -31,17 +32,25 @@
output.bundleName = `${userOptions.bundleName}-${options.name}`;
}

const cwd = process.cwd();
const assets: Asset[] = [];
const chunks: Chunk[] = [];
const moduleByFileName = new Map<string, Module>();
const items = Object.values(bundle);

Check warning on line 39 in packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts#L35-L39

Added lines #L35 - L39 were not covered by tests
const customOptions = {
moduleOriginalSize: false,
...options,
};

const assets: Asset[] = [];
const chunks: Chunk[] = [];
const moduleByFileName = new Map<string, Module>();
const items = Object.values(bundle);
let assetFormatString = "";

Check warning on line 45 in packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts#L45

Added line #L45 was not covered by tests
if (typeof customOptions.assetFileNames === "string") {
assetFormatString = customOptions.assetFileNames;

Check warning on line 47 in packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts#L47

Added line #L47 was not covered by tests
}

const cwd = process.cwd();
let chunkFormatString = "";

Check warning on line 50 in packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts#L50

Added line #L50 was not covered by tests
if (typeof customOptions.chunkFileNames === "string") {
chunkFormatString = customOptions.chunkFileNames;

Check warning on line 52 in packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts#L52

Added line #L52 was not covered by tests
}

let counter = 0;
for (const item of items) {
Expand All @@ -53,6 +62,7 @@
assets.push({
name: fileName,
size: size,
normalized: normalizePath(fileName, assetFormatString),
});
} else {
const fileName = item?.fileName ?? "";
Expand All @@ -61,6 +71,7 @@
assets.push({
name: fileName,
size: size,
normalized: normalizePath(fileName, assetFormatString),
});
}
}
Expand All @@ -75,14 +86,15 @@
assets.push({
name: fileName,
size: size,
normalized: normalizePath(fileName, chunkFormatString),
});

chunks.push({
id: chunkId,
uniqueId: uniqueId,
entry: item?.isEntry,
initial: item?.isDynamicEntry,
files: [item?.fileName],
files: [fileName],
names: [item?.name],
});

Expand All @@ -103,17 +115,15 @@
// if the modules exists append chunk ids to the grabbed module
// else create a new module and create a new entry in the map
if (moduleEntry) {
moduleEntry.chunks.push(chunkId);
moduleEntry.chunkUniqueIds.push(uniqueId);
} else {
const size = customOptions.moduleOriginalSize
? moduleInfo.originalLength
: moduleInfo.renderedLength;

const module = {
const module: Module = {

Check warning on line 124 in packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/rollup-plugin/src/rollup-bundle-analysis/rollupBundleAnalysisPlugin.ts#L124

Added line #L124 was not covered by tests
name: relativeModulePathWithPrefix,
size: size,
chunks: [chunkId],
chunkUniqueIds: [uniqueId],
};

Expand Down
Loading