Skip to content

Commit

Permalink
Refactor (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
atmelmicro authored Mar 1, 2024
2 parents 179df78 + a445d07 commit 32fe0a1
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 298 deletions.
297 changes: 3 additions & 294 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,302 +1,11 @@
import { bundleAll } from "./build";
import { deploy as cdkDeploy } from "./deploy";
import { argv } from "node:process";
import { startDev } from "./dev";
import { writeFile, mkdir, watch } from "node:fs/promises";
import { resolve } from "node:path";

export type LogFunction = (...text: string[]) => void;
export type Command = {
title: string;
props: { name: string; required: boolean; hasValue: boolean }[];
fn: () => any;
};

const commands: Command[] = [
{
title: "deploy",
props: [],
fn: deploy,
},
{
title: "build",
props: [],
fn: build,
},
{
title: "dev",
props: [],
fn: dev,
},
{
title: "create",
props: [],
fn: newApp,
},
{
title: "get-llrt",
props: [],
fn: getLlrt,
},
];

const sampleConfig = `export const name = "example-app"
export const llrt = "./llrt.zip"
`;

const sampleGitignore = `./llrt.zip
out/
node_modules/`;

const samplePackage = {
type: "module",
name: "example-app",
version: "0.1.0",
devDependencies: {
lackFrw: "^0.1.0",
},
scripts: {
dev: "lack-frw dev",
build: "lack-frw build",
deploy: "lack-frw deploy",
},
};

// copied from bun init - might need to change
const sampleTsConfig = {
compilerOptions: {
lib: ["ESNext"],
target: "ESNext",
module: "ESNext",
moduleDetection: "force",
jsx: "react-jsx",
allowJs: true,

moduleResolution: "bundler",
allowImportingTsExtensions: true,
verbatimModuleSyntax: true,
noEmit: true,

strict: true,
skipLibCheck: true,
noFallthroughCasesInSwitch: true,
},
};

const debounce = 100;
let lastTrigger = -Infinity;

async function watchApp(log: LogFunction) {
const watcher = watch(resolve("./app"), { recursive: true });
for await (const event of watcher) {
if (lastTrigger + debounce > Date.now()) continue;
lastTrigger = Date.now();
const start = Date.now();
log("Rebuilding...");
await build();
log(`Rebuilt in ${Date.now() - start} ms`);
}
}

async function dev() {
const log = (...text: string[]) => {
console.log(
withHeading(
text.reduce((acc, x) => `${acc}${x}`, ""),
"Dev",
"Magenta"
)
);
};

await build();
watchApp(log);
await startDev(log);
}

async function getLlrt() {
const log = (text: string) => {
console.log(color(" Get LLRT ", "Blue"), text);
};

const config = await import("file://" + resolve("./lack.config.js"));
const path = config.llrt ?? "llrt.zip";

log("downloading llrt");
const repoRes = await fetch(
"https://api.github.com/repos/awslabs/llrt/releases/latest"
);
const data: {
assets: {
browser_download_url: string;
name: string;
}[];
} = await repoRes.json();

const correctVersion = data.assets.find(
(x) => x.name === "llrt-lambda-arm64.zip"
);
if (!correctVersion) {
error(
"Couldn't download the correct LLRT version, please set up Lack manually"
);
return;
}

const llrtBin = await fetch(correctVersion.browser_download_url);
const blob = await llrtBin.blob();
await writeFile(path, Buffer.from(await blob.arrayBuffer()));
log("downloaded llrt");
}

async function newApp() {
const log = (text: string) => {
console.log(color(" Create ", "Gray"), text);
};

await getLlrt();

log("creating new lack app");
log("generating config, gitignore, package.json and tsconfig");
await writeFile(".gitignore", sampleGitignore);
await writeFile("lack.config.js", sampleConfig);
await writeFile("package.json", JSON.stringify(samplePackage, undefined, 4));
await writeFile(
"tsconfig.json",
JSON.stringify(sampleTsConfig, undefined, 4)
);

await mkdir("./app");
log("done");
}

async function deploy() {
const log = (text: string) => {
console.log(color(" Deploy ", "Green"), text);
};

log("starting deploying app");
await build();
log("done building, starting deploy");
cdkDeploy();
}

async function build() {
const log = (text: string) => {
console.log(color(" Build ", "Blue"), text);
};

log("bulding app from ./app");
const routes = await bundleAll();
routes.forEach((x) => log(`created route ${x}`));
}

const colors = {
Black: "\x1b[40m",
Red: "\x1b[41m",
Green: "\x1b[42m",
Yellow: "\x1b[43m",
Blue: "\x1b[44m",
Magenta: "\x1b[45m",
Cyan: "\x1b[46m",
White: "\x1b[47m",
Gray: "\x1b[100m",
Reset: "\x1b[0m",
};

export function color(text: string, color: keyof typeof colors) {
return `${colors[color]}${text}${colors.Reset}`;
}

export function withHeading(
text: string,
heading: string,
color: keyof typeof colors
) {
return `${colors[color]} ${heading} ${colors.Reset} ${text}`;
}

export function error(...text: string[]) {
console.log(
withHeading(
text.reduce((acc, x) => `${acc}${x}`, ""),
"ERROR",
"Red"
)
);
}

export function warn(...text: string[]) {
console.log(
withHeading(
text.reduce((acc, x) => `${acc}${x}`, ""),
"WARN",
"Yellow"
)
);
}

function cliParser(args: string[]) {
args.splice(0, 2); // todo this might not the best idea
const commandName = args[0];
if (!commands.map((x) => x.title).includes(commandName)) {
error("avalible commands are: ", ...commands.map((x) => `"${x.title}" `));
return;
}

// finish parsing command
args.splice(0, 1);
const command = commands.find((x) => x.title === commandName);
if (!command) throw new Error("couldnt find command");

// parse props
const props: { name: string; value?: string }[] = [];
for (const prop of args) {
// confrim proper formating of prop
if (!prop.startsWith("--")) {
error("props have to start with --");
return;
}
const propSlice = prop.slice(2).split("=");
const setProp = command.props.find((x) => x.name === propSlice[0]);
if (!setProp) {
error(
"invalid prop, avalible ones are: ",
...command.props.map((x) => `"${x.name}" `)
);
return;
}

// if the props is set as it needs a value
if (setProp.hasValue) {
if (propSlice.length !== 2) {
error(
`the prop ${setProp.name} has a value, please provide it by doing --prop=value`
);
return;
}

props.push({ name: setProp.name, value: propSlice[1] });
continue;
}
if (propSlice.length === 2 && !setProp.hasValue)
warn(
`you passed a value to the prop "${setProp.name}" that actaully doesnt need it`
);

props.push({ name: setProp.name });
}

for (const required of command.props
.filter((x) => x.required)
.map((x) => x.name)) {
if (!props.map((x) => x.name).includes(required)) {
error(`the prop "${required}" needs to be set`);
return;
}
}
return { name: command.title, props, fn: command.fn } as const;
}
import { build } from "./cli/build";
import { color, error, warn, withHeading } from "./cli/console";
import { cliParser } from "./cli/parser";

async function main() {
const params = cliParser(argv);
Expand Down
12 changes: 12 additions & 0 deletions src/cli/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { bundleAll } from "../build";
import { color } from "./console";

export async function build() {
const log = (text: string) => {
console.log(color(" Build ", "Blue"), text);
};

log("bulding app from ./app");
const routes = await bundleAll();
routes.forEach((x) => log(`created route ${x}`));
}
44 changes: 44 additions & 0 deletions src/cli/console.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const colors = {
Black: "\x1b[40m",
Red: "\x1b[41m",
Green: "\x1b[42m",
Yellow: "\x1b[43m",
Blue: "\x1b[44m",
Magenta: "\x1b[45m",
Cyan: "\x1b[46m",
White: "\x1b[47m",
Gray: "\x1b[100m",
Reset: "\x1b[0m",
};

export function color(text: string, color: keyof typeof colors) {
return `${colors[color]}${text}${colors.Reset}`;
}

export function withHeading(
text: string,
heading: string,
color: keyof typeof colors
) {
return `${colors[color]} ${heading} ${colors.Reset} ${text}`;
}

export function error(...text: string[]) {
console.log(
withHeading(
text.reduce((acc, x) => `${acc}${x}`, ""),
"ERROR",
"Red"
)
);
}

export function warn(...text: string[]) {
console.log(
withHeading(
text.reduce((acc, x) => `${acc}${x}`, ""),
"WARN",
"Yellow"
)
);
}
14 changes: 14 additions & 0 deletions src/cli/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { build } from "./build";
import { color } from "./console";
import { deploy as cdkDeploy } from "../deploy";

export async function deploy() {
const log = (text: string) => {
console.log(color(" Deploy ", "Green"), text);
};

log("starting deploying app");
await build();
log("done building, starting deploy");
cdkDeploy();
}
Loading

0 comments on commit 32fe0a1

Please sign in to comment.