Skip to content
Closed
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
115 changes: 108 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,114 @@ This ensures documentation stays in sync with upstream webpack without manual in

## Scripts

| Script | Description |
| ----------------------- | ------------------------------------ |
| `npm run generate-docs` | Generate Markdown from webpack types |
| `npm run build-html` | Convert Markdown to HTML |
| `npm run build` | Generate docs + build HTML |
| `npm run lint` | Run ESLint |
| `npm run format:check` | Check Prettier formatting |
| Script | Description |
| --------------------------- | ----------------------------------------------------------------- |
| `npm run generate-docs` | Generate Markdown from webpack types |
| `npm run build-html` | Convert Markdown to HTML |
| `npm run build` | Generate docs + build HTML |
| `npm run bootstrap:webpack` | Clone/update local webpack source at `HEAD_COMMIT` |
| `npm run docs:quickstart` | Run bootstrap + markdown generation + HTML build with smart skips |
| `npm run docs:doctor` | Run quickstart pipeline in verbose mode |
| `npm run lint` | Run ESLint |
| `npm run format:check` | Check Prettier formatting |

## Local Quickstart

Install dependencies:

```bash
npm install
```

Bootstrap local webpack source to the commit pinned in `HEAD_COMMIT`:

```bash
npm run bootstrap:webpack
```

Generate markdown docs:

```bash
npm run generate-docs
```

Build HTML output:

```bash
npm run build-html
```

Or run everything in one command:

```bash
npm run docs:quickstart
```

Useful flags for the quickstart runner:

- `--force` reruns every step
- `--verbose` prints detailed diagnostics
- `--no-html` skips the HTML build

Example:

```bash
node scripts/docs-pipeline.mjs --force --verbose
```
Comment on lines +83 to +93
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

The README describes flags for the quickstart runner but the example uses node scripts/docs-pipeline.mjs .... If the intended entrypoint is npm run docs:quickstart, passing flags requires npm run docs:quickstart -- --force --verbose. Consider updating the example (or adding a second example) so contributors don’t try npm run docs:quickstart --force and get unexpected npm behavior.

Copilot uses AI. Check for mistakes.

## Troubleshooting

### Missing webpack folder

If `npm run generate-docs` fails with "Webpack source not found", run:

```bash
npm run bootstrap:webpack
```

### Wrong webpack commit

If local webpack is out of sync with `HEAD_COMMIT`, run:

```bash
npm run bootstrap:webpack
```

This updates `./webpack` and checks out the exact pinned commit.

### Stale output

If you need to regenerate everything regardless of skip checks:

```bash
node scripts/docs-pipeline.mjs --force
```

## Contributor Workflow

1. Bootstrap webpack source:

```bash
npm run bootstrap:webpack
```

2. Regenerate markdown docs:

```bash
npm run generate-docs
```

3. Build HTML output:

```bash
npm run build-html
```

Output locations:

- Markdown: `pages/vX.x/`
- Type map: `pages/vX.x/type-map.json`
- HTML site: `out/`

## Contributing

Expand Down
17 changes: 16 additions & 1 deletion generate-md.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import { Application } from 'typedoc';
import webpack from './webpack/package.json' with { type: 'json' };
import { major } from 'semver';
import { existsSync, readFileSync } from 'node:fs';

if (!existsSync('./webpack')) {
throw new Error('Webpack source not found. Run: npm run bootstrap:webpack');
}

if (
!existsSync('./webpack/package.json') ||
!existsSync('./webpack/types.d.ts')
) {
throw new Error(
'Webpack source is incomplete. Run: npm run bootstrap:webpack'
);
}
Comment on lines +5 to +16
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

generate-md.mjs now checks that webpack/ exists, but it can still generate docs from a webpack checkout that’s not the HEAD_COMMIT pinned commit (e.g., if a contributor has webpack/ at a different SHA). That can produce pages that don’t match CI’s docs-check behavior and will fail in PRs. Consider validating git -C ./webpack rev-parse HEAD against HEAD_COMMIT and erroring with an actionable message when they differ.

Copilot uses AI. Check for mistakes.

