diff --git a/deno.lock b/deno.lock index f0e55fe..ea15510 100644 --- a/deno.lock +++ b/deno.lock @@ -11,6 +11,7 @@ "jsr:@std/internal@^1.0.10": "1.0.10", "jsr:@std/internal@^1.0.6": "1.0.10", "jsr:@std/internal@^1.0.9": "1.0.10", + "jsr:@std/path@*": "1.1.1", "jsr:@std/path@1": "1.1.1", "jsr:@std/path@^1.1.1": "1.1.1", "jsr:@std/testing@*": "1.0.15", diff --git a/src/enum/enum.test.ts b/src/enum/enum.test.ts index 5b37b83..174e7a3 100644 --- a/src/enum/enum.test.ts +++ b/src/enum/enum.test.ts @@ -59,7 +59,7 @@ Deno.test("enum usage with arrays, maps and complex data structures", async () = await testFn("data-structures"); }); -Deno.test.only( +Deno.test( "converts enums across multiple files with imports", async () => { await projectTestFn("proj"); diff --git a/src/file-extension/file-extension.test.ts b/src/file-extension/file-extension.test.ts index d5a9358..16a4644 100644 --- a/src/file-extension/file-extension.test.ts +++ b/src/file-extension/file-extension.test.ts @@ -1,11 +1,19 @@ -import { executeTest } from "../utils/test.ts"; -import { fileExtensionCodemod } from "./file-extension.ts"; +import { executeProjectTest, executeTest } from "../utils/test.ts"; +import { + fileExtensionCodemod, + processJavaScriptFilesWithGlob, +} from "./file-extension.ts"; const testFileExtension = executeTest({ testDir: "src/file-extension/spec/", codemod: fileExtensionCodemod, }); +const testProject = executeProjectTest({ + testDir: "src/file-extension/spec/", + codemod: processJavaScriptFilesWithGlob, +}); + Deno.test("converts .js extensions to .ts", async () => { await testFileExtension("js-to-ts"); }); @@ -57,3 +65,7 @@ Deno.test("handles multiple imports in single file", async () => { Deno.test("handles complex relative paths", async () => { await testFileExtension("complex-paths"); }); + +Deno.test("handles project files with glob pattern", async () => { + await testProject("proj"); +}); diff --git a/src/file-extension/file-extension.ts b/src/file-extension/file-extension.ts index 98a74f8..7f46872 100644 --- a/src/file-extension/file-extension.ts +++ b/src/file-extension/file-extension.ts @@ -5,6 +5,7 @@ import { StringLiteral, SyntaxKind, } from "ts-morph"; +import { globToRegExp, joinGlobs } from "jsr:@std/path"; const project = new Project(); @@ -106,3 +107,36 @@ export function fileExtensionCodemod(filePath: string): void { sourceFile.saveSync(); } + +export function processJavaScriptFilesWithGlob(globPath: string): void { + // The glob path is passed from executeProjectTest and looks like "path/to/dir/*.ts" + // Extract the directory part and use the glob pattern + const lastSlash = globPath.lastIndexOf('/'); + const basePath = globPath.substring(0, lastSlash); + const globPattern = globPath.substring(lastSlash + 1); + + // Create regex for the glob pattern + const regex = globToRegExp(globPattern); + + try { + // Read the directory and find files matching the pattern + const entries = Array.from(Deno.readDirSync(basePath)); + + for (const entry of entries) { + if (entry.isFile && regex.test(entry.name)) { + const filePath = `${basePath}/${entry.name}`; + console.log(`Processing TypeScript file with .js import extensions: ${filePath}`); + + try { + // Apply the same file extension transformation as the main codemod + fileExtensionCodemod(filePath); + console.log(`Converted .js import extensions to .ts in: ${filePath}`); + } catch (error) { + console.error(`Error processing ${filePath}:`, error); + } + } + } + } catch (error) { + console.error(`Error reading directory ${basePath}:`, error); + } +} diff --git a/src/file-extension/spec/proj/config.in.ts b/src/file-extension/spec/proj/config.in.ts new file mode 100644 index 0000000..07f0922 --- /dev/null +++ b/src/file-extension/spec/proj/config.in.ts @@ -0,0 +1,61 @@ +enum LogLevel { + DEBUG = "debug", + INFO = "info", + WARN = "warn", + ERROR = "error", +} + +enum Environment { + Development = "development", + Staging = "staging", + Production = "production", +} + +interface AppConfig { + environment: Environment; + logLevel: LogLevel; + port: number; + databaseUrl?: string; +} + +class ConfigManager { + private config: AppConfig; + + constructor() { + this.config = { + environment: Environment.Development, + logLevel: LogLevel.INFO, + port: 3000, + }; + } + + setEnvironment(env: Environment): void { + this.config.environment = env; + + if (env === Environment.Production) { + this.config.logLevel = LogLevel.ERROR; + this.config.port = 8080; + } else if (env === Environment.Staging) { + this.config.logLevel = LogLevel.WARN; + this.config.port = 4000; + } + } + + setLogLevel(level: LogLevel): void { + this.config.logLevel = level; + } + + getConfig(): Readonly { + return { ...this.config }; + } + + isProduction(): boolean { + return this.config.environment === Environment.Production; + } +} + +const configManager = new ConfigManager(); +configManager.setEnvironment(Environment.Staging); +configManager.setLogLevel(LogLevel.DEBUG); + +export { type AppConfig, ConfigManager, Environment, LogLevel }; diff --git a/src/file-extension/spec/proj/config.out.ts b/src/file-extension/spec/proj/config.out.ts new file mode 100644 index 0000000..07f0922 --- /dev/null +++ b/src/file-extension/spec/proj/config.out.ts @@ -0,0 +1,61 @@ +enum LogLevel { + DEBUG = "debug", + INFO = "info", + WARN = "warn", + ERROR = "error", +} + +enum Environment { + Development = "development", + Staging = "staging", + Production = "production", +} + +interface AppConfig { + environment: Environment; + logLevel: LogLevel; + port: number; + databaseUrl?: string; +} + +class ConfigManager { + private config: AppConfig; + + constructor() { + this.config = { + environment: Environment.Development, + logLevel: LogLevel.INFO, + port: 3000, + }; + } + + setEnvironment(env: Environment): void { + this.config.environment = env; + + if (env === Environment.Production) { + this.config.logLevel = LogLevel.ERROR; + this.config.port = 8080; + } else if (env === Environment.Staging) { + this.config.logLevel = LogLevel.WARN; + this.config.port = 4000; + } + } + + setLogLevel(level: LogLevel): void { + this.config.logLevel = level; + } + + getConfig(): Readonly { + return { ...this.config }; + } + + isProduction(): boolean { + return this.config.environment === Environment.Production; + } +} + +const configManager = new ConfigManager(); +configManager.setEnvironment(Environment.Staging); +configManager.setLogLevel(LogLevel.DEBUG); + +export { type AppConfig, ConfigManager, Environment, LogLevel }; diff --git a/src/file-extension/spec/proj/logger.in.ts b/src/file-extension/spec/proj/logger.in.ts new file mode 100644 index 0000000..92edd07 --- /dev/null +++ b/src/file-extension/spec/proj/logger.in.ts @@ -0,0 +1,71 @@ +import { ConfigManager, Environment, LogLevel } from "./config.js"; + +class Logger { + private configManager: ConfigManager; + private name: string; + + constructor(name: string, configManager: ConfigManager) { + this.name = name; + this.configManager = configManager; + } + + private shouldLog(level: LogLevel): boolean { + const config = this.configManager.getConfig(); + const levels = [ + LogLevel.DEBUG, + LogLevel.INFO, + LogLevel.WARN, + LogLevel.ERROR, + ]; + return levels.indexOf(level) >= levels.indexOf(config.logLevel); + } + + private formatMessage(level: LogLevel, message: string): string { + const timestamp = new Date().toISOString(); + const env = this.configManager.getConfig().environment; + return `[${timestamp}] [${env.toUpperCase()}] [${level.toUpperCase()}] ${this.name}: ${message}`; + } + + debug(message: string): void { + if (this.shouldLog(LogLevel.DEBUG)) { + console.log(this.formatMessage(LogLevel.DEBUG, message)); + } + } + + info(message: string): void { + if (this.shouldLog(LogLevel.INFO)) { + console.log(this.formatMessage(LogLevel.INFO, message)); + } + } + + warn(message: string): void { + if (this.shouldLog(LogLevel.WARN)) { + console.warn(this.formatMessage(LogLevel.WARN, message)); + } + } + + error(message: string): void { + if (this.shouldLog(LogLevel.ERROR)) { + console.error(this.formatMessage(LogLevel.ERROR, message)); + } + } +} + +// Usage example +const configManager = new ConfigManager(); +configManager.setLogLevel(LogLevel.INFO); + +const appLogger = new Logger("App", configManager); +const dbLogger = new Logger("Database", configManager); + +appLogger.debug("This will not be logged"); +appLogger.info("Application starting"); +appLogger.warn("Low memory warning"); + +dbLogger.error("Connection failed"); + +// Test different environments +configManager.setEnvironment(Environment.Production); +appLogger.info("Running in production mode"); + +export { Logger }; diff --git a/src/file-extension/spec/proj/logger.out.ts b/src/file-extension/spec/proj/logger.out.ts new file mode 100644 index 0000000..1295c70 --- /dev/null +++ b/src/file-extension/spec/proj/logger.out.ts @@ -0,0 +1,71 @@ +import { ConfigManager, Environment, LogLevel } from "./config.ts"; + +class Logger { + private configManager: ConfigManager; + private name: string; + + constructor(name: string, configManager: ConfigManager) { + this.name = name; + this.configManager = configManager; + } + + private shouldLog(level: LogLevel): boolean { + const config = this.configManager.getConfig(); + const levels = [ + LogLevel.DEBUG, + LogLevel.INFO, + LogLevel.WARN, + LogLevel.ERROR, + ]; + return levels.indexOf(level) >= levels.indexOf(config.logLevel); + } + + private formatMessage(level: LogLevel, message: string): string { + const timestamp = new Date().toISOString(); + const env = this.configManager.getConfig().environment; + return `[${timestamp}] [${env.toUpperCase()}] [${level.toUpperCase()}] ${this.name}: ${message}`; + } + + debug(message: string): void { + if (this.shouldLog(LogLevel.DEBUG)) { + console.log(this.formatMessage(LogLevel.DEBUG, message)); + } + } + + info(message: string): void { + if (this.shouldLog(LogLevel.INFO)) { + console.log(this.formatMessage(LogLevel.INFO, message)); + } + } + + warn(message: string): void { + if (this.shouldLog(LogLevel.WARN)) { + console.warn(this.formatMessage(LogLevel.WARN, message)); + } + } + + error(message: string): void { + if (this.shouldLog(LogLevel.ERROR)) { + console.error(this.formatMessage(LogLevel.ERROR, message)); + } + } +} + +// Usage example +const configManager = new ConfigManager(); +configManager.setLogLevel(LogLevel.INFO); + +const appLogger = new Logger("App", configManager); +const dbLogger = new Logger("Database", configManager); + +appLogger.debug("This will not be logged"); +appLogger.info("Application starting"); +appLogger.warn("Low memory warning"); + +dbLogger.error("Connection failed"); + +// Test different environments +configManager.setEnvironment(Environment.Production); +appLogger.info("Running in production mode"); + +export { Logger };