Skip to content

Commit 14ecd62

Browse files
authored
fix: regenerate workspace Cargo.lock after extension overlay (contracts/ layout) (#13)
* fix: generate missing Cargo.lock after extension overlay The erc-20 extension ships contracts/erc20-example/Cargo.toml but no Cargo.lock. cargo-stylus 0.10.x runs cargo build --locked internally, which hard-fails when no lock file exists. New task `refreshCargoLocks` runs after copyTemplateFiles and before install. It finds every Cargo.toml directory under packages/stylus/ that is missing a Cargo.lock and generates one via `cargo metadata --format-version 1` (offline-first, online fallback). Empirically verified: parity-scale-codec resolves to 3.7.5 in all lock files. Task warns-and-continues if cargo is not on PATH; base scaffold with no extension is unaffected (existing locks are skipped). * fix: update Cargo.lock refresh for contracts/ workspace layout - Fix isYourContract regex to match new packages/stylus/contracts/your-contract path so your-contract is correctly skipped when an extension is used - Refresh Cargo.lock for all found contract/workspace dirs, not just missing ones, so a stale workspace lock is updated after extension overlay adds new members
1 parent fd9d27f commit 14ecd62

4 files changed

Lines changed: 75 additions & 1 deletion

File tree

src/main.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
createFirstGitCommit,
66
prettierFormat,
77
setConfigNetworkToSepolia,
8+
refreshCargoLocks,
89
} from "./tasks";
910
import type { Options } from "./types";
1011
import { renderOutroMessage } from "./utils/render-outro-message";
@@ -57,6 +58,10 @@ export async function createProject(options: Options) {
5758
}
5859
},
5960
},
61+
{
62+
title: `🔒 Refreshing Cargo.lock for contracts`,
63+
task: async () => await refreshCargoLocks(targetDirectory),
64+
},
6065
{
6166
title: `📦 Installing dependencies with yarn, this could take a while`,
6267
task: async (ctx, task) => {

src/tasks/copy-template-files.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const copyBaseFiles = async (
4141
const isYarnLock = isYarnLockRegex.test(fileName);
4242
const isNextGenerated = isNextGeneratedRegex.test(fileName);
4343
const isGitKeep = isGitKeepRegex.test(fileName);
44-
const isYourContract = /packages\/stylus\/your-contract/.test(fileName);
44+
const isYourContract = /packages\/stylus\/contracts\/your-contract/.test(fileName);
4545

4646
// Check if file matches any exclude pattern
4747
const isExcluded = excludePatterns.some(pattern => pattern.test(fileName));

src/tasks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from "./install-packages";
44
export * from "./create-first-git-commit";
55
export * from "./prettier-format";
66
export * from "./config-to-sepolia";
7+
export * from "./refresh-cargo-lock";

src/tasks/refresh-cargo-lock.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { execa } from "execa";
2+
import fs from "fs";
3+
import path from "path";
4+
5+
const SKIP_DIRS = new Set([".git", "target", "node_modules", ".cargo"]);
6+
7+
function findContractDirs(dir: string): string[] {
8+
const result: string[] = [];
9+
10+
let entries: fs.Dirent[];
11+
try {
12+
entries = fs.readdirSync(dir, { withFileTypes: true });
13+
} catch {
14+
return result;
15+
}
16+
17+
for (const entry of entries) {
18+
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) continue;
19+
const fullPath = path.join(dir, entry.name);
20+
21+
if (fs.existsSync(path.join(fullPath, "Cargo.toml"))) {
22+
result.push(fullPath);
23+
} else {
24+
result.push(...findContractDirs(fullPath));
25+
}
26+
}
27+
28+
return result;
29+
}
30+
31+
export async function refreshCargoLocks(targetDir: string) {
32+
const stylusDir = path.join(targetDir, "packages", "stylus");
33+
34+
if (!fs.existsSync(stylusDir)) return;
35+
36+
const contractDirs = findContractDirs(stylusDir);
37+
38+
if (contractDirs.length === 0) return;
39+
40+
for (const contractDir of contractDirs) {
41+
try {
42+
try {
43+
await execa(
44+
"cargo",
45+
["metadata", "--format-version", "1", "--offline"],
46+
{ cwd: contractDir, stdio: "pipe" }
47+
);
48+
} catch {
49+
await execa("cargo", ["metadata", "--format-version", "1"], {
50+
cwd: contractDir,
51+
stdio: "pipe",
52+
});
53+
}
54+
} catch (error: any) {
55+
const isNotFound = error?.code === "ENOENT";
56+
const label = path.relative(targetDir, contractDir);
57+
if (isNotFound) {
58+
console.warn(
59+
`\n[warn] cargo not found on PATH — skipping Cargo.lock refresh for ${label}`
60+
);
61+
} else {
62+
console.warn(
63+
`\n[warn] Could not refresh Cargo.lock for ${label}: ${error?.message ?? error}`
64+
);
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)