Skip to content

Commit

Permalink
cli: add skeleton flow of cli
Browse files Browse the repository at this point in the history
  • Loading branch information
jaipaljadeja committed Jul 18, 2024
1 parent 17a2e36 commit b283a29
Show file tree
Hide file tree
Showing 24 changed files with 600 additions and 148 deletions.
7 changes: 7 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `@apibara/cli`

TODO

## Installation

TODO
46 changes: 46 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "@apibara/cli",
"version": "2.0.0",
"type": "module",
"source": "./src/index.ts",
"main": "./src/index.ts",
"exports": {
".": "./src/index.ts"
},
"publishConfig": {
"files": ["dist", "src", "README.md"],
"main": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
}
}
},
"scripts": {
"build": "unbuild",
"lint": "biome check .",
"typecheck": "tsc --noEmit",
"lint:fix": "pnpm lint --write",
"test": "vitest",
"test:ci": "vitest run",
"format": "biome format . --write"
},
"devDependencies": {
"@types/node": "^20.14.0",
"unbuild": "^2.0.0",
"vitest": "^1.6.0"
},
"dependencies": {
"@apibara/indexer": "workspace:*",
"c12": "^1.11.1",
"citty": "^0.1.6",
"hookable": "^5.5.3",
"klona": "^2.0.6",
"pathe": "^1.1.2",
"rollup": "^4.18.1",
"untyped": "^1.4.2"
}
}
29 changes: 29 additions & 0 deletions packages/cli/src/apibara.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { createHooks } from "hookable";
import { loadOptions } from "./config/loader";
import { updateApibaraConfig } from "./config/update";
import type { Apibara } from "./types/apibara";
import type {
ApibaraConfig,
ApibaraDynamicConfig,
LoadConfigOptions,
} from "./types/config";

export async function createApibara(
config: ApibaraConfig = {},
opts: LoadConfigOptions = {},
) {
const options = await loadOptions(config, opts);

// create apibara context
const apibara: Apibara = {
options,
hooks: createHooks(),
close: async () => {},
async updateConfig(config: ApibaraDynamicConfig) {
updateApibaraConfig(apibara, config);
},
};

// TODO
return apibara;
}
20 changes: 20 additions & 0 deletions packages/cli/src/build/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { type JSValue, generateTypes, resolveSchema } from "untyped";
import type { Apibara } from "../types/apibara";

export async function writeTypes(apibara: Apibara) {
// TODO
generateTypes(
await resolveSchema(
Object.fromEntries(
Object.entries(apibara.options.runtimeConfig),
) as Record<string, JSValue>,
),
{
interfaceName: "NitroRuntimeConfig",
addExport: false,
addDefaults: false,
allowExtraKeys: false,
indentation: 2,
},
);
}
15 changes: 15 additions & 0 deletions packages/cli/src/commands/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineCommand } from "citty";
import { commonArgs } from "../common";

export default defineCommand({
meta: {
name: "build",
description: "Build indexer",
},
args: {
...commonArgs,
},
async run({ args }) {
// TODO
},
});
15 changes: 15 additions & 0 deletions packages/cli/src/commands/dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineCommand } from "citty";
import { commonArgs } from "../common";

export default defineCommand({
meta: {
name: "dev",
description: "Start the development server",
},
args: {
...commonArgs,
},
async run({ args }) {
// TODO
},
});
20 changes: 20 additions & 0 deletions packages/cli/src/commands/prepare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { defineCommand } from "citty";
import { resolve } from "pathe";
import { createApibara } from "../apibara";
import { writeTypes } from "../build/types";
import { commonArgs } from "../common";

export default defineCommand({
meta: {
name: "prepare",
description: "Generate types for the project",
},
args: {
...commonArgs,
},
async run({ args }) {
const rootDir = resolve((args.dir || ".") as string);
const apibara = await createApibara({ rootDir });
await writeTypes(apibara);
},
});
8 changes: 8 additions & 0 deletions packages/cli/src/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { ArgsDef } from "citty";

export const commonArgs = <ArgsDef>{
dir: {
type: "string",
description: "project root directory",
},
};
5 changes: 5 additions & 0 deletions packages/cli/src/config/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { ApibaraConfig } from "../types/config";

export const ApibaraDefaults: ApibaraConfig = {
// TODO
};
5 changes: 5 additions & 0 deletions packages/cli/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { ApibaraConfig } from "../types/config";

export function defineConfig(config: ApibaraConfig): ApibaraConfig {
return config;
}
63 changes: 63 additions & 0 deletions packages/cli/src/config/loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { loadConfig, watchConfig } from "c12";
import { klona } from "klona/full";
import type {
ApibaraConfig,
ApibaraOptions,
LoadConfigOptions,
} from "../types/config";
import { ApibaraDefaults } from "./defaults";
import { resolvePathOptions } from "./resolvers/paths.resolver";
import { resolveRuntimeConfigOptions } from "./resolvers/runtime-config.resolver";

const configResolvers = [
resolvePathOptions,
resolveRuntimeConfigOptions,
] as const;

export async function loadOptions(
configOverrides: ApibaraConfig = {},
opts: LoadConfigOptions = {},
): Promise<ApibaraOptions> {
const options = await _loadUserConfig(configOverrides, opts);
for (const resolver of configResolvers) {
await resolver(options);
}
return options;
}

