import { f as throttle, l as removeExtension, m as debug_default, t as VirtualFileSystem } from "./virtual_file_system-BFFXmame.js";
import { mkdir, writeFile } from "node:fs/promises";
import string from "@poppinss/utils/string";
import { dirname, join, relative } from "node:path/posix";
import StringBuilder from "@poppinss/utils/string_builder";
//#region src/file_buffer.ts
/**
* Buffer class to construct template output with proper indentation and formatting.
*
* The FileBuffer class provides a fluent API for building text output with automatic
* indentation management. It's commonly used for generating code or template files
* where proper formatting is important.
*
* @example
* const buffer = new FileBuffer()
* buffer
*   .writeLine('function example() {')
*   .indent()
*   .writeLine('return "Hello World"')
*   .dedent()
*   .writeLine('}')
* console.log(buffer.flush())
*/
var FileBuffer = class FileBuffer {
	/**
	* Whether to add an end-of-line character at the end of the output
	*/
	#eol = false;
	/**
	* Collected lines
	*/
	#buffer = [];
	/**
	* Current indentation size. Each call to indent will increment
	* it by 2
	*/
	#identationSize = 0;
	/**
	* Cached compiled output. Once this value is set, the `flush`
	* method will become a noop
	*/
	#compiledOutput;
	/**
	* Creates a new child buffer instance
	*
	* @returns A new FileBuffer instance
	*/
	create() {
		return new FileBuffer();
	}
	/**
	* Returns the size of buffer text
	*
	* @returns The number of lines in the buffer
	*/
	get size() {
		return this.#buffer.length;
	}
	/**
	* Enable or disable end-of-line character at the end of output
	*
	* @param enabled - Whether to add EOL character
	* @returns This FileBuffer instance for method chaining
	*
	* @example
	* const buffer = new FileBuffer()
	* buffer.eol(true).writeLine('Hello').flush() // 'Hello\n\n'
	*/
	eol(enabled) {
		this.#eol = enabled;
		return this;
	}
	/**
	* Write a new line to the output with current indentation
	*
	* @param text - The text to write as a new line
	* @returns This FileBuffer instance for method chaining
	*/
	writeLine(text) {
		if (typeof text === "string") this.#buffer.push(`${" ".repeat(this.#identationSize)}${text}\n`);
		else {
			this.#buffer.push(text);
			this.#buffer.push("");
		}
		return this;
	}
	/**
	* Write text to the output without adding a new line
	*
	* @param text - The text to write without a newline
	* @returns This FileBuffer instance for method chaining
	*/
	write(text) {
		if (typeof text === "string") this.#buffer.push(`${" ".repeat(this.#identationSize)}${text}`);
		else this.#buffer.push(text);
		return this;
	}
	/**
	* Increase indentation by 2 spaces
	*
	* @returns This FileBuffer instance for method chaining
	*/
	indent() {
		this.#identationSize += 2;
		return this;
	}
	/**
	* Decrease indentation by 2 spaces (minimum of 0)
	*
	* @returns This FileBuffer instance for method chaining
	*/
	dedent() {
		this.#identationSize -= 2;
		if (this.#identationSize < 0) this.#identationSize = 0;
		return this;
	}
	/**
	* Convert the buffer to a string representation
	*
	* @example
	* const buffer = new FileBuffer()
	* buffer.writeLine('Hello')
	* console.log(buffer.toString())
	*/
	toString() {
		return this.flush();
	}
	/**
	* Return template as a string, joining all buffer lines
	*
	* Once called, the output is cached and subsequent calls return the same result.
	* The flush method becomes a no-op after the first call.
	*
	* @returns The complete buffer content as a string
	*/
	flush() {
		if (this.#compiledOutput !== void 0) return this.#compiledOutput;
		this.#compiledOutput = this.#buffer.join("\n");
		return this.#eol ? `${this.#compiledOutput}\n` : this.#compiledOutput;
	}
};
//#endregion
//#region src/index_generator/source.ts
/**
* IndexGeneratorSource handles the generation of a single index file.
*
* This class is responsible for scanning a source directory, processing files
* according to configuration, and generating an index file that exports the
* discovered modules. It supports both barrel file generation and custom
* generation strategies.
*
* @example
* const source = new IndexGeneratorSource(appRoot, {
*   source: 'app/controllers',
*   output: 'app/controllers/index.ts',
*   as: 'barrelFile',
*   exportName: 'controllers'
* })
* await source.generate()
*/
var IndexGeneratorSource = class {
	/**
	* The application root directory path
	*/
	#appRoot;
	/**
	* The absolute path to the output file
	*/
	#output;
	/**
	* The absolute path to the source directory
	*/
	#source;
	/**
	* The directory containing the output file
	*/
	#outputDirname;
	/**
	* Virtual file system for scanning source files
	*/
	#vfs;
	/**
	* Configuration for this index generator source
	*/
	#config;
	/**
	* CLI logger instance for output messages
	*/
	#cliLogger;
	/**
	* Generate the output content and write it to the output file
	*
	* This method creates the file buffer, populates it with the generated
	* content based on configuration, and writes it to disk.
	*/
	#generateOutput = throttle(async () => {
		const buffer = new FileBuffer().eol(true);
		if (this.#config.comment) buffer.writeLine(this.#textToComment(typeof this.#config.comment === "string" ? this.#config.comment : `This file is automatically generated.\nDO NOT EDIT manually`));
		if (this.#config.as === "barrelFile") this.#asBarrelFile(this.#vfs, buffer, this.#config.exportName, this.#config.disableLazyImports);
		else this.#config.as(this.#vfs, buffer, this.#config, { toImportPath: this.#createBarrelFileImportGenerator(this.#source, this.#outputDirname, this.#config) });
		await mkdir(dirname(this.#output), { recursive: true });
		await writeFile(this.#output, buffer.flush());
	});
	/**
	* Unique name for this index generator source
	*/
	name;
	/**
	* Create a new IndexGeneratorSource instance
	*
	* @param name - Unique name for this index generator source
	* @param appRoot - The application root directory path
	* @param cliLogger - Logger instance for CLI output
	* @param config - Configuration for this index generator source
	*/
	constructor(name, appRoot, cliLogger, config) {
		this.name = name;
		this.#config = config;
		this.#appRoot = appRoot;
		this.#cliLogger = cliLogger;
		this.#source = join(this.#appRoot, this.#config.source);
		this.#output = join(this.#appRoot, this.#config.output);
		this.#outputDirname = dirname(this.#output);
		this.#vfs = new VirtualFileSystem(this.#source, {
			glob: this.#config.glob,
			filter: this.#config.filter
		});
	}
	/**
	* Converts a recursive file tree to a string representation
	*
	* This method recursively processes a file tree structure and writes
	* it as JavaScript object notation to the provided buffer.
	*
	* @param input - The recursive file tree to convert
	* @param buffer - The file buffer to write the output to
	*/
	#treeToString(input, buffer) {
		Object.keys(input).forEach((key) => {
			const value = input[key];
			if (typeof value === "string") buffer.write(`${key}: ${value},`);
			else {
				buffer.write(`${key}: {`).indent();
				this.#treeToString(value, buffer);
				buffer.dedent().write(`},`);
			}
		});
	}
	/**
	* Converts a text into a JS comment block. Respecting line-breaks.
	*/
	#textToComment(text) {
		return `/**\n * ${text.split("\n").join("\n * ")}\n */`;
	}
	/**
	* Transforms the barrel file index key. Converts basename to PascalCase
	* and all other paths to camelCase
	*
	* @param config - Configuration containing suffix removal options
	* @returns Function that transforms file paths to appropriate keys
	*/
	#createBarrelFileKeyGenerator(config) {
		return function(key) {
			let paths = key.split("/");
			let baseName = new StringBuilder(paths.pop());
			if (config.skipSegments?.length) paths = paths.filter((p) => !config.skipSegments.includes(p));
			if (config.computeBaseName) baseName = config.computeBaseName(baseName);
			else baseName = baseName.removeSuffix(config.removeSuffix ?? "").pascalCase();
			return [...paths.map((p) => string.camelCase(p)), baseName.toString()].join("/");
		};
	}
	/**
	* Converts the file path to a lazy import. In case of an alias, the source
	* path is replaced with the alias, otherwise a relative import is created
	* from the output dirname.
	*
	* @param source - The source directory path
	* @param outputDirname - The output directory path
	* @param config - Configuration containing import alias options
	* @returns Function that converts file paths to import statements
	*/
	#createBarrelFileImportGenerator(source, outputDirname, config) {
		return function(filePath) {
			if (config.importAlias) {
				debug_default("converting \"%s\" to import alias, source \"%s\"", filePath, source);
				return removeExtension(filePath.replace(source, config.importAlias));
			}
			debug_default("converting \"%s\" to relative import, source \"%s\"", filePath, outputDirname);
			return relative(outputDirname, filePath);
		};
	}
	/**
	* Generate a barrel file export structure
	*
	* This method creates a nested object structure that represents all
	* discovered files as lazy imports, organized by directory structure.
	*
	* @param vfs - Virtual file system containing the scanned files
	* @param buffer - File buffer to write the barrel exports to
	* @param exportName - Name for the main export object
	*/
	#asBarrelFile(vfs, buffer, exportName, disableLazyImports) {
		const useEagerImports = disableLazyImports === true ? true : false;
		const keyGenerator = this.#createBarrelFileKeyGenerator(this.#config);
		const importsBuffer = buffer.create();
		const importGenerator = this.#createBarrelFileImportGenerator(this.#source, this.#outputDirname, this.#config);
		const tree = vfs.asTree({
			transformKey: keyGenerator,
			transformValue: (filePath, key) => {
				if (useEagerImports) {
					const importKey = new StringBuilder(key).pascalCase().toString();
					importsBuffer.write(`import ${importKey} from '${importGenerator(filePath)}'`);
					return importKey;
				}
				return `() => import('${importGenerator(filePath)}')`;
			}
		});
		if (!Object.keys(tree).length) {
			buffer.write(`export const ${exportName} = {}`);
			return;
		}
		if (useEagerImports) buffer.writeLine(importsBuffer);
		buffer.write(`export const ${exportName} = {`).indent();
		this.#treeToString(tree, buffer);
		buffer.dedent().write(`}`);
	}
	/**
	* Displays the log message after generating the index file
	*
	* @example
	* const startTime = process.hrtime()
	* // ... perform operations
	* this.#logCreation(startTime)
	*/
	#logCreation(startTime) {
		this.#cliLogger.info(`created ${this.#config.output}`, { startTime });
	}
	/**
	* Add a file to the virtual file system and regenerate index if needed
	*
	* If the file matches the configured glob patterns, it will be added
	* to the virtual file system and the index file will be regenerated.
	*
	* @param filePath - Absolute path of the file to add
	*/
	async addFile(filePath) {
		if (this.#vfs.add(filePath)) {
			debug_default("file added, re-generating \"%s\" index", this.name);
			const startTime = process.hrtime();
			await this.#generateOutput();
			this.#logCreation(startTime);
		}
	}
	/**
	* Remove a file from the virtual file system and regenerate index if needed
	*
	* If the file was previously tracked, it will be removed from the
	* virtual file system and the index file will be regenerated.
	*
	* @param filePath - Absolute path of the file to remove
	*/
	async removeFile(filePath) {
		if (this.#vfs.remove(filePath)) {
			debug_default("file removed, re-generating \"%s\" index", this.name);
			const startTime = process.hrtime();
			await this.#generateOutput();
			this.#logCreation(startTime);
		}
	}
	/**
	* Generate the index file
	*
	* This method scans the source directory, processes files according to
	* the configuration, and writes the generated index file to disk.
	*/
	async generate() {
		debug_default("generating \"%s\" index", this.name);
		await this.#vfs.scan();
		await this.#generateOutput();
	}
};
//#endregion
//#region src/index_generator/main.ts
/**
* IndexGenerator manages the generation of index files for AdonisJS applications.
*
* This class provides a way to configure multiple sources and generate index files
* that export collections of modules, typically used for barrel exports or
* auto-discovery patterns in AdonisJS applications.
*
* @example
* const generator = new IndexGenerator(appRoot)
* generator.add('controllers', {
*   source: 'app/controllers',
*   output: 'app/controllers/index.ts',
*   as: 'barrelFile',
*   exportName: 'controllers'
* })
* await generator.generate()
*/
var IndexGenerator = class {
	/**
	* The application root directory path
	*/
	appRoot;
	/**
	* Collection of registered index generator sources
	*/
	#sources = {};
	#cliLogger;
	/**
	* Create a new IndexGenerator instance
	*
	* @param appRoot - The application root directory path
	* @param cliLogger - Logger instance for CLI output
	*/
	constructor(appRoot, cliLogger) {
		this.appRoot = appRoot;
		this.#cliLogger = cliLogger;
	}
	/**
	* Add a new index generator source
	*
	* @param name - Unique name for the source
	* @param config - Configuration for the index generator source
	* @returns This IndexGenerator instance for method chaining
	*/
	add(name, config) {
		this.#sources[name] = new IndexGeneratorSource(name, this.appRoot, this.#cliLogger, config);
		return this;
	}
	/**
	* Add a file to all registered index generator sources
	*
	* This method propagates the file addition to all registered sources,
	* allowing them to regenerate their index files if the new file matches
	* their glob patterns.
	*
	* @param filePath - Absolute path of the file to add
	*/
	async addFile(filePath) {
		const sources = Object.values(this.#sources);
		for (let source of sources) await source.addFile(filePath);
	}
	/**
	* Remove a file from all registered index generator sources
	*
	* This method propagates the file removal to all registered sources,
	* allowing them to regenerate their index files if the removed file
	* was previously tracked.
	*
	* @param filePath - Absolute path of the file to remove
	*/
	async removeFile(filePath) {
		const sources = Object.values(this.#sources);
		for (let source of sources) await source.removeFile(filePath);
	}
	/**
	* Generate all registered index files
	*
	* Iterates through all registered sources and generates their
	* corresponding index files.
	*/
	async generate() {
		const sources = Object.values(this.#sources);
		for (let source of sources) await source.generate();
		if (sources.length) this.#cliLogger.info(`codegen: created ${sources.length} file(s)`);
	}
};
//#endregion
export { FileBuffer as n, IndexGenerator as t };
