Skip to content

Commit 0a11eb2

Browse files
committed
Pipfile.lock Dependency Parser
Built this today to help fix an old development environment. https://gist.github.com/simonw/e13064e2e830db77887373ced5df1b5b
1 parent a57b5c3 commit 0a11eb2

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ This collection is partly **an experiment** in how much it's possible to get don
3030
- [Word Counter](https://tools.simonwillison.net/word-counter) - count words across multiple blocks of text, persisted to localStorage
3131
- [PHP Deserializer](https://tools.simonwillison.net/php-deserializer) - paste in serealized PHP data, get back JSON
3232
- [SQL Pretty Printer](https://tools.simonwillison.net/sql-pretty-printer) - paste in SQL to pretty print it
33+
- [SQL Pretty Printer](https://tools.simonwillison.net/sql-pretty-printer) - paste in SQL to pretty print it
34+
- [Pipfile.lock Dependency Parser](https://tools.simonwillison.net/pipfile) - paste in a `Pipfile.lock` JSON file to extract just the dependency versions
3335

3436
## LLM playgrounds and debuggers
3537

pipfile.html

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Pipfile.lock Dependency Parser</title>
7+
<style>
8+
* {
9+
box-sizing: border-box;
10+
}
11+
12+
body {
13+
font-family: Helvetica, Arial, sans-serif;
14+
line-height: 1.6;
15+
max-width: 800px;
16+
margin: 0 auto;
17+
padding: 20px;
18+
color: #333;
19+
}
20+
21+
h1 {
22+
margin-top: 0;
23+
}
24+
25+
textarea {
26+
width: 100%;
27+
height: 300px;
28+
padding: 10px;
29+
margin-bottom: 15px;
30+
border: 1px solid #ccc;
31+
border-radius: 4px;
32+
font-size: 16px;
33+
font-family: monospace;
34+
}
35+
36+
button {
37+
background-color: #4CAF50;
38+
color: white;
39+
border: none;
40+
padding: 10px 15px;
41+
cursor: pointer;
42+
border-radius: 4px;
43+
font-size: 16px;
44+
}
45+
46+
button:hover {
47+
background-color: #45a049;
48+
}
49+
50+
pre {
51+
background-color: #f5f5f5;
52+
padding: 15px;
53+
border-radius: 4px;
54+
overflow-x: auto;
55+
white-space: pre-wrap;
56+
margin-top: 20px;
57+
}
58+
59+
.error {
60+
color: #d9534f;
61+
margin-top: 10px;
62+
}
63+
64+
.controls {
65+
display: flex;
66+
gap: 10px;
67+
margin-bottom: 15px;
68+
}
69+
</style>
70+
</head>
71+
<body>
72+
<h1>Pipfile.lock Dependency Parser</h1>
73+
<p>Paste your Pipfile.lock content below and click "Parse" to extract dependencies with their versions.</p>
74+
75+
<textarea id="input" placeholder="Paste Pipfile.lock content here..."></textarea>
76+
77+
<div class="controls">
78+
<button id="parseBtn">Parse Dependencies</button>
79+
<button id="copyPipfileBtn">Copy Pipfile Format</button>
80+
<button id="copyReqsBtn">Copy Requirements.txt</button>
81+
<button id="clearBtn">Clear All</button>
82+
</div>
83+
84+
<div id="error" class="error"></div>
85+
86+
<h2>Pipfile Format:</h2>
87+
<pre id="output"></pre>
88+
89+
<h2>Requirements.txt Format:</h2>
90+
<pre id="reqsOutput"></pre>
91+
92+
<script type="module">
93+
// Parse the Pipfile.lock content and extract dependencies
94+
function parsePipfileLock(content) {
95+
try {
96+
// Parse the JSON content
97+
const data = JSON.parse(content);
98+
let pipfileFormat = [];
99+
let reqsFormat = [];
100+
101+
// Process dependencies from "default" section
102+
if (data.default) {
103+
for (const [name, info] of Object.entries(data.default)) {
104+
if (info.version) {
105+
// Extract version without the '==' prefix
106+
const version = info.version.replace(/^==/, '');
107+
pipfileFormat.push(`"${name}" = "==${version}"`);
108+
reqsFormat.push(`${name}==${version}`);
109+
}
110+
}
111+
}
112+
113+
// Process dependencies from "develop" section if needed
114+
if (data.develop) {
115+
for (const [name, info] of Object.entries(data.develop)) {
116+
if (info.version) {
117+
const version = info.version.replace(/^==/, '');
118+
pipfileFormat.push(`"${name}" = "==${version}"`);
119+
reqsFormat.push(`${name}==${version}`);
120+
}
121+
}
122+
}
123+
124+
return {
125+
pipfileFormat: pipfileFormat.join('\n'),
126+
reqsFormat: reqsFormat.join('\n')
127+
};
128+
} catch (error) {
129+
throw new Error(`Failed to parse Pipfile.lock: ${error.message}`);
130+
}
131+
}
132+
133+
// Set up event handlers
134+
document.getElementById('parseBtn').addEventListener('click', () => {
135+
const input = document.getElementById('input').value.trim();
136+
const pipfileOutputEl = document.getElementById('output');
137+
const reqsOutputEl = document.getElementById('reqsOutput');
138+
const errorEl = document.getElementById('error');
139+
140+
errorEl.textContent = '';
141+
142+
if (!input) {
143+
errorEl.textContent = 'Please paste Pipfile.lock content first.';
144+
return;
145+
}
146+
147+
try {
148+
const result = parsePipfileLock(input);
149+
pipfileOutputEl.textContent = result.pipfileFormat;
150+
reqsOutputEl.textContent = result.reqsFormat;
151+
} catch (error) {
152+
errorEl.textContent = error.message;
153+
}
154+
});
155+
156+
function copyToClipboard(text, successMessage) {
157+
if (!text) {
158+
document.getElementById('error').textContent = 'No content to copy.';
159+
return;
160+
}
161+
162+
navigator.clipboard.writeText(text)
163+
.then(() => {
164+
const errorEl = document.getElementById('error');
165+
errorEl.textContent = successMessage || 'Output copied to clipboard!';
166+
errorEl.style.color = '#4CAF50';
167+
setTimeout(() => {
168+
errorEl.textContent = '';
169+
errorEl.style.color = '#d9534f';
170+
}, 2000);
171+
})
172+
.catch(err => {
173+
document.getElementById('error').textContent = 'Failed to copy: ' + err;
174+
});
175+
}
176+
177+
document.getElementById('copyPipfileBtn').addEventListener('click', () => {
178+
const output = document.getElementById('output').textContent;
179+
copyToClipboard(output, 'Pipfile format copied to clipboard!');
180+
});
181+
182+
document.getElementById('copyReqsBtn').addEventListener('click', () => {
183+
const output = document.getElementById('reqsOutput').textContent;
184+
copyToClipboard(output, 'Requirements.txt format copied to clipboard!');
185+
});
186+
187+
document.getElementById('clearBtn').addEventListener('click', () => {
188+
document.getElementById('input').value = '';
189+
document.getElementById('output').textContent = '';
190+
document.getElementById('error').textContent = '';
191+
});
192+
193+
// Initialize with example data from the textarea if present
194+
const initialContent = document.getElementById('input').value.trim();
195+
if (initialContent) {
196+
document.getElementById('parseBtn').click();
197+
}
198+
</script>
199+
</body>
200+
</html>

0 commit comments

Comments
 (0)