Skip to content

feat(output): add the output.copy option - #1

Open
anurag6569201 wants to merge 1 commit into
qa/agent-webpack-webpack/pr-01-21938/basefrom
qa/agent-webpack-webpack/pr-01-21938/head
Open

anurag6569201 wants to merge 1 commit into
qa/agent-webpack-webpack/pr-01-21938/basefrom
qa/agent-webpack-webpack/pr-01-21938/head

Conversation

@anurag6569201

Copy link
Copy Markdown

Summary

Copying static files into the output directory needs copy-webpack-plugin today, so output.copy brings it into core: a pattern is a from plus an optional to, filename and transform, glob matching reuses webpack's own lib/util/globUtils.js, and every copied file is cached behind a file system snapshot so a rebuild re-reads only what changed.

The option surface is deliberately smaller than the plugin's, calibrated against Vite's publicDir, vite-plugin-static-copy, rollup-plugin-copy and esbuild-plugin-copy: to is a destination directory (never a dir/file/template guess, so toType is gone), filename is a webpack filename template within it (default [path][base], so it renames, flattens and hashes), a pattern that copies nothing warns rather than failing the build (so noErrorOnMissing is gone, and ignoreWarnings silences it), a later pattern replaces an earlier one (so force is gone), and from is the only file selector. output.copy also accepts a bare string or an array of strings.

What kind of change does this PR introduce?

feat

Did you add tests for your changes?

Yes — test/configCases/copy/{basic,glob,shorthand,transform,to-option,missing,errors}, plus test/watchCases/copy/{copy,clean} for rebuilds and output.clean, and test/hotCases/copy/copy for HMR.

Does this PR introduce a breaking change?

No, it is a new option.

If relevant, what needs to be documented once your changes are merged or what have you already documented?

output.copy needs a page on webpack.js.org: the pattern shape (from, to, filename, transform), the [path][base] default, the warning on a pattern that copies nothing, and a migration note from copy-webpack-plugin (to: "dir" keeps working; toType/filter/force/priority/info/noErrorOnMissing have no equivalent).

Use of AI

AI was used throughout, under review at each step: it wrote the implementation and tests, ran the option surface against the other bundlers' copy plugins to decide what to keep, and measured the claims made above (glob base extraction against glob-parent, the exclusion and re-emit behaviour, watch rebuilds). Every design decision here was made or confirmed by me.

🤖 Generated with Claude Code

https://claude.ai/code/session_012Bq1dTfsXi8stgF7XQsyEg


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added output.copy for copying files and directories into build output.
    • Supports glob patterns, custom destinations and filenames, content hashing, transformations, symlinks, and configurable glob behavior.
    • Supports preserving file permissions and timestamps.
    • Added a reusable public copy plugin and copied-asset labels in build statistics.
    • Copy operations stay synchronized during watch mode and hot reloads.
    • Copied files are removed when their source files are deleted.
  • Bug Fixes
    • Added warnings for unmatched patterns and errors for failed, unreadable, or conflicting copies.

Source merge-base: 9cb49ef1474b807fbc0b852611610eb24289eee5
Source head: 06195d8ac727be35ce6acbe3fca7cf518fc33fa6

@shipwright-agent

Copy link
Copy Markdown

⚠️ Shipwright · Approve with conditions

Recommendation: approve PR #1 with conditions · Tier T3
Checks: 0 total · 0 needing attention

Next step: an authorized approver must satisfy the approval condition.

Findings (6)

  • HIGH The example's 'MergeCopiedAssetsPlugin' caches the merged asset under a key built from 'filename' and asset names, with an etag over asset sources. · examples/output-copy/webpack.config.js:55
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH The 'to' function example routes '.css' files to 'css' and everything else to 'media', but the source directory is named 'img' and contains 'theme.css'. · examples/output-copy/webpack.config.js:104
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH The symlink test fixture builds a symlink loop ('real/loop' points back at the source base) and the default 'followSymlinks: true' path must walk it. · test/configCases/copy/symlink/webpack.config.js:60
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH 'output.copy' accepts a 'transform' that can return a Promise and a 'concurrency' option, but the diff does not show validation of 'concurrency' (e.g. · declarations/WebpackOptions.d.ts:4242
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH 'output.copy' can copy arbitrary files and directories into the output directory, including files outside the compiler context when 'from' is an absolute path or 'context' is attac · declarations/WebpackOptions.d.ts:3195
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH The new 'symlink' asset info allows emitting symbolic links whose 'target' is preserved as written. · lib/Compilation.js:367
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Conditions

  • human approval required (T3): apply the approval label

Fireworks usage: 54,710 input · 761 output · 55,471 total tokens · $0.0125 · 16s · 0 fix iteration(s)

Open the Shipwright check for full evidence and the audit bundle. Use /shipwright rerun to verify again.

.sort((a, b) => (a.name < b.name ? -1 : 1));
if (assets.length === 0) return;

// the merged content carries the name of every part as well as its

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shipwright · HIGH

The example's 'MergeCopiedAssetsPlugin' caches the merged asset under a key built from 'filename' and asset names, with an etag over asset sources.

Impact: The example's 'MergeCopiedAssetsPlugin' caches the merged asset under a key built from 'filename' and asset names, with an etag over asset sources. If 'merge' behavior changes without changing the key or etag, stale cached output is served. The example documents caching but does not include a cache-busting version key, so a reader copying this pattern can ship stale merged output after changing 'merge'.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

},
// content rewritten on the way through, cached on what it reads
{
from: "config.json",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shipwright · HIGH

The 'to' function example routes '.css' files to 'css' and everything else to 'media', but the source directory is named 'img' and contains 'theme.css'.

Impact: The 'to' function example routes '.css' files to 'css' and everything else to 'media', but the source directory is named 'img' and contains 'theme.css'. This is surprising and likely to confuse a new contributor about whether 'to' receives the source-relative path or the destination filename; the type name 'CopiedFileData.filename' is ambiguous without reading the implementation.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

);
fs.symlinkSync(source, path.join(source, "real/loop"), "junction");
// relative, so it still resolves once the link and its target are copied
fs.symlinkSync("real/a.txt", path.join(source, "relative.txt"), "file");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shipwright · HIGH

The symlink test fixture builds a symlink loop ('real/loop' points back at the source base) and the default 'followSymlinks: true' path must walk it.

Impact: The symlink test fixture builds a symlink loop ('real/loop' points back at the source base) and the default 'followSymlinks: true' path must walk it. If the implementation lacks cycle detection or a visited-set, this can recurse until stack overflow or copy unboundedly. The fixture itself proves the loop exists, but no assertion in the visible diff verifies termination or bounded output for the follow-symlinks case.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant