build(coding-agent): embedded assets into binary to eliminate runtime reads
- Embedded assets (themes, templates, agents, commands) directly into binary at compile time using Bun imports. - Removed runtime filesystem reads for bundled assets in favor of static build-time imports. - Simplified build scripts by removing copy-assets steps and compiling directly from TypeScript source. - Updated publish workflow to use bun publish with NPM_CONFIG_TOKEN environment variable. - Added TypeScript declarations for importing .md, .css, .html, and .js files as text strings.
This commit is contained in:
@@ -97,14 +97,14 @@ jobs:
|
||||
- name: Publish packages to npm
|
||||
if: ${{ !inputs.skip_npm }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: |
|
||||
for pkg in packages/*/; do
|
||||
pkg_name=$(basename "$pkg")
|
||||
echo "Publishing $pkg_name..."
|
||||
output=$(cd "$pkg" && npm publish --access public --provenance 2>&1) && echo "$output" && continue
|
||||
output=$(cd "$pkg" && bun publish --access public 2>&1) && echo "$output" && continue
|
||||
echo "$output"
|
||||
if echo "$output" | grep -q "previously published\|cannot publish over"; then
|
||||
if echo "$output" | grep -q "previously published\|cannot publish over\|You cannot publish over"; then
|
||||
echo "✓ Already published, skipping"
|
||||
else
|
||||
echo "✗ Failed to publish $pkg_name"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
### Changed
|
||||
|
||||
- Simplified build process by embedding assets (themes, templates, agents, commands) directly into the binary at compile time
|
||||
- Removed separate asset copying steps from build scripts
|
||||
|
||||
## [2.0.1337] - 2026-01-03
|
||||
### Added
|
||||
|
||||
@@ -31,11 +31,9 @@
|
||||
"scripts": {
|
||||
"check": "tsgo --noEmit",
|
||||
"clean": "rm -rf dist",
|
||||
"build": "tsgo -p tsconfig.build.json && chmod +x dist/cli.js && npm run copy-assets",
|
||||
"build:binary": "npm run build && bun build --compile ./dist/cli.js --outfile dist/pi && npm run copy-binary-assets",
|
||||
"copy-assets": "mkdir -p dist/modes/interactive/theme && cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && mkdir -p dist/core/export-html && cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && mkdir -p dist/core/tools/task/bundled-agents && cp src/core/tools/task/bundled-agents/*.md dist/core/tools/task/bundled-agents/",
|
||||
"copy-binary-assets": "cp package.json dist/ && cp README.md dist/ && cp CHANGELOG.md dist/ && mkdir -p dist/theme && cp src/modes/interactive/theme/*.json dist/theme/ && mkdir -p dist/export-html && cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/export-html/ && cp -r docs dist/ && cp -r examples dist/",
|
||||
"test": "vitest --run",
|
||||
"build": "tsgo -p tsconfig.build.json && chmod +x dist/cli.js",
|
||||
"build:binary": "bun build --compile ./src/cli.ts --outfile dist/pi",
|
||||
"test": "bun test",
|
||||
"prepublishOnly": "npm run clean && npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Type declarations for Bun's import attributes.
|
||||
* These allow importing non-JS files as text or JSON at build time.
|
||||
*/
|
||||
|
||||
// Markdown files imported as text
|
||||
declare module "*.md" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
// CSS files imported as text
|
||||
declare module "*.css" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
// HTML files imported as text
|
||||
declare module "*.html" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
// Text files imported as text
|
||||
declare module "*.txt" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
@@ -1,34 +1,31 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { existsSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
|
||||
// Embed package.json at build time for config
|
||||
import packageJson from "../package.json" with { type: "json" };
|
||||
|
||||
// =============================================================================
|
||||
// Package Detection
|
||||
// App Config (from embedded package.json)
|
||||
// =============================================================================
|
||||
|
||||
export const APP_NAME: string = (packageJson as { piConfig?: { name?: string } }).piConfig?.name || "pi";
|
||||
export const CONFIG_DIR_NAME: string =
|
||||
(packageJson as { piConfig?: { configDir?: string } }).piConfig?.configDir || ".pi";
|
||||
export const VERSION: string = (packageJson as { version: string }).version;
|
||||
|
||||
// e.g., PI_CODING_AGENT_DIR or TAU_CODING_AGENT_DIR
|
||||
export const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;
|
||||
|
||||
// =============================================================================
|
||||
// Package Directory (for optional external docs/examples)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Detect if we're running as a Bun compiled binary.
|
||||
* Bun binaries have import.meta.url containing "$bunfs", "~BUN", or "%7EBUN" (Bun's virtual filesystem path)
|
||||
*/
|
||||
export const isBunBinary =
|
||||
import.meta.url.includes("$bunfs") || import.meta.url.includes("~BUN") || import.meta.url.includes("%7EBUN");
|
||||
|
||||
// =============================================================================
|
||||
// Package Asset Paths (shipped with executable)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Get the base directory for resolving package assets (themes, package.json, README.md, CHANGELOG.md).
|
||||
* - For Bun binary: returns the directory containing the executable
|
||||
* - For Node.js (dist/): returns __dirname (the dist/ directory)
|
||||
* - For tsx (src/): returns parent directory (the package root)
|
||||
* Get the base directory for resolving optional package assets (docs, examples).
|
||||
* Walk up from import.meta.dir until we find package.json, or fall back to cwd.
|
||||
*/
|
||||
export function getPackageDir(): string {
|
||||
if (isBunBinary) {
|
||||
// Bun binary: process.execPath points to the compiled executable
|
||||
return dirname(process.execPath);
|
||||
}
|
||||
// Node.js: walk up from import.meta.dir until we find package.json
|
||||
let dir = import.meta.dir;
|
||||
while (dir !== dirname(dir)) {
|
||||
if (existsSync(join(dir, "package.json"))) {
|
||||
@@ -36,79 +33,30 @@ export function getPackageDir(): string {
|
||||
}
|
||||
dir = dirname(dir);
|
||||
}
|
||||
// Fallback (shouldn't happen)
|
||||
return import.meta.dir;
|
||||
// Fallback to cwd (docs/examples won't be found, but that's fine)
|
||||
return process.cwd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to built-in themes directory (shipped with package)
|
||||
* - For Bun binary: theme/ next to executable
|
||||
* - For Node.js (dist/): dist/modes/interactive/theme/
|
||||
* - For tsx (src/): src/modes/interactive/theme/
|
||||
*/
|
||||
export function getThemesDir(): string {
|
||||
if (isBunBinary) {
|
||||
return join(dirname(process.execPath), "theme");
|
||||
}
|
||||
// Theme is in modes/interactive/theme/ relative to src/ or dist/
|
||||
const packageDir = getPackageDir();
|
||||
const srcOrDist = existsSync(join(packageDir, "src")) ? "src" : "dist";
|
||||
return join(packageDir, srcOrDist, "modes", "interactive", "theme");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to HTML export template directory (shipped with package)
|
||||
* - For Bun binary: export-html/ next to executable
|
||||
* - For Node.js (dist/): dist/core/export-html/
|
||||
* - For tsx (src/): src/core/export-html/
|
||||
*/
|
||||
export function getExportTemplateDir(): string {
|
||||
if (isBunBinary) {
|
||||
return join(dirname(process.execPath), "export-html");
|
||||
}
|
||||
const packageDir = getPackageDir();
|
||||
const srcOrDist = existsSync(join(packageDir, "src")) ? "src" : "dist";
|
||||
return join(packageDir, srcOrDist, "core", "export-html");
|
||||
}
|
||||
|
||||
/** Get path to package.json */
|
||||
export function getPackageJsonPath(): string {
|
||||
return join(getPackageDir(), "package.json");
|
||||
}
|
||||
|
||||
/** Get path to README.md */
|
||||
/** Get path to README.md (optional, may not exist in binary) */
|
||||
export function getReadmePath(): string {
|
||||
return resolve(join(getPackageDir(), "README.md"));
|
||||
}
|
||||
|
||||
/** Get path to docs directory */
|
||||
/** Get path to docs directory (optional, may not exist in binary) */
|
||||
export function getDocsPath(): string {
|
||||
return resolve(join(getPackageDir(), "docs"));
|
||||
}
|
||||
|
||||
/** Get path to examples directory */
|
||||
/** Get path to examples directory (optional, may not exist in binary) */
|
||||
export function getExamplesPath(): string {
|
||||
return resolve(join(getPackageDir(), "examples"));
|
||||
}
|
||||
|
||||
/** Get path to CHANGELOG.md */
|
||||
/** Get path to CHANGELOG.md (optional, may not exist in binary) */
|
||||
export function getChangelogPath(): string {
|
||||
return resolve(join(getPackageDir(), "CHANGELOG.md"));
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// App Config (from package.json piConfig)
|
||||
// =============================================================================
|
||||
|
||||
const pkg = JSON.parse(readFileSync(getPackageJsonPath(), "utf-8"));
|
||||
|
||||
export const APP_NAME: string = pkg.piConfig?.name || "pi";
|
||||
export const CONFIG_DIR_NAME: string = pkg.piConfig?.configDir || ".pi";
|
||||
export const VERSION: string = pkg.version;
|
||||
|
||||
// e.g., PI_CODING_AGENT_DIR or TAU_CODING_AGENT_DIR
|
||||
export const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;
|
||||
|
||||
// =============================================================================
|
||||
// User Config Paths (~/.pi/agent/*)
|
||||
// =============================================================================
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Handles loading, saving, and refreshing credentials from auth.json.
|
||||
*/
|
||||
|
||||
import { chmodSync, existsSync, mkdirSync } from "node:fs";
|
||||
import { chmodSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
||||
import { dirname } from "node:path";
|
||||
import {
|
||||
getEnvApiKey,
|
||||
@@ -73,8 +73,7 @@ export class AuthStorage {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const file = Bun.file(this.authPath);
|
||||
this.data = JSON.parse(file.text() as unknown as string);
|
||||
this.data = JSON.parse(readFileSync(this.authPath, "utf-8"));
|
||||
} catch {
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { basename, join } from "node:path";
|
||||
import { existsSync, writeFileSync } from "node:fs";
|
||||
import { basename } from "node:path";
|
||||
import type { AgentState } from "@oh-my-pi/pi-agent-core";
|
||||
import { APP_NAME, getExportTemplateDir } from "../../config";
|
||||
import { APP_NAME } from "../../config";
|
||||
import { getResolvedThemeColors, getThemeExportColors } from "../../modes/interactive/theme/theme";
|
||||
import { SessionManager } from "../session-manager";
|
||||
|
||||
// Cached minified assets (populated on first use)
|
||||
let cachedTemplate: string | null = null;
|
||||
let cachedJs: string | null = null;
|
||||
// Embed template files at build time
|
||||
import templateCss from "./template-css.txt" with { type: "text" };
|
||||
import templateHtml from "./template-html.txt" with { type: "text" };
|
||||
import templateJs from "./template-js.txt" with { type: "text" };
|
||||
|
||||
/** Minify CSS by removing comments, unnecessary whitespace, and newlines. */
|
||||
function minifyCss(css: string): string {
|
||||
@@ -134,22 +134,22 @@ interface SessionData {
|
||||
tools?: { name: string; description: string }[];
|
||||
}
|
||||
|
||||
// Pre-minified embedded assets (cached on first use)
|
||||
let cachedTemplate: string | null = null;
|
||||
let cachedJs: string | null = null;
|
||||
|
||||
/**
|
||||
* Core HTML generation logic shared by both export functions.
|
||||
*/
|
||||
function generateHtml(sessionData: SessionData, themeName?: string): string {
|
||||
const templateDir = getExportTemplateDir();
|
||||
|
||||
// Load and minify assets on first use
|
||||
// Minify embedded assets on first use
|
||||
if (!cachedTemplate) {
|
||||
cachedTemplate = minifyHtml(readFileSync(join(templateDir, "template.html"), "utf-8"));
|
||||
cachedTemplate = minifyHtml(templateHtml);
|
||||
}
|
||||
if (!cachedJs) {
|
||||
cachedJs = minifyJs(readFileSync(join(templateDir, "template.js"), "utf-8"));
|
||||
cachedJs = minifyJs(templateJs);
|
||||
}
|
||||
|
||||
const templateCss = readFileSync(join(templateDir, "template.css"), "utf-8");
|
||||
|
||||
const themeVars = generateThemeVars(themeName);
|
||||
const colors = getResolvedThemeColors(themeName);
|
||||
const exportColors = deriveExportColors(colors.userMessageBg || "#343541");
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
/**
|
||||
* Bundled agent definitions.
|
||||
*
|
||||
* Agents are loaded from .md files in the bundled-agents directory.
|
||||
* These serve as defaults when no user/project agents are discovered.
|
||||
* Agents are embedded at build time via Bun's import with { type: "text" }.
|
||||
*/
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
// Embed agent markdown files at build time
|
||||
import browserMd from "./bundled-agents/browser.md" with { type: "text" };
|
||||
import exploreMd from "./bundled-agents/explore.md" with { type: "text" };
|
||||
import planMd from "./bundled-agents/plan.md" with { type: "text" };
|
||||
import reviewerMd from "./bundled-agents/reviewer.md" with { type: "text" };
|
||||
import taskMd from "./bundled-agents/task.md" with { type: "text" };
|
||||
import type { AgentDefinition, AgentSource } from "./types";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const BUNDLED_AGENTS_DIR = path.join(__dirname, "bundled-agents");
|
||||
const EMBEDDED_AGENTS: { name: string; content: string }[] = [
|
||||
{ name: "browser.md", content: browserMd },
|
||||
{ name: "explore.md", content: exploreMd },
|
||||
{ name: "plan.md", content: planMd },
|
||||
{ name: "reviewer.md", content: reviewerMd },
|
||||
{ name: "task.md", content: taskMd },
|
||||
];
|
||||
|
||||
/**
|
||||
* Parse YAML frontmatter from markdown content.
|
||||
@@ -47,16 +54,9 @@ function parseFrontmatter(content: string): { frontmatter: Record<string, string
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a single agent from a markdown file.
|
||||
* Parse an agent from embedded content.
|
||||
*/
|
||||
function loadAgentFromFile(filePath: string, source: AgentSource): AgentDefinition | null {
|
||||
let content: string;
|
||||
try {
|
||||
content = fs.readFileSync(filePath, "utf-8");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseAgent(fileName: string, content: string, source: AgentSource): AgentDefinition | null {
|
||||
const { frontmatter, body } = parseFrontmatter(content);
|
||||
|
||||
if (!frontmatter.name || !frontmatter.description) {
|
||||
@@ -79,7 +79,7 @@ function loadAgentFromFile(filePath: string, source: AgentSource): AgentDefiniti
|
||||
recursive,
|
||||
systemPrompt: body,
|
||||
source,
|
||||
filePath,
|
||||
filePath: `embedded:${fileName}`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ function loadAgentFromFile(filePath: string, source: AgentSource): AgentDefiniti
|
||||
let bundledAgentsCache: AgentDefinition[] | null = null;
|
||||
|
||||
/**
|
||||
* Load all bundled agents from the bundled-agents directory.
|
||||
* Load all bundled agents from embedded content.
|
||||
* Results are cached after first load.
|
||||
*/
|
||||
export function loadBundledAgents(): AgentDefinition[] {
|
||||
@@ -97,24 +97,8 @@ export function loadBundledAgents(): AgentDefinition[] {
|
||||
|
||||
const agents: AgentDefinition[] = [];
|
||||
|
||||
if (!fs.existsSync(BUNDLED_AGENTS_DIR)) {
|
||||
bundledAgentsCache = agents;
|
||||
return agents;
|
||||
}
|
||||
|
||||
let entries: fs.Dirent[];
|
||||
try {
|
||||
entries = fs.readdirSync(BUNDLED_AGENTS_DIR, { withFileTypes: true });
|
||||
} catch {
|
||||
bundledAgentsCache = agents;
|
||||
return agents;
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.name.endsWith(".md")) continue;
|
||||
|
||||
const filePath = path.join(BUNDLED_AGENTS_DIR, entry.name);
|
||||
const agent = loadAgentFromFile(filePath, "bundled");
|
||||
for (const { name, content } of EMBEDDED_AGENTS) {
|
||||
const agent = parseAgent(name, content, "bundled");
|
||||
if (agent) {
|
||||
agents.push(agent);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
/**
|
||||
* Workflow commands for orchestrating multi-agent workflows.
|
||||
*
|
||||
* Commands are loaded from .md files with YAML frontmatter.
|
||||
* They define multi-step workflows that chain agent outputs.
|
||||
* Commands are embedded at build time via Bun's import with { type: "text" }.
|
||||
*/
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const BUNDLED_COMMANDS_DIR = path.join(__dirname, "bundled-commands");
|
||||
// Embed command markdown files at build time
|
||||
import architectPlanMd from "./bundled-commands/architect-plan.md" with { type: "text" };
|
||||
import implementMd from "./bundled-commands/implement.md" with { type: "text" };
|
||||
import implementWithCriticMd from "./bundled-commands/implement-with-critic.md" with { type: "text" };
|
||||
|
||||
const EMBEDDED_COMMANDS: { name: string; content: string }[] = [
|
||||
{ name: "architect-plan.md", content: architectPlanMd },
|
||||
{ name: "implement-with-critic.md", content: implementWithCriticMd },
|
||||
{ name: "implement.md", content: implementMd },
|
||||
];
|
||||
|
||||
/** Workflow command definition */
|
||||
export interface WorkflowCommand {
|
||||
@@ -56,9 +62,9 @@ function parseFrontmatter(content: string): { frontmatter: Record<string, string
|
||||
}
|
||||
|
||||
/**
|
||||
* Load commands from a directory.
|
||||
* Load commands from a directory (for user/project commands).
|
||||
*/
|
||||
function loadCommandsFromDir(dir: string, source: "bundled" | "user" | "project"): WorkflowCommand[] {
|
||||
function loadCommandsFromDir(dir: string, source: "user" | "project"): WorkflowCommand[] {
|
||||
const commands: WorkflowCommand[] = [];
|
||||
|
||||
if (!fs.existsSync(dir)) {
|
||||
@@ -137,15 +143,30 @@ function findNearestDir(cwd: string, relPath: string): string | null {
|
||||
let bundledCommandsCache: WorkflowCommand[] | null = null;
|
||||
|
||||
/**
|
||||
* Load all bundled commands.
|
||||
* Load all bundled commands from embedded content.
|
||||
*/
|
||||
export function loadBundledCommands(): WorkflowCommand[] {
|
||||
if (bundledCommandsCache !== null) {
|
||||
return bundledCommandsCache;
|
||||
}
|
||||
|
||||
bundledCommandsCache = loadCommandsFromDir(BUNDLED_COMMANDS_DIR, "bundled");
|
||||
return bundledCommandsCache;
|
||||
const commands: WorkflowCommand[] = [];
|
||||
|
||||
for (const { name, content } of EMBEDDED_COMMANDS) {
|
||||
const { frontmatter, body } = parseFrontmatter(content);
|
||||
const cmdName = name.replace(/\.md$/, "");
|
||||
|
||||
commands.push({
|
||||
name: cmdName,
|
||||
description: frontmatter.description || "",
|
||||
instructions: body,
|
||||
source: "bundled",
|
||||
filePath: `embedded:${name}`,
|
||||
});
|
||||
}
|
||||
|
||||
bundledCommandsCache = commands;
|
||||
return commands;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,11 @@ import { type Static, Type } from "@sinclair/typebox";
|
||||
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
||||
import chalk from "chalk";
|
||||
import { highlight, supportsLanguage } from "cli-highlight";
|
||||
import { getCustomThemesDir, getThemesDir } from "../../../config";
|
||||
import { getCustomThemesDir } from "../../../config";
|
||||
import { logger } from "../../../core/logger";
|
||||
// Embed theme JSON files at build time
|
||||
import darkThemeJson from "./dark.json" with { type: "json" };
|
||||
import lightThemeJson from "./light.json" with { type: "json" };
|
||||
|
||||
// ============================================================================
|
||||
// Types & Schema
|
||||
@@ -450,18 +453,12 @@ export class Theme {
|
||||
// Theme Loading
|
||||
// ============================================================================
|
||||
|
||||
let BUILTIN_THEMES: Record<string, ThemeJson> | undefined;
|
||||
const BUILTIN_THEMES: Record<string, ThemeJson> = {
|
||||
dark: darkThemeJson as ThemeJson,
|
||||
light: lightThemeJson as ThemeJson,
|
||||
};
|
||||
|
||||
function getBuiltinThemes(): Record<string, ThemeJson> {
|
||||
if (!BUILTIN_THEMES) {
|
||||
const themesDir = getThemesDir();
|
||||
const darkPath = path.join(themesDir, "dark.json");
|
||||
const lightPath = path.join(themesDir, "light.json");
|
||||
BUILTIN_THEMES = {
|
||||
dark: JSON.parse(fs.readFileSync(darkPath, "utf-8")) as ThemeJson,
|
||||
light: JSON.parse(fs.readFileSync(lightPath, "utf-8")) as ThemeJson,
|
||||
};
|
||||
}
|
||||
return BUILTIN_THEMES;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist", "**/*.d.ts", "src/**/*.d.ts"]
|
||||
"include": ["src/**/*.ts", "src/bun-imports.d.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user