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
5 changes: 5 additions & 0 deletions .changeset/banner-aware-use-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eventuras/vite-config': patch
---

Skip leading banner/license comments (e.g. Rollup's `output.banner`) when checking whether a chunk already starts with a `'use client'` directive, so banner-carrying chunks no longer get a duplicate directive prepended.
22 changes: 16 additions & 6 deletions config/vite-config/src/react-lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ import { getRuntimeDependencyExternals, NODE_BUILTINS_EXTERNAL } from './externa
*/
const requireFromHere = createRequire(import.meta.url);

/**
* Strip leading whitespace and any interleaved mix of line and block comments,
* so a directive check sees the first real token. Sequential per-style passes
* miss mixed orders (a line comment, then a block comment, then the directive).
*/
function stripLeadingComments(code: string): string {
return code.replace(/^(?:\s+|\/\*[\s\S]*?\*\/\s*|\/\/.*(?:\r?\n|$)\s*)+/, '');
}

/**
* Plugin to preserve 'use client' directives in React Server Components.
* This ensures client-side code is properly marked when building for Next.js.
Expand All @@ -33,20 +42,21 @@ function preserveUseClient() {
try {
const content = fs.readFileSync(id, 'utf-8');
// Remove leading comments to check for 'use client'
const withoutComments = content
.replace(/^(\s*\/\/.*\n)+/, '')
.replace(/^(\s*\/\*[\s\S]*?\*\/\s*)/, '');
const withoutComments = stripLeadingComments(content);
return (
withoutComments.trimStart().startsWith("'use client'") ||
withoutComments.trimStart().startsWith('"use client"')
withoutComments.startsWith("'use client'") ||
withoutComments.startsWith('"use client"')
);
} catch {
return false;
}
});

if (hasClientDirective) {
const codeStart = chunkData.code.trimStart();
// Ignore leading banner/license comments (e.g. Rollup's
// output.banner) when checking for the directive, so we don't
// prepend a duplicate above a directive that's already there.
const codeStart = stripLeadingComments(chunkData.code);
if (!codeStart.startsWith("'use client'") && !codeStart.startsWith('"use client"')) {
chunkData.code = `'use client';\n${chunkData.code}`;
}
Expand Down