-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
238 additions
and
106 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
|
||
import js from "@eslint/js"; | ||
import globals from "globals"; | ||
|
||
export default [ | ||
js.configs.recommended, | ||
{ | ||
languageOptions: { | ||
parserOptions: { | ||
ecmaVersion: 2020, | ||
}, | ||
globals: { | ||
...globals.node, | ||
...globals.es2020, | ||
} | ||
}, | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
const { createCompletion } = require('gpt4all'); | ||
|
||
const systemPromptTemplate = ` | ||
You are an expert web developer, who conforms | ||
to the latest best practices, and has access to all knowledge necessary | ||
to complete the tasks I assign to you. Please think carefully before | ||
giving me an answer, and check your work for correctness. Consider | ||
accessibility, seo, and performance when designing solutions, and take | ||
as much time as you need to come to a good solution. | ||
`; | ||
|
||
const generateFiles = async ({ model, prompt, initialCode, codeReview }) => { | ||
const getInitialCode = async (model, prompt, lang) => { | ||
const response = await createCompletion(model, [ | ||
{ role: 'user', content: initialCode(prompt, lang) } | ||
], { systemPromptTemplate }); | ||
return response.choices[0].message.content; | ||
} | ||
|
||
const doCodeReview = async (model, code, lang) => { | ||
const response = await createCompletion(model, [ | ||
{ role: 'user', content: codeReview(lang, code) } | ||
], { systemPromptTemplate }); | ||
return response.choices[0].message.content; | ||
}; | ||
|
||
const htmlResponse = await getInitialCode(model, prompt, 'html'); | ||
const cssResponse = await getInitialCode(model, prompt, 'css'); | ||
const jsResponse = await getInitialCode(model, prompt, 'javascript'); | ||
|
||
const reviewedHtml = await doCodeReview(model, htmlResponse, 'html'); | ||
const reviewedCss = await doCodeReview(model, cssResponse, 'css'); | ||
const reviewedJs = await doCodeReview(model, jsResponse, 'javascript'); | ||
|
||
return { | ||
html: reviewedHtml, | ||
js: reviewedJs, | ||
css: reviewedCss, | ||
}; | ||
} | ||
|
||
module.exports = generateFiles; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
const path = require('node:path'); | ||
const fs = require('fs/promises'); | ||
const { JSDOM } = require('jsdom'); | ||
const generateFiles = require('./generateFiles'); | ||
|
||
const generatePageFromPrompt = async ({ prompt, model, outputPath, verbose, initialCode, codeReview }) => { | ||
const log = verbose ? console.log : () => {}; | ||
const pageName = path.basename(outputPath, '.html'); | ||
const dirName = path.dirname(outputPath); | ||
|
||
log(`Generating page ${dirName}/${pageName} from prompt`); | ||
|
||
// Call gpt4all with the prompt and get the statics | ||
const { html, css, js } = await generateFiles({ model, prompt, initialCode, codeReview }); | ||
|
||
log(`Generated html, css, and js for ${dirName}/${pageName}`); | ||
|
||
// Parse the html into a DOM for us to operate on. | ||
// I checked to make sure that jsdom doesn't execute script | ||
// tags, but if it does, we'll need to find a way to sandbox | ||
// it, because we can't have ai-generated code running on our | ||
// build boxes. | ||
const dom = new JSDOM(html); | ||
const document = dom.window.document; | ||
|
||
// Generate the css and js files | ||
const jsFileName = path.join(dirName, `${pageName}.js`); | ||
const cssFileName = path.join(dirName, `${pageName}.css`); | ||
|
||
await Promise.all([ | ||
fs.writeFile(jsFileName, js), | ||
fs.writeFile(cssFileName, css), | ||
]); | ||
|
||
log(`Wrote ${jsFileName} and ${cssFileName}`); | ||
|
||
// Add the script tag to the body | ||
const scriptTag = document.createElement('script'); | ||
scriptTag.setAttribute('src', `${jsFileName}`); | ||
document.body.appendChild(scriptTag); | ||
|
||
// Add the style tag to the body | ||
const styleTag = document.createElement('style'); | ||
styleTag.setAttribute('type', 'text/css'); | ||
styleTag.setAttribute('rel', 'stylesheet'); | ||
styleTag.setAttribute('href', `${cssFileName}`); | ||
document.body.appendChild(styleTag); | ||
|
||
log(`Added script and style tags to ${pageName}`); | ||
|
||
return `<!DOCTYPE html>${document.documentElement.outerHTML}`; | ||
}; | ||
|
||
module.exports = generatePageFromPrompt; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const { afterEach, describe, it, mock } = require('node:test'); | ||
const assert = require('node:assert'); | ||
|
||
const gpt4all = require('gpt4all'); | ||
mock.method(gpt4all, 'createCompletion', async () => ({ | ||
choices: [{ message: { content: 'mock content' }}], | ||
})); | ||
|
||
const generateFiles = require('../src/generateFiles'); | ||
|
||
describe('generateFiles', () => { | ||
afterEach(() => { | ||
mock.reset(); | ||
}); | ||
|
||
it('should call createCompletion six times', async () => { | ||
await generateFiles({ model: {}, prompt: 'prompt', initialCode: mock.fn(), codeReview: mock.fn() }); | ||
assert.strictEqual(gpt4all.createCompletion.mock.calls.length, 6); | ||
}); | ||
|
||
it('should call initialCode three times', async () => { | ||
const initialCode = mock.fn(); | ||
await generateFiles({ model: {}, prompt: 'prompt', initialCode, codeReview: mock.fn() }); | ||
assert.strictEqual(initialCode.mock.calls.length, 3); | ||
}); | ||
|
||
it('should call codeReview three times', async () => { | ||
const codeReview = mock.fn(); | ||
await generateFiles({ model: {}, prompt: 'prompt', initialCode: mock.fn(), codeReview }); | ||
assert.strictEqual(codeReview.mock.calls.length, 3); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
const { afterEach, describe, it, mock } = require('node:test'); | ||
const assert = require('node:assert'); | ||
|
||
const fs = require('fs/promises'); | ||
mock.method(fs, 'writeFile', async () => ({})); | ||
const gpt4all = require('gpt4all'); | ||
mock.method(gpt4all, 'createCompletion', async () => ({ | ||
choices: [{ message: { content: 'mock content' }}], | ||
})); | ||
|
||
|
||
describe('generatePageFromPrompt', () => { | ||
afterEach(() => { | ||
mock.reset(); | ||
}); | ||
|
||
it('should call createChatCompletion six times', async () => { | ||
const generatePageFromPrompt = require('../src/generatePageFromPrompt'); | ||
const initialCode = mock.fn(); | ||
const codeReview = mock.fn(); | ||
await generatePageFromPrompt({ | ||
prompt: 'prompt', | ||
client: {}, | ||
outputPath: 'outputPath', | ||
verbose: false, | ||
initialCode, | ||
codeReview | ||
}); | ||
assert.strictEqual(gpt4all.createCompletion.mock.calls.length, 6); | ||
assert.strictEqual(initialCode.mock.calls.length, 3); | ||
assert.strictEqual(codeReview.mock.calls.length, 3); | ||
}); | ||
|
||
it('should call writeFile three times', async () => { | ||
const generatePageFromPrompt = require('../src/generatePageFromPrompt'); | ||
const initialCode = mock.fn(); | ||
const codeReview = mock.fn(); | ||
await generatePageFromPrompt({ | ||
prompt: 'prompt', | ||
client: {}, | ||
outputPath: 'outputPath', | ||
verbose: false, | ||
initialCode, | ||
codeReview | ||
}); | ||
assert.strictEqual(fs.promises.writeFile.mock.calls.length, 3); | ||
}); | ||
|
||
}); |
Oops, something went wrong.