#!/usr/bin/env python3
"""
Generate comprehensive JavaScript training data for Dev-AI.
Covers: security, performance, modern patterns, DOM, async, Node.js, testing, build tools.
Target: 120-150 high-quality entries.
"""
import json
import os
import random
from pathlib import Path
BASE = Path(__file__).parent.parent / "data" / "processed"
def g(issue, bad, good):
return {
"messages": [
{"role": "user", "content": f"Review this JavaScript code:\n\n```javascript\n{bad}\n```"},
{"role": "assistant", "content": f"**Issue: {issue}**\n\n**Fix:**\n```javascript\n{good}\n```"}
],
"source": "javascript_training",
"tags": ["javascript", issue.lower().replace(" ", "_")]
}
entries = []
# =============================================================================
# SECURITY (15 entries)
# =============================================================================
entries.append(g(
"XSS via innerHTML",
"document.getElementById('output').innerHTML = userInput;",
"const el = document.getElementById('output');\nel.textContent = userInput;\n// textContent escapes HTML. innerHTML renders it."
))
entries.append(g(
"XSS via eval()",
"const result = eval(userExpression);",
"// Never eval user input.\n// Use JSON.parse for JSON data:\nconst data = JSON.parse(userInput);\n// Or a safe expression parser library."
))
entries.append(g(
"XSS via document.write()",
"document.write('<div>' + userComment + '</div>');",
"const container = document.getElementById('comments');\nconst div = document.createElement('div');\ndiv.textContent = userComment;\ncontainer.appendChild(div);"
))
entries.append(g(
"Insecure Cookie",
"document.cookie = 'session=' + token;",
"document.cookie = 'session=' + token + '; SameSite=Strict; Secure; Path=/';\n// Secure flag = HTTPS only\n// SameSite prevents CSRF\n// Path limits scope"
))
entries.append(g(
"Prototype Pollution",
"function merge(target, source) {\n for (let key in source) {\n target[key] = source[key];\n }\n}",
"function merge(target, source) {\n for (let key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n target[key] = source[key];\n }\n }\n}"
))
entries.append(g(
"CSP Missing",
"<!DOCTYPE html>\n<html>\n<head>\n <script src=\"app.js\"></script>\n</head>",
"<!DOCTYPE html>\n<html>\n<head>\n <meta http-equiv=\"Content-Security-Policy\" \n content=\"default-src 'self'; script-src 'self'; object-src 'none'\">\n <script src=\"app.js\"></script>\n</head>"
))
entries.append(g(
"Client-side Secret",
"const API_KEY = 'sk_live_abc123';\nfetch('/api/data', { headers: { 'Authorization': API_KEY } });",
"// Never store secrets in client-side code.\n// Use a backend proxy:\nfetch('/api/proxy/data')\n .then(r => r.json());\n// Server adds the API key server-side."
))
entries.append(g(
"Open Redirect",
"const url = req.query.redirect;\nres.redirect(url);",
"const allowedHosts = ['example.com', 'app.example.com'];\nconst url = req.query.redirect;\nconst parsed = new URL(url, 'https://example.com');\nif (!allowedHosts.includes(parsed.hostname)) {\n return res.redirect('/');\n}\nres.redirect(url);"
))
entries.append(g(
"No CSRF Protection",
"app.post('/transfer', (req, res) => {\n // Process transfer\n});",
"const csrf = require('csurf');\nconst csrfProtection = csrf({ cookie: true });\n\napp.get('/form', csrfProtection, (req, res) => {\n res.json({ csrfToken: req.csrfToken() });\n});\n\napp.post('/transfer', csrfProtection, (req, res) => {\n // Token validated automatically\n});"
))
entries.append(g(
"Unsafe Redirect",
"window.location = userInput;",
"// Validate before redirecting:\nconst allowedUrls = ['/dashboard', '/profile', '/settings'];\nif (allowedUrls.includes(userInput)) {\n window.location.href = userInput;\n}"
))
entries.append(g(
"Sensitive Data in URL",
"fetch(`/api/users/${userId}?token=${authToken}`)",
"fetch(`/api/users/${userId}`, {\n headers: { 'Authorization': `Bearer ${authToken}` }\n})"
))
entries.append(g(
"Insecure deserialization",
"const data = eval('(' + jsonString + ')');",
"const data = JSON.parse(jsonString);\n// JSON.parse is safe. eval() executes arbitrary code."
))
entries.append(g(
"Missing Rate Limiting",
"app.post('/login', async (req, res) => {\n // Auth check - no rate limit\n});",
"const rateLimit = require('express-rate-limit');\nconst loginLimiter = rateLimit({\n windowMs: 15 * 60 * 1000,\n max: 5,\n message: 'Too many login attempts'\n});\n\napp.post('/login', loginLimiter, async (req, res) => {\n // Auth check\n});"
))
entries.append(g(
"Unsafe iframe",
"<iframe src=\"userProvidedUrl\"></iframe>",
"<iframe src=\"userProvidUrl\" sandbox=\"allow-scripts allow-sameorigin\"></iframe>\n// sandbox restricts frame capabilities\n// Only allow specific origins in production"
))
entries.append(g(
"No input sanitization",
"app.get('/search', (req, res) => {\n const query = req.query.q;\n res.render('results', { query });\n});",
"const DOMPurify = require('dompurify');\nconst { JSDOM } = require('jsdom');\nconst window = new JSDOM('').window;\nconst purify = DOMPurify(window);\n\napp.get('/search', (req, res) => {\n const query = purify.sanitize(req.query.q);\n res.render('results', { query });\n});"
))
# =============================================================================
# PERFORMANCE (15 entries)
# =============================================================================
entries.append(g(
"var instead of let/const",
"var count = 0;\nvar name = 'test';\nvar MAX = 100;",
"let count = 0;\nconst name = 'test';\nconst MAX = 100;\n// const for values that don't change\n// let for reassignable variables\n// var is function-scoped and hoisted"
))
entries.append(g(
"String concatenation in loop",
"let result = '';\nfor (let i = 0; i < items.length; i++) {\n result += items[i];\n}",
"const result = items.join('');\n// join() is optimized and avoids string re-allocation"
))
entries.append(g(
"Inefficient DOM manipulation",
"for (const item of items) {\n document.getElementById('list').innerHTML += `<li>${item}</li>`;\n}",
"// Build once, insert once:\nconst fragment = document.createDocumentFragment();\nitems.forEach(item => {\n const li = document.createElement('li');\n li.textContent = item;\n fragment.appendChild(li);\n});\ndocument.getElementById('list').appendChild(fragment);"
))
entries.append(g(
"Using array.indexOf for large sets",
"if (allowedList.indexOf(value) !== -1) { ... }\n// allowedList has thousands of items",
"const allowedSet = new Set(allowedList);\nif (allowedSet.has(value)) { ... }\n// Set lookups are O(1) vs O(n)"
))
entries.append(g(
"Blocking the main thread",
"function processData(hugeArray) {\n // Heavy computation blocks UI\n return hugeArray.map(x => expensiveCalc(x));\n}",
"// Offload to Web Worker:\nconst worker = new Worker('calc-worker.js');\nworker.postMessage(hugeArray);\nworker.onmessage = (e) => {\n const results = e.data;\n // UI stays responsive\n};"
))
entries.append(g(
"Unnecessary array copy",
"const copy = [...array];\nconst filtered = copy.filter(x => x > 0);\nconst mapped = filtered.map(x => x * 2);",
"// Chain operations without intermediate copies:\nconst result = array.filter(x => x > 0).map(x => x * 2);\n// Each step returns a new array, but no unnecessary spread"
))
entries.append(g(
"For loop instead of built-in",
"let sum = 0;\nfor (let i = 0; i < arr.length; i++) {\n sum += arr[i];\n}",
"const sum = arr.reduce((acc, val) => acc + val, 0);\n// Or for simple sum:\nconst sum = arr.reduce((a, b) => a + b, 0);"
))
entries.append(g(
"Memory leak with setInterval",
"setInterval(() => {\n fetch('/api/data').then(updateUI);\n}, 5000);\n// Never cleared - keeps running after component unmount",
"const intervalId = setInterval(() => {\n fetch('/api/data').then(updateUI);\n}, 5000);\n// Clean up:\nclearInterval(intervalId);"
))
entries.append(g(
"Event listener leak",
"function setup() {\n document.addEventListener('click', handleClick);\n}\n// Called multiple times, adding duplicate listeners",
"// Use a flag or remove before adding:\ndocument.removeEventListener('click', handleClick);\ndocument.addEventListener('click', handleClick);\n// Better: use event delegation\nparentElement.addEventListener('click', (e) => {\n if (e.target.matches('.button')) handleClick(e);\n});"
))
entries.append(g(
"Large bundle - full lodash import",
"import _ from 'lodash';\n_.debounce(...);\n// Imports entire lodash (~70KB)",
"import debounce from 'lodash/debounce';\n// Tree-shakeable - only imports what's used\n// Or use a lightweight alternative:"
))
entries.append(g(
"Unoptimized image loading",
"<img src=\"photo.jpg\"> <!-- 4MB image -->",
"<img src=\"photo.webp\" \n srcset=\"photo-400w.webp 400w, photo-800w.webp 800w\"\n sizes=\"(max-width: 600px) 400px, 800px\"\n loading=\"lazy\"\n width=\"800\" height=\"600\">\n// WebP format, responsive, lazy-loaded, sized"
))
entries.append(g(
"Synchronous XHR",
"const xhr = new XMLHttpRequest();\nxhr.open('GET', '/data', false); // synchronous!\nxhr.send();\nconst data = xhr.responseText;",
"fetch('/data')\n .then(r => r.json())\n .then(data => {\n // Process response\n });\n// Or async/await:\nconst resp = await fetch('/data');\nconst data = await resp.json();"
))
entries.append(g(
"Using setTimeout(0) for async",
"function asyncWork() {\n setTimeout(() => {\n // 'async' work\n }, 0);\n}",
"// Use Promise for proper async semantics:\nasync function asyncWork() {\n await new Promise(resolve => setTimeout(resolve, 0));\n // 'async' work\n}\n// Or queueMicrotask for same-tick scheduling"
))
entries.append(g(
"Inefficient reflow",
"const el = document.getElementById('box');\nel.style.width = '100px'; // forces reflow\nconst h = el.offsetHeight; // forces reflow\nel.style.height = h + 'px'; // forces reflow",
"// Batch reads and writes:\nconst h = el.offsetHeight; // read first\nrequestAnimationFrame(() => {\n el.style.width = '100px'; // write batched\n el.style.height = h + 'px'; // write batched\n});"
))
entries.append(g(
"Not using requestAnimationFrame",
"function animate() {\n updatePosition();\n setTimeout(animate, 16);\n}",
"function animate() {\n updatePosition();\n requestAnimationFrame(animate);\n // Syncs with display refresh rate, smoother animation\n}"
))
# =============================================================================
# MODERN PATTERNS & ES6+ (15 entries)
# =============================================================================
entries.append(g(
"function declaration in block scope",
"if (condition) {\n function helper() { return 1; }\n return helper();\n} // helper leaks in non-strict mode",
"if (condition) {\n const helper = () => 1;\n return helper();\n // Arrow functions are properly block-scoped\n}"
))
entries.append(g(
"this binding lost",
"class Timer {\n constructor() {\n this.seconds = 0;\n setInterval(function() {\n this.seconds++; // 'this' is undefined\n }, 1000);\n }\n}",
"class Timer {\n constructor() {\n this.seconds = 0;\n setInterval(() => {\n this.seconds++; // Arrow function preserves 'this'\n }, 1000);\n }\n}"
))
entries.append(g(
"Using Object.keys for iteration",
"for (const key of Object.keys(obj)) {\n console.log(key, obj[key]);\n}",
"for (const [key, value] of Object.entries(obj)) {\n console.log(key, value);\n}"
))
entries.append(g(
"Manual deep clone",
"function deepClone(obj) {\n const clone = {};\n for (let key in obj) {\n clone[key] = typeof obj[key] === 'object'\n ? deepClone(obj[key]) : obj[key];\n }\n return clone;\n}",
"// Built-in for most cases:\nconst clone = structuredClone(original);\n// Handles circular refs, Dates, Maps, Sets\n// Or: JSON.parse(JSON.stringify(obj)) for simple data"
))
entries.append(g(
"Using == instead of ==",
"if (value == 0) { ... }\nif (value == undefined) { ... }",
"if (value === 0) { ... }\nif (value === undefined) { ... }\n// === checks both value and type\n// == performs type coercion (source of bugs)"
))
entries.append(g(
"Missing optional chaining",
"const city = user && user.address && user.address.city;",
"const city = user?.address?.city;\n// Optional chaining (?.) - cleaner null safety"
))
entries.append(g(
"Missing nullish coalescing",
"const port = options.port || 8080;\n// Problem: port=0 evaluates to false",
"const port = options.port ?? 8080;\n// ?? only falls back on null/undefined, not 0 or ''"
))
entries.append(g(
"Using arguments object",
"function sum() {\n let total = 0;\n for (let i = 0; i < arguments.length; i++) {\n total += arguments[i];\n }\n return total;\n}",
"function sum(...args) {\n return args.reduce((acc, val) => acc + val, 0);\n // Rest parameters are proper arrays\n // No 'this' binding issues\n}"
))
entries.append(g(
"Template literal concatenation",
"const greeting = 'Hello, ' + name + '! You are ' + age + ' years old.';",
"const greeting = `Hello, ${name}! You are ${age} years old.`;\n// Template literals are cleaner and support expressions"
))
entries.append(g(
"Missing destructuring",
"const name = user.name;\nconst email = user.email;\nconst role = user.role;",
"const { name, email, role } = user;\n// Or with renaming:\nconst { name: userName, email } = user;"
))
entries.append(g(
"Array map for side effects",
"items.map(item => {\n console.log(item);\n process(item);\n});",
"items.forEach(item => {\n console.log(item);\n process(item);\n});\n// map() returns a new array - use forEach() for side effects"
))
entries.append(g(
"Missing Symbol usage",
"class Cache {\n get(key) { return this.data[key]; }\n set(key, val) { this.data[key] = val; }\n}\n// Collision risk with user data keys",
"const _data = Symbol('data');\nclass Cache {\n constructor() { this[_data] = new Map(); }\n get(key) { return this[_data].get(key); }\n set(key, val) { this[_data].set(key, val); }\n // Symbol keys won't collide with user data\n}"
))
entries.append(g(
"Using for...in on arrays",
"for (let i in array) {\n process(array[i]); // wrong iteration order, includes properties\n}",
"for (let i = 0; i < array.length; i++) {\n process(array[i]);\n}\n// Or:\nfor (const item of array) {\n process(item);\n}"
))
entries.append(g(
"Missing class field syntax",
"class Button {\n constructor() {\n this.handleClick = this.handleClick.bind(this);\n }\n handleClick() { ... }\n}",
"class Button {\n handleClick = () => { ... };\n // Class fields are auto-bound to the instance\n}"
))
entries.append(g(
"Object spread for partial update",
"const updated = {};\nfor (const key in original) {\n updated[key] = original[key];\n}\nupdated.name = newName;",
"const updated = { ...original, name: newName };\n// Spread operator copies and overrides in one line"
))
# =============================================================================
# ASYNC / PROMISES (12 entries)
# =============================================================================
entries.append(g(
"Callback hell",
"getData(function(a) {\n getMore(a, function(b) {\n getMore(b, function(c) {\n getMore(c, function(d) {\n // ...\n });\n });\n });\n});",
"async function load() {\n const a = await getData();\n const b = await getMore(a);\n const c = await getMore(b);\n const d = await getMore(c);\n // Linear and readable\n}"
))
entries.append(g(
"Uncaught promise rejection",
"fetch('/api/data')\n .then(r => r.json())\n .then(data => updateUI(data));\n// No .catch() - silent failure",
"async function loadData() {\n try {\n const resp = await fetch('/api/data');\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n const data = await resp.json();\n updateUI(data);\n } catch (err) {\n console.error('Load failed:', err);\n showErrorMessage(err.message);\n }\n}"
))
entries.append(g(
"Promise.all without error handling",
"const [users, posts] = await Promise.all([\n fetchUsers(),\n fetchPosts()\n]);\n// One failure cancels everything",
"const [users, posts] = await Promise.allSettled([\n fetchUsers(),\n fetchPosts()\n]);\n// All resolve individually\nconst userList = users.status === 'fulfilled' ? users.value : [];\nconst postList = posts.status === 'fulfilled' ? posts.value : [];"
))
entries.append(g(
"Race condition with shared state",
"let count = 0;\nasync function increment() {\n await delay(100);\n count++; // Non-atomic with async gap\n}",
"// Use a lock or atomic operations:\nlet count = 0;\nconst lock = { held: false };\nasync function increment() {\n while (lock.held) await delay(10);\n lock.held = true;\n try {\n await delay(100);\n count++;\n } finally {\n lock.held = false;\n }\n}"
))
entries.append(g(
"Missing Promise.all for parallel requests",
"const users = await fetchUsers();\nconst posts = await fetchPosts();\nconst comments = await fetchComments();\n// Sequential - waits for each",
"const [users, posts, comments] = await Promise.all([\n fetchUsers(),\n fetchPosts(),\n fetchComments()\n]);\n// Parallel - total time = slowest request"
))
entries.append(g(
"Stale closure in async callback",
"for (let i = 0; i < items.length; i++) {\n fetch(items[i].url).then(data => {\n items[i] = data; // 'i' may have changed\n });\n}",
"const results = await Promise.all(\n items.map(async (item, index) => {\n const data = await fetch(item.url);\n return data;\n })\n);\nitems = results;"
))
entries.append(g(
"Missing timeout for fetch",
"const resp = await fetch('/api/data');",
"const controller = new AbortController();\nconst timeout = setTimeout(() => controller.abort(), 5000);\ntry {\n const resp = await fetch('/api/data', {\n signal: controller.signal\n });\n} finally {\n clearTimeout(timeout);\n}"
))
entries.append(g(
"Fire and forget promise",
"function handle() {\n logEvent('click'); // Promise returned but not awaited\n return navigate('/next');\n}",
"// If you don't need the result, still handle errors:\nlogEvent('click').catch(err => console.error('Log failed:', err));\nreturn navigate('/next');"
))
entries.append(g(
"Await in loop instead of parallel",
"for (const id of ids) {\n const item = await fetchItem(id);\n results.push(item);\n}\n// Sequential - slow for many items",
"// Parallel:\nconst results = await Promise.all(\n ids.map(id => fetchItem(id))\n);\n// With concurrency limit:\nconst results = await pLimit(ids.map(id => () => fetchItem(id)));"
))
entries.append(g(
"Not using async iterators",
"async function getAllPages(url) {\n let page = 1;\n const all = [];\n while (true) {\n const resp = await fetch(`${url}?page=${page}`);\n const data = await resp.json();\n if (data.items.length === 0) break;\n all.push(...data.items);\n page++;\n }\n return all;\n}",
"async function* pageIterator(url) {\n let page = 1;\n while (true) {\n const resp = await fetch(`${url}?page=${page}`);\n const data = await resp.json();\n if (data.items.length === 0) break;\n yield* data.items;\n page++;\n }\n}\n// Consumer:\nconst all = [];\nfor await (const item of pageIterator(url)) {\n all.push(item);\n}"
))
entries.append(g(
"Double await",
"const data = await Promise.resolve(value);\n// Unnecessary wrapping",
"const data = value;\n// If already resolved, no need for await\n// Or if it might be a promise:\nconst data = await Promise.resolve(value);\n// Only wrap if the value is uncertain"
))
entries.append(g(
"Missing finally cleanup",
"async function load() {\n showSpinner();\n const data = await fetch('/api/data');\n hideSpinner();\n return data;\n // Spinner stays if fetch throws\n}",
"async function load() {\n showSpinner();\n try {\n const data = await fetch('/api/data');\n return data;\n } finally {\n hideSpinner(); // Always runs\n }\n}"
))
# =============================================================================
# DOM & BROWSER (12 entries)
# =============================================================================
entries.append(g(
"QuerySelector in loop",
"for (const id of ids) {\n const el = document.getElementById(id);\n el.classList.add('active');\n}",
"// Cache references:\nconst els = ids.map(id => document.getElementById(id));\nfor (const el of els) {\n el.classList.add('active');\n}\n// Or: document.querySelectorAll('#id1, #id2, ...')"
))
entries.append(g(
"Using getElementsBy* collections",
"const items = document.getElementsByClassName('item');\nfor (let i = 0; i < items.length; i++) { ... }\n// Live collection - re-evaluates on DOM changes",
"const items = document.querySelectorAll('.item');\nitems.forEach(item => { ... });\n// querySelectorAll returns static NodeList"
))
entries.append(g(
"Missing event delegation",
"document.querySelectorAll('.btn').forEach(btn => {\n btn.addEventListener('click', handleClick);\n});\n// Breaks when elements are dynamically added",
"document.getElementById('container').addEventListener('click', (e) => {\n const btn = e.target.closest('.btn');\n if (btn) handleClick(e);\n});\n// Works for dynamic elements too"
))
entries.append(g(
"Inline event handlers",
"<button onclick=\"handleClick()\">Click</button>",
"<button data-action=\"click\">Click</button>\n\n// In JavaScript:\ndocument.querySelector('[data-action=\"click\"]').addEventListener('click', handleClick);"
))
entries.append(g(
"Memory leak - detached DOM reference",
"const el = document.getElementById('chart');\nconst chart = new Chart(el);\n// el is removed from DOM but chart still references it",
"const el = document.getElementById('chart');\nconst chart = new Chart(el);\n// On cleanup:\nchart.destroy();\n// Properly releases references"
))
entries.append(g(
"Using document.cookie for complex data",
"document.cookie = 'cart=' + JSON.stringify(cartItems);",
"// Use localStorage or sessionStorage for structured data:\nlocalStorage.setItem('cart', JSON.stringify(cartItems));\nconst cartItems = JSON.parse(localStorage.getItem('cart'));\n// 5MB limit vs 4KB for cookies, no bandwidth waste"
))
entries.append(g(
"Not checking feature support",
"element.animate([...]);\n// Web Animations API not supported in all browsers",
"if ('animate' in Element.prototype) {\n element.animate([...]);\n} else {\n // Fallback: CSS classes or library\n element.classList.add('animated');\n}"
))
entries.append(g(
"Forcing layout thrashing",
"for (let i = 0; i < items.length; i++) {\n const div = document.createElement('div');\n div.textContent = items[i];\n container.appendChild(div);\n const rect = div.getBoundingClientRect(); // forces layout\n console.log(rect);\n}",
"// Read all, then write all:\nconst fragment = document.createDocumentFragment();\nconst divs = items.map(item => {\n const div = document.createElement('div');\n div.textContent = item;\n fragment.appendChild(div);\n return div;\n});\ncontainer.appendChild(fragment);\nconst rects = divs.map(d => d.getBoundingClientRect());"
))
entries.append(g(
"Using window.onload",
"window.onload = function() {\n initApp();\n};\n// Waits for all resources, fires once",
"document.addEventListener('DOMContentLoaded', () => {\n initApp();\n // Fires when DOM is ready, before images/stylesheets\n});"
))
entries.append(g(
"Not using IntersectionObserver for lazy loading",
"window.addEventListener('scroll', () => {\n images.forEach(img => {\n if (img.getBoundingClientRect().top < window.innerHeight) {\n img.src = img.dataset.src;\n }\n });\n});\n// Runs on every scroll event",
"const observer = new IntersectionObserver((entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n entry.target.src = entry.target.dataset.src;\n observer.unobserve(entry.target);\n }\n });\n});\ndocument.querySelectorAll('[data-src]').forEach(img => observer.observe(img));"
))
entries.append(g(
"Using jQuery for DOM queries",
"$('.items').each(function() {\n $(this).addClass('active');\n});\n// jQuery dependency",
"document.querySelectorAll('.items').forEach(el => {\n el.classList.add('active');\n});\n// Native DOM API is sufficient"
))
entries.append(g(
"Not using custom properties (CSS variables)",
".primary { color: #333; }\n.secondary { color: #333; }\n.footer { color: #333; }\n// Repeated values",
":root {\n --color-text: #333;\n}\n.primary, .secondary, .footer {\n color: var(--color-text);\n}\n// Single source of truth, runtime modifiable"
))
# =============================================================================
# NODE.JS / BACKEND (15 entries)
# =============================================================================
entries.append(g(
"Callback-based file read",
"fs.readFile('config.json', 'utf8', (err, data) => {\n if (err) throw err;\n const config = JSON.parse(data);\n});",
"const { readFile } = require('fs/promises');\nasync function loadConfig() {\n const data = await readFile('config.json', 'utf8');\n return JSON.parse(data);\n}"
))
entries.append(g(
"Missing error handling in Express",
"app.get('/api/data', (req, res) => {\n const data = db.query();\n res.json(data);\n});\n// If db.query() throws, unhandled rejection",
"app.get('/api/data', async (req, res, next) => {\n try {\n const data = await db.query();\n res.json(data);\n } catch (err) {\n next(err); // Pass to error handler\n }\n});\n\napp.use((err, req, res, next) => {\n console.error(err);\n res.status(500).json({ error: 'Internal error' });\n});"
))
entries.append(g(
"Process.exit() in server",
"app.listen(3000, () => {\n if (process.env.NODE_ENV === 'test') {\n process.exit(0);\n }\n});",
"const server = app.listen(3000);\n// For tests:\nafter(() => server.close());\n// Don't call process.exit() - it kills pending requests"
))
entries.append(g(
"Not handling unhandledRejection",
"async function handler() {\n await riskyOperation();\n}",
"process.on('unhandledRejection', (reason, promise) => {\n console.error('Unhandled Rejection at:', promise, 'reason:', reason);\n});\n// In Express with async handlers:\napp.use(async (req, res, next) => {\n try { await next(); }\n catch (err) { next(err); }\n});"
))
entries.append(g(
"Blocking event loop in Node",
"const hash = crypto.createHash('md5').update(bigBuffer).digest();\n// Synchronous crypto blocks the event loop",
"// Use async crypto APIs:\nconst hash = await new Promise((resolve, reject) => {\n const hash = crypto.createHash('sha256');\n hash.update(bigBuffer);\n resolve(hash.digest());\n});\n// Or worker thread for heavy computation"
))
entries.append(g(
"Not using environment variables",
"const PORT = 3000;\nconst DB_URL = 'mongodb://localhost/mydb';",
"const PORT = process.env.PORT || 3000;\nconst DB_URL = process.env.DB_URL;\nif (!DB_URL) throw new Error('DB_URL is required');\n// Or with dotenv:\nrequire('dotenv').config();"
))
entries.append(g(
"Memory leak with growing array",
"const requestLog = [];\napp.use((req, res, next) => {\n requestLog.push({ path: req.path, time: Date.now() });\n next();\n});\n// Grows forever",
"const requestLog = [];\nconst MAX_LOGS = 10000;\napp.use((req, res, next) => {\n requestLog.push({ path: req.path, time: Date.now() });\n if (requestLog.length > MAX_LOGS) {\n requestLog.splice(0, requestLog.length - MAX_LOGS);\n }\n next();\n});// Or use a circular buffer / logging library"
))
entries.append(g(
"Not streaming large responses",
"app.get('/download', (req, res) => {\n const data = fs.readFileSync('large-file.zip');\n res.send(data);\n // Loads entire file into memory",
"app.get('/download', (req, res) => {\n const stream = fs.createReadStream('large-file.zip');\n res.setHeader('Content-Type', 'application/zip');\n stream.pipe(res);\n // Streams data without loading into memory\n});"
))
entries.append(g(
"Missing body-parser size limit",
"app.use(express.json());\n// Default: no limit",
"app.use(express.json({ limit: '1mb' }));\napp.use(express.urlencoded({ extended: true, limit: '1mb' }));\n// Prevents DoS via large payloads"
))
entries.append(g(
"Not using ESM modules",
"const express = require('express');\nconst app = express();",
"// package.json: { \"type\": \"module\" }\nimport express from 'express';\nimport { readFile } from 'node:fs/promises';\n// Native ESM - tree-shakeable, standard imports"
))
entries.append(g(
"Process on uncaughtException",
"process.on('uncaughtException', (err) => {\n console.error(err);\n // Server continues in unknown state\n});",
"process.on('uncaughtException', (err) => {\n console.error('Uncaught Exception:', err);\n // Graceful shutdown:\n server.close(() => process.exit(1));\n});\n// Better: fix the root cause, don't catch exceptions"
))
entries.append(g(
"Not using clustering",
"const express = require('express');\nconst app = express();\napp.listen(3000);",
"const cluster = require('cluster');\nconst os = require('os');\nconst numCPUs = os.cpus().length;\n\nif (cluster.isPrimary) {\n for (let i = 0; i < numCPUs; i++) cluster.fork();\n cluster.on('exit', (worker, code) => cluster.fork());\n} else {\n app.listen(3000);\n}"
))
entries.append(g(
"Missing graceful shutdown",
"const server = app.listen(3000);\n// No cleanup on SIGTERM",
"const server = app.listen(3000);\n\nprocess.on('SIGTERM', () => {\n console.log('SIGTERM received. Shutting down...');\n server.close(() => {\n // Close DB connections, finish requests\n process.exit(0);\n });\n});"
))
entries.append(g(
"Not using health check endpoint",
"const app = express();\napp.listen(3000);",
"const app = express();\n\napp.get('/health', (req, res) => {\n res.json({\n status: 'ok',\n uptime: process.uptime(),\n timestamp: Date.now()\n });\n});\n\napp.listen(3000);"
))
entries.append(g(
"Global module state",
"// server.js\nconst db = require('./db');\nconst app = require('./app')(db);\n// Shared state across hot-reloads in dev",
"// Use dependency injection:\nconst createApp = (db) => {\n const app = express();\n app.use('/api', apiRoutes(db));\n return app;\n};\n// Fresh state per test / reload"
))
# =============================================================================
# TESTING (10 entries)
# =============================================================================
entries.append(g(
"Testing implementation details",
"describe('Counter', () => {\n it('increments', () => {\n const counter = new Counter();\n counter.inc();\n expect(counter._value).toBe(1); // Testing private property\n });\n});",
"describe('Counter', () => {\n it('increments', () => {\n const counter = new Counter();\n counter.inc();\n expect(counter.getValue()).toBe(1); // Test public API\n });\n});"
))
entries.append(g(
"Async test without await",
"it('fetches user', () => {\n const user = fetchUser(1); // Returns Promise\n expect(user.name).toBe('Alice'); // user is Promise, not object\n});",
"it('fetches user', async () => {\n const user = await fetchUser(1);\n expect(user.name).toBe('Alice');\n});"
))
entries.append(g(
"Testing with real HTTP server",
"it('creates user', async () => {\n const res = await fetch('http://localhost:3000/users', {\n method: 'POST',\n body: JSON.stringify({ name: 'Alice' })\n });\n // Hits real server - slow, fragile",
"import { request } from 'supertest';\nimport app from '../app';\n\nit('creates user', async () => {\n const res = await request(app)\n .post('/users')\n .send({ name: 'Alice' })\n .expect(201);\n expect(res.body.name).toBe('Alice');\n});"
))
entries.append(g(
"Missing test cleanup",
"beforeEach(() => {\n fs.mkdirSync('/tmp/test');\n});\n// No cleanup - stale files between tests",
"beforeEach(() => {\n fs.mkdirSync('/tmp/test', { recursive: true });\n});\nafterEach(() => {\n fs.rmSync('/tmp/test', { recursive: true, force: true });\n});"
))
entries.append(g(
"Mocking the wrong thing",
"jest.mock('fs');\n// Mocking the entire module",
"jest.mock('fs', () => ({\n ...jest.requireActual('fs'),\n readFile: jest.fn().mockResolvedValue('{\"key\":\"value\"}')\n}));\n// Only mock what you need"
))
entries.append(g(
"Flaky test with randomness",
"it('generates token', () => {\n const token = generateToken();\n expect(token.length).toBe(32);\n expect(token).toBe('abc...'); // Hardcoded expected value\n});",
"it('generates token', () => {\n const token = generateToken();\n expect(token).toHaveLength(32);\n expect(token).toMatch(/^[a-zA-Z0-9]+$/);\n // Test properties, not exact values\n});"
))
entries.append(g(
"Missing snapshot test for UI",
"it('renders component', () => {\n render(<UserCard user={user} />);\n expect(screen.getByText('Alice')).toBeInTheDocument();\n expect(screen.getByText('alice@example.com')).toBeInTheDocument();\n // Breaks when layout changes",
"it('renders user card', () => {\n const { container } = render(<UserCard user={user} />);\n expect(container).toMatchSnapshot();\n // Captures full render, catches unintended changes\n});"
))
entries.append(g(
"Testing without isolation",
"it('creates user', () => {\n db.insert({ name: 'Alice' });\n});\nit('lists users', () => {\n const users = db.findAll();\n expect(users).toHaveLength(1); // Depends on previous test\n});",
"beforeEach(() => db.clear());\nafterEach(() => db.clear());\n\nit('creates user', () => {\n db.insert({ name: 'Alice' });\n});\nit('lists users', () => {\n db.insert({ name: 'Alice' });\n const users = db.findAll();\n expect(users).toHaveLength(1); // Independent\n});"
))
entries.append(g(
"Using console.log for debugging in tests",
"it('should work', () => {\n const result = process(input);\n console.log(result); // Debug output\n expect(result).toBe(true);\n});",
"it('should work', () => {\n const result = process(input);\n expect(result).toBe(true);\n});\n// Use jest.spyOn for inspecting behavior:\nconst spy = jest.spyOn(module, 'func');\n// Spy is auto-restored after test"
))
entries.append(g(
"Not testing error cases",
"it('parses JSON', () => {\n expect(parse('{}')).toEqual({});\n // Only tests the happy path\n});",
"describe('parse', () => {\n it('parses valid JSON', () => {\n expect(parse('{}')).toEqual({});\n });\n it('throws on invalid input', () => {\n expect(() => parse('invalid')).toThrow(SyntaxError);\n });\n it('handles empty string', () => {\n expect(() => parse('')).toThrow(SyntaxError);\n });\n});"
))
# =============================================================================
# BUILD TOOLS & MODULES (10 entries)
# =============================================================================
entries.append(g(
"Missing .gitignore",
"node_modules/\npackage.json\napp.js",
"# .gitignore\nnode_modules/\n*.log\n.env\n.DS_Store\ndist/\ncoverage/\n*.tgz\n# Never commit node_modules, env files, or build artifacts"
))
entries.append(g(
"Not using package.json scripts",
"node build.js\nnpx jest\nnpx eslint src/",
"# package.json\n{\n \"scripts\": {\n \"build\": \"node build.js\",\n \"test\": \"jest\",\n \"lint\": \"eslint src/\",\n \"dev\": \"nodemon src/index.js\"\n }\n}"
))
entries.append(g(
"Missing TypeScript config",
"// Using JSDoc comments for types\n// No type checking",
"# tsconfig.json\n{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"ESNext\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"outDir\": \"dist\",\n \"rootDir\": \"src\",\n \"skipLibCheck\": true\n },\n \"include\": [\"src\"]\n}"
))
entries.append(g(
"Not using source maps",
"webpack config with minification only\n// Production errors show minified code",
"// webpack.config.js\nmodule.exports = {\n devtool: 'source-map', // For production\n // 'eval-source-map' for dev (faster)\n // 'inline-source-map' embeds in bundle\n mode: 'production'\n}"
))
entries.append(g(
"Missing .eslintrc",
"// No linting",
"# .eslintrc.json\n{\n \"env\": { \"browser\": true, \"es2022\": true, \"node\": true },\n \"extends\": \"eslint:recommended\",\n \"rules\": {\n \"no-unused-vars\": \"error\",\n \"no-console\": \"warn\",\n \"prefer-const\": \"error\"\n }\n}"
))
entries.append(g(
"Using CommonJS in ESM project",
"// package.json has \"type\": \"module\"\nconst fs = require('fs'); // Error!",
"// package.json has \"type\": \"module\"\nimport fs from 'node:fs';\nimport { readFile } from 'node:fs/promises';\n// Or use createRequire for CJS-only packages:\nimport { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\nconst lodash = require('lodash');"
))
entries.append(g(
"Not bundling for browser",
"<script src=\"node_modules/lodash/lodash.js\"></script>\n<script src=\"app.js\"></script>\n// Individual script tags, no module resolution",
"// Use a bundler:\n// npx esbuild app.js --bundle --outfile=dist/bundle.js\n// Or vite:\n// npx vite build\n// Bundling: resolves imports, minifies, tree-shakes"
))
entries.append(g(
"Missing dependency pinning",
"{\n \"dependencies\": {\n \"express\": \"^4.18.0\"\n }\n}",
"{\n \"dependencies\": {\n \"express\": \"4.18.2\"\n },\n \"devDependencies\": {\n \"jest\": \"29.7.0\",\n \"eslint\": \"8.56.0\"\n }\n}\n# Pin versions for reproducible builds\n# Use package-lock.json / yarn.lock"
))
entries.append(g(
"Not using tree shaking",
"// Webpack config missing sideEffects flag",
"// package.json\n{\n \"sideEffects\": false\n}\n// Tells bundler all modules are pure - safe to tree-shake\n// Or list files with side effects:\n// \"sideEffects\": [\"*.css\", \"./src/polyfills.js\"]"
))
entries.append(g(
"Missing pre/post build scripts",
"{\n \"scripts\": {\n \"build\": \"webpack\"\n }\n}",
"{\n \"scripts\": {\n \"prebuild\": \"npm run lint\",\n \"build\": \"webpack --mode production\",\n \"postbuild\": \"npm run test\"\n }\n}\n# Pre/post scripts run automatically"
))
# =============================================================================
# FUNCTIONAL PATTERN / IMMUTABILITY (10 entries)
# =============================================================================
entries.append(g(
"Mutating array in-place",
"const items = [1, 2, 3];\nitems.push(4);\nitems.filter(x => x > 2);\n// Original modified",
"const items = [1, 2, 3];\nconst withNew = [...items, 4];\nconst filtered = items.filter(x => x > 2);\n// Original unchanged - predictable state"
))
entries.append(g(
"Mutating object in-place",
"const config = { host: 'localhost', port: 3000 };\nconfig.port = 8080;\n// Side effect",
"const config = { host: 'localhost', port: 3000 };\nconst updated = { ...config, port: 8080 };\n// New object, original unchanged"
))
entries.append(g(
"Side effects in pure function",
"function calculateTotal(items) {\n const total = items.reduce((s, i) => s + i.price, 0);\n logTransaction(total); // side effect\n return total;\n}",
"function calculateTotal(items) {\n return items.reduce((s, i) => s + i.price, 0);\n}\n// Separate pure calculation from side effects:\nconst total = calculateTotal(items);\nlogTransaction(total);"
))
entries.append(g(
"Not using Array.prototype methods",
"const result = [];\nfor (let i = 0; i < arr.length; i++) {\n if (arr[i] > 0) result.push(arr[i] * 2);\n}",
"const result = arr.filter(x => x > 0).map(x => x * 2);\n// Composable, declarative, no mutation"
))
entries.append(g(
"Using Object.assign for deep merge",
"const merged = Object.assign({}, defaults, overrides);\n// Shallow merge - nested objects replaced",
"// Deep merge utility:\nfunction deepMerge(target, ...sources) {\n return sources.reduce((acc, source) => {\n for (const key of Object.keys(source)) {\n if (source[key] && typeof source[key] === 'object') {\n acc[key] = deepMerge(acc[key] || {}, source[key]);\n } else {\n acc[key] = source[key];\n }\n }\n return acc;\n }, { ...target });\n}"
))
entries.append(g(
"Missing immutability helper",
"state.items[index].name = newName;\n// Direct mutation of nested state",
"const newState = {\n ...state,\n items: state.items.map((item, i) =>\n i === index ? { ...item, name: newName } : item\n )\n};\n// Or use Immer for simpler mutable-style updates"
))
entries.append(g(
"Function without default parameters",
"function createUrl(host, port, path) {\n host = host || 'localhost';\n port = port || 80;\n path = path || '/';\n return `http://${host}:${port}${path}`;\n}",
"function createUrl(host = 'localhost', port = 80, path = '/') {\n return `http://${host}:${port}${path}`;\n}\n// Default parameters handle undefined explicitly"
))
entries.append(g(
"Not using curry for partial application",
"function add(a, b) { return a + b; }\nconst add5 = (x) => add(5, x);\nconst add10 = (x) => add(10, x);",
"const add = (a) => (b) => a + b;\nconst add5 = add(5);\nconst add10 = add(10);\n// Curried functions enable partial application"
))
entries.append(g(
"Using reduce for multiple operations",
"const doubled = arr.map(x => x * 2);\nconst filtered = doubled.filter(x => x > 10);\nconst summed = filtered.reduce((a, b) => a + b, 0);\n// Multiple array traversals",
"// Single traversal:\nconst summed = arr.reduce((acc, x) => {\n const doubled = x * 2;\n if (doubled > 10) acc += doubled;\n return acc;\n}, 0);\n// Or use pipeline for readability:\nimport pipe from 'lodash/fp/flow';\nconst pipeline = pipe(map(double), filter(gt(10)), sum);"
))
entries.append(g(
"Not using pipeline composition",
"const result = capitalize(trim(toLower(input)));",
"// Right-to-left composition (lodash fp):\nimport { flow, toLower, trim, capitalize } from 'lodash/fp';\nconst normalize = flow(toLower, trim, capitalize);\nconst result = normalize(input);\n// Left-to-right (native):\nconst normalize = pipe(toLower, trim, capitalize);"
))
# =============================================================================
# SAVE
# =============================================================================
random.seed(42)
random.shuffle(entries)
os.makedirs(BASE, exist_ok=True)
out = BASE / "dev_finetuning_javascript.json"
with open(out, 'w') as f:
json.dump(entries, f, indent=2)
# Count by tag category
categories = {}
for e in entries:
for tag in e['tags']:
if tag != 'javascript':
categories[tag] = categories.get(tag, 0) + 1
print(f"Total entries: {len(entries)}")
print(f"Categories:")
for cat, count in sorted(categories.items(), key=lambda x: -x[1]):
print(f" {cat}: {count}")
print(f"Saved: {out}")