Skip to content

Commit db80824

Browse files
justin808claude
andauthored
Add comprehensive docs for newcomers (#8)
* Add comprehensive documentation for newcomers Split monolithic README into focused docs pages covering input formats, CLI reference, output formats, comparison modes, and programmatic API. The README now serves as a concise overview with links to deeper docs. Key improvements: - Explain what config files are and where YAML/JSON configs come from - Show clear before/after examples for plugin-aware and rule matching - Document all supported file formats with inline code examples - Clarify the left/right convention and how labels are derived Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update package-lock.json from local install Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add dump command and modularize CLI architecture Introduce `dump` subcommand to serialize live webpack/rspack configs to YAML/JSON/inspect format. Refactor CLI into focused modules: configLoader, configSerializer, configCleaner, fileWriter, yamlSerializer, and buildConfigFile. Update README, CLI reference, and programmatic API docs to reflect new capabilities. Add integration tests for dump command. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address all PR review comments on documentation accuracy - Fix detailed output examples in getting-started.md, output-formats.md, README.md to match actual formatContextual output (correct header, comparing format, field labels, values grouping, legend) - Add normalizePaths and format to DiffEngine constructor options in programmatic-api.md - Remove unsupported ES module export default section from .js input format docs and add note about hardcoded argv.mode="production" - Add bash/text language tags to all unlabeled fenced code blocks for markdownlint MD040 compliance - Fix exit code 0 description to include --help case - Fix YAML format description to document grouped changes structure vs flat entries array - Fix plugin-aware output example to show instance-level change reporting instead of incorrect nested property diffs - Move JSON file labels out of json-fenced blocks to avoid invalid comment syntax Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address PR review comments on source code quality and security - Fix hardcoded mode:"production" in configLoader — dump command now passes --environment value to function config exports - Fix fileWriter path validation: throw instead of warn-only when writing outside cwd/tmpdir; route diff --output through FileWriter - Fix naive path prefix match in configCleaner (cherry-pick 7fa0898) - Deduplicate identical array serialization branches in yamlSerializer - Quote YAML keys containing special characters (colons, brackets, etc.) - Error on unset env vars in build config instead of silent empty string - Fix N+1 file reads in resolveAllBuilds - Filter DefinePlugin definitions in --clean mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address remaining PR review comments on correctness and safety - Quote YAML-ambiguous scalar strings (true, false, null, numbers, etc.) in both serializeString and quoteKey to preserve round-trip fidelity - Use explicit CORE_SCHEMA for yaml.load in configLoader to block unsafe tags while allowing standard scalar coercion - Add RegExp instance check in configCleaner to prevent silent destruction of regex patterns (e.g., module.rules[].test) during --clean - Add BigInt handling to jsonReplacer to prevent JSON.stringify TypeError - Add readOptionValue helper to CLI parsers to prevent flags from being consumed as values for preceding options (e.g., --left --right=b) - Validate --output and --save-dir as mutually exclusive in dump parser - Remove no-op array filter in configCleaner to preserve array indices - Align dump default mode to "production" to match diff and webpack CLI - Log target directory when using default save dir for multi-config output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix inconsistent environment default and env var expansion bug - Make build-config dump path default to "production" matching both webpack's own default and the single-file dump path - Use nullish coalescing (??) instead of logical OR (||) in env var expansion so empty string values are preserved Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix YAML serialization edge cases and improve safety checks - Quote empty strings to prevent YAML null interpretation (key: "" not key: ) - Add YAML quoting for strings starting with >, ?, -, % (block indicators) - Escape control characters (\t, \r) in quoted YAML strings - Validate output path before creating directory in writeSingleFile - Preserve original ts-node error when it fails for non-missing reasons - Align EnvironmentPlugin filtering to use [FILTERED] placeholder (consistent with DefinePlugin handling) - Use Object.getPrototypeOf in configCleaner's getConstructorName (matches yamlSerializer, prevents spoofing via own property) - Preserve constructor names on empty nested objects in YAML output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix option parsing, env defaults, and serialization edge cases - Extract readOptionValue helper to fix option parsing when next arg starts with a dash (e.g. --format --left would misparse) - Change default environment from "development" to "production" for dump commands to match typical production config inspection use case - Use ?? instead of || for env var expansion so empty string defaults are preserved - Add --output/--save-dir mutual exclusivity validation - Add informational message when auto-selecting save directory - Handle BigInt values in JSON serializer to prevent crash Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review comments: cache eviction, trailing newlines, env ordering, blank lines - Recursively clear require.cache subtree so helper modules that read process.env at module scope get fresh values across build-matrix iterations - Normalize trailing-newline handling: both dump paths now trimEnd() before printing to stdout for consistent piped output - Apply --env before resolving build matrix so placeholders in build definitions see CLI-supplied environment variables - Preserve blank lines in YAML array serialization by iterating all lines (not just non-empty) when emitting block content Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add dump module unit tests and review workflow command * Fix lint errors and improve documentation clarity Fix eslint errors: preserve-caught-error violations in buildConfigFile and configLoader, no-useless-escape in test templates, unused import in configCleaner test. Add .context/ and .claude/ to eslint and prettier ignore lists. Improve docs formatting and readability across all documentation files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review comments: env expansion, YAML quoting, path validation, exit codes - Fix ${BUNDLER:-default} to use CLI bundler param instead of process.env - Use single-pass regex in expandString to prevent double-expansion - Treat empty env vars as unset for ${VAR:-default} (bash :- semantics) - Add | to serializeString quoting triggers for valid YAML output - Apply makePathRelative to multiline strings in serializeObject - Validate output directory path before creating it in writeMultipleFiles - Apply --env variables before --list-builds returns - Use exit code 2 for errors, reserving 1 for "differences found" - Fix ES2020 compat: remove Error cause (requires ES2022+), add lint disables Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fail fast on filename collisions in writeMultipleFiles Detect duplicate basenames before writing to prevent silent data loss when two outputs normalize to the same filename. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review comments: YAML control chars, dash values, filename sanitization, list-builds validation - Escape C0 control characters (\x00-\x08, \x0b, \x0c, \x0e-\x1f, \x7f) in YAML double-quoted strings to produce valid YAML output - Remove dash-prefix rejection in readOptionValue to restore old behavior where space-separated option values like --path-separator - work - Sanitize OS-problematic characters in generateFilename components - Reject --output/--save-dir when combined with --list-builds Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix prettier formatting in yamlSerializer.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR review comments: exit codes, option parsing, cache clearing, YAML key order, symlink safety, clean filtering, empty output guard - Fix exit code table in docs to show code 2 for errors (was incorrectly merged into code 1) - Fix --environment default in docs from "development" to "production" to match implementation - Remove unused optionName parameter from readOptionValue - Add flag-prefix check in readOptionValue to prevent flags being consumed as option values - Skip node_modules in recursive require cache clearing to avoid evicting shared dependencies - Preserve insertion order for YAML object keys instead of alphabetical sorting - Resolve symlinks in validateOutputPath to prevent symlink-based path traversal - Expand --clean secret filtering to cover ProvidePlugin - Add empty outputs guard in runDumpFromBuildConfig to prevent TypeError Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address unresolved PR review items for dump and serializers * Fix prettier formatting in buildConfigFile.ts and cli.integration.test.js Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d8ac19d commit db80824

26 files changed

Lines changed: 4598 additions & 270 deletions

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
dist/
22
coverage/
33
node_modules/
4+
.claude/
5+
.context/
46
*.log

README.md

Lines changed: 132 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,193 +1,195 @@
11
# pack-config-diff
22

3-
Semantic configuration differ for webpack and rspack projects.
3+
Semantic configuration tooling for webpack and rspack projects.
44

5-
`pack-config-diff` is the missing tool between config merge helpers and bundle-size differs: it compares **configuration objects themselves** and explains what changed and why it matters.
5+
`pack-config-diff` supports both:
6+
7+
- `diff`: compare two webpack/rspack **configuration objects** and explain what changed.
8+
- `dump`: serialize live webpack/rspack configs to YAML/JSON/inspect for review or diffing.
69

710
Extracted from [Shakapacker](https://github.com/shakacode/shakapacker), battle-tested in production workflows.
811

912
## Why use this?
1013

11-
- Debug "works in dev, broken in prod" by comparing two generated configs
14+
- Debug "works in dev, broken in prod" by comparing two configs side by side
1215
- Validate webpack -> rspack migration parity
1316
- Audit config changes before/after dependency upgrades
1417
- Compare CI vs local bundler behavior
18+
- Generate PR-ready markdown diff reports
1519

1620
## Install
1721

1822
```bash
1923
npm install pack-config-diff
2024
```
2125

22-
## CLI
26+
## Quick start
2327

24-
```bash
25-
pack-config-diff --left=webpack-development-client.yaml --right=webpack-production-client.yaml
26-
```
28+
### Compare two configs (`diff`)
2729

28-
### Shakapacker-style workflow examples
30+
The tool takes two config files (`--left` and `--right`) and shows what's different between them. Config files can be **JavaScript, TypeScript, JSON, or YAML**.
2931

3032
```bash
31-
# 1) Works in dev, breaks in prod
32-
pack-config-diff \
33-
--left=shakapacker-config-exports/webpack-development-client.yaml \
34-
--right=shakapacker-config-exports/webpack-production-client.yaml \
35-
--format=summary
33+
pack-config-diff --left=webpack.dev.js --right=webpack.prod.js
3634
```
3735

38-
```bash
39-
# 2) Compare client vs server production bundles
40-
pack-config-diff \
41-
--left=shakapacker-config-exports/webpack-production-client.yaml \
42-
--right=shakapacker-config-exports/webpack-production-server.yaml
43-
```
36+
```text
37+
================================================================================
38+
Webpack/Rspack Configuration Comparison
39+
================================================================================
4440
45-
```bash
46-
# 3) Focus on core config during webpack -> rspack migration
47-
pack-config-diff \
48-
--left=webpack-config.yaml \
49-
--right=rspack-config.yaml \
50-
--ignore-paths="plugins.*"
51-
```
41+
Comparing: webpack.dev.js
42+
vs: webpack.prod.js
5243
53-
```bash
54-
# 4) Emit machine-readable report for CI or PR artifacts
55-
pack-config-diff \
56-
--left=baseline.json \
57-
--right=current.json \
58-
--format=json \
59-
--output=diff-report.json
60-
```
44+
Found 4 difference(s): 0 added, 0 removed, 4 changed
6145
62-
```bash
63-
# 5) Reduce plugin-instance noise (constructor + option-aware comparison)
64-
pack-config-diff \
65-
--left=webpack.dev.js \
66-
--right=webpack.prod.js \
67-
--plugin-aware
46+
================================================================================
47+
48+
1. [~] mode
49+
50+
What it does:
51+
Defines the environment mode (development, production, or none). Controls built-in optimizations and defaults.
52+
53+
Affects: Minification, tree-shaking, source maps, and performance optimizations
54+
55+
Values:
56+
dev: "development"
57+
prod: "production"
58+
59+
Impact: Enabling production optimizations (minification, tree-shaking)
60+
61+
Documentation: https://webpack.js.org/configuration/mode/
62+
63+
2. [~] output.filename
64+
65+
What it does:
66+
Filename template for entry chunks. Can include [name], [hash], [contenthash].
67+
68+
Affects: Output filenames and cache busting strategy
69+
70+
Values:
71+
dev: "bundle.js"
72+
prod: "bundle-[contenthash].js"
73+
74+
Impact: Cache busting enabled - better long-term caching
75+
76+
Documentation: https://webpack.js.org/configuration/output/#outputfilename
77+
78+
...
79+
80+
================================================================================
81+
82+
Legend:
83+
[+] = Added in prod
84+
[-] = Removed from prod
85+
[~] = Changed between configs
6886
```
6987

88+
### Export a live config snapshot (`dump`)
89+
7090
```bash
71-
# 6) Ignore module.rules reorder noise by matching rules on `test`
72-
pack-config-diff \
73-
--left=webpack-before.yaml \
74-
--right=webpack-after.yaml \
75-
--match-rules-by-test
91+
pack-config-diff dump webpack.config.js --format=yaml --output=webpack-development-client.yml
7692
```
7793

94+
### More examples
95+
7896
```bash
79-
# 7) Generate markdown output for PR comments
80-
pack-config-diff \
81-
--left=baseline.json \
82-
--right=current.json \
83-
--format=markdown
84-
```
97+
# Quick summary for CI scripts
98+
pack-config-diff --left=dev.json --right=prod.json --format=summary
99+
# => 3 changes: +1 -0 ~2
85100

86-
### Example detailed output
101+
# Markdown table for PR comments
102+
pack-config-diff --left=baseline.json --right=current.json --format=markdown
87103

88-
```text
89-
pack-config-diff — Semantic config diff
90-
Comparing: webpack-development-client.yaml ↔ webpack-production-client.yaml
91-
Found 3 difference(s): 1 added, 0 removed, 2 changed
104+
# Ignore plugin noise when comparing JS configs with class instances
105+
pack-config-diff --left=webpack.dev.js --right=webpack.prod.js --plugin-aware
92106

93-
1. [~] mode
94-
Description: Sets webpack optimization defaults for development or production.
95-
dev-client: "development"
96-
prod-client: "production"
97-
Impact: Switching mode from development to production changes optimization defaults and debugging behavior.
98-
Docs: https://webpack.js.org/configuration/mode/
99-
100-
2. [~] optimization.minimize
101-
Description: Enables or disables code minimization.
102-
dev-client: false
103-
prod-client: true
104-
Impact: Minification is now enabled, usually reducing bundle size at the cost of build time.
105-
Docs: https://webpack.js.org/configuration/optimization/#optimizationminimize
106-
107-
3. [+] target
108-
Description: Defines target runtime environment for output bundles.
109-
prod-client: "web"
110-
Docs: https://webpack.js.org/configuration/target/
111-
112-
Legend: [+] added, [-] removed, [~] changed
113-
```
107+
# Ignore rule reorder noise
108+
pack-config-diff --left=before.yaml --right=after.yaml --match-rules-by-test
114109

115-
### Help
110+
# Focus on specific areas by ignoring paths
111+
pack-config-diff --left=a.json --right=b.json --ignore-paths="plugins.*,devServer"
116112

117-
```text
118-
pack-config-diff — Semantic config differ for webpack and rspack
119-
120-
Compare two webpack/rspack configuration files and identify differences.
121-
122-
Usage:
123-
pack-config-diff --left=<file1> --right=<file2> [options]
124-
125-
Required Options:
126-
--left=<file> Path to the first (left) config file
127-
--right=<file> Path to the second (right) config file
128-
129-
Output Options:
130-
--format=<format> Output format: detailed, summary, json, yaml, markdown (default: detailed)
131-
--output=<file> Write output to file instead of stdout
132-
133-
Comparison Options:
134-
--include-unchanged Include unchanged values in output
135-
--max-depth=<number> Maximum depth for comparison (default: unlimited)
136-
--ignore-keys=<keys> Comma-separated list of keys to ignore
137-
--ignore-paths=<paths> Comma-separated list of paths to ignore (supports wildcards)
138-
--plugin-aware Compare class-instance plugins by constructor + options
139-
--match-rules-by-test Match module.rules entries by rule test instead of index
140-
--no-normalize-paths Disable automatic path normalization
141-
--path-separator=<sep> Path separator for human-readable paths (default: ".")
142-
143-
Other Options:
144-
--help, -h Show this help message
145-
146-
Supported File Formats:
147-
- JSON (.json)
148-
- YAML (.yaml, .yml)
149-
- JavaScript (.js)
150-
- TypeScript (.ts) - requires ts-node
151-
152-
Exit Codes:
153-
0 - No differences found
154-
1 - Differences found
155-
2 - Error occurred
113+
# Save report to a file
114+
pack-config-diff --left=a.json --right=b.json --format=json --output=report.json
115+
116+
# Dump a config with inline docs (YAML only)
117+
pack-config-diff dump webpack.config.js --annotate
118+
119+
# Dump as JSON with special value placeholders for functions/RegExp/class instances
120+
pack-config-diff dump webpack.config.js --format=json
121+
122+
# Dump one named build from a build-matrix config
123+
pack-config-diff dump --build=prod --config-file=config/pack-config-diff-builds.yml --save-dir=./config-exports
124+
125+
# Dump every build in the matrix
126+
pack-config-diff dump --all-builds --config-file=config/pack-config-diff-builds.yml
156127
```
157128

158-
## Programmatic API
129+
## What can `--left` and `--right` be?
159130

160-
```js
161-
const { DiffEngine, DiffFormatter } = require("pack-config-diff");
131+
Any file that contains a webpack/rspack configuration object:
162132

163-
const engine = new DiffEngine({
164-
includeUnchanged: false,
165-
ignorePaths: ["plugins.*"],
166-
});
133+
| Format | Extensions | Notes |
134+
| -------------- | --------------- | -------------------------------------------------------------------------------------------- |
135+
| **JavaScript** | `.js` | Loaded via `require()`. Supports object exports and function exports `(env, argv) => config` |
136+
| **TypeScript** | `.ts` | Same as JS, requires `ts-node` as a peer dependency |
137+
| **JSON** | `.json` | A plain JSON object representing the config |
138+
| **YAML** | `.yaml`, `.yml` | Same structure as JSON, just in YAML syntax |
139+
140+
You can mix formats: `--left=config.yaml --right=webpack.config.js` works fine.
141+
142+
**YAML and JSON configs** are snapshots of resolved webpack configuration objects — the same data structure that `webpack.config.js` exports, just serialized to a different format. They're useful when you want to compare configs generated by a framework (like Shakapacker), dump configs from CI builds, or store config snapshots in version control.
143+
144+
See [Input Formats](./docs/input-formats.md) for full details and examples.
145+
146+
## Programmatic API
167147

148+
```javascript
149+
const { DiffEngine, DiffFormatter, loadConfigFile, serializeConfig } = require("pack-config-diff");
150+
151+
const engine = new DiffEngine({ ignorePaths: ["plugins.*"] });
168152
const result = engine.compare(leftConfig, rightConfig, {
169153
leftFile: "webpack.dev.js",
170154
rightFile: "webpack.prod.js",
171155
});
172156

173157
const formatter = new DiffFormatter();
174158
console.log(formatter.formatDetailed(result));
159+
160+
const config = loadConfigFile("webpack.config.js");
161+
const output = serializeConfig(
162+
config,
163+
{
164+
exportedAt: new Date().toISOString(),
165+
bundler: "webpack",
166+
environment: "development",
167+
configType: "client",
168+
configCount: 1,
169+
},
170+
{ format: "yaml" },
171+
);
172+
console.log(output);
175173
```
176174

177-
## TypeScript build
175+
See [Programmatic API docs](./docs/programmatic-api.md) for full API reference.
178176

179-
```bash
180-
npm run build
181-
```
177+
## Documentation
178+
179+
- **[Getting Started](./docs/getting-started.md)** — what this tool does and how to use it
180+
- **[Input Formats](./docs/input-formats.md)** — JS, TS, JSON, and YAML config files explained
181+
- **[CLI Reference](./docs/cli-reference.md)** — all options and flags
182+
- **[Output Formats](./docs/output-formats.md)** — detailed, summary, json, yaml, and markdown
183+
- **[Comparison Modes](./docs/comparison-modes.md)** — plugin-aware mode, rule matching, path normalization
184+
- **[Programmatic API](./docs/programmatic-api.md)** — using pack-config-diff from Node.js
185+
- **[Releasing](./docs/releasing.md)** — how to publish new versions
182186

183187
## Tests
184188

185189
```bash
186190
npm test
187191
```
188192

189-
Test suites are ported from Shakapacker's `test/configDiffer` and run against this extracted package.
190-
191193
## License
192194

193195
MIT

0 commit comments

Comments
 (0)