computer-scienceangular-roadmapbackend-roadmapblockchain-roadmapdba-roadmapdeveloper-roadmapdevops-roadmapfrontend-roadmapgo-roadmaphactoberfestjava-roadmapjavascript-roadmapnodejs-roadmappython-roadmapqa-roadmapreact-roadmaproadmapstudy-planvue-roadmapweb3-roadmap
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
4.8 KiB
173 lines
4.8 KiB
const fs = require('fs'); |
|
const path = require('path'); |
|
|
|
const OPEN_AI_API_KEY = process.env.OPEN_AI_API_KEY; |
|
const ALL_BEST_PRACTICES_DIR = path.join( |
|
__dirname, |
|
'../src/data/best-practices' |
|
); |
|
const BEST_PRACTICE_JSON_DIR = path.join( |
|
__dirname, |
|
'../public/jsons/best-practices' |
|
); |
|
|
|
const bestPracticeId = process.argv[2]; |
|
const bestPracticeTitle = bestPracticeId.replace(/-/g, ' '); |
|
|
|
const allowedBestPracticeIds = fs.readdirSync(ALL_BEST_PRACTICES_DIR); |
|
if (!bestPracticeId) { |
|
console.error('bestPracticeId is required'); |
|
process.exit(1); |
|
} |
|
|
|
if (!allowedBestPracticeIds.includes(bestPracticeId)) { |
|
console.error(`Invalid bestPractice key ${bestPracticeId}`); |
|
console.error(`Allowed keys are ${allowedBestPracticeIds.join(', ')}`); |
|
process.exit(1); |
|
} |
|
|
|
const BEST_PRACTICE_CONTENT_DIR = path.join( |
|
ALL_BEST_PRACTICES_DIR, |
|
bestPracticeId, |
|
'content' |
|
); |
|
const { Configuration, OpenAIApi } = require('openai'); |
|
const configuration = new Configuration({ |
|
apiKey: OPEN_AI_API_KEY, |
|
}); |
|
|
|
const openai = new OpenAIApi(configuration); |
|
|
|
function getFilesInFolder(folderPath, fileList = {}) { |
|
const files = fs.readdirSync(folderPath); |
|
|
|
files.forEach((file) => { |
|
const filePath = path.join(folderPath, file); |
|
const stats = fs.statSync(filePath); |
|
|
|
if (stats.isDirectory()) { |
|
getFilesInFolder(filePath, fileList); |
|
} else if (stats.isFile()) { |
|
const fileUrl = filePath |
|
.replace(BEST_PRACTICE_CONTENT_DIR, '') // Remove the content folder |
|
.replace(/\/\d+-/g, '/') // Remove ordering info `/101-ecosystem` |
|
.replace(/\/index\.md$/, '') // Make the `/index.md` to become the parent folder only |
|
.replace(/\.md$/, ''); // Remove `.md` from the end of file |
|
|
|
fileList[fileUrl] = filePath; |
|
} |
|
}); |
|
|
|
return fileList; |
|
} |
|
|
|
function writeTopicContent(topicTitle) { |
|
let prompt = `I am reading a guide that has best practices about "${bestPracticeTitle}". I want to know more about "${topicTitle}". Write me a brief introductory paragraph about this and some tips on how I make sure of this? Behave as if you are the author of the guide.`; |
|
|
|
console.log(`Generating '${topicTitle}'...`); |
|
|
|
return new Promise((resolve, reject) => { |
|
openai |
|
.createChatCompletion({ |
|
model: 'gpt-4', |
|
messages: [ |
|
{ |
|
role: 'user', |
|
content: prompt, |
|
}, |
|
], |
|
}) |
|
.then((response) => { |
|
const article = response.data.choices[0].message.content; |
|
|
|
resolve(article); |
|
}) |
|
.catch((err) => { |
|
reject(err); |
|
}); |
|
}); |
|
} |
|
|
|
async function writeFileForGroup(group, topicUrlToPathMapping) { |
|
const topicId = group?.properties?.controlName; |
|
const topicTitle = group?.children?.controls?.control?.find( |
|
(control) => control?.typeID === 'Label' |
|
)?.properties?.text; |
|
const currTopicUrl = `/${topicId}`; |
|
if (currTopicUrl.startsWith('/check:')) { |
|
return; |
|
} |
|
|
|
const contentFilePath = topicUrlToPathMapping[currTopicUrl]; |
|
|
|
if (!contentFilePath) { |
|
console.log(`Missing file for: ${currTopicUrl}`); |
|
process.exit(0); |
|
return; |
|
} |
|
|
|
const currentFileContent = fs.readFileSync(contentFilePath, 'utf8'); |
|
const isFileEmpty = currentFileContent.replace(/^#.+/, ``).trim() === ''; |
|
|
|
if (!isFileEmpty) { |
|
console.log(`Ignoring ${topicId}. Not empty.`); |
|
return; |
|
} |
|
|
|
let newFileContent = `# ${topicTitle}`; |
|
|
|
if (!OPEN_AI_API_KEY) { |
|
console.log(`Writing ${topicId}..`); |
|
|
|
fs.writeFileSync(contentFilePath, newFileContent, 'utf8'); |
|
return; |
|
} |
|
|
|
const topicContent = await writeTopicContent(topicTitle); |
|
newFileContent += `\n\n${topicContent}`; |
|
|
|
console.log(`Writing ${topicId}..`); |
|
fs.writeFileSync(contentFilePath, newFileContent, 'utf8'); |
|
|
|
// console.log(currentFileContent); |
|
// console.log(currTopicUrl); |
|
// console.log(topicTitle); |
|
// console.log(topicUrlToPathMapping[currTopicUrl]); |
|
} |
|
|
|
async function run() { |
|
const topicUrlToPathMapping = getFilesInFolder(BEST_PRACTICE_CONTENT_DIR); |
|
|
|
const bestPracticeJson = require(path.join( |
|
BEST_PRACTICE_JSON_DIR, |
|
`${bestPracticeId}.json` |
|
)); |
|
const groups = bestPracticeJson?.mockup?.controls?.control?.filter( |
|
(control) => |
|
control.typeID === '__group__' && |
|
!control.properties?.controlName?.startsWith('ext_link') |
|
); |
|
|
|
if (!OPEN_AI_API_KEY) { |
|
console.log('----------------------------------------'); |
|
console.log('OPEN_AI_API_KEY not found. Skipping openai api calls...'); |
|
console.log('----------------------------------------'); |
|
} |
|
|
|
const writePromises = []; |
|
for (let group of groups) { |
|
writePromises.push(writeFileForGroup(group, topicUrlToPathMapping)); |
|
} |
|
|
|
console.log('Waiting for all files to be written...'); |
|
await Promise.all(writePromises); |
|
} |
|
|
|
run() |
|
.then(() => { |
|
console.log('Done'); |
|
}) |
|
.catch((err) => { |
|
console.error(err); |
|
process.exit(1); |
|
});
|
|
|