import { i as __toDynamicImportESM } from "./chunk-CoQ2GtNH.js";
import { glob } from "tinyglobby";
import picomatch from "picomatch";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { extractGraphQLSources, generateCode } from "@mearie/native";
import path from "node:path";
import { configureSync, getAnsiColorFormatter, getConsoleSink, getLogger } from "@logtape/logtape";
import pc from "picocolors";

//#region src/glob.ts
/**
* Finds files matching the given patterns.
* @param cwd - Current working directory.
* @param patterns - File patterns to match.
* @returns Array of absolute file paths.
*/
const findFiles = async (cwd, patterns) => {
	const includePatterns = Array.isArray(patterns.include) ? patterns.include : [patterns.include];
	const negatedExcludes = (patterns.exclude ? Array.isArray(patterns.exclude) ? patterns.exclude : [patterns.exclude] : []).map((pattern) => `!${pattern}`);
	return await glob([...includePatterns, ...negatedExcludes], {
		cwd,
		absolute: true
	});
};
/**
* Creates a matcher function.
* @internal
* @param patterns - File patterns to match.
* @returns A function that checks if a file path matches the patterns.
*/
const createMatcher = (patterns) => {
	const includePatterns = Array.isArray(patterns.include) ? patterns.include : [patterns.include];
	const excludePatterns = patterns.exclude ? Array.isArray(patterns.exclude) ? patterns.exclude : [patterns.exclude] : [];
	const isMatch = picomatch(includePatterns);
	const isExcluded = excludePatterns.length > 0 ? picomatch(excludePatterns) : () => false;
	return (filePath) => {
		return isMatch(filePath) && !isExcluded(filePath);
	};
};

//#endregion
//#region src/errors.ts
/**
* Custom error class for Mearie-specific errors.
*/
var MearieError = class MearieError extends Error {
	filePath;
	line;
	column;
	constructor(message, filePath, line, column) {
		super(message);
		this.name = "MearieError";
		this.filePath = filePath;
		this.line = line;
		this.column = column;
	}
	static fromNative(data) {
		if (!data || typeof data !== "object") throw new TypeError("Invalid native error data");
		const error = data;
		const filePath = error.location?.file_path;
		const line = error.location?.line;
		const column = error.location?.column;
		return new MearieError(error.message, filePath, line, column);
	}
};
/**
* Aggregate error for multiple Mearie errors.
*/
var MearieAggregateError = class extends Error {
	errors;
	constructor(errors, message) {
		super(message ?? `${errors.length} error${errors.length > 1 ? "s" : ""} occurred`);
		this.name = "MearieAggregateError";
		this.errors = errors;
	}
};
/**
* Error thrown when `@vue/compiler-sfc` is not installed.
*/
var MissingVueCompilerError = class extends Error {
	constructor() {
		super(`
GraphQL operations cannot be extracted from Vue files without @vue/compiler-sfc.

Install it with:
  npm install @vue/compiler-sfc
  # or
  pnpm add @vue/compiler-sfc
  # or
  yarn add @vue/compiler-sfc
`);
		this.name = "MissingVueCompilerError";
	}
};
/**
* Error thrown when svelte compiler is not installed.
*/
var MissingSvelteCompilerError = class extends Error {
	constructor() {
		super(`
GraphQL operations cannot be extracted from Svelte files without svelte.

Install it with:
  npm install svelte
  # or
  pnpm add svelte
  # or
  yarn add svelte
`);
		this.name = "MissingSvelteCompilerError";
	}
};
/**
* Error thrown when TypeScript is not installed.
*/
var MissingTypeScriptError = class extends Error {
	constructor() {
		super(`
GraphQL operations cannot be extracted from Vue files without typescript.

Install it with:
  npm install typescript
  # or
  pnpm add typescript
  # or
  yarn add typescript
`);
		this.name = "MissingTypeScriptError";
	}
};

//#endregion
//#region src/loaders.ts
/**
* Loads the Vue compiler dynamically.
* @returns The Vue compiler module.
* @throws {MissingVueCompilerError} If `@vue/compiler-sfc` is not installed.
*/
const loadVueCompiler = async () => {
	try {
		return await import("@vue/compiler-sfc");
	} catch {
		throw new MissingVueCompilerError();
	}
};
/**
* Loads TypeScript dynamically.
* @returns The TypeScript module.
* @throws {MissingTypeScriptError} If typescript is not installed.
*/
const loadTypeScript = async () => {
	try {
		return await import("./typescript-dgELwr0O.js").then(__toDynamicImportESM(1));
	} catch {
		throw new MissingTypeScriptError();
	}
};
/**
* Loads the Svelte compiler dynamically.
* @returns The Svelte compiler module.
* @throws {MissingSvelteCompilerError} If svelte is not installed.
*/
const loadSvelteCompiler = async () => {
	try {
		return await import("./compiler-DysSfBLv.js");
	} catch {
		throw new MissingSvelteCompilerError();
	}
};

//#endregion
//#region src/parsers.ts
const extractVueScript = async (source) => {
	const [vueCompiler, typescript] = await Promise.all([loadVueCompiler(), loadTypeScript()]);
	vueCompiler.registerTS(() => typescript);
	const { descriptor } = vueCompiler.parse(source.code, { filename: source.filePath });
	const blocks = [];
	if (descriptor.script) blocks.push({
		code: descriptor.script.content,
		filePath: source.filePath,
		startLine: source.startLine + descriptor.script.loc.start.line - 1
	});
	if (descriptor.scriptSetup) blocks.push({
		code: descriptor.scriptSetup.content,
		filePath: source.filePath,
		startLine: source.startLine + descriptor.scriptSetup.loc.start.line - 1
	});
	return blocks;
};
const extractSvelteScript = async (source) => {
	const ast = (await loadSvelteCompiler()).parse(source.code);
	const blocks = [];
	if (ast.instance?.content) {
		const code = source.code.slice(ast.instance.content.start, ast.instance.content.end);
		const lineOffset = source.code.slice(0, ast.instance.content.start).split("\n").length - 1;
		blocks.push({
			code,
			filePath: `${source.filePath}.instance.ts`,
			startLine: source.startLine + lineOffset
		});
	}
	if (ast.module?.content) {
		const code = source.code.slice(ast.module.content.start, ast.module.content.end);
		const lineOffset = source.code.slice(0, ast.module.content.start).split("\n").length - 1;
		blocks.push({
			code,
			filePath: `${source.filePath}.module.ts`,
			startLine: source.startLine + lineOffset
		});
	}
	return blocks;
};
const extractAstroScripts = (source) => {
	const blocks = [];
	const frontmatterMatch = /^---\s*\n([\s\S]*?)\n---/.exec(source.code);
	if (frontmatterMatch) {
		const lineOffset = source.code.slice(0, frontmatterMatch.index).split("\n").length;
		blocks.push({
			code: frontmatterMatch[1],
			filePath: `${source.filePath}.frontmatter.ts`,
			startLine: source.startLine + lineOffset
		});
	}
	const scriptMatches = source.code.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script>/gi);
	let index = 0;
	for (const match of scriptMatches) {
		const lineOffset = source.code.slice(0, match.index).split("\n").length;
		blocks.push({
			code: match[1],
			filePath: `${source.filePath}.${index}.ts`,
			startLine: source.startLine + lineOffset
		});
		index++;
	}
	return blocks;
};
const extractMarkdownCodeBlocks = (source) => {
	const codeBlocks = [];
	const codeBlockRegex = /```(tsx|ts)[^\n]*mearie[^\n]*\n([\s\S]*?)```/g;
	let match;
	let index = 0;
	while ((match = codeBlockRegex.exec(source.code)) !== null) {
		const lineOffset = source.code.slice(0, match.index).split("\n").length;
		codeBlocks.push({
			code: match[2],
			filePath: `${source.filePath}.${index}.${match[1]}`,
			startLine: source.startLine + lineOffset
		});
		index++;
	}
	return codeBlocks;
};

//#endregion
//#region src/extractor.ts
const extractGraphQLSources$1 = async (source) => {
	const ext = source.filePath.split(".").pop()?.toLowerCase();
	const sources = [];
	const errors = [];
	const blocks = [];
	try {
		switch (ext) {
			case "vue":
				blocks.push(...await extractVueScript(source));
				break;
			case "svelte":
				blocks.push(...await extractSvelteScript(source));
				break;
			case "astro":
				blocks.push(...extractAstroScripts(source));
				break;
			case "md":
				blocks.push(...extractMarkdownCodeBlocks(source));
				break;
			case "js":
			case "jsx":
			case "ts":
			case "tsx":
				blocks.push(source);
				break;
			default: return {
				sources,
				errors
			};
		}
	} catch (error) {
		throw new MearieError(error instanceof Error ? error.message : String(error), source.filePath);
	}
	for (const block of blocks) {
		const result = extractGraphQLSources(block);
		sources.push(...result.sources);
		errors.push(...result.errors.map((error) => MearieError.fromNative(error)));
	}
	return {
		sources,
		errors
	};
};

//#endregion
//#region src/generator.ts
/**
* Generates code from GraphQL documents and schemas.
* @param options - Generation options.
* @returns Generated code.
* @throws {Error} If code generation fails.
*/
const generate = (options) => {
	const { schemas, documents, config } = options;
	const { sources, errors } = generateCode(schemas, documents, config);
	return {
		sources,
		errors: errors.map((error) => MearieError.fromNative(error))
	};
};

//#endregion
//#region src/writer.ts
const writeFiles = async (cwd, sources) => {
	const mearieDir = path.resolve(cwd, ".mearie");
	await mkdir(mearieDir, { recursive: true });
	await Promise.all(sources.map((source) => writeFile(path.join(mearieDir, source.filePath), source.code, "utf8")));
};

//#endregion
//#region src/context.ts
/**
* Stateful context for incremental code generation.
* Reads and caches file contents when files are added/updated.
*/
var CodegenContext = class {
	schemas = /* @__PURE__ */ new Map();
	documents = /* @__PURE__ */ new Map();
	cwd;
	config;
	constructor(cwd = process.cwd()) {
		this.cwd = cwd;
	}
	setConfig(config) {
		this.config = config;
	}
	/**
	* Adds a schema file by reading it.
	* @param filePath - Schema file path.
	*/
	async addSchema(filePath) {
		const code = await readFile(filePath, "utf8");
		this.schemas.set(filePath, {
			code,
			filePath,
			startLine: 1
		});
	}
	/**
	* Removes a schema file.
	* @param filePath - Schema file path.
	*/
	removeSchema(filePath) {
		this.schemas.delete(filePath);
	}
	/**
	* Adds a document file by reading and extracting operations.
	* @param filePath - Document file path.
	*/
	async addDocument(filePath) {
		const { sources, errors } = await extractGraphQLSources$1({
			code: await readFile(filePath, "utf8"),
			filePath,
			startLine: 1
		});
		this.documents.set(filePath, sources);
		if (errors.length > 0) throw new MearieAggregateError(errors);
	}
	/**
	* Removes a document file.
	* @param filePath - Document file path.
	*/
	removeDocument(filePath) {
		this.documents.delete(filePath);
	}
	/**
	* Generates code from cached schemas and documents and writes to files.
	* @returns Written file paths.
	* @throws {Error} If generation or writing fails.
	*/
	async generate() {
		const { sources, errors } = generate({
			schemas: [...this.schemas.values()],
			documents: [...this.documents.values()].flat(),
			config: this.config
		});
		await writeFiles(this.cwd, sources);
		if (errors.length > 0) throw new MearieAggregateError(errors);
	}
};

//#endregion
//#region src/logger.ts
configureSync({
	reset: true,
	sinks: { console: getConsoleSink({ formatter: getAnsiColorFormatter({
		level: "FULL",
		timestamp: "time",
		category: (category) => `💬 ${category.join("·")}`
	}) }) },
	loggers: [{
		category: "mearie",
		lowestLevel: "info",
		sinks: ["console"]
	}, {
		category: ["logtape", "meta"],
		lowestLevel: "warning",
		sinks: ["console"]
	}]
});
const logger = getLogger(["mearie"]);
const formatMearieError = (error) => {
	const parts = [
		error.filePath,
		error.line,
		error.column
	].filter((part) => part !== void 0 && part !== null).map(String);
	const location = parts.length > 0 ? parts.join(":") : "";
	if (location) return `${pc.bold(error.message)} ${pc.cyan(pc.underline(location))}`;
	return pc.bold(error.message);
};
/**
* Reports an error using the provided logger.
* @param logger - The logger to use.
* @param error - The error to report.
*/
const report = (logger$1, error) => {
	if (error instanceof MearieAggregateError) for (const err of error.errors) logger$1.error(formatMearieError(err));
	else if (error instanceof MearieError) logger$1.error(formatMearieError(error));
	else logger$1.error("{error}", { error });
};

//#endregion
export { CodegenContext, createMatcher, findFiles, logger, report };