diff --git a/lib/commands/embedding/embed.ts b/lib/commands/embedding/embed.ts index f5c488cd46..fd3b58ac63 100644 --- a/lib/commands/embedding/embed.ts +++ b/lib/commands/embedding/embed.ts @@ -24,7 +24,7 @@ export class EmbedCommand extends PrepareCommand implements ICommand { private $logger: ILogger, private $fs: IFileSystem, - private $projectConfigService: IProjectConfigService + private $projectConfigService: IProjectConfigService, ) { super( $options, @@ -34,7 +34,7 @@ export class EmbedCommand extends PrepareCommand implements ICommand { $platformCommandParameter, $platformsDataService, $prepareDataService, - $migrateController + $migrateController, ); } @@ -59,8 +59,8 @@ export class EmbedCommand extends PrepareCommand implements ICommand { hostProjectPath, )} (resolved to: ${color.styleText( ["yellow", "dim"], - resolvedHostProjectPath - )}) does not exist.` + resolvedHostProjectPath, + )}) does not exist.`, ); return; } @@ -90,7 +90,7 @@ export class EmbedCommand extends PrepareCommand implements ICommand { if (!args[1]) { const hostProjectPath = this.getEmbedConfigForKey( "hostProjectPath", - platform + platform, ); if (hostProjectPath) { args[1] = hostProjectPath; @@ -100,7 +100,7 @@ export class EmbedCommand extends PrepareCommand implements ICommand { if (!args[2]) { const hostProjectModuleName = this.getEmbedConfigForKey( "hostProjectModuleName", - platform + platform, ); if (hostProjectModuleName) { args[2] = hostProjectModuleName; @@ -120,7 +120,7 @@ export class EmbedCommand extends PrepareCommand implements ICommand { // get the embed.. value, or fallback to embed. value return this.$projectConfigService.getValue( `embed.${platform}.${key}`, - this.$projectConfigService.getValue(`embed.${key}`) + this.$projectConfigService.getValue(`embed.${key}`), ); } } diff --git a/lib/commands/post-install.ts b/lib/commands/post-install.ts index c0a6ac8a45..6062467680 100644 --- a/lib/commands/post-install.ts +++ b/lib/commands/post-install.ts @@ -18,7 +18,7 @@ export class PostInstallCliCommand implements ICommand { private $settingsService: ISettingsService, private $analyticsService: IAnalyticsService, private $logger: ILogger, - private $hostInfo: IHostInfo + private $hostInfo: IHostInfo, ) {} public disableAnalytics = true; @@ -35,7 +35,7 @@ export class PostInstallCliCommand implements ICommand { // TODO: Check if this is the correct place, probably we should set this at the end of the command. await this.$fs.setCurrentUserAsOwner( this.$settingsService.getProfileDir(), - process.env.SUDO_USER + process.env.SUDO_USER, ); } } @@ -66,7 +66,7 @@ export class PostInstallCliCommand implements ICommand { this.$logger.info(""); this.$logger.printMarkdown( - "If you have any questions, check Stack Overflow: `https://stackoverflow.com/questions/tagged/nativescript` and our public Discord channel: `https://nativescript.org/discord`" + "If you have any questions, check Stack Overflow: `https://stackoverflow.com/questions/tagged/nativescript` and our public Discord channel: `https://nativescript.org/discord`", ); } } diff --git a/lib/commands/typings.ts b/lib/commands/typings.ts index 6811a247d8..9159e6f5f5 100644 --- a/lib/commands/typings.ts +++ b/lib/commands/typings.ts @@ -20,7 +20,7 @@ export class TypingsCommand implements ICommand { private $childProcess: IChildProcess, private $hostInfo: IHostInfo, private $staticConfig: IStaticConfig, - private $prompter: IPrompter + private $prompter: IPrompter, ) {} public async execute(args: string[]): Promise { @@ -35,7 +35,7 @@ export class TypingsCommand implements ICommand { if (this.$options.copyTo) { this.$fs.copyFile( path.resolve(this.$projectData.projectDir, "typings"), - this.$options.copyTo + this.$options.copyTo, ); typingsFolder = this.$options.copyTo; } @@ -43,7 +43,7 @@ export class TypingsCommand implements ICommand { if (result !== false) { this.$logger.info( "Typings have been generated in the following directory:", - typingsFolder + typingsFolder, ); } } @@ -56,7 +56,7 @@ export class TypingsCommand implements ICommand { private async resolveGradleDependencies(target: string) { const gradleHome = path.resolve( - process.env.GRADLE_USER_HOME ?? path.join(homedir(), `/.gradle`) + process.env.GRADLE_USER_HOME ?? path.join(homedir(), `/.gradle`), ); const gradleFiles = path.resolve(gradleHome, "caches/modules-2/files-2.1/"); @@ -92,7 +92,7 @@ export class TypingsCommand implements ICommand { const choices = await this.$prompter.promptForChoice( `Select dependencies to generate typings for (${color.greenBright( - target + target, )})`, items .sort((a, b) => { @@ -118,7 +118,7 @@ export class TypingsCommand implements ICommand { true, { optionsPerPage: process.stdout.rows - 6, // 6 lines are taken up by the instructions - } as Partial + } as Partial, ); this.$logger.clearScreen(); @@ -139,7 +139,7 @@ export class TypingsCommand implements ICommand { } catch (err) { this.$logger.trace( `Failed to resolve gradle dependencies for target "${target}"`, - err + err, ); } } @@ -151,13 +151,13 @@ export class TypingsCommand implements ICommand { "No .jar or .aar file specified. Please specify at least one of the following:", " - path to .jar file with --jar ", " - path to .aar file with --aar ", - ].join("\n") + ].join("\n"), ); return false; } this.$fs.ensureDirectoryExists( - path.resolve(this.$projectData.projectDir, "typings", "android") + path.resolve(this.$projectData.projectDir, "typings", "android"), ); const dtsGeneratorPath = path.resolve( @@ -165,7 +165,7 @@ export class TypingsCommand implements ICommand { "platforms", "android", "build-tools", - "dts-generator.jar" + "dts-generator.jar", ); if (!this.$fs.exists(dtsGeneratorPath)) { this.$logger.warn("No platforms folder found, preparing project now..."); @@ -173,7 +173,7 @@ export class TypingsCommand implements ICommand { this.$hostInfo.isWindows ? "ns.cmd" : "ns", ["prepare", "android"], "exit", - { stdio: "inherit", shell: this.$hostInfo.isWindows } + { stdio: "inherit", shell: this.$hostInfo.isWindows }, ); } @@ -206,7 +206,7 @@ export class TypingsCommand implements ICommand { path.resolve(this.$projectData.projectDir, "typings", "android"), ], "exit", - { stdio: "inherit" } + { stdio: "inherit" }, ); } @@ -216,7 +216,7 @@ export class TypingsCommand implements ICommand { } this.$fs.ensureDirectoryExists( - path.resolve(this.$projectData.projectDir, "typings", "ios") + path.resolve(this.$projectData.projectDir, "typings", "ios"), ); await this.$childProcess.spawnFromEvent( @@ -229,11 +229,11 @@ export class TypingsCommand implements ICommand { TNS_TYPESCRIPT_DECLARATIONS_PATH: path.resolve( this.$projectData.projectDir, "typings", - "ios" + "ios", ), }, stdio: "inherit", - } + }, ); } } diff --git a/lib/common/header.ts b/lib/common/header.ts index 6fb1f2c671..7c3d00643c 100644 --- a/lib/common/header.ts +++ b/lib/common/header.ts @@ -31,10 +31,10 @@ export function printHeader() { console.info(" " + color.dim("┌" + "─".repeat(width - 1) + "┐")); console.info( - " " + header + " ".repeat(width - headerLength) + color.dim("│") + " " + header + " ".repeat(width - headerLength) + color.dim("│"), ); console.info( - " " + tagLine + " ".repeat(width - tagLineLength) + color.dim("│") + " " + tagLine + " ".repeat(width - tagLineLength) + color.dim("│"), ); console.info(" " + color.dim("└" + "─".repeat(width - 1) + "┘")); } diff --git a/lib/helpers/key-command-helper.ts b/lib/helpers/key-command-helper.ts index 821a1ea113..59d9804e8c 100644 --- a/lib/helpers/key-command-helper.ts +++ b/lib/helpers/key-command-helper.ts @@ -115,18 +115,18 @@ export default class KeyCommandHelper implements IKeyCommandHelper { [ "", ` The CLI is ${color.underline( - `interactive` + `interactive`, )}, you can press the following keys any time (make sure the terminal has focus).`, "", ...commandHelp, "", - ].join("\n") + ].join("\n"), ); } public attachKeyCommands( platform: IKeyCommandPlatform, - processType: SupportedProcessType + processType: SupportedProcessType, ) { this.processType = processType; this.platform = platform; diff --git a/lib/services/analytics-settings-service.ts b/lib/services/analytics-settings-service.ts index 825ef0e682..1c44a8ffdf 100644 --- a/lib/services/analytics-settings-service.ts +++ b/lib/services/analytics-settings-service.ts @@ -19,7 +19,7 @@ class AnalyticsSettingsService implements IAnalyticsSettingsService { private $staticConfig: IStaticConfig, private $hostInfo: IHostInfo, private $osInfo: IOsInfo, - private $logger: ILogger + private $logger: ILogger, ) {} public async canDoRequest(): Promise { @@ -33,7 +33,7 @@ class AnalyticsSettingsService implements IAnalyticsSettingsService { @exported("analyticsSettingsService") public getClientId(): Promise { return this.getSettingValueOrDefault( - this.$staticConfig.ANALYTICS_INSTALLATION_ID_SETTING_NAME + this.$staticConfig.ANALYTICS_INSTALLATION_ID_SETTING_NAME, ); } @@ -54,11 +54,11 @@ class AnalyticsSettingsService implements IAnalyticsSettingsService { public async setUserSessionsCount( count: number, - projectName: string + projectName: string, ): Promise { return this.$userSettingsService.saveSetting( this.getSessionsProjectKey(projectName), - count + count, ); } diff --git a/lib/services/bundler/bundler-compiler-service.ts b/lib/services/bundler/bundler-compiler-service.ts index 12f637b389..a888600bcf 100644 --- a/lib/services/bundler/bundler-compiler-service.ts +++ b/lib/services/bundler/bundler-compiler-service.ts @@ -50,6 +50,9 @@ interface IBundlerCompilation { staleAssets: string[]; } +/* for specific bundling debugging separate from logger */ +const debugLog = false; + export class BundlerCompilerService extends EventEmitter implements IBundlerCompilerService @@ -118,7 +121,9 @@ export class BundlerCompilerService (message as IBundlerEmitMessage).emittedFiles ) { message = message as IBundlerEmitMessage; - console.log("Received Vite IPC message:", message); + if (debugLog) { + console.log("Received Vite IPC message:", message); + } // Copy Vite output files directly to platform destination const distOutput = path.join(projectData.projectDir, "dist"); @@ -127,18 +132,19 @@ export class BundlerCompilerService this.$options.hostProjectModuleName, ); - console.log(`🔥 Copying from ${distOutput} to ${destDir}`); + if (debugLog) { + console.log(`🔥 Copying from ${distOutput} to ${destDir}`); + } - // For HMR updates, only copy changed files; for full builds, copy everything - if ( - message.isHMR && - message.changedFiles && - message.changedFiles.length > 0 - ) { - console.log( - "🔥 HMR update - copying only changed files for:", - message.changedFiles, - ); + // Determine which files to copy based on build type and changes + if (message.isHMR) { + // HMR updates: only copy changed files + if (debugLog) { + console.log( + "🔥 HMR update - copying only changed files for:", + message.changedFiles, + ); + } // For HTML template changes, we need to copy the component files that were rebuilt let filesToCopy = message.emittedFiles; @@ -155,24 +161,55 @@ export class BundlerCompilerService f === "bundle.mjs" || f === "bundle.mjs.map", ); + if (debugLog) { + console.log( + "🔥 HTML change detected - copying component files:", + filesToCopy, + ); + } + } + + this.copyViteBundleToNative(distOutput, destDir, filesToCopy); + } else if ( + message.buildType === "incremental" && + message.changedFiles && + message.changedFiles.length > 0 + ) { + // Incremental builds: only copy files that are likely affected by the changes + if (debugLog) { + console.log( + "🔥 Incremental build - copying only relevant files for:", + message.changedFiles, + ); + } + + const filesToCopy = this.getIncrementalFilesToCopy( + message.emittedFiles, + message.changedFiles, + ); + if (debugLog) { console.log( - "🔥 HTML change detected - copying component files:", + "🔥 Incremental build - files to copy:", filesToCopy, ); } this.copyViteBundleToNative(distOutput, destDir, filesToCopy); } else { - console.log("🔥 Full build - copying all files"); + if (debugLog) { + console.log("🔥 Full build - copying all files"); + } this.copyViteBundleToNative(distOutput, destDir); } // Resolve the promise on first build completion if (isFirstBundlerWatchCompilation) { isFirstBundlerWatchCompilation = false; - console.log( - "Vite first build completed, resolving compileWithWatch", - ); + if (debugLog) { + console.log( + "Vite first build completed, resolving compileWithWatch", + ); + } resolve(childProcess); } @@ -209,15 +246,19 @@ export class BundlerCompilerService }); if (message.isHMR) { - console.log( - "🔥 Skipping BUNDLER_COMPILATION_COMPLETE for HMR update - app will not restart", - ); + if (debugLog) { + console.log( + "🔥 Skipping BUNDLER_COMPILATION_COMPLETE for HMR update - app will not restart", + ); + } } else { // Only emit BUNDLER_COMPILATION_COMPLETE for non-HMR builds // This prevents the CLI from restarting the app during HMR updates - console.log( - "🔥 Emitting BUNDLER_COMPILATION_COMPLETE for full build", - ); + if (debugLog) { + console.log( + "🔥 Emitting BUNDLER_COMPILATION_COMPLETE for full build", + ); + } this.emit(BUNDLER_COMPILATION_COMPLETE, data); } return; @@ -522,7 +563,9 @@ export class BundlerCompilerService }); } - console.log("args:", args); + if (debugLog) { + console.log("args:", args); + } const childProcess = this.$childProcess.spawn( process.execPath, @@ -866,14 +909,21 @@ export class BundlerCompilerService specificFiles: string[] = null, ) { // Clean and copy Vite output to native platform folder - console.log(`Copying Vite bundle from "${distOutput}" to "${destDir}"`); + if (debugLog) { + console.log(`Copying Vite bundle from "${distOutput}" to "${destDir}"`); + } const fs = require("fs"); try { if (specificFiles) { - // HMR mode: only copy specific files - console.log("🔥 HMR: Copying specific files:", specificFiles); + // Selective mode: only copy specific files (HMR or incremental) + if (debugLog) { + console.log( + "🔥 Selective copy - copying specific files:", + specificFiles, + ); + } // Ensure destination directory exists fs.mkdirSync(destDir, { recursive: true }); @@ -890,11 +940,15 @@ export class BundlerCompilerService fs.copyFileSync(srcPath, destPath); - console.log(`🔥 HMR: Copied ${file}`); + if (debugLog) { + console.log(`🔥 Copied ${file}`); + } } } else { // Full build mode: clean and copy everything - console.log("🔥 Full build: Copying all files"); + if (debugLog) { + console.log("🔥 Full build: Copying all files"); + } // Clean destination directory if (fs.existsSync(destDir)) { @@ -916,6 +970,58 @@ export class BundlerCompilerService } } + private getIncrementalFilesToCopy( + emittedFiles: string[], + changedFiles: string[], + ): string[] { + // For incremental builds, we need to determine which emitted files are likely affected + // by the source file changes + + const filesToCopy: string[] = []; + + // Always copy bundle files as they contain the compiled source code + // ignoring vendor files as they are less likely to change frequently + const bundleFiles = emittedFiles.filter( + (file) => + !file.includes("vendor") && + (file.includes("bundle") || + file.includes("main") || + file.includes("app") || + file.endsWith(".mjs") || + file.endsWith(".js")), + ); + filesToCopy.push(...bundleFiles); + + // Always copy source maps for debugging + const sourceMapFiles = emittedFiles.filter( + (file) => !file.includes("vendor") && file.endsWith(".map"), + ); + filesToCopy.push(...sourceMapFiles); + + // Only handle assets if they're explicitly referenced in the changed files + const hasAssetChanges = changedFiles.some( + (file) => + file.includes("/assets/") || + file.includes("/static/") || + file.includes("/public/"), + ); + + if (hasAssetChanges) { + // Only copy assets if there are explicit asset-related changes + const assetFiles = emittedFiles.filter( + (file) => + file.includes("assets/") || + file.includes("static/") || + file.includes("fonts/") || + file.includes("images/"), + ); + filesToCopy.push(...assetFiles); + } + + // Remove duplicates and return + return [...new Set(filesToCopy)]; + } + private notifyHMRClients(message: any) { // Send WebSocket notification to HMR clients try { @@ -925,18 +1031,26 @@ export class BundlerCompilerService const ws = new WebSocket("ws://localhost:24678"); ws.on("open", () => { - console.log("🔥 Sending HMR notification to bridge:", message.type); + if (debugLog) { + console.log("🔥 Sending HMR notification to bridge:", message.type); + } ws.send(JSON.stringify(message)); ws.close(); }); ws.on("error", () => { // HMR bridge not available, which is fine - console.log("🔥 HMR bridge not available (this is normal without HMR)"); + if (debugLog) { + console.log( + "🔥 HMR bridge not available (this is normal without HMR)", + ); + } }); } catch (error) { // WebSocket not available, which is fine - console.log("🔥 WebSocket not available for HMR notifications"); + if (debugLog) { + console.log("🔥 WebSocket not available for HMR notifications"); + } } }