-
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.
frogasaurus is now built with itself
- Loading branch information
Showing
9 changed files
with
668 additions
and
299 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import { capitalise } from "./string.js" | ||
import { readDirectory, writeFile } from "./file.js" | ||
import { parseExport, parseImport } from "./parse.js" | ||
|
||
const HEADER_TITLE_LINES = [ | ||
`//=============//`, | ||
`// FROGASAURUS //`, | ||
`//=============//`, | ||
``, | ||
] | ||
const SOURCE_TITLE_LINES = [ | ||
``, | ||
`//========//`, | ||
`// SOURCE //`, | ||
`//========//`, | ||
``, | ||
] | ||
|
||
const FOOTER_TITLE_LINES = [ | ||
``, | ||
``, | ||
`//=========//`, | ||
`// EXPORTS //`, | ||
`//=========//`, | ||
``, | ||
] | ||
|
||
const MAIN_TITLE_LINES = [ | ||
``, | ||
``, | ||
`//======//`, | ||
`// MAIN //`, | ||
`//======//`, | ||
``, | ||
] | ||
|
||
const HEADER_TITLE = HEADER_TITLE_LINES.join("\n") | ||
const SOURCE_TITLE = SOURCE_TITLE_LINES.join("\n") | ||
const FOOTER_TITLE = FOOTER_TITLE_LINES.join("\n") | ||
const MAIN_TITLE = MAIN_TITLE_LINES.join("\n") | ||
|
||
const transpileSource = (source, name, path, projectName) => { | ||
|
||
const fileConstantName = `${capitalise(projectName)}Frogasaurus` | ||
const lines = source.split("\n") | ||
|
||
const strippedLines = [] | ||
const exportResults = [] | ||
const importResults = [] | ||
|
||
for (let i = 0; i < lines.length; i++) { | ||
const line = lines[i] | ||
|
||
const metadata = {fileName: name, lineNumber: i} | ||
const exportResult = parseExport(line, metadata) | ||
if (exportResult.success) { | ||
strippedLines.push(`\t${exportResult.margin}${exportResult.tail}`) | ||
exportResults.push(exportResult) | ||
continue | ||
} | ||
|
||
const importResult = parseImport(line, metadata) | ||
if (importResult.success) { | ||
importResults.push(importResult) | ||
continue | ||
} | ||
|
||
strippedLines.push(`\t${line}`) | ||
} | ||
|
||
const exportLines = [] | ||
for (const exportResult of exportResults) { | ||
exportLines.push(`\t\t${fileConstantName}["${path}"].${exportResult.name} = ${exportResult.name}`) | ||
} | ||
const exportSource = exportLines.join("\n") | ||
const innerSource = `\t\t${fileConstantName}["${path}"] = {}\n\t${strippedLines.join("\n\t")}\n\n${exportSource}` | ||
const scopedSource = `\t//====== ${path} ======\n\t{\n${innerSource}\n\t}` | ||
|
||
return {success: true, output: scopedSource, exportResults, importResults, path} | ||
} | ||
|
||
export const build = async (projectName) => { | ||
|
||
console.clear() | ||
|
||
const fileConstantName = `${capitalise(projectName)}Frogasaurus` | ||
|
||
const entries = await readDirectory("source") | ||
const sourceResults = entries.map(entry => transpileSource(entry.source, entry.name, entry.path, projectName)) | ||
if (sourceResults.some(result => !result.success)) { | ||
console.log("%cFailed build", RED) | ||
return | ||
} | ||
|
||
// Check for duplicate export names | ||
const exportNames = new Set() | ||
for (const result of sourceResults) { | ||
for (const exportResult of result.exportResults) { | ||
if (exportNames.has(exportResult.name)) { | ||
console.log("%cSorry, you can't have multiple exports with the same name", RED) | ||
console.log("%cThis is because Frogasaurus mashes all your exports together <3", RED) | ||
console.log(`${result.path}`) | ||
console.log(`\n\t${exportResult.name}\n`) | ||
console.log("%cFailed build", RED) | ||
return | ||
} | ||
exportNames.add(exportResult.name) | ||
} | ||
} | ||
|
||
// Check for 'main' function export | ||
let mainFuncDenoSource = "" | ||
for (const result of sourceResults) { | ||
for (const exportResult of result.exportResults) { | ||
if (exportResult.name === "main") { | ||
mainFuncDenoSource = `${MAIN_TITLE}${fileConstantName}["${result.path}"].main(...Deno.args)` | ||
} | ||
} | ||
} | ||
|
||
const exportFooterLines = sourceResults.map(result => `export const { ${result.exportResults.map(exportResult => exportResult.name).join(", ")} } = ${fileConstantName}["${result.path}"]`) | ||
const exportFooterSource = exportFooterLines.join("\n") | ||
|
||
const globalFooterLines = [ | ||
`const ${capitalise(projectName)} = {`, | ||
] | ||
|
||
for (const sourceResult of sourceResults) { | ||
for (const exportResult of sourceResult.exportResults) { | ||
globalFooterLines.push(`\t${exportResult.name}: ${fileConstantName}["${sourceResult.path}"].${exportResult.name},`) | ||
} | ||
} | ||
|
||
globalFooterLines.push(`}`) | ||
const globalFooterSource = globalFooterLines.join("\n") | ||
|
||
const importLists = new Map() | ||
for (const sourceResult of sourceResults) { | ||
|
||
for (const importResult of sourceResult.importResults) { | ||
if (importLists.get(importResult.path) === undefined) { | ||
importLists.set(importResult.path, new Set()) | ||
} | ||
|
||
const importList = importLists.get(importResult.path) | ||
for (const name of importResult.names) { | ||
importList.add(name) | ||
} | ||
} | ||
} | ||
|
||
const importFooterLines = [] | ||
for (const [path, importList] of importLists.entries()) { | ||
importFooterLines.push(`\tconst { ${[...importList.values()].join(", ")} } = ${fileConstantName}["${path}"]`) | ||
} | ||
const importFooterSource = "\n\n" + importFooterLines.join("\n") | ||
|
||
const transpiledSource = "{\n" + sourceResults.map(result => result.output).join("\n\n") + importFooterSource + "\n\n}" | ||
|
||
const headerLine = `const ${fileConstantName} = {}\n` | ||
|
||
const importSource = HEADER_TITLE + headerLine + SOURCE_TITLE + transpiledSource + FOOTER_TITLE + exportFooterSource + "\n\nexport " + globalFooterSource | ||
const embedSource = HEADER_TITLE + headerLine + SOURCE_TITLE + transpiledSource + FOOTER_TITLE + globalFooterSource | ||
const standaloneSource = HEADER_TITLE + headerLine + SOURCE_TITLE + transpiledSource + mainFuncDenoSource | ||
|
||
await writeFile(`${projectName.toLowerCase()}-import.js`, importSource) | ||
await writeFile(`${projectName.toLowerCase()}-embed.js`, embedSource) | ||
await writeFile(`${projectName.toLowerCase()}-standalone.js`, standaloneSource) | ||
|
||
console.log("%cFinished build!", YELLOW) | ||
console.log("Waiting for file changes...") | ||
} |
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,4 @@ | ||
export const BLUE = "color: rgb(0, 128, 255)" | ||
export const RED = "color: rgb(255, 70, 70)" | ||
export const GREEN = "color: rgb(0, 255, 128)" | ||
export const YELLOW = "color: #ffcc46" |
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,39 @@ | ||
import { BLUE, RED, GREEN, YELLOW } from "./colour.js" | ||
|
||
export const readFile = async (path) => { | ||
console.log(`%cReading File: ${path}`, BLUE) | ||
const source = await Deno.readTextFile(path) | ||
return source | ||
} | ||
|
||
export const writeFile = async (path, source) => { | ||
console.log(`%cWriting File: ${path}`, GREEN) | ||
return await Deno.writeTextFile(path, source) | ||
} | ||
|
||
export const readDirectory = async (path) => { | ||
|
||
const entries = [] | ||
|
||
for await (const entry of Deno.readDir(path)) { | ||
|
||
const {name} = entry | ||
const entryPath = `${path}/${name}` | ||
|
||
// Go deeper if it's a directory | ||
if (entry.isDirectory) { | ||
entries.push(...await readDirectory(entryPath)) | ||
continue | ||
} | ||
|
||
// Make sure it's a javascript file | ||
const [head, extension] = name.split(".") | ||
if (extension !== "js") continue | ||
|
||
const source = await readFile(entryPath) | ||
entries.push({source, name, path: "./" + entryPath.slice("source/".length)}) | ||
|
||
} | ||
|
||
return entries | ||
} |
This file was deleted.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { build } from "./build.js" | ||
|
||
export const main = async () => { | ||
const directory = Deno.cwd() | ||
const directoryParts = directory.split("\\") | ||
const projectName = directoryParts[directoryParts.length-1] | ||
|
||
await Deno.permissions.request({name: "read", path: "."}) | ||
await Deno.permissions.request({name: "write", path: "."}) | ||
|
||
await build(projectName) | ||
|
||
const watcher = Deno.watchFs("./source") | ||
for await (const event of watcher) { | ||
await build(projectName) | ||
} | ||
} |
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,71 @@ | ||
import { trimStart } from "./string.js" | ||
|
||
export const getConstName = (line) => { | ||
for (let i = "const ".length; i < line.length; i++) { | ||
const char = line[i] | ||
if (char === " " || char === " " || char === "=") { | ||
return line.slice("const ".length, i) | ||
} | ||
} | ||
} | ||
|
||
export const getImportNames = (line) => { | ||
const [head, tail] = line.split("{") | ||
const [inner] = tail.split("}") | ||
const names = inner.split(",").map(name => name.trim()) | ||
return names | ||
} | ||
|
||
export const getImportPath = (line) => { | ||
const [head, tail] = line.split(" from ") | ||
const [start, path, end] = tail.split(`"`) | ||
return path | ||
} | ||
|
||
export const parseExport = (line, {fileName, lineNumber}) => { | ||
|
||
const trim = trimStart(line) | ||
const {trimmed, trimming} = trim | ||
const exportSnippet = trimmed.slice(0, "export ".length) | ||
if (exportSnippet !== "export ") return {success: false} | ||
|
||
const trimLength = line.length - trimmed.length | ||
const tail = line.slice("export ".length + trimLength) | ||
|
||
const constSnippet = tail.slice(0, "const ".length) | ||
if (constSnippet !== "const ") { | ||
console.log(`%cError: Sorry, Frogasaurus only supports exports when you write 'const' immediately after.\n%c${fileName}:${lineNumber}\n\n ${line}\n`, RED, "") | ||
return {success: false} | ||
} | ||
|
||
const name = getConstName(tail) | ||
return { | ||
success: true, | ||
name, | ||
margin: trimming, | ||
tail, | ||
} | ||
} | ||
|
||
export const parseImport = (line, {fileName, lineNumber}) => { | ||
const trim = trimStart(line) | ||
const {trimmed, trimming} = trim | ||
const importSnippet = trimmed.slice(0, "import ".length) | ||
if (importSnippet !== "import ") return {success: false} | ||
|
||
const trimLength = line.length - trimmed.length | ||
const tail = line.slice("import ".length + trimLength) | ||
|
||
const path = getImportPath(tail) | ||
const names = getImportNames(tail) | ||
const output = `{ ${names.join(", ")} }` | ||
|
||
return { | ||
success: true, | ||
names, | ||
path, | ||
output, | ||
margin: trimming, | ||
tail, | ||
} | ||
} |
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,15 @@ | ||
export const trimStart = (string) => { | ||
for (let i = 0; i < string.length; i++) { | ||
const char = string[i] | ||
if (char === " " || char === " ") continue | ||
const trimmed = string.slice(i) | ||
const trimming = string.slice(0, i) | ||
return {trimmed, trimming} | ||
} | ||
return {trimmed: "", trimming: ""} | ||
} | ||
|
||
export const capitalise = (string) => { | ||
const [head, ...tail] = string.split("") | ||
return `${head.toUpperCase()}${tail.join("")}` | ||
} |