async function _loadUserConfig(
configOverrides: ApibaraConfig = {},
opts: LoadConfigOptions = {},
): Promise<ApibaraOptions> {
let presetOverride = configOverrides.preset;
if (configOverrides.dev) {
presetOverride = "apibara-dev";
}

// biome-ignore lint: noParameterAssign
configOverrides = klona(configOverrides);

const loadedConfig = await (opts.watch
? watchConfig<ApibaraConfig>
: loadConfig<ApibaraConfig>)({
name: "apibara",
cwd: configOverrides.rootDir,
overrides: {
...configOverrides,
preset: presetOverride,
},
defaults: ApibaraDefaults,
...opts.c12,
});

const options = klona(loadedConfig.config) as ApibaraOptions;

options._config = configOverrides;
options._c12 = loadedConfig;

if (presetOverride) {
options.preset = presetOverride;
}

return options;
}
6 changes: 6 additions & 0 deletions packages/cli/src/config/resolvers/paths.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { resolve } from "pathe";
import type { ApibaraOptions } from "../../types/config";

export async function resolvePathOptions(options: ApibaraOptions) {
options.rootDir = resolve(options.rootDir || ".");
}
5 changes: 5 additions & 0 deletions packages/cli/src/config/resolvers/runtime-config.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { ApibaraOptions } from "../../types/config";

export async function resolveRuntimeConfigOptions(options: ApibaraOptions) {
options.runtimeConfig = { ...options.runtimeConfig, default: "value" };
}
11 changes: 11 additions & 0 deletions packages/cli/src/config/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Apibara } from "../types/apibara";
import type { ApibaraDynamicConfig } from "../types/config";

export async function updateApibaraConfig(
apibara: Apibara,
config: ApibaraDynamicConfig,
) {
// Do something
// await apibara.hooks.callHook("rollup:reload");
// consola.success("Apibara config hot reloaded!");
}
1 change: 1 addition & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./main";
16 changes: 16 additions & 0 deletions packages/cli/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineCommand, runMain } from "citty";

const main = defineCommand({
meta: {
name: "apibara",
description: "Apibara CLI",
version: "2.0.0",
},
subCommands: {
dev: () => import("./commands/dev").then((r) => r.default),
build: () => import("./commands/build").then((r) => r.default),
prepare: () => import("./commands/prepare").then((r) => r.default),
},
});

runMain(main);
4 changes: 4 additions & 0 deletions packages/cli/src/types/_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
export type DeepPartial<T> = T extends Record<string, any>
? { [P in keyof T]?: DeepPartial<T[P]> | T[P] }
: T;
10 changes: 10 additions & 0 deletions packages/cli/src/types/apibara.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { Hookable } from "hookable";
import type { ApibaraDynamicConfig, ApibaraOptions } from "./config";
import type { ApibaraHooks } from "./hooks";

export interface Apibara {
options: ApibaraOptions;
hooks: Hookable<ApibaraHooks>;
close: () => Promise<void>;
updateConfig: (config: ApibaraDynamicConfig) => void | Promise<void>;
}
51 changes: 51 additions & 0 deletions packages/cli/src/types/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Sink } from "@apibara/indexer";
import type { ConfigWatcher, ResolvedConfig, WatchConfigOptions } from "c12";
import type { DeepPartial } from "./_utils";
import type { PresetName } from "./presets";
import type { RollupConfig } from "./rollup";

/**
* Apibara Config type (apibara.config)
*/
export interface ApibaraConfig
extends DeepPartial<Omit<ApibaraOptions, "preset">> {
sink?: {
default: Sink;
[key: string]: Sink;
};
dev?: boolean;
runtimeConfig?: RuntimeConfig;
presets?: {
[key: string]: Partial<ApibaraConfig>;
};
preset?: string;
}

export interface RuntimeConfig {
[key: string]: unknown;
}

export type ApibaraDynamicConfig = Pick<ApibaraConfig, "runtimeConfig">;

/**
* Config loader options
*/
export interface LoadConfigOptions {
watch?: boolean;
c12?: WatchConfigOptions;
}

export interface ApibaraOptions {
// Internal
_config: ApibaraConfig;
_c12: ResolvedConfig<ApibaraConfig> | ConfigWatcher<ApibaraConfig>;
// General
debug: boolean;
preset: PresetName;
runtimeConfig: RuntimeConfig;
rootDir: string;
outputDir: string;
// Dev
dev: boolean;
rollupConfig?: RollupConfig;
}
11 changes: 11 additions & 0 deletions packages/cli/src/types/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Apibara } from "./apibara";
import type { RollupConfig } from "./rollup";

export interface ApibaraHooks {
"rollup:before": (apibara: Apibara, rollupConfig: RollupConfig) => void;
compiled: (apibara: Apibara) => void;
"dev:reload": () => void;
"rollup:reload": () => void;
restart: () => void;
close: () => void;
}
2 changes: 2 additions & 0 deletions packages/cli/src/types/presets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// TODO
export type PresetName = string;
8 changes: 8 additions & 0 deletions packages/cli/src/types/rollup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type {
InputOptions as RollupInputOptions,
OutputOptions as RollupOutputOptions,
} from "rollup";

export type RollupConfig = RollupInputOptions & {
output: RollupOutputOptions;
};
11 changes: 11 additions & 0 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"declarationDir": "dist",
"noEmit": false,
"rootDir": "src",
"types": ["node"]
},
"include": ["src/"]
}
Loading

0 comments on commit b283a29

Please sign in to comment.