|
| 1 | +import { existsSync, mkdirSync, rmSync } from 'fs'; |
| 2 | +import { homedir, platform } from 'os'; |
| 3 | +import { join } from 'path'; |
| 4 | +import { legacyPathToArtifact } from './ArtifactsDir'; // eslint-disable-line no-restricted-imports |
| 5 | +import { ExtensionId } from './ExtensionConfig'; |
| 6 | + |
| 7 | +const AppName = ExtensionId; |
| 8 | + |
| 9 | +/** |
| 10 | + * Manages the storage directory for language server artifacts (logs, caches, databases). |
| 11 | + * @example |
| 12 | + * // Initialize with default location |
| 13 | + * Storage.initialize(); |
| 14 | + * |
| 15 | + * // Initialize with custom location |
| 16 | + * Storage.initialize('/custom/path'); |
| 17 | + * |
| 18 | + * // Get path to storage root |
| 19 | + * const root = Storage.pathToStorage(); |
| 20 | + * |
| 21 | + * // Get path to subdirectory (creates if needed) |
| 22 | + * const logsDir = Storage.pathToStorage('logs'); |
| 23 | + */ |
| 24 | +export class Storage { |
| 25 | + private static root: string | undefined; |
| 26 | + |
| 27 | + /** |
| 28 | + * Initialize storage with optional custom directory. Must be called before pathToStorage(). |
| 29 | + * Cleans up legacy storage location (.aws-cfn-storage) from previous versions. |
| 30 | + * @param storageDir Optional explicit storage directory path |
| 31 | + */ |
| 32 | + static initialize(storageDir?: string): void { |
| 33 | + this.root = storageDir ?? this.getDefaultStorageRoot(); |
| 34 | + this.cleanupLegacyStorage(); |
| 35 | + } |
| 36 | + |
| 37 | + /** |
| 38 | + * Get absolute path to storage directory or subdirectory. Creates directory if it doesn't exist. |
| 39 | + * @param artifactDir Optional subdirectory name |
| 40 | + * @returns Absolute path to the storage location |
| 41 | + * @throws Error if initialize() has not been called |
| 42 | + */ |
| 43 | + static pathToStorage(artifactDir?: string): string { |
| 44 | + if (this.root === undefined) { |
| 45 | + throw new Error('Storage directory not initialized'); |
| 46 | + } |
| 47 | + const path = artifactDir ? join(this.root, artifactDir) : this.root; |
| 48 | + |
| 49 | + if (!existsSync(path)) { |
| 50 | + mkdirSync(path, { recursive: true }); |
| 51 | + } |
| 52 | + |
| 53 | + return path; |
| 54 | + } |
| 55 | + |
| 56 | + /** |
| 57 | + * Storage location priority: |
| 58 | + * 1. Explicit path passed to initialize() |
| 59 | + * 2. CFN_LSP_STORAGE_DIR environment variable |
| 60 | + * 3. Platform-specific default: |
| 61 | + * - Windows: %LOCALAPPDATA%\aws-cloudformation-languageserver |
| 62 | + * - macOS: ~/Library/Application Support/aws-cloudformation-languageserver |
| 63 | + * - Linux: $XDG_STATE_HOME/aws-cloudformation-languageserver (or ~/.local/state/...) |
| 64 | + */ |
| 65 | + private static getDefaultStorageRoot(): string { |
| 66 | + if (process.env.CFN_LSP_STORAGE_DIR) { |
| 67 | + return process.env.CFN_LSP_STORAGE_DIR; |
| 68 | + } |
| 69 | + |
| 70 | + switch (platform()) { |
| 71 | + case 'win32': { |
| 72 | + return join(process.env.LOCALAPPDATA ?? join(homedir(), 'AppData', 'Local'), AppName); |
| 73 | + } |
| 74 | + case 'darwin': { |
| 75 | + return join(homedir(), 'Library', 'Application Support', AppName); |
| 76 | + } |
| 77 | + default: { |
| 78 | + return join(process.env.XDG_STATE_HOME ?? join(homedir(), '.local', 'state'), AppName); |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + private static cleanupLegacyStorage(): void { |
| 84 | + const legacyPath = legacyPathToArtifact(); |
| 85 | + if (existsSync(legacyPath)) { |
| 86 | + rmSync(legacyPath, { recursive: true, force: true }); |
| 87 | + } |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +/** |
| 92 | + * Convenience function for Storage.pathToStorage() |
| 93 | + * @param artifactDir Optional subdirectory name |
| 94 | + */ |
| 95 | +export function pathToStorage(artifactDir?: string): string { |
| 96 | + return Storage.pathToStorage(artifactDir); |
| 97 | +} |
0 commit comments