const webpack = JSON.parse(readFileSync('./webpack/package.json', 'utf8'));

const app = await Application.bootstrapWithPlugins({
entryPoints: ['./webpack/types.d.ts'],
Expand Down
27 changes: 13 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"generate-docs": "node generate-md.mjs",
"build-html": "doc-kit generate -t web --config-file ./doc-kit.config.mjs",
"build": "npm run generate-docs && npm run build-html",
"bootstrap:webpack": "node scripts/bootstrap-webpack.mjs",
"docs:quickstart": "node scripts/docs-pipeline.mjs",
"docs:doctor": "node scripts/docs-pipeline.mjs --verbose",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"format": "prettier --write .",
Expand Down
83 changes: 83 additions & 0 deletions scripts/bootstrap-webpack.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { spawnSync } from 'node:child_process';

const ROOT = process.cwd();
const WEBPACK_DIR = resolve(ROOT, 'webpack');
const HEAD_COMMIT_FILE = resolve(ROOT, 'HEAD_COMMIT');
const WEBPACK_REPO_URL = 'https://github.com/webpack/webpack.git';

const log = message => {
console.log(`[bootstrap:webpack] ${message}`);
};

const run = (command, args, options = {}) => {
const result = spawnSync(command, args, {
cwd: ROOT,
stdio: 'inherit',
...options,
});

if (result.status !== 0) {
const fullCommand = [command, ...args].join(' ');
throw new Error(`Command failed: ${fullCommand}`);
}
};

const runCapture = (command, args, options = {}) => {
const result = spawnSync(command, args, {
cwd: ROOT,
encoding: 'utf8',
...options,
});

if (result.status !== 0) {
const fullCommand = [command, ...args].join(' ');
throw new Error(`Command failed: ${fullCommand}`);
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

In runCapture(), failures throw Error("Command failed: ...") without surfacing the command’s stderr (and without checking result.error when the executable is missing). This can make bootstrap failures hard to diagnose. Consider appending result.stderr / result.error?.message to the thrown error to keep the message actionable.

Suggested change
throw new Error(`Command failed: ${fullCommand}`);
const details = [];
if (result.error && result.error.message) {
details.push(`Error: ${result.error.message}`);
}
if (typeof result.stderr === 'string' && result.stderr.trim() !== '') {
details.push(`stderr:\n${result.stderr.trim()}`);
}
const detailSuffix = details.length > 0 ? `\n${details.join('\n')}` : '';
throw new Error(`Command failed: ${fullCommand}${detailSuffix}`);

Copilot uses AI. Check for mistakes.
}

return result.stdout.trim();
};

if (!existsSync(HEAD_COMMIT_FILE)) {
throw new Error('Missing HEAD_COMMIT file.');
}

const targetCommit = readFileSync(HEAD_COMMIT_FILE, 'utf8').trim();

if (!/^[a-f0-9]{40}$/i.test(targetCommit)) {
throw new Error(`Invalid commit SHA in HEAD_COMMIT: ${targetCommit}`);
}

let currentCommit = null;

if (existsSync(WEBPACK_DIR)) {
if (!existsSync(resolve(WEBPACK_DIR, '.git'))) {
throw new Error('webpack directory exists but is not a git repository.');
}

currentCommit = runCapture('git', ['-C', WEBPACK_DIR, 'rev-parse', 'HEAD']);
}

if (currentCommit === targetCommit) {
log(`Already up to date at ${targetCommit}.`);
process.exit(0);
}

if (!existsSync(WEBPACK_DIR)) {
log('Cloning webpack repository...');
run('git', ['clone', WEBPACK_REPO_URL, WEBPACK_DIR]);
} else {
log('Updating existing webpack repository...');
}

run('git', ['-C', WEBPACK_DIR, 'fetch', '--all', '--tags', '--prune']);
run('git', ['-C', WEBPACK_DIR, 'checkout', '--detach', targetCommit]);

const checkedOutCommit = runCapture('git', [
'-C',
WEBPACK_DIR,
'rev-parse',
'HEAD',
]);
log(`Checked out ${checkedOutCommit}.`);
Loading
Loading