[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: subwaytime"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n.DS_Store\ndist\n.prettierignore\n.vscode\npackage-lock.json\nexample/jsconfig.json\nexmaple/tsconfig.json"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"endOfLine\": \"auto\",\n  \"jsxBracketSameLine\": true,\n  \"jsxSingleQuote\": true,\n  \"printWidth\": 120,\n  \"singleQuote\": true,\n  \"tabWidth\": 4,\n  \"trailingComma\": \"all\",\n  \"useTabs\": true,\n  \"vueIndentScriptAndStyle\": false\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020-PRESENT Leon Langer<https://github.com/subwaytime>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<h2 align=\"left\">vite-aliases</h2>\n\n<p align=\"left\">Alias auto generation for Vite 6</p>\n\n<p align=\"left\">\n<a href=\"https://www.npmjs.com/package/vite-aliases\">\n<img src=\"https://img.shields.io/npm/v/vite-aliases?color=222&style=flat-square\">\n</a>\n</p>\n\n## Usage\n\nInstall\n\n```bash\nnpm i vite-aliases -D\n```\n\nAdd it to `vite.config.js`\n\n```ts\n// vite.config.js\nimport { ViteAliases } from 'vite-aliases'\n\nexport default {\n  plugins: [\n    ViteAliases()\n  ]\n};\n\n```\n\nAdd this to `package.json` (Plugin is only available for ESM)\n```json\n{\n  \"type\": \"module\"\n}\n```\n\nThat's it!\n\n<p>\n<br/>\nBased on your Folderstructure, it will now automatically generate all needed aliases.\n<br />\nYou can configure it to any desired Depth, but it is recommend to stay on the first Level!\n<br />\nFurthermore it will use your native OS filesystem, so it works on Linux, Mac, Windows and other OS.\n</p>\n\n<br />\nThis structure:\n\n```\nsrc\n  assets\n  components\n  pages\n  store\n  utils\n```\n\nwill generate the following:\n\n```ts\n[\n  {\n    find: '~',\n    replacement: '${your_project_path}/src'\n  },\n  {\n    find: '~assets',\n    replacement: '${your_project_path}/src/assets'\n  },\n  {\n    find: '~components',\n    replacement: '${your_project_path}/src/components'\n  },\n  {\n    find: '~pages',\n    replacement: '${your_project_path}/src/pages'\n  },\n  {\n    find: '~store',\n    replacement: '${your_project_path}/src/store'\n  },\n  {\n    find: '~utils',\n    replacement: '${your_project_path}/src/utils'\n  },\n]\n```\n\n## Best Practice\n\n`vite-aliases` is meant to simply take the first Layer of your folders and turn it into useful Shortcuts.\n<br />\nTherefore i advise you to use the default Configuration and not use folders with the same name, otherwise it will create an Error.\n\nIf however you need duplicate Foldernames, enable `adjustDuplicates`.\n<br />\nThis will turn the entire Filepath of said duplicate into the alias itself, like shown in the Example below.\n\nExample:\n```\n`src/components` -> `~components`\n`src/pages/components` -> `~pagesComponents`\n`src/test/new/partials/components` -> `~testNewPartialsComponents`\n```\nand so on..\n\n## Configuration\n\nCurrent available options:\n\n```ts\nViteAliases({\n  /**\n  * Relative path to the project directory\n  */\n  dir: 'src',\n\n  /**\n  * Prefix symbol for the aliases\n  */\n  prefix: '~',\n\n  /**\n  * Allow searching for subdirectories\n  */\n  deep: true,\n\n  /**\n  * Search depthlevel for subdirectories\n  */\n  depth: 1,\n\n  /**\n  * Creates a Logfile\n  * use `logPath` to change the location\n  */\n  createLog: false,\n\n  /**\n  * Path for Logfile\n  */\n  logPath: 'src/logs',\n\n  /**\n  * Create global project directory alias\n  */\n  createGlobalAlias: true,\n\n  /**\n  * Turns duplicates into camelCased path aliases\n  */\n  adjustDuplicates: false,\n\n  /**\n  * Used paths in JS/TS configs will now be relative to baseUrl\n  */\n  useAbsolute: false,\n\n  /**\n  * Adds seperate index paths\n  * approach created by @davidohlin\n  */\n  useIndexes: false,\n\n  /**\n  * Generates paths in IDE config file\n  * works with JS or TS\n  */\n  useConfig: true,\n\n  /**\n  * Override config paths\n  */\n  ovrConfig: false,\n\n  /**\n  * Will generate Paths in tsconfig\n  * used in combination with `useConfig`\n  * Typescript will be auto detected\n  */\n  dts: false,\n\n  /**\n  * Disables any terminal output\n  */\n  silent: true,\n\n  /**\n  * Root path of Vite project\n  */\n  root: process.cwd()\n});\n```\n\n## Thanks\n\nThanks to [@brattonross](https://github.com/brattonross) and [@antfu](https://github.com/antfu),\ndue to this tiny library beeing inspired by both projects:\n\n[vite-plugin-voie](https://github.com/vamplate/vite-plugin-voie)\n\n[unplugin-vue-components](https://github.com/antfu/unplugin-vue-components).\n\n## License\n\nMIT License © 2020-PRESENT [Leon Langer](https://github.com/subwaytime)\n"
  },
  {
    "path": "example/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Vite App</title>\n</head>\n<body>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./src/main.ts\"></script>\n</body>\n</html>"
  },
  {
    "path": "example/package.json",
    "content": "{\n\t\"name\": \"@vite-aliases/example\",\n\t\"description\": \"Example: Alias auto generation for Vite\",\n\t\"version\": \"0.1.0\",\n\t\"private\": true,\n\t\"license\": \"MIT\",\n\t\"author\": \"Subwaytime <leon.l@nophase.de>\",\n\t\"type\": \"module\",\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"https://github.com/subwaytime/vite-aliases\"\n\t},\n\t\"homepage\": \"https://github.com/subwaytime/vite-aliases#readme\",\n\t\"bugs\": \"https://github.com/subwaytime/vite-aliases/issues\",\n\t\"scripts\": {\n\t\t\"update:packages\": \"npm update --save-dev && npm update --save\",\n\t\t\"dev\": \"vite\",\n\t\t\"build\": \"vite build\"\n\t},\n\t\"dependencies\": {\n\t\t\"@vitejs/plugin-vue\": \"^4.3.3\",\n\t\t\"@vue/compiler-sfc\": \"^3.3.4\",\n\t\t\"vite\": \"^4.4.9\",\n\t\t\"vitest\": \"^0.34.3\",\n\t\t\"vue\": \"^3.3.4\"\n\t},\n\t\"devDependencies\": {\n\t\t\"typescript\": \"^5.2.2\"\n\t}\n}\n"
  },
  {
    "path": "example/src/app.vue",
    "content": "<template>\n\t<div class=\"wrapper\">\n\t\t<h1> Vite Aliases Example </h1>\n\t\t<home />\n\t\t<navigation />\n\t\t<div class=\"box\">\n\t\t\t<p>\n\t\t\t\tRandomized Hexcode\n\t\t\t\t<br />\n\t\t\t\t<code> loaded from ~utils/random.js </code>\n\t\t\t</p>\n\t\t\t<div class=\"colored\" :style=\"`background-color: ${color};`\">\n\t\t\t\t{{ color }}\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport home from '~pages/home.vue';\nimport navigation from '~components/navigation.vue';\nimport { random } from '~utils/random';\n\nconst color = ref(random());\n</script>\n\n<style lang=\"css\">\n\tbody {\n\t\tfont-family: Avenir, Helvetica, Arial, sans-serif;\n\t\t-webkit-font-smoothing: antialiased;\n\t\t-moz-osx-font-smoothing: grayscale;\n\t\talign-items: center;\n\t\tbackground: #e9ecef;\n\t\tdisplay: flex;\n\t\theight: 100vh;\n\t\tjustify-content: center;\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t\ttext-align: center;\n\t\twidth: 100%;\n\t}\n\n\tp {\n\t\tmargin: 0.5rem 0;\n\t}\n\n\t.wrapper {\n\t\talign-items: center;\n\t\tbackground: #f8f9fa;\n\t\tborder-radius: 1rem;\n\t\tbox-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n\t\tdisplay: flex;\n\t\tflex-flow: column wrap;\n\t\theight: 24rem;\n\t\tjustify-content: center;\n\t\tmargin: 0 auto;\n\t\tpadding: 1.5rem;\n\t\twidth: 24rem;\n\t}\n\n\t.colored {\n\t\tcolor: #151515;\n\t\tborder-radius: 1rem;\n\t\tpadding: 0.75em;\n\t\tmargin-top: 0.5rem;\n\t\ttext-align: center;\n\t}\n</style>"
  },
  {
    "path": "example/src/components/navigation.vue",
    "content": "<template>\n\t<p> Navigation loaded from\n\t\t<br />\n\t\t<code> ~components/navigation.vue </code>\n\t</p>\n</template>\n"
  },
  {
    "path": "example/src/logs/vite-aliases.json",
    "content": "[\n    {\n        \"find\": \"@assets\",\n        \"replacement\": \"D:/work/vite-aliases/example/src/assets\"\n    },\n    {\n        \"find\": \"@components\",\n        \"replacement\": \"D:/work/vite-aliases/example/src/components\"\n    },\n    {\n        \"find\": \"@logs\",\n        \"replacement\": \"D:/work/vite-aliases/example/src/logs\"\n    },\n    {\n        \"find\": \"@pages\",\n        \"replacement\": \"D:/work/vite-aliases/example/src/pages\"\n    },\n    {\n        \"find\": \"@utils\",\n        \"replacement\": \"D:/work/vite-aliases/example/src/utils\"\n    },\n    {\n        \"find\": \"@\",\n        \"replacement\": \"D:/work/vite-aliases/example/src\"\n    }\n]"
  },
  {
    "path": "example/src/main.ts",
    "content": "import App from './app.vue'\nimport { createApp } from 'vue'\n\ncreateApp(App).mount('#app');"
  },
  {
    "path": "example/src/pages/home.vue",
    "content": "<template>\n\t<p> Home loaded from\n\t\t<br />\n\t\t<code> ~pages/home.vue </code>\n\t</p>\n</template>"
  },
  {
    "path": "example/src/utils/random.ts",
    "content": "/**\n * Return a Random Hexcode\n */\n\nexport function random() {\n\tlet n = (Math.random() * 0xfffff * 1000000).toString(16);\n\treturn '#' + n.slice(0, 6);\n};"
  },
  {
    "path": "example/tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"baseUrl\": \".\",\n        \"paths\": {\n            \"~components/*\": [\n                \"src/components/*\"\n            ],\n            \"~logs/*\": [\n                \"src/logs/*\"\n            ],\n            \"~pages/*\": [\n                \"src/pages/*\"\n            ],\n            \"~utils/*\": [\n                \"src/utils/*\"\n            ],\n            \"~/*\": [\n                \"src/*\"\n            ]\n        }\n    }\n}"
  },
  {
    "path": "example/vite-aliases.json",
    "content": "[\n    {\n        \"find\": \"~components\",\n        \"replacement\": \"/home/subway/work/vite-aliases/example/src/components\"\n    },\n    {\n        \"find\": \"~logs\",\n        \"replacement\": \"/home/subway/work/vite-aliases/example/src/logs\"\n    },\n    {\n        \"find\": \"~pages\",\n        \"replacement\": \"/home/subway/work/vite-aliases/example/src/pages\"\n    },\n    {\n        \"find\": \"~utils\",\n        \"replacement\": \"/home/subway/work/vite-aliases/example/src/utils\"\n    },\n    {\n        \"find\": \"~\",\n        \"replacement\": \"/home/subway/work/vite-aliases/example/src\"\n    }\n]"
  },
  {
    "path": "example/vite.config.ts",
    "content": "import vue from '@vitejs/plugin-vue';\nimport { defineConfig } from 'vitest/config';\nimport { ViteAliases } from '../dist/index.js';\n\n\nexport default defineConfig({\n\tplugins: [\n\t\tvue(),\n\t\tViteAliases({\n\t\t\tcreateLog: true,\n\t\t\tlogPath: './',\n\t\t\tadjustDuplicates: true,\n\t\t}),\n\t],\n\tserver: {\n\t\tport: 8080,\n\t},\n\tlogLevel: 'silent',\n});"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"vite-aliases\",\n  \"description\": \"Alias auto generation for Vite\",\n  \"version\": \"0.11.8\",\n  \"type\": \"module\",\n  \"main\": \"./dist/index.js\",\n  \"module\": \"./dist/index.js\",\n  \"types\": \"./dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"module\": \"./dist/index.js\",\n      \"import\": \"./dist/index.js\"\n    }\n  },\n  \"private\": false,\n  \"license\": \"MIT\",\n  \"author\": \"Subwaytime <leon.l@nophase.de>\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/subwaytime/vite-aliases\"\n  },\n  \"homepage\": \"https://github.com/subwaytime/vite-aliases#readme\",\n  \"bugs\": \"https://github.com/subwaytime/vite-aliases/issues\",\n  \"files\": [\n    \"dist\",\n    \"*.d.ts\"\n  ],\n  \"keywords\": [\n    \"vite\",\n    \"vue\",\n    \"alias\",\n    \"aliases\",\n    \"auto\",\n    \"generation\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsup\",\n    \"dev\": \"npm run build -- --watch src\",\n    \"release\": \"npx git-ensure -a && npx bumpp --commit --tag --push && npm publish\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^22.10.5\",\n    \"terser\": \"^5.37.0\",\n    \"tsup\": \"^8.3.5\",\n    \"typescript\": \"^5.7.2\"\n  },\n  \"dependencies\": {\n    \"chokidar\": \"^4.0.3\",\n    \"comment-json\": \"^4.2.5\",\n    \"consola\": \"^3.3.3\",\n    \"fast-glob\": \"^3.3.3\",\n    \"local-pkg\": \"^0.5.1\"\n  },\n  \"peerDependencies\": {\n    \"vite\": \"6.x\"\n  },\n  \"packageManager\": \"pnpm@9.15.2\",\n  \"engines\": {\n    \"node\": \">=22.x.x\"\n  }\n}\n"
  },
  {
    "path": "src/constants.ts",
    "content": "import { isPackageExists } from 'local-pkg';\nimport type { Options } from './types';\n\nexport const MODULE_NAME = 'vite-aliases';\n\nexport const config: Required<Options> = {\n\tdir: 'src',\n\n\tprefix: '~',\n\tdeep: true,\n\tdepth: 1,\n\n\tcreateGlobalAlias: true,\n\tcreateLog: false,\n\tlogPath: 'src/logs',\n\tadjustDuplicates: false,\n\n\tuseAbsolute: false,\n\tuseConfig: true,\n\tovrConfig: false,\n\tuseIndexes: false,\n\n\tdts: false,\n\tsilent: true,\n\troot: process.cwd(),\n};\n\nexport const IDEConfig = {\n\tcompilerOptions: {\n\t\tbaseUrl: '.',\n\t\tpaths: {},\n\t},\n};\n"
  },
  {
    "path": "src/fs/config.ts",
    "content": "import { abort, readJSON, interpretFileIndentation, writeJSON } from '../utils';\n\nimport { IDEConfig } from '../constants';\nimport type { Generator } from '../generator';\nimport type { Process } from '../types';\nimport type { Indentation } from '../utils';\nimport { normalizePath } from 'vite';\nimport { isEmpty } from '../utils';\n\n/**\n * Creates a JS or TS Configfile\n */\n\nexport async function writeConfig(gen: Generator, process: Process = 'default') {\n\tconst { root, dir, dts, useConfig, ovrConfig } = gen.options;\n\n\tif (!useConfig) {\n\t\treturn;\n\t}\n\n\tconst name = dts ? 'tsconfig' : 'jsconfig';\n\tconst file = normalizePath(`${root}/${name}.json`);\n\n\ttry {\n\t\tlet [indentation, json]: [Indentation, any] = await Promise.all([\n\t\t\tinterpretFileIndentation(file),\n\t\t\treadJSON(file),\n\t\t]);\n\n\t\tif (isEmpty(json) || isEmpty(json.compilerOptions)) {\n\t\t\tjson = Object.assign({}, IDEConfig);\n\t\t}\n\n\t\tlet paths = json.compilerOptions.paths || {};\n\n\t\tif (process === 'remove') {\n\t\t\tpaths = Object.fromEntries(\n\t\t\t\tObject.entries(paths).filter((p: any) => {\n\t\t\t\t\tif (Object.values(gen.paths).flat().includes(p[1][0]) && p[1][0].includes(dir)) {\n\t\t\t\t\t\treturn p;\n\t\t\t\t\t} else if (!p[1][0].includes(dir)) {\n\t\t\t\t\t\treturn p;\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tjson.compilerOptions.paths = ovrConfig ? gen.paths : { ...paths, ...gen.paths };\n\t\tawait writeJSON(file, json, process, indentation);\n\t} catch (error) {\n\t\tabort(`Cannot write Config: ${file}.`);\n\t}\n}\n"
  },
  {
    "path": "src/fs/glob.ts",
    "content": "import fg from 'fast-glob';\nimport type { Generator } from '../generator';\nimport { logger } from '../utils';\n\n/**\n * Return all folders from the project directory\n * @param options\n */\n\nexport async function getDirectories(gen: Generator) {\n\tconst { dir, root, deep, depth } = gen.options;\n\n\tconst directories = await fg.sync(deep ? `${dir}/**/*` : `${dir}/*`, {\n\t\tignore: ['node_modules'],\n\t\tonlyDirectories: true,\n\t\tcwd: root,\n\t\tdeep: depth,\n\t\tabsolute: true,\n\t});\n\n\tif (!directories.length) {\n\t\tlogger.error(new Error('No Directories could be found!'));\n\t}\n\n\tgen.addAlias(directories);\n}\n"
  },
  {
    "path": "src/fs/index.ts",
    "content": "export { writeConfig } from './config';\nexport { getDirectories } from './glob';\nexport { writeLog } from './log';\n"
  },
  {
    "path": "src/fs/log.ts",
    "content": "import { existsSync } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport { normalizePath } from 'vite';\n\nimport { MODULE_NAME } from '../constants';\nimport type { Generator } from '../generator';\nimport type { Process } from '../types';\nimport { abort, writeJSON } from '../utils';\n\n\n\n/**\n * Creates a Logfile\n * If needed it will also create a Logfolder\n */\n\nexport async function writeLog(gen: Generator, process: Process = 'normal') {\n\tconst { createLog, logPath } = gen.options;\n\n\tif (!createLog) {\n\t\treturn;\n\t}\n\n\tconst folder = normalizePath(logPath);\n\tconst file = normalizePath(`${folder}/${MODULE_NAME}.json`);\n\tconst data = gen.aliases;\n\n\ttry {\n\t\tif (!existsSync(folder)) {\n\t\t\tawait mkdir(folder, { recursive: true });\n\t\t}\n\t\tawait writeJSON(file, data, process);\n\t} catch (error) {\n\t\tabort(`Cannot create Logfolder ${folder}.`);\n\t}\n}\n"
  },
  {
    "path": "src/generator.ts",
    "content": "import type { Alias, Options, Path } from './types';\nimport { normalizePath } from 'vite';\nimport { logger, split, toArray, toCamelCase, toRelative } from './utils';\nimport { writeLog, writeConfig } from './fs';\n\nimport chokidar from 'chokidar';\nimport { resolve } from 'path';\nimport { config } from \"./constants\";\nimport { getDirectories } from './fs';\nimport { isPackageExists } from 'local-pkg';\n\n/**\n * Reads the Projectpath and returns Vite Aliases\n * @param options\n * @returns Record<string, string>\n */\n\nexport class Generator {\n\treadonly options: Options;\n\treadonly fullPath: string;\n\n\tpublic aliases: Alias[] = [];\n\tpublic directories = new Set<string>();\n\tpublic paths: Path = {};\n\n\tconstructor(public readonly servermode: string, options?: Partial<Options>) {\n\t\tthis.options = Object.assign({}, config, options);\n\n\t\tlogger.level = this.options.silent ? -999 : 3;\n\n\t\tthis.fullPath = normalizePath(resolve(this.options.root, this.options.dir)); // needed for absolute paths in watcher\n\n\t\tthis.detectTypescript();\n\n\t\t// only watch on dev not on build\n\t\tif (servermode === 'serve') {\n\t\t\tthis.observe();\n\t\t}\n\t}\n\n\t/**\n\t * Add Alias\n\t * @param path\n\t */\n\n\taddAlias(path: string | string[]) {\n\t\ttoArray(path).forEach((p) => {\n\t\t\tconst correctedPath = normalizePath(p);\n\t\t\tconst folders = split(correctedPath.replace(this.fullPath, this.options.dir), '/').filter(Boolean);\n\t\t\tconst lastDir = folders.slice(-1)[0];\n\t\t\tlet key = `${this.options.prefix}${lastDir}`;\n\n\t\t\tconst uniqueFolders = [...new Set(folders)] as string[];\n\t\t\tthis.checkForDuplicates(correctedPath, folders, uniqueFolders);\n\n\t\t\tif(this.aliases.some((a) => a.find === key)) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t'There are duplicate Aliases generated, either fix the folderstructure or enable adjustDuplicates.',\n\t\t\t\t);\n\n\t\t\t\tif (this.options.adjustDuplicates && this.options.depth > 1) {\n\t\t\t\t\tconst name = folders.filter((f) => !split(normalizePath(this.options.dir), '/').includes(f)).join('-');\n\t\t\t\t\tkey = `${this.options.prefix}${toCamelCase(name)}`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(lastDir === this.options.dir && this.options.createGlobalAlias) {\n\t\t\t\tkey = `${this.options.prefix}`;\n\t\t\t}\n\n\t\t\tthis.directories.add(p);\n\t\t\tthis.aliases.push({\n\t\t\t\tfind: `${key}`,\n\t\t\t\treplacement: `${p}`\n\t\t\t});\n\n\t\t\tconst configPath = this.options.useAbsolute ? correctedPath : toRelative(correctedPath, this.options.dir);\n\n\t\t\tif(this.options.useIndexes) {\n\t\t\t\tthis.paths[key] = [configPath];\n\t\t\t} else {\n\t\t\t\tthis.paths[`${key}/*`] = [`${configPath}/*`];\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Remove Alias\n\t * @param path\n\t */\n\n\tremoveAlias(path: string | string[]) {\n\t\ttoArray(path).forEach((p) => {\n\t\t\tconst correctedPath = normalizePath(p);\n\n\t\t\tif(this.directories.has(correctedPath)) {\n\t\t\t\tthis.directories.delete(correctedPath);\n\t\t\t\tthis.aliases = this.aliases.filter((a) => a.replacement != correctedPath);\n\t\t\t\tthis.paths = Object.fromEntries(\n\t\t\t\t\tObject.entries(this.paths).filter(\n\t\t\t\t\t\t(configPath) => configPath[1][0].slice(0, -2) != (\n\t\t\t\t\t\t\tthis.options.useIndexes ? correctedPath : `${correctedPath}/*`\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Check for duplicates before adding them as aliases\n\t * @param initialPath\n\t * @param folders\n\t * @param uniqueFolders\n\t */\n\n\tcheckForDuplicates(initialPath: string, folders: string[], uniqueFolders: string[]) {\n\t\tif (folders.length !== uniqueFolders.length) {\n\t\t\tconst duplicateFolders = [...folders].sort().filter((f, i, self) => {\n\t\t\t\tif (self[i + 1] === self[i]) {\n\t\t\t\t\treturn f;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tlogger.warn(`Path: '${initialPath}' contains multiple folders with same name: ${duplicateFolders.toString()}.`);\n\t\t}\n\t}\n\n\t/**\n\t *  Check if TypeScript is installed\n\t */\n\n\tdetectTypescript() {\n\t\tthis.options.dts = this.options.dts || isPackageExists(`${this.options.root}/node_modules/typescript`) || isPackageExists('typescript');\n\t\tlogger.info(this.options.dts ? 'TypeScript got detected.' : 'TypeScript is not installed, fallback to JS only.');\n\t}\n\n\t/**\n\t * Glob directories\n\t * writes Logfile\n\t * writes IDE Config\n\t */\n\n\tprivate searched: boolean = false;\n\n\tasync init() {\n\t\tif (this.searched) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait getDirectories(this);\n\n\t\t// add global alias if allowed\n\t\tif (this.options.createGlobalAlias) {\n\t\t\tthis.addAlias(this.fullPath);\n\t\t}\n\n\t\t// start alias logger if allowed\n\t\twriteLog(this);\n\n\t\t// write js/ts config if allowed\n\t\twriteConfig(this);\n\n\t\tthis.searched = true;\n\t}\n\n\t/**\n\t * Watch for directory changes\n\t */\n\n\tobserve() {\n\t\tconst watcher = chokidar.watch(this.fullPath, { ignoreInitial: true, depth: this.options.depth });\n\n\t\twatcher\n\t\t\t.on('addDir', (path) => {\n\t\t\t\tthis.addAlias(path);\n\t\t\t\twriteLog(this, 'add');\n\t\t\t\twriteConfig(this, 'add');\n\t\t\t\tlogger.info(`Watcher added new Path: ${path}`);\n\t\t\t})\n\t\t\t.on('unlinkDir', (path) => {\n\t\t\t\tthis.removeAlias(path);\n\t\t\t\twriteLog(this, 'remove');\n\t\t\t\twriteConfig(this, 'remove');\n\t\t\t\tlogger.info(`Watcher removed Path: ${path}`);\n\t\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "import type { Plugin } from 'vite';\nimport { Generator } from './generator';\nimport type { Options } from './types';\nimport { toArray } from './utils';\n\nexport function ViteAliases(options: Partial<Options> = {}): Plugin {\n\tlet gen: Generator;\n\n\treturn {\n\t\tname: 'vite-aliases',\n\t\tenforce: 'pre',\n\t\tconfig(config, { command }) {\n\t\t\tgen = new Generator(command, options);\n\t\t\tgen.init();\n\n\t\t\tconfig.resolve = {\n\t\t\t\talias: config.resolve?.alias ? [...toArray(config.resolve.alias as any), ...gen.aliases] : gen.aliases,\n\t\t\t};\n\t\t},\n\t};\n};\n"
  },
  {
    "path": "src/types.ts",
    "content": "/**\n * Library options.\n */\nexport interface Options {\n\t/**\n\t * Relative path to the project directory\n\t * @default 'src'\n\t */\n\tdir: string;\n\n\t/**\n\t * Prefix symbol for the aliases\n\t * @default '~'\n\t */\n\tprefix: string;\n\n\t/**\n\t * Allow searching for subdirectories\n\t * @default true\n\t */\n\tdeep: boolean;\n\n\t/**\n\t * Search depthlevel for subdirectories\n\t * @default 1\n\t */\n\tdepth: number;\n\n\t/**\n\t * Creates a Logfile\n\t * use `logPath` to change the location\n\t * @default false\n\t */\n\tcreateLog: boolean;\n\n\t/**\n\t * Path for Logfile\n\t * @default 'src/logs'\n\t */\n\n\tlogPath: string;\n\n\t/**\n\t * Create global project directory alias\n\t * @default true\n\t */\n\tcreateGlobalAlias: boolean;\n\n\t/**\n\t * Turns duplicates into camelCased path aliases\n\t * @default false\n\t */\n\tadjustDuplicates: boolean;\n\n\t/**\n\t * Used paths in JS/TS configs will now be relative to baseUrl\n\t * @default false\n\t */\n\tuseAbsolute: boolean;\n\n\t/**\n\t * Adds seperate index paths\n\t * @default false\n\t */\n\tuseIndexes: boolean;\n\n\t/**\n\t * Generates paths in IDE config file\n\t * works with JS or TS\n\t * @default true\n\t */\n\tuseConfig: boolean;\n\n\t/**\n\t * Override config paths\n\t * @default false\n\t */\n\tovrConfig: boolean\n\n\t/**\n\t * Will generate Paths in tsconfig\n\t * used in combination with `useConfig`\n\t * Typescript will be auto detected\n\t * @default false\n\t */\n\tdts: boolean;\n\n\t/**\n\t * Disables any terminal output\n\t * @default true\n\t */\n\tsilent: boolean;\n\n\t/**\n\t * Root path of Vite project\n\t * @default 'process.cwd()'\n\t */\n\troot: string;\n}\n\nexport interface Alias {\n\tfind: string;\n\treplacement: string;\n}\n\nexport interface Path {\n\t[key: string]: string[];\n}\n\nexport type Process = 'add' | 'remove' | 'default' | 'normal';\n"
  },
  {
    "path": "src/utils.ts",
    "content": "import { createConsola } from 'consola';\nimport fs from 'node:fs/promises';\nimport { normalizePath } from 'vite';\nimport { MODULE_NAME } from './constants';\nimport type { Process } from './types';\nimport { parse, stringify } from 'comment-json';\n\n/**\n * Split String on Seperator into Array\n * @param string\n * @param seperator\n */\n\nexport function split(string: string, seperator: string): string[] {\n\treturn string.split(seperator);\n}\n\n/**\n * Turns a Value into Array\n * @param string\n * @param seperator\n */\n\nexport function toArray<T>(value: T | T[]): T[] {\n\tif (Array.isArray(value)) {\n\t\treturn value;\n\t} else {\n\t\treturn [value];\n\t}\n}\n\n/**\n * Turns a absolute Path into an Relative Path\n * @param string\n * @param seperator\n */\n\nexport function toRelative(path: string, dir: string): string {\n\tlet folders = split(normalizePath(path), '/');\n\tfolders = folders.slice(\n\t\tfolders.findIndex((f) => f === dir),\n\t\tfolders.length,\n\t);\n\treturn normalizePath(`./${folders.join('/')}`);\n}\n\n/**\n * Turns any String into Camelcased String\n * @param string\n */\n\nexport function toCamelCase(string: string): string {\n\treturn string.trim().replace(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''));\n}\n\n/**\n * Check if Value is Empty\n * supports: Array, Object, String\n * @param value\n */\n\nexport function isEmpty(value: any) {\n\tif (value === null || value === undefined || value === '{}' || value === '' || JSON.stringify(value) === '{}') {\n\t\treturn true;\n\t}\n\n\tif ((Array.isArray(value) && Object.keys(value).length <= 0) || (Array.isArray(value) && value.length === 0)) {\n\t\treturn true;\n\t}\n\n\t// if (Reflect.ownKeys(value).length === 0 && value.constructor === Object) {\n\t// \treturn true;\n\t// }\n\n\treturn false;\n}\n\n/**\n * Simple Info/Warn/Error Consola Instance\n */\n\nexport const logger = createConsola({ defaults: { message: `[${MODULE_NAME}] -` } });\nexport function abort(message: any) {\n\tthrow logger.error(new Error(message));\n}\n\n/**\n * Reads a JSON File\n */\n\nexport async function readJSON(path: string) {\n\ttry {\n\t\tconst file = (await fs.readFile(path, 'utf-8')).toString();\n\t\tlogger.success(`Config: ${path} successfully read!`);\n\t\treturn parse(file);\n\t} catch (error) {\n\t\tlogger.error(`File: ${path} was not found!`);\n\t}\n}\n\n/**\n * Writes a JSON File\n */\n\nexport const DEFAULT_INDENTATION: Indentation = 4; // default to 4 spaces before introducing the intepretation feature\n\nexport async function writeJSON(path: string, data: any, process: Process, indentation: Indentation = DEFAULT_INDENTATION) {\n\tconst name = path.replace(/^.*[\\\\\\/]/, '');\n\tconst state = process === 'add' || process === 'default' ? 'created' : 'updated';\n\n\ttry {\n\t\tawait fs.writeFile(path, stringify(data, null, indentation));\n\t\tlogger.success(`File: ${name} successfully ${state}`);\n\t} catch (error) {\n\t\tlogger.error(`File: ${name} could not be ${state}.`);\n\t\tabort(error);\n\t}\n}\n\n/**\n * Interprets file indentations\n */\n\nexport type Indentation = number | '\\t';\n\nexport async function interpretFileIndentation(path: string): Promise<Indentation> {\n\tconst name = path.replace(/^.*[\\\\\\/]/, '');\n\n\ttry {\n\t\tconst content = (await fs.readFile(path, 'utf-8')).toString();\n\t\tconst lines = content.split('\\n');\n\t\tconst secondLine = lines[1];\n\t\tlet indentation: Indentation;\n\n\t\tif (secondLine.startsWith('\\t')) {\n\t\t\tindentation = '\\t';\n\t\t} else {\n\t\t\tconst firstNonSpaceCharacter = split(secondLine, '').findIndex(char => char !== ' ');\n\n\t\t\tif (firstNonSpaceCharacter === -1) {\n\t\t\t\tlogger.error('Failed to interpret indentation from file. (No indentation found)');\n\t\t\t}\n\n\t\t\tindentation = firstNonSpaceCharacter;\n\t\t}\n\n\t\tlogger.info(`File: Interpreted indentation as (${typeof indentation === 'number' ? `${indentation} spaces` : 'tabs'}) from file ${name} successfully`);\n\n\t\treturn indentation;\n\t} catch (error) {\n\t\tlogger.error(`File: Failed to interpret indentation from ${name}.`);\n\t\treturn DEFAULT_INDENTATION;\n\t}\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"module\": \"ESNext\",\n        \"target\": \"ESNext\",\n        \"lib\": [\n            \"ESNext\"\n        ],\n        \"allowSyntheticDefaultImports\": true,\n        \"esModuleInterop\": false,\n        \"strict\": true,\n        \"strictNullChecks\": true,\n        \"moduleResolution\": \"node\",\n        \"resolveJsonModule\": true\n    },\n    \"exclude\": [\n        \"**/dist\",\n        \"**/node_modules\",\n        \"**/test\"\n    ]\n}"
  },
  {
    "path": "tsup.config.ts",
    "content": "import type { Options } from 'tsup';\n\nexport const tsup: Options = {\n\tentry: ['./src/index.ts'],\n\tformat: ['esm'],\n\tminify: 'terser',\n\tdts: true,\n\tsplitting: true,\n\tclean: true,\n\tshims: true,\n\ttreeshake: 'smallest'\n};\n"
  }
]