Skip to content

Commit

Permalink
Merge pull request #1119 from ethereum-optimism/lint-check
Browse files Browse the repository at this point in the history
Automate Internal Link Updates Using Redirects File
  • Loading branch information
krofax authored Nov 15, 2024
2 parents 3dc670f + 2da87a6 commit 1a27d54
Show file tree
Hide file tree
Showing 17 changed files with 365 additions and 19 deletions.
2 changes: 1 addition & 1 deletion lychee.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ exclude = [
]

# Accept these status codes
accept = ["100..=103", "200..=299", "403..=403", "502..=502"]
accept = ["100..=103", "200..=299", "403..=403", "502..=502"]
83 changes: 83 additions & 0 deletions notes/fix-redirects.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Redirect links management guide

## Scripts overview
Two scripts help maintain internal links when pages are redirected:

* `check-redirects`: Identifies links that need updating based on the `_redirects` file.
* `fix-redirects`: Automatically updates links to match `_redirects` entries.

## Checking for broken links

Run the check script:

```bash
pnpm lint //OR
pnpm check-redirects
```
## What it does

* Scans all `.mdx` files in the docs
* Compares internal links against `_redirects` file
* Reports any outdated links that need updating
* Provides a summary of total, broken, and valid links

## Example output

```bash
File "builders/overview.mdx" contains outdated link "/chain/overview" - should be updated to "/stack/overview"

Summary:
Total pages 🔍 - 50
Pages broken 🚫 - 2
Pages OK ✅ - 48

```

## Fixing broken links

Fix links automatically:

```bash
pnpm fix //OR
pnpm fix-redirects
```

## What it does

* Updates all internal links to match `_redirects` entries
* Preserves other content and formatting
* Shows which files and links were updated
* Provides a summary of changes made

## Example output

```bash
Fixed in "builders/overview.mdx": /chain/overview → /stack/overview

Summary:
Total files 🔍 - 50
Files fixed ✅ - 2
Files skipped ⏭️ - 48
```

## Best practices

1. Before running

* Commit current changes
* Review `_redirects` file is up-to-date
* Run `check-redirects` first to preview changes


2. After running

* Review git diff of updated files
* Test updated links locally
* Commit changes with descriptive message



## Common issues

* Script fails: Ensure `_redirects` file exists in public folder, it should always be there!
* No broken links found: Verify `_redirects` entries are correct.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
"version": "0.0.1",
"description": "Optimism Docs",
"scripts": {
"lint": "eslint . --ext mdx --max-warnings 0 && pnpm spellcheck:lint && pnpm check-breadcrumbs",
"fix": "eslint . --ext mdx --fix && pnpm spellcheck:fix && pnpm check-breadcrumbs",
"lint": "eslint . --ext mdx --max-warnings 0 && pnpm spellcheck:lint && pnpm check-breadcrumbs && pnpm check-redirects",
"fix": "eslint . --ext mdx --fix && pnpm spellcheck:fix && pnpm check-breadcrumbs && pnpm fix-redirects",
"spellcheck:lint": "cspell lint \"**/*.mdx\"",
"spellcheck:fix": "cspell --words-only --unique \"**/*.mdx\" | sort --ignore-case | uniq > words.txt",
"linkcheck": "lychee --config ./lychee.toml --quiet \"./pages\"",
"breadcrumbs":"npx ts-node --skip-project utils/create-breadcrumbs.ts",
"check-redirects": "npx ts-node --skip-project utils/redirects.ts",
"fix-redirects": "npx ts-node --skip-project utils/fix-redirects.ts",
"check-breadcrumbs":"npx ts-node --skip-project utils/breadcrumbs.ts",
"index:docs": "npx ts-node --skip-project utils/algolia-indexer.ts",
"dev": "next dev",
Expand Down
2 changes: 1 addition & 1 deletion pages/builders.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ Welcome to the Builders section. Here you'll find resources and guides for devel
<Card title="App Developers" href="/builders/app-developers" />
<Card title="Chain Operators" href="/builders/chain-operators" />
<Card title="Node Operators" href="/builders/node-operators" />
<Card title="Wallets & CEXs" href="/builders/cex-wallet-developers" />
<Card title="Wallets & CEXs" href="/builders/app-developers/overview" />
<Card title="Developer Tools" href="/builders/tools" />
</Cards>
6 changes: 3 additions & 3 deletions pages/builders/app-developers/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ In this area of the Optimism Docs you'll find everything you need to know about
If you're brand new to OP Mainnet, try starting with the guide on [deploying a basic contract](/chain/getting-started).
It'll get you familiar with the basic steps required to get a contract deployed to the network.
OP Mainnet is [EVM equivalent](https://web.archive.org/web/20231127160757/https://medium.com/ethereum-optimism/introducing-evm-equivalence-5c2021deb306) so you can feel confident that your existing Ethereum smart contract skills will carry over to OP Mainnet.
Just make sure to be aware of the few small [differences between Ethereum and OP Mainnet](/chain/differences).
Just make sure to be aware of the few small [differences between Ethereum and OP Mainnet](/stack/differences).

You might also want to check out the [testing on OP Networks guide](/chain/testing/testing-apps) and the tutorial on [running a local development environment](/chain/testing/dev-node) to help you feel totally confident in your OP Mainnet deployment.

<Cards>
<Card title="Deploying Your First Contract to OP Mainnet" href="/builders/app-developers/tutorials/first-contract" icon={<img src="/img/icons/shapes.svg" />} />
<Card title="Deploying Your First Contract to OP Mainnet" href="/builders/app-developers/overview" icon={<img src="/img/icons/shapes.svg" />} />

<Card title="Solidity Compatibility on OP Mainnet" href="/builders/app-developers/contracts/compatibility" icon={<img src="/img/icons/shapes.svg" />} />
<Card title="Solidity Compatibility on OP Mainnet" href="/stack/differences" icon={<img src="/img/icons/shapes.svg" />} />

<Card title="Testing Apps on OP Mainnet" href="/chain/testing/testing-apps" icon={<img src="/img/icons/shapes.svg" />} />
</Cards>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Make sure to check out the [Standard Bridge guide](/builders/app-developers/brid

The Optimism SDK supports any of the [Superchain networks](/chain/networks).
[Some Superchain networks](https://sdk.optimism.io/enums/l2chainid) are already included in the SDK by default.
If you want to use a network that isn't included by default, you can simply [instantiate the SDK with the appropriate contract addresses](/builders/chain-operators/tutorials/sdk).
If you want to use a network that isn't included by default, you can simply [instantiate the SDK with the appropriate contract addresses](/builders/app-developers/overview).

## Dependencies

Expand Down
2 changes: 1 addition & 1 deletion pages/builders/chain-operators/tutorials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ This section provides information on adding attributes to the derivation functio

<Card title="Modifying predeployed contracts" href="/builders/chain-operators/tutorials/modifying-predeploys" />

<Card title="Using viem" href="/builders/chain-operators/tutorials/sdk" />
<Card title="Using viem" href="/builders/app-developers/overview" />
</Cards>
2 changes: 1 addition & 1 deletion pages/chain/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Steps } from 'nextra/components'
This guide explains the basics of OP Mainnet development.
OP Mainnet is [EVM equivalent](https://web.archive.org/web/20231127160757/https://medium.com/ethereum-optimism/introducing-evm-equivalence-5c2021deb306), meaning we run a slightly modified version of the same `geth` you run on mainnet.
Therefore, the differences between OP Mainnet development and Ethereum development are minor.
But a few differences [do exist](/chain/differences).
But a few differences [do exist](/stack/differences).

## OP Mainnet and OP Sepolia endpoint URLs

Expand Down
2 changes: 1 addition & 1 deletion pages/chain/testing/dev-node.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ We generally recommend using the local development environment if your applicati

1. **You're building contracts on both OP Mainnet and Ethereum that need to interact with one another.** The local development environment is a great way to quickly test interactions between L1 and L2. The OP Mainnet and test networks have a communication delay between L1 and L2 that can make testing slow during the early stages of development.

2. **You're building an application that might be subject to one of the few [differences between Ethereum and OP Mainnet](/chain/differences).** Although OP Mainnet is [EVM equivalent](https://web.archive.org/web/20231127160757/https://medium.com/ethereum-optimism/introducing-evm-equivalence-5c2021deb306), it's not exactly the same as Ethereum. If you're building an application that might be subject to one of these differences, you should use the local development environment to double check that everything is running as expected. You might otherwise have unexpected issues when you move to testnet. We strongly recommend reviewing these differences carefully to see if you might fall into this category.
2. **You're building an application that might be subject to one of the few [differences between Ethereum and OP Mainnet](/stack/differences).** Although OP Mainnet is [EVM equivalent](https://web.archive.org/web/20231127160757/https://medium.com/ethereum-optimism/introducing-evm-equivalence-5c2021deb306), it's not exactly the same as Ethereum. If you're building an application that might be subject to one of these differences, you should use the local development environment to double check that everything is running as expected. You might otherwise have unexpected issues when you move to testnet. We strongly recommend reviewing these differences carefully to see if you might fall into this category.

However, not everyone will need to use the local development environment.
OP Mainnet is [EVM equivalent](https://web.archive.org/web/20231127160757/https://medium.com/ethereum-optimism/introducing-evm-equivalence-5c2021deb306), which means that OP Mainnet looks almost exactly like Ethereum under the hood.
Expand Down
2 changes: 1 addition & 1 deletion pages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Whether you're a developer building a app on OP Mainnet, a node operator running

<Card title="Node operators" href="/builders/node-operators/rollup-node" icon={<img src="img/icons/computer-line.svg" />} />

<Card title="Wallets & CEXs" href="/builders/cex-wallet-developers/wallet-support" icon={<img src="img/icons/wallet.svg" />} />
<Card title="Wallets & CEXs" href="/builders/app-developers/overview" icon={<img src="img/icons/wallet.svg" />} />

<Card title="Developer tools" href="/builders/tools/overview" icon={<img src="img/icons/tools.svg" />} />

Expand Down
4 changes: 2 additions & 2 deletions pages/stack/differences.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ However, there are some minor differences between the behavior of Ethereum and O

### Bridging - Deposit Transactions

Deposit transactions don't exist on L1's, and are how transactions on an L2 can be initiated from the L1. Importantly, this is how bridge applications can get L1 ETH or tokens into an L2 OP Stack chain. You can read more on deposit transactions [here](/stack/protocol/rollup/deposit-flow).
Deposit transactions don't exist on L1's, and are how transactions on an L2 can be initiated from the L1. Importantly, this is how bridge applications can get L1 ETH or tokens into an L2 OP Stack chain. You can read more on deposit transactions [here](/stack/transactions/deposit-flow).

### Bridging - Withdrawal Transactions and Fault Proofs

Withdrawal transactions are how the state of the L2 rollup can be proven to the L1. Often this involves users withdrawing tokens or ETH to the L1. Fault proofs are the mechanism by which withdrawal transactions are currently proven to the L1. You can read more about fault proofs [here](/stack/protocol/fault-proofs/explainer).
Withdrawal transactions are how the state of the L2 rollup can be proven to the L1. Often this involves users withdrawing tokens or ETH to the L1. Fault proofs are the mechanism by which withdrawal transactions are currently proven to the L1. You can read more about fault proofs [here](/stack/fault-proofs/explainer).

## Opcodes

Expand Down
4 changes: 2 additions & 2 deletions pages/stack/features/send-raw-transaction-conditional.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ Successful submission does **NOT** guarantee inclusion! The caller must observe
This feature can be enabled with the addition of a flag to op-geth.

* `--rollup.sequencertxconditionalenabled` (default: false) a boolean flag which enables the rpc.
* `--rollup.sequencertxconditionalcostratelimit` (default: 5000) an integer flag that sets the rate limit for cost observable per second.
* `--rollup.sequencertxconditionalcostratelimit` (default: 5000) an integer flag that sets the rate limit for cost observable per second.

<Callout type="warning">
It is not advised to publicly expose this sequencer endpoint due to DoS concerns. This supplemental proxy, [op-txproxy](/stack/operators/features/op-txproxy), should be used to apply additional constraints on this endpoint prior to passing through to the sequencer.
It is not advised to publicly expose this sequencer endpoint due to DoS concerns. This supplemental proxy, [op-txproxy](/builders/chain-operators/tools/op-txproxy), should be used to apply additional constraints on this endpoint prior to passing through to the sequencer.
</Callout>
2 changes: 1 addition & 1 deletion pages/stack/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { Callout } from 'nextra/components'

The OP Stack consists of the many different software components managed and maintained by the Optimism Collective that, together, form the backbone of Optimism.
The OP Stack is built as a public good for the Ethereum and Optimism ecosystems.
To understand how to operate an OP Stack chain, including roll-up and chain deployment basics, visit [Chain Operator guide](/builders/chain-operators/self-hosted). Check out these guides to get an overview of everything you need to know to properly support OP mainnet within your [exchange](/builders/cex-wallet-developers/cex-support) and [wallet](/builders/cex-wallet-developers/wallet-support).
To understand how to operate an OP Stack chain, including roll-up and chain deployment basics, visit [Chain Operator guide](/builders/chain-operators/self-hosted). Check out these guides to get an overview of everything you need to know to properly support OP mainnet within your [exchange](/builders/app-developers/overview) and [wallet](/builders/app-developers/overview).

## The OP Stack powers Optimism

Expand Down
2 changes: 1 addition & 1 deletion pages/stack/interop/assets/deploy-superchain-erc20.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Steps } from 'nextra/components'
Interop is currently in active development and not yet ready for production use. The information provided here may change. Check back regularly for the most up-to-date information.
</Callout>

This guide explains how to issue new assets with the `SuperchainERC20` and bridge them effectively using the `SuperchainERC20Bridge`. If you want more information about the `SuperchainERC20 standard`, see our [`SuperchainERC20` standard explainer](/stack/interop/superchain-erc20)
This guide explains how to issue new assets with the `SuperchainERC20` and bridge them effectively using the `SuperchainERC20Bridge`. If you want more information about the `SuperchainERC20 standard`, see our [`SuperchainERC20` standard explainer](/stack/interop/assets/superchain-erc20)

Note that bridging assets through the Superchain using `SuperchainERC20` never affects the total supply of your asset. The supply remains fixed, and bridging only changes the chain on which your asset is located. This keeps the token's total amount the same across all networks, ensuring its value stays stable during the move and that the `SuperchainERC20` retains a unified, global supply count.

Expand Down
2 changes: 1 addition & 1 deletion pages/stack/interop/explainer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Native OP Stack interoperability provides the ability to read messages and trans
Superchain interop includes both the protocol layer message passing and the Superchain ERC20 token specification.

* **Message passing protocol:** the initial + finalizing/executing [message](cross-chain-message) that fire events to be consumed by the chains in the [dependency set](https://specs.optimism.io/interop/dependency-set.html)
* **SuperchainERC20 token specification**: the [SuperchainERC20](superchain-erc20) turns message passing into asset transfer between chains in the interop set. Learn more about how the SuperchainERC20 token standard enables asset interoperability in the Superchain [here](/stack/interop/superchain-erc20)
* **SuperchainERC20 token specification**: the [SuperchainERC20](superchain-erc20) turns message passing into asset transfer between chains in the interop set. Learn more about how the SuperchainERC20 token standard enables asset interoperability in the Superchain [here](/stack/interop/assets/superchain-erc20)

This means ETH and ERC-20s can seamlessly and securely move across L2s, and intent-based protocols (i.e., bridges) can build better experiences on top of the message passing protocol.

Expand Down
126 changes: 126 additions & 0 deletions utils/fix-redirects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import * as fs from 'fs/promises';
import * as path from 'path';

const rootDir = path.join(process.cwd(), 'pages');
const redirectsPath = path.join(process.cwd(), 'public', '_redirects');
const updates: string[] = [];

// ANSI color codes
const WHITE = '\x1b[37m';
const GREEN = '\x1b[32m';
const YELLOW = '\x1b[33m';
const RESET = '\x1b[0m';
const BOLD = '\x1b[1m';

interface Redirect {
from: string;
to: string;
}

interface Summary {
total: number;
fixed: number;
skipped: number;
}

async function getRedirects(): Promise<Redirect[]> {
const content = await fs.readFile(redirectsPath, 'utf-8');
return content.split('\n')
.filter(line => line.trim() && !line.startsWith('#'))
.map(line => {
const [from, to] = line.split(/\s+/);
return { from, to };
});
}

async function findMdxFiles(dir: string): Promise<string[]> {
const files: string[] = [];
const entries = await fs.readdir(dir, { withFileTypes: true });

for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory() && !entry.name.startsWith('_')) {
files.push(...await findMdxFiles(fullPath));
} else if (entry.isFile() && /\.(md|mdx)$/.test(entry.name)) {
files.push(fullPath);
}
}
return files;
}

async function fixFile(filePath: string, redirects: Redirect[]): Promise<boolean> {
let content = await fs.readFile(filePath, 'utf-8');
let hasChanges = false;
const relativeFilePath = path.relative(rootDir, filePath);

redirects.forEach(redirect => {
const markdownRegex = new RegExp(`\\[([^\\]]+)\\]\\(${redirect.from}\\)`, 'g');
const hrefRegex = new RegExp(`href="${redirect.from}"`, 'g');

if (content.match(markdownRegex) || content.match(hrefRegex)) {
content = content
.replace(markdownRegex, `[$1](${redirect.to})`)
.replace(hrefRegex, `href="${redirect.to}"`);

updates.push(`${WHITE}Fixed in "${relativeFilePath}": ${YELLOW}${redirect.from}${WHITE}${GREEN}${redirect.to}${RESET}`);
hasChanges = true;
}
});

if (hasChanges) {
await fs.writeFile(filePath, content);
}

return hasChanges;
}

function printSummary(summary: Summary) {
console.log('\nSummary:');
console.log(`${WHITE}Total files 🔍 - ${summary.total}`);
console.log(`${GREEN}Files fixed ✅ - ${summary.fixed}`);
console.log(`${WHITE}Files skipped ⏭️ - ${summary.skipped}${RESET}`);
}

async function main() {
const summary: Summary = {
total: 0,
fixed: 0,
skipped: 0
};

console.log('Starting to fix redirect links...');
console.log('Root directory:', rootDir);

try {
const redirects = await getRedirects();
const files = await findMdxFiles(rootDir);

summary.total = files.length;

for (const file of files) {
const wasFixed = await fixFile(file, redirects);
if (wasFixed) {
summary.fixed++;
} else {
summary.skipped++;
}
}

if (updates.length > 0) {
console.log(`${GREEN}${BOLD}Fixed links:${RESET}`);
updates.forEach(update => console.log(update));
printSummary(summary);
} else {
console.log(`${GREEN}No broken links found. Everything is up to date.${RESET}`);
printSummary(summary);
}
} catch (error) {
console.error(`${YELLOW}${BOLD}Error fixing redirects:${RESET}`, error);
process.exit(1);
}
}

main().catch(error => {
console.error(`${YELLOW}${BOLD}Error in main process:${RESET}`, error);
process.exit(1);
});
Loading

0 comments on commit 1a27d54

Please sign in to comment.