From 79d78648b5aeb909b721970c192a30eaa7866b6a Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Tue, 15 Apr 2025 16:39:12 +0800 Subject: [PATCH 01/10] feat(cli): Add check-client command to verify bundle freshness --- .changeset/lucky-adults-tan.md | 6 + packages/qwik/src/cli/add/run-add-command.ts | 3 +- .../cli/check-client/check-client-command.ts | 224 ++++++++++++++++++ .../check-client/run-qwik-client-command.ts | 12 + packages/qwik/src/cli/run.ts | 12 + 5 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 .changeset/lucky-adults-tan.md create mode 100644 packages/qwik/src/cli/check-client/check-client-command.ts create mode 100644 packages/qwik/src/cli/check-client/run-qwik-client-command.ts diff --git a/.changeset/lucky-adults-tan.md b/.changeset/lucky-adults-tan.md new file mode 100644 index 00000000000..c96643cd9e3 --- /dev/null +++ b/.changeset/lucky-adults-tan.md @@ -0,0 +1,6 @@ +--- +'@builder.io/qwik-city': major +'@builder.io/qwik': major +--- + +feat(cli): Add check-client command to verify bundle freshness diff --git a/packages/qwik/src/cli/add/run-add-command.ts b/packages/qwik/src/cli/add/run-add-command.ts index 57532ecb7e4..8c7683bfde5 100644 --- a/packages/qwik/src/cli/add/run-add-command.ts +++ b/packages/qwik/src/cli/add/run-add-command.ts @@ -2,7 +2,7 @@ import type { AppCommand } from '../utils/app-command'; import { red } from 'kleur/colors'; import { runAddInteractive } from './run-add-interactive'; import { printAddHelp } from './print-add-help'; - +import { runQwikClientCommand } from '../check-client/run-qwik-client-command'; export async function runAddCommand(app: AppCommand) { try { const id = app.args[1]; @@ -10,6 +10,7 @@ export async function runAddCommand(app: AppCommand) { await printAddHelp(app); } else { await runAddInteractive(app, id); + await runQwikClientCommand(app); } } catch (e) { console.error(`❌ ${red(String(e))}\n`); diff --git a/packages/qwik/src/cli/check-client/check-client-command.ts b/packages/qwik/src/cli/check-client/check-client-command.ts new file mode 100644 index 00000000000..93daaca8744 --- /dev/null +++ b/packages/qwik/src/cli/check-client/check-client-command.ts @@ -0,0 +1,224 @@ +#!/usr/bin/env node + +import fs from 'fs/promises'; +import type { Stats } from 'fs'; +import path from 'path'; + +// Import Clack and Kleur for interactive prompts and colors +import { intro, isCancel, log, outro, select, spinner } from '@clack/prompts'; +import { bye, getPackageManager } from '../utils/utils'; // Assuming these utils exist +import { bgBlue, bgMagenta, bold, cyan, gray, green, red, yellow } from 'kleur/colors'; +import type { AppCommand } from '../utils/app-command'; // Assuming this type exists +import { runInPkg } from '../utils/install-deps'; + +const DISK_DIR: string = path.resolve('dist'); +const SRC_DIR: string = path.resolve('src'); +const MANIFEST_PATH: string = path.resolve(DISK_DIR, 'q-manifest.json'); +const BUILD_COMMAND: string = 'npm'; +const BUILD_ARGS: string[] = ['run', 'build']; + +/** + * Recursively finds the latest modification time (mtime) of any file in the given directory. + * + * @param {string} directoryPath - The directory path to search. + * @returns {Promise} Returns the latest mtime (Unix timestamp in milliseconds), or 0 if the + * directory doesn't exist or is empty. + */ +async function getLatestMtime(directoryPath: string): Promise { + let latestTime = 0; + + async function traverse(dir: string): Promise { + let items: Array; + try { + items = await fs.readdir(dir, { withFileTypes: true }); + } catch (err: any) { + if (err.code !== 'ENOENT') { + console.warn(`Cannot read directory ${dir}: ${err.message}`); + } + return; + } + + for (const item of items) { + const fullPath = path.join(dir, item.name); + try { + if (item.isDirectory()) { + await traverse(fullPath); + } else if (item.isFile()) { + const stats = await fs.stat(fullPath); + if (stats.mtimeMs > latestTime) { + latestTime = stats.mtimeMs; + } + } + } catch (err: any) { + console.warn(`Cannot access ${fullPath}: ${err.message}`); + } + } + } + + await traverse(directoryPath); + return latestTime; +} + +/** + * Handles the core logic for the 'check-client' command. Exports this function so other modules can + * import and call it. + * + * @param {AppCommand} app - Application command context (assuming structure). + */ +export async function checkClientCommand(app: AppCommand): Promise { + // Display introductory message + intro(`🚀 ${bgBlue(bold(' Qiwk Client Check '))}`); + const pkgManager = getPackageManager(); + + let manifestMtime: number = 0; + let manifestExists: boolean = false; + let needsBuild: boolean = false; + const reasonsForBuild: string[] = []; + + // Step 1: Check the manifest file + log.step(`Checking manifest file: ${cyan(MANIFEST_PATH)}`); + try { + // Get stats for the manifest file + const stats: Stats = await fs.stat(MANIFEST_PATH); // Use the resolved path + manifestMtime = stats.mtimeMs; + manifestExists = true; + log.info(`Manifest file found, modified: ${gray(new Date(manifestMtime).toLocaleString())}`); + } catch (err: any) { + // Handle errors accessing the manifest file + if (err.code === 'ENOENT') { + log.warn(`Manifest file not found: ${yellow(MANIFEST_PATH)}`); + needsBuild = true; + reasonsForBuild.push('Manifest file not found'); + } else { + log.error(`Error accessing manifest file ${MANIFEST_PATH}: ${err.message}`); + needsBuild = true; + reasonsForBuild.push(`Cannot access manifest file (${err.code})`); + } + } + + // Step 2: Check the source directory + log.step(`Checking source directory: ${cyan(SRC_DIR)}`); + let latestSrcMtime: number = 0; + try { + // Confirm source directory exists and is accessible + await fs.access(SRC_DIR); + // Find the latest modification time within the source directory + latestSrcMtime = await getLatestMtime(SRC_DIR); + + if (latestSrcMtime > 0) { + log.info( + `Latest file modification in source directory: ${gray(new Date(latestSrcMtime).toLocaleString())}` + ); + // Compare source modification time with manifest modification time + if (manifestExists && latestSrcMtime > manifestMtime) { + log.warn('Source files are newer than the manifest.'); + needsBuild = true; + reasonsForBuild.push('Source files (src) are newer than the manifest'); + } else if (manifestExists) { + log.info('Manifest file is up-to-date relative to source files.'); + } + } else { + // Handle case where source directory is empty or inaccessible + log.info(`No source files found or directory is empty/inaccessible in '${SRC_DIR}'.`); + // Note: Depending on requirements, you might want to set needsBuild = true here + } + } catch (err: any) { + // Handle errors accessing the source directory + if (err.code === 'ENOENT') { + log.error(`Source directory '${SRC_DIR}' not found! Build might fail.`); + // Decide whether to force build or exit if source directory access fails + // Setting needsBuild = true might be appropriate if the build process creates it. + } else { + log.error(`Error accessing source directory '${SRC_DIR}': ${err.message}`); + // Consider setting needsBuild = true or exiting based on severity + } + } + + // Step 3: Perform build if necessary + let buildSuccess: boolean | undefined = undefined; // Initialize build success status + if (needsBuild) { + log.step(yellow('Client build detected as necessary')); + // Log reasons why a build is needed + reasonsForBuild.forEach((reason) => log.info(` - ${reason}`)); + + // Confirm with the user before proceeding with the build + const proceed: boolean | symbol = await select({ + message: `Proceed with client build based on the reasons above (${cyan(BUILD_COMMAND + ' ' + BUILD_ARGS.join(' '))})?`, + options: [ + { value: true, label: 'Yes, proceed with build', hint: 'Will run the build command' }, + { value: false, label: 'No, cancel operation' }, + ], + initialValue: true, + }); + + // Check if the user cancelled the operation + if (isCancel(proceed) || proceed === false) { + bye(); // Exit gracefully (assuming bye handles this) + return; // Stop further execution + } + + // Show a spinner while the build command runs + const s = spinner(); + s.start('Running client build...'); + try { + // Execute the build command + // Ensure runCommand returns an object with an 'install' promise or similar structure + const { install } = await runInPkg(pkgManager, BUILD_ARGS, app.rootDir); + buildSuccess = await install; // Await the promise indicating build completion/success + + if (buildSuccess) { + s.stop(green('Client build completed successfully.')); + // **Important:** Re-check manifest mtime after successful build + try { + const newStats = await fs.stat(MANIFEST_PATH); + manifestMtime = newStats.mtimeMs; + manifestExists = true; // Mark as existing now + log.info(`Manifest updated: ${gray(new Date(manifestMtime).toLocaleString())}`); + } catch (statErr: any) { + log.error(`Failed to re-stat manifest after build: ${statErr.message}`); + // Handle this case - maybe the build didn't create the manifest? + } + } else { + // Handle build failure reported by runCommand + s.stop(red('Client build failed.'), 1); + throw new Error('Client build command reported failure.'); + } + } catch (buildError: any) { + // Catch errors during the build process itself (e.g., command not found, script errors) + s.stop(red('Client build failed.'), 1); + log.error(`Build error: ${buildError.message}`); + // Throw error to indicate failure, let the caller handle exit logic if needed + throw new Error('Client build process encountered an error.'); + } + } else { + // If no build was needed + log.info(green('Client is up-to-date, no build needed.')); + } + + // Step 4: Check the Disk directory (usually after build) + log.step(`Checking Disk directory: ${cyan(DISK_DIR)}`); + try { + // Check if the disk directory exists and is accessible + await fs.access(DISK_DIR); + log.info(`Disk directory found: ${green(DISK_DIR)}`); + } catch (err: any) { + // Handle errors accessing the disk directory + if (err.code === 'ENOENT') { + log.warn(`Disk directory not found: ${yellow(DISK_DIR)}`); + // Provide context if a build just happened + if (needsBuild && buildSuccess === true) { + // Check if build was attempted and successful + log.warn( + `Note: Build completed, but '${DISK_DIR}' directory was not found. The build process might not create it.` + ); + } else if (needsBuild && !buildSuccess) { + log.warn(`Note: Build failed, and '${DISK_DIR}' directory was not found.`); + } + } else { + log.error(`Error accessing disk directory ${DISK_DIR}: ${err.message}`); + } + } + + // Display completion message + outro(`✅ ${bgMagenta(bold(' Check complete '))}`); +} diff --git a/packages/qwik/src/cli/check-client/run-qwik-client-command.ts b/packages/qwik/src/cli/check-client/run-qwik-client-command.ts new file mode 100644 index 00000000000..45980638507 --- /dev/null +++ b/packages/qwik/src/cli/check-client/run-qwik-client-command.ts @@ -0,0 +1,12 @@ +import type { AppCommand } from '../utils/app-command'; +import { red } from 'kleur/colors'; +import { checkClientCommand } from './check-client-command'; + +export async function runQwikClientCommand(app: AppCommand) { + try { + await checkClientCommand(app); + } catch (e) { + console.error(`❌ ${red(String(e))}\n`); + process.exit(1); + } +} diff --git a/packages/qwik/src/cli/run.ts b/packages/qwik/src/cli/run.ts index cd4dccd0987..c2c92cb8781 100644 --- a/packages/qwik/src/cli/run.ts +++ b/packages/qwik/src/cli/run.ts @@ -8,6 +8,7 @@ import { note, panic, pmRunCmd, printHeader, bye } from './utils/utils'; import { runBuildCommand } from './utils/run-build-command'; import { intro, isCancel, select, confirm } from '@clack/prompts'; import { runV2Migration } from './migrate-v2/run-migration'; +import { runQwikClientCommand } from './check-client/run-qwik-client-command'; const SPACE_TO_HINT = 18; const COMMANDS = [ @@ -53,6 +54,13 @@ const COMMANDS = [ run: (app: AppCommand) => runV2Migration(app), showInHelp: false, }, + { + value: 'check-client', + label: 'check-client', + hint: 'Check if the bundle is latest version', + run: (app: AppCommand) => runQwikClientCommand(app), + showInHelp: true, + }, { value: 'help', label: 'help', @@ -110,6 +118,10 @@ async function runCommand(app: AppCommand) { await runV2Migration(app); return; } + case 'check-client': { + await runQwikClientCommand(app); + return; + } case 'version': { printVersion(); return; From 179de2fa83658c15ca32cc23630a8d4a218173fa Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Fri, 18 Apr 2025 17:23:40 +0800 Subject: [PATCH 02/10] take some improvements according to a comment --- .changeset/lucky-adults-tan.md | 4 +- packages/qwik/src/cli/add/run-add-command.ts | 3 +- .../cli/check-client/check-client-command.ts | 44 +++---------------- packages/qwik/src/cli/run.ts | 2 +- 4 files changed, 10 insertions(+), 43 deletions(-) diff --git a/.changeset/lucky-adults-tan.md b/.changeset/lucky-adults-tan.md index c96643cd9e3..92a75073295 100644 --- a/.changeset/lucky-adults-tan.md +++ b/.changeset/lucky-adults-tan.md @@ -1,6 +1,6 @@ --- -'@builder.io/qwik-city': major -'@builder.io/qwik': major +'@builder.io/qwik-city': patch +'@builder.io/qwik': patch --- feat(cli): Add check-client command to verify bundle freshness diff --git a/packages/qwik/src/cli/add/run-add-command.ts b/packages/qwik/src/cli/add/run-add-command.ts index 8c7683bfde5..57532ecb7e4 100644 --- a/packages/qwik/src/cli/add/run-add-command.ts +++ b/packages/qwik/src/cli/add/run-add-command.ts @@ -2,7 +2,7 @@ import type { AppCommand } from '../utils/app-command'; import { red } from 'kleur/colors'; import { runAddInteractive } from './run-add-interactive'; import { printAddHelp } from './print-add-help'; -import { runQwikClientCommand } from '../check-client/run-qwik-client-command'; + export async function runAddCommand(app: AppCommand) { try { const id = app.args[1]; @@ -10,7 +10,6 @@ export async function runAddCommand(app: AppCommand) { await printAddHelp(app); } else { await runAddInteractive(app, id); - await runQwikClientCommand(app); } } catch (e) { console.error(`❌ ${red(String(e))}\n`); diff --git a/packages/qwik/src/cli/check-client/check-client-command.ts b/packages/qwik/src/cli/check-client/check-client-command.ts index 93daaca8744..4fc40a40d22 100644 --- a/packages/qwik/src/cli/check-client/check-client-command.ts +++ b/packages/qwik/src/cli/check-client/check-client-command.ts @@ -1,21 +1,18 @@ #!/usr/bin/env node - import fs from 'fs/promises'; import type { Stats } from 'fs'; import path from 'path'; -// Import Clack and Kleur for interactive prompts and colors -import { intro, isCancel, log, outro, select, spinner } from '@clack/prompts'; -import { bye, getPackageManager } from '../utils/utils'; // Assuming these utils exist +import { intro, log, outro, spinner } from '@clack/prompts'; +import { getPackageManager } from '../utils/utils'; import { bgBlue, bgMagenta, bold, cyan, gray, green, red, yellow } from 'kleur/colors'; -import type { AppCommand } from '../utils/app-command'; // Assuming this type exists +import type { AppCommand } from '../utils/app-command'; import { runInPkg } from '../utils/install-deps'; const DISK_DIR: string = path.resolve('dist'); const SRC_DIR: string = path.resolve('src'); const MANIFEST_PATH: string = path.resolve(DISK_DIR, 'q-manifest.json'); -const BUILD_COMMAND: string = 'npm'; -const BUILD_ARGS: string[] = ['run', 'build']; +const BUILD_ARGS: string[] = ['run', 'build.client']; /** * Recursively finds the latest modification time (mtime) of any file in the given directory. @@ -66,7 +63,6 @@ async function getLatestMtime(directoryPath: string): Promise { * @param {AppCommand} app - Application command context (assuming structure). */ export async function checkClientCommand(app: AppCommand): Promise { - // Display introductory message intro(`🚀 ${bgBlue(bold(' Qiwk Client Check '))}`); const pkgManager = getPackageManager(); @@ -75,18 +71,14 @@ export async function checkClientCommand(app: AppCommand): Promise { let needsBuild: boolean = false; const reasonsForBuild: string[] = []; - // Step 1: Check the manifest file - log.step(`Checking manifest file: ${cyan(MANIFEST_PATH)}`); try { // Get stats for the manifest file const stats: Stats = await fs.stat(MANIFEST_PATH); // Use the resolved path manifestMtime = stats.mtimeMs; manifestExists = true; - log.info(`Manifest file found, modified: ${gray(new Date(manifestMtime).toLocaleString())}`); } catch (err: any) { // Handle errors accessing the manifest file if (err.code === 'ENOENT') { - log.warn(`Manifest file not found: ${yellow(MANIFEST_PATH)}`); needsBuild = true; reasonsForBuild.push('Manifest file not found'); } else { @@ -96,13 +88,10 @@ export async function checkClientCommand(app: AppCommand): Promise { } } - // Step 2: Check the source directory log.step(`Checking source directory: ${cyan(SRC_DIR)}`); let latestSrcMtime: number = 0; try { - // Confirm source directory exists and is accessible await fs.access(SRC_DIR); - // Find the latest modification time within the source directory latestSrcMtime = await getLatestMtime(SRC_DIR); if (latestSrcMtime > 0) { @@ -134,30 +123,11 @@ export async function checkClientCommand(app: AppCommand): Promise { } } - // Step 3: Perform build if necessary - let buildSuccess: boolean | undefined = undefined; // Initialize build success status + let buildSuccess: boolean | undefined = undefined; if (needsBuild) { - log.step(yellow('Client build detected as necessary')); - // Log reasons why a build is needed + log.info('Client build outdated, building...'); reasonsForBuild.forEach((reason) => log.info(` - ${reason}`)); - // Confirm with the user before proceeding with the build - const proceed: boolean | symbol = await select({ - message: `Proceed with client build based on the reasons above (${cyan(BUILD_COMMAND + ' ' + BUILD_ARGS.join(' '))})?`, - options: [ - { value: true, label: 'Yes, proceed with build', hint: 'Will run the build command' }, - { value: false, label: 'No, cancel operation' }, - ], - initialValue: true, - }); - - // Check if the user cancelled the operation - if (isCancel(proceed) || proceed === false) { - bye(); // Exit gracefully (assuming bye handles this) - return; // Stop further execution - } - - // Show a spinner while the build command runs const s = spinner(); s.start('Running client build...'); try { @@ -195,7 +165,6 @@ export async function checkClientCommand(app: AppCommand): Promise { log.info(green('Client is up-to-date, no build needed.')); } - // Step 4: Check the Disk directory (usually after build) log.step(`Checking Disk directory: ${cyan(DISK_DIR)}`); try { // Check if the disk directory exists and is accessible @@ -219,6 +188,5 @@ export async function checkClientCommand(app: AppCommand): Promise { } } - // Display completion message outro(`✅ ${bgMagenta(bold(' Check complete '))}`); } diff --git a/packages/qwik/src/cli/run.ts b/packages/qwik/src/cli/run.ts index c2c92cb8781..40ee04a3940 100644 --- a/packages/qwik/src/cli/run.ts +++ b/packages/qwik/src/cli/run.ts @@ -57,7 +57,7 @@ const COMMANDS = [ { value: 'check-client', label: 'check-client', - hint: 'Check if the bundle is latest version', + hint: 'Make sure the client bundle is up-to-date with the source code', run: (app: AppCommand) => runQwikClientCommand(app), showInHelp: true, }, From 10759623d70f55b63405573928b88fba7c327eac Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Fri, 18 Apr 2025 17:29:45 +0800 Subject: [PATCH 03/10] take some improvements according to a comment --- .../qwik/src/cli/check-client/check-client-command.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/qwik/src/cli/check-client/check-client-command.ts b/packages/qwik/src/cli/check-client/check-client-command.ts index 4fc40a40d22..3f7cac0e6d3 100644 --- a/packages/qwik/src/cli/check-client/check-client-command.ts +++ b/packages/qwik/src/cli/check-client/check-client-command.ts @@ -3,9 +3,9 @@ import fs from 'fs/promises'; import type { Stats } from 'fs'; import path from 'path'; -import { intro, log, outro, spinner } from '@clack/prompts'; +import { intro, log, outro } from '@clack/prompts'; import { getPackageManager } from '../utils/utils'; -import { bgBlue, bgMagenta, bold, cyan, gray, green, red, yellow } from 'kleur/colors'; +import { bgBlue, bgMagenta, bold, cyan, gray, green, yellow } from 'kleur/colors'; import type { AppCommand } from '../utils/app-command'; import { runInPkg } from '../utils/install-deps'; @@ -128,8 +128,7 @@ export async function checkClientCommand(app: AppCommand): Promise { log.info('Client build outdated, building...'); reasonsForBuild.forEach((reason) => log.info(` - ${reason}`)); - const s = spinner(); - s.start('Running client build...'); + log.info('Running client build...'); try { // Execute the build command // Ensure runCommand returns an object with an 'install' promise or similar structure @@ -137,7 +136,6 @@ export async function checkClientCommand(app: AppCommand): Promise { buildSuccess = await install; // Await the promise indicating build completion/success if (buildSuccess) { - s.stop(green('Client build completed successfully.')); // **Important:** Re-check manifest mtime after successful build try { const newStats = await fs.stat(MANIFEST_PATH); @@ -150,12 +148,10 @@ export async function checkClientCommand(app: AppCommand): Promise { } } else { // Handle build failure reported by runCommand - s.stop(red('Client build failed.'), 1); throw new Error('Client build command reported failure.'); } } catch (buildError: any) { // Catch errors during the build process itself (e.g., command not found, script errors) - s.stop(red('Client build failed.'), 1); log.error(`Build error: ${buildError.message}`); // Throw error to indicate failure, let the caller handle exit logic if needed throw new Error('Client build process encountered an error.'); From df4758b33633fd9cf898ddb31d925dee91cfb406 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Tue, 22 Apr 2025 19:27:04 +0800 Subject: [PATCH 04/10] fix(cli): update check-client command and improve hints; adjust package versioning --- packages/qwik/src/cli/check-client/build.ts | 27 +++ .../cli/check-client/check-client-command.ts | 188 ++---------------- .../qwik/src/cli/check-client/check-disk.ts | 26 +++ .../qwik/src/cli/check-client/constant.ts | 6 + .../src/cli/check-client/get-latest-m-time.ts | 44 ++++ .../src/cli/check-client/get-manifest-file.ts | 30 +++ .../check-client/run-qwik-client-command.ts | 3 +- starters/adapters/aws-lambda/package.json | 2 +- starters/adapters/azure-swa/package.json | 2 +- starters/adapters/bun/package.json | 2 +- starters/adapters/cloud-run/package.json | 2 +- .../adapters/cloudflare-pages/package.json | 2 +- starters/adapters/deno/package.json | 2 +- starters/adapters/express/package.json | 2 +- starters/adapters/fastify/package.json | 2 +- starters/adapters/firebase/package.json | 2 +- starters/adapters/netlify-edge/package.json | 2 +- starters/adapters/node-server/package.json | 2 +- starters/adapters/static/package.json | 2 +- starters/adapters/vercel-edge/package.json | 2 +- 20 files changed, 167 insertions(+), 183 deletions(-) create mode 100644 packages/qwik/src/cli/check-client/build.ts create mode 100644 packages/qwik/src/cli/check-client/check-disk.ts create mode 100644 packages/qwik/src/cli/check-client/constant.ts create mode 100644 packages/qwik/src/cli/check-client/get-latest-m-time.ts create mode 100644 packages/qwik/src/cli/check-client/get-manifest-file.ts diff --git a/packages/qwik/src/cli/check-client/build.ts b/packages/qwik/src/cli/check-client/build.ts new file mode 100644 index 00000000000..fcf8b2f3542 --- /dev/null +++ b/packages/qwik/src/cli/check-client/build.ts @@ -0,0 +1,27 @@ +import { log } from '@clack/prompts'; +import type { AppCommand } from '../utils/app-command'; +import { runInPkg } from '../utils/install-deps'; +import { getPackageManager } from '../utils/utils'; +import { BUILD_ARGS } from './constant'; + +/** + * Builds the application using the appropriate package manager. + * + * @param {AppCommand} app - The application command object containing app details. + * @param {string} manifestPath - The path to the manifest file (though it's not used in the current + * function). + * @throws {Error} Throws an error if the build process encounters any issues. + */ + +export async function goBuild(app: AppCommand, manifestPath: string) { + const pkgManager = getPackageManager(); + try { + const { install } = await runInPkg(pkgManager, BUILD_ARGS, app.rootDir); + if (!(await install)) { + throw new Error('Client build command reported failure.'); + } + } catch (buildError: any) { + log.error(`Build error: ${buildError.message}`); + throw new Error('Client build process encountered an error.'); + } +} diff --git a/packages/qwik/src/cli/check-client/check-client-command.ts b/packages/qwik/src/cli/check-client/check-client-command.ts index 3f7cac0e6d3..a318e6fa239 100644 --- a/packages/qwik/src/cli/check-client/check-client-command.ts +++ b/packages/qwik/src/cli/check-client/check-client-command.ts @@ -1,60 +1,12 @@ -#!/usr/bin/env node -import fs from 'fs/promises'; -import type { Stats } from 'fs'; -import path from 'path'; - +#!/usr/bin/env node'; import { intro, log, outro } from '@clack/prompts'; -import { getPackageManager } from '../utils/utils'; -import { bgBlue, bgMagenta, bold, cyan, gray, green, yellow } from 'kleur/colors'; +import { bgBlue, bgMagenta, bold, cyan } from 'kleur/colors'; import type { AppCommand } from '../utils/app-command'; -import { runInPkg } from '../utils/install-deps'; - -const DISK_DIR: string = path.resolve('dist'); -const SRC_DIR: string = path.resolve('src'); -const MANIFEST_PATH: string = path.resolve(DISK_DIR, 'q-manifest.json'); -const BUILD_ARGS: string[] = ['run', 'build.client']; - -/** - * Recursively finds the latest modification time (mtime) of any file in the given directory. - * - * @param {string} directoryPath - The directory path to search. - * @returns {Promise} Returns the latest mtime (Unix timestamp in milliseconds), or 0 if the - * directory doesn't exist or is empty. - */ -async function getLatestMtime(directoryPath: string): Promise { - let latestTime = 0; - - async function traverse(dir: string): Promise { - let items: Array; - try { - items = await fs.readdir(dir, { withFileTypes: true }); - } catch (err: any) { - if (err.code !== 'ENOENT') { - console.warn(`Cannot read directory ${dir}: ${err.message}`); - } - return; - } - - for (const item of items) { - const fullPath = path.join(dir, item.name); - try { - if (item.isDirectory()) { - await traverse(fullPath); - } else if (item.isFile()) { - const stats = await fs.stat(fullPath); - if (stats.mtimeMs > latestTime) { - latestTime = stats.mtimeMs; - } - } - } catch (err: any) { - console.warn(`Cannot access ${fullPath}: ${err.message}`); - } - } - } - - await traverse(directoryPath); - return latestTime; -} +import { isCheckDisk } from './check-disk'; +import { DISK_DIR, SRC_DIR } from './constant'; +import { getLatestMtime } from './get-latest-m-time'; +import { getManifestFile } from './get-manifest-file'; +import { goBuild } from './build'; /** * Handles the core logic for the 'check-client' command. Exports this function so other modules can @@ -62,125 +14,23 @@ async function getLatestMtime(directoryPath: string): Promise { * * @param {AppCommand} app - Application command context (assuming structure). */ -export async function checkClientCommand(app: AppCommand): Promise { +export async function checkClientCommand(app: AppCommand, manifestPath: string): Promise { intro(`🚀 ${bgBlue(bold(' Qiwk Client Check '))}`); - const pkgManager = getPackageManager(); - let manifestMtime: number = 0; - let manifestExists: boolean = false; - let needsBuild: boolean = false; - const reasonsForBuild: string[] = []; - - try { - // Get stats for the manifest file - const stats: Stats = await fs.stat(MANIFEST_PATH); // Use the resolved path - manifestMtime = stats.mtimeMs; - manifestExists = true; - } catch (err: any) { - // Handle errors accessing the manifest file - if (err.code === 'ENOENT') { - needsBuild = true; - reasonsForBuild.push('Manifest file not found'); - } else { - log.error(`Error accessing manifest file ${MANIFEST_PATH}: ${err.message}`); - needsBuild = true; - reasonsForBuild.push(`Cannot access manifest file (${err.code})`); - } - } - - log.step(`Checking source directory: ${cyan(SRC_DIR)}`); - let latestSrcMtime: number = 0; - try { - await fs.access(SRC_DIR); - latestSrcMtime = await getLatestMtime(SRC_DIR); - - if (latestSrcMtime > 0) { - log.info( - `Latest file modification in source directory: ${gray(new Date(latestSrcMtime).toLocaleString())}` - ); - // Compare source modification time with manifest modification time - if (manifestExists && latestSrcMtime > manifestMtime) { - log.warn('Source files are newer than the manifest.'); - needsBuild = true; - reasonsForBuild.push('Source files (src) are newer than the manifest'); - } else if (manifestExists) { - log.info('Manifest file is up-to-date relative to source files.'); - } - } else { - // Handle case where source directory is empty or inaccessible - log.info(`No source files found or directory is empty/inaccessible in '${SRC_DIR}'.`); - // Note: Depending on requirements, you might want to set needsBuild = true here - } - } catch (err: any) { - // Handle errors accessing the source directory - if (err.code === 'ENOENT') { - log.error(`Source directory '${SRC_DIR}' not found! Build might fail.`); - // Decide whether to force build or exit if source directory access fails - // Setting needsBuild = true might be appropriate if the build process creates it. - } else { - log.error(`Error accessing source directory '${SRC_DIR}': ${err.message}`); - // Consider setting needsBuild = true or exiting based on severity - } - } - - let buildSuccess: boolean | undefined = undefined; - if (needsBuild) { - log.info('Client build outdated, building...'); - reasonsForBuild.forEach((reason) => log.info(` - ${reason}`)); - - log.info('Running client build...'); - try { - // Execute the build command - // Ensure runCommand returns an object with an 'install' promise or similar structure - const { install } = await runInPkg(pkgManager, BUILD_ARGS, app.rootDir); - buildSuccess = await install; // Await the promise indicating build completion/success + log.step(`Checking Disk directory: ${cyan(DISK_DIR)}`); - if (buildSuccess) { - // **Important:** Re-check manifest mtime after successful build - try { - const newStats = await fs.stat(MANIFEST_PATH); - manifestMtime = newStats.mtimeMs; - manifestExists = true; // Mark as existing now - log.info(`Manifest updated: ${gray(new Date(manifestMtime).toLocaleString())}`); - } catch (statErr: any) { - log.error(`Failed to re-stat manifest after build: ${statErr.message}`); - // Handle this case - maybe the build didn't create the manifest? - } - } else { - // Handle build failure reported by runCommand - throw new Error('Client build command reported failure.'); - } - } catch (buildError: any) { - // Catch errors during the build process itself (e.g., command not found, script errors) - log.error(`Build error: ${buildError.message}`); - // Throw error to indicate failure, let the caller handle exit logic if needed - throw new Error('Client build process encountered an error.'); - } + if (await isCheckDisk()) { + await goBuild(app, manifestPath); } else { - // If no build was needed - log.info(green('Client is up-to-date, no build needed.')); - } - - log.step(`Checking Disk directory: ${cyan(DISK_DIR)}`); - try { - // Check if the disk directory exists and is accessible - await fs.access(DISK_DIR); - log.info(`Disk directory found: ${green(DISK_DIR)}`); - } catch (err: any) { - // Handle errors accessing the disk directory - if (err.code === 'ENOENT') { - log.warn(`Disk directory not found: ${yellow(DISK_DIR)}`); - // Provide context if a build just happened - if (needsBuild && buildSuccess === true) { - // Check if build was attempted and successful - log.warn( - `Note: Build completed, but '${DISK_DIR}' directory was not found. The build process might not create it.` - ); - } else if (needsBuild && !buildSuccess) { - log.warn(`Note: Build failed, and '${DISK_DIR}' directory was not found.`); - } + log.step(`Checking q-manifest.json file: ${cyan(DISK_DIR)}`); + const manifest = await getManifestFile(manifestPath); + if (!manifest) { + await goBuild(app, manifestPath); } else { - log.error(`Error accessing disk directory ${DISK_DIR}: ${err.message}`); + log.step(`Compare source modification time with q-manifest.json modification time`); + if (await getLatestMtime(SRC_DIR, manifest)) { + await goBuild(app, manifestPath); + } } } diff --git a/packages/qwik/src/cli/check-client/check-disk.ts b/packages/qwik/src/cli/check-client/check-disk.ts new file mode 100644 index 00000000000..923d1bf0fbd --- /dev/null +++ b/packages/qwik/src/cli/check-client/check-disk.ts @@ -0,0 +1,26 @@ +import fs from 'fs/promises'; + +import { log } from '@clack/prompts'; +import { panic } from '../utils/utils'; +import { yellow } from 'kleur/colors'; +import { DISK_DIR } from './constant'; + +/** + * Checks if the specified disk directory exists and is accessible. + * + * @returns {Promise} Returns true if the directory exists and can be accessed, returns + * false if it doesn't exist or an error occurs. + */ +export async function isCheckDisk(): Promise { + try { + await fs.access(DISK_DIR); + return true; // Directory exists + } catch (err: any) { + if (err.code === 'ENOENT') { + log.warn(`Disk directory not found: ${yellow(DISK_DIR)}`); + } else { + panic(`Error accessing disk directory ${DISK_DIR}: ${err.message}`); + } + return false; // Directory doesn't exist or there was an error + } +} diff --git a/packages/qwik/src/cli/check-client/constant.ts b/packages/qwik/src/cli/check-client/constant.ts new file mode 100644 index 00000000000..d25d1ab2041 --- /dev/null +++ b/packages/qwik/src/cli/check-client/constant.ts @@ -0,0 +1,6 @@ +import path from 'path'; + +export const DISK_DIR: string = path.resolve('dist'); +export const SRC_DIR: string = path.resolve('src'); +export const BUILD_ARGS: string[] = ['run', 'build.client']; +export const MANIFEST_PATH: string = path.resolve(DISK_DIR, 'q-manifest.json'); diff --git a/packages/qwik/src/cli/check-client/get-latest-m-time.ts b/packages/qwik/src/cli/check-client/get-latest-m-time.ts new file mode 100644 index 00000000000..17329c6dd1b --- /dev/null +++ b/packages/qwik/src/cli/check-client/get-latest-m-time.ts @@ -0,0 +1,44 @@ +import path from 'path'; +import fs from 'fs/promises'; + +/** + * Recursively finds the latest modification time (mtime) of any file in the given directory. + * + * @param {string} directoryPath - The directory path to search. + * @returns {Promise} Returns the latest mtime (Unix timestamp in milliseconds), or 0 if the + * directory doesn't exist or is empty. + */ +export async function getLatestMtime(directoryPath: string, timestamp: number): Promise { + let returnValue = false; + async function traverse(dir: string): Promise { + let items: Array; + try { + items = await fs.readdir(dir, { withFileTypes: true }); + } catch (err: any) { + if (err.code !== 'ENOENT') { + console.warn(`Cannot read directory ${dir}: ${err.message}`); + } + return; + } + + for (const item of items) { + const fullPath = path.join(dir, item.name); + try { + if (item.isDirectory()) { + await traverse(fullPath); + } else if (item.isFile()) { + const stats = await fs.stat(fullPath); + if (stats.mtimeMs > timestamp) { + returnValue = true; + return; + } + } + } catch (err: any) { + console.warn(`Cannot access ${fullPath}: ${err.message}`); + } + } + } + + await traverse(directoryPath); + return returnValue; +} diff --git a/packages/qwik/src/cli/check-client/get-manifest-file.ts b/packages/qwik/src/cli/check-client/get-manifest-file.ts new file mode 100644 index 00000000000..5050be9a4a1 --- /dev/null +++ b/packages/qwik/src/cli/check-client/get-manifest-file.ts @@ -0,0 +1,30 @@ +import fs from 'fs/promises'; +import type { Stats } from 'fs'; + +import { log } from '@clack/prompts'; +import { panic } from '../utils/utils'; +import { yellow } from 'kleur/colors'; +import { DISK_DIR, MANIFEST_PATH } from './constant'; + +/** + * Retrieves the last modified timestamp of the manifest file. + * + * @param {string} manifestPath - The path to the manifest file. + * @returns {Promise} Returns the last modified timestamp (in milliseconds) of the + * manifest file, or null if an error occurs. + */ +export async function getManifestFile(manifestPath: string = MANIFEST_PATH) { + try { + // Get stats for the manifest file + const stats: Stats = await fs.stat(manifestPath); + return stats.mtimeMs; + } catch (err: any) { + // Handle errors accessing the manifest file + if (err.code === 'ENOENT') { + log.warn(`q-manifest.json file not found: ${yellow(DISK_DIR)}`); + } else { + panic(`Error accessing manifest file ${manifestPath}: ${err.message}`); + } + return null; + } +} diff --git a/packages/qwik/src/cli/check-client/run-qwik-client-command.ts b/packages/qwik/src/cli/check-client/run-qwik-client-command.ts index 45980638507..7259cf372bc 100644 --- a/packages/qwik/src/cli/check-client/run-qwik-client-command.ts +++ b/packages/qwik/src/cli/check-client/run-qwik-client-command.ts @@ -4,7 +4,8 @@ import { checkClientCommand } from './check-client-command'; export async function runQwikClientCommand(app: AppCommand) { try { - await checkClientCommand(app); + const manifestPath = app.args[1]; + await checkClientCommand(app, manifestPath); } catch (e) { console.error(`❌ ${red(String(e))}\n`); process.exit(1); diff --git a/starters/adapters/aws-lambda/package.json b/starters/adapters/aws-lambda/package.json index 668181564e6..df22718258d 100755 --- a/starters/adapters/aws-lambda/package.json +++ b/starters/adapters/aws-lambda/package.json @@ -1,7 +1,7 @@ { "description": "AWS Lambda", "scripts": { - "build.server": "vite build -c adapters/aws-lambda/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/aws-lambda/vite.config.ts", "serve": "qwik build && serverless offline", "deploy": "serverless deploy" }, diff --git a/starters/adapters/azure-swa/package.json b/starters/adapters/azure-swa/package.json index f22018583ef..4979977b32e 100644 --- a/starters/adapters/azure-swa/package.json +++ b/starters/adapters/azure-swa/package.json @@ -1,7 +1,7 @@ { "description": "Azure Static Web Apps", "scripts": { - "build.server": "vite build -c adapters/azure-swa/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/azure-swa/vite.config.ts", "serve": "swa start" }, "devDependencies": { diff --git a/starters/adapters/bun/package.json b/starters/adapters/bun/package.json index bf97dc87e93..8d0a3f17486 100644 --- a/starters/adapters/bun/package.json +++ b/starters/adapters/bun/package.json @@ -1,7 +1,7 @@ { "description": "Bun server", "scripts": { - "build.server": "vite build -c adapters/bun/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/bun/vite.config.ts", "serve": "bun server/entry.bun.js" }, "__qwik__": { diff --git a/starters/adapters/cloud-run/package.json b/starters/adapters/cloud-run/package.json index 818283b12aa..54d69971260 100644 --- a/starters/adapters/cloud-run/package.json +++ b/starters/adapters/cloud-run/package.json @@ -1,7 +1,7 @@ { "description": "Google Cloud Run server", "scripts": { - "build.server": "vite build -c adapters/cloud-run/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/cloud-run/vite.config.ts", "deploy": "gcloud run deploy my-cloud-run-app --source ." }, "__qwik__": { diff --git a/starters/adapters/cloudflare-pages/package.json b/starters/adapters/cloudflare-pages/package.json index 4c871e1956f..2d9a8cd1bd9 100644 --- a/starters/adapters/cloudflare-pages/package.json +++ b/starters/adapters/cloudflare-pages/package.json @@ -1,7 +1,7 @@ { "description": "Cloudflare Pages", "scripts": { - "build.server": "vite build -c adapters/cloudflare-pages/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/cloudflare-pages/vite.config.ts", "deploy": "wrangler pages deploy ./dist", "serve": "wrangler pages dev ./dist --compatibility-flags=nodejs_als" }, diff --git a/starters/adapters/deno/package.json b/starters/adapters/deno/package.json index 231fe75d9f1..f6f4704a7a7 100644 --- a/starters/adapters/deno/package.json +++ b/starters/adapters/deno/package.json @@ -1,7 +1,7 @@ { "description": "Deno server", "scripts": { - "build.server": "vite build -c adapters/deno/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/deno/vite.config.ts", "serve": "deno run --allow-net --allow-read --allow-env server/entry.deno.js" }, "__qwik__": { diff --git a/starters/adapters/express/package.json b/starters/adapters/express/package.json index 53e8145b86c..ebbc3a4ae1b 100644 --- a/starters/adapters/express/package.json +++ b/starters/adapters/express/package.json @@ -1,7 +1,7 @@ { "description": "Express.js server", "scripts": { - "build.server": "vite build -c adapters/express/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/express/vite.config.ts", "serve": "node server/entry.express" }, "dependencies": { diff --git a/starters/adapters/fastify/package.json b/starters/adapters/fastify/package.json index 861be8e0ec4..a10d280ce3a 100644 --- a/starters/adapters/fastify/package.json +++ b/starters/adapters/fastify/package.json @@ -1,7 +1,7 @@ { "description": "Fastify server", "scripts": { - "build.server": "vite build -c adapters/fastify/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/fastify/vite.config.ts", "serve": "node server/entry.fastify" }, "dependencies": { diff --git a/starters/adapters/firebase/package.json b/starters/adapters/firebase/package.json index b9b3868bcb7..44f9268a744 100644 --- a/starters/adapters/firebase/package.json +++ b/starters/adapters/firebase/package.json @@ -1,7 +1,7 @@ { "description": "Firebase", "scripts": { - "build.server": "vite build -c adapters/firebase/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/firebase/vite.config.ts", "serve": "qwik build && firebase emulators:start", "deploy": "firebase deploy" }, diff --git a/starters/adapters/netlify-edge/package.json b/starters/adapters/netlify-edge/package.json index e5b68aadb91..64980f9ba97 100644 --- a/starters/adapters/netlify-edge/package.json +++ b/starters/adapters/netlify-edge/package.json @@ -1,7 +1,7 @@ { "description": "Netlify Edge Functions", "scripts": { - "build.server": "vite build -c adapters/netlify-edge/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/netlify-edge/vite.config.ts", "deploy": "netlify deploy --build" }, "devDependencies": { diff --git a/starters/adapters/node-server/package.json b/starters/adapters/node-server/package.json index 8d698f78bf4..654c0b4c645 100644 --- a/starters/adapters/node-server/package.json +++ b/starters/adapters/node-server/package.json @@ -1,7 +1,7 @@ { "description": "Vanilla Node server", "scripts": { - "build.server": "vite build -c adapters/node-server/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/node-server/vite.config.ts", "serve": "node server/entry.node-server" }, "__qwik__": { diff --git a/starters/adapters/static/package.json b/starters/adapters/static/package.json index bf70695a465..59c670d1a22 100644 --- a/starters/adapters/static/package.json +++ b/starters/adapters/static/package.json @@ -1,7 +1,7 @@ { "description": "Static Site Generator", "scripts": { - "build.server": "vite build -c adapters/static/vite.config.ts" + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/static/vite.config.ts" }, "__qwik__": { "priority": 10, diff --git a/starters/adapters/vercel-edge/package.json b/starters/adapters/vercel-edge/package.json index 83144b458c1..1b3e76216db 100644 --- a/starters/adapters/vercel-edge/package.json +++ b/starters/adapters/vercel-edge/package.json @@ -1,7 +1,7 @@ { "description": "Vercel Edge Functions", "scripts": { - "build.server": "vite build -c adapters/vercel-edge/vite.config.ts", + "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/vercel-edge/vite.config.ts", "deploy": "vercel deploy" }, "devDependencies": { From ac2b4ef6056805254385a9357d2c2402e31a28eb Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 28 Apr 2025 13:57:11 +0800 Subject: [PATCH 05/10] refactor(cli): consolidate check-client functionality into a single file Move all check-client related functions and constants into a single index.ts file to improve maintainability and reduce file fragmentation --- packages/qwik/src/cli/check-client/build.ts | 27 --- .../cli/check-client/check-client-command.ts | 38 ----- .../qwik/src/cli/check-client/check-disk.ts | 26 --- .../qwik/src/cli/check-client/constant.ts | 6 - .../src/cli/check-client/get-latest-m-time.ts | 44 ----- .../src/cli/check-client/get-manifest-file.ts | 30 ---- packages/qwik/src/cli/check-client/index.ts | 155 ++++++++++++++++++ .../check-client/run-qwik-client-command.ts | 13 -- packages/qwik/src/cli/run.ts | 2 +- 9 files changed, 156 insertions(+), 185 deletions(-) delete mode 100644 packages/qwik/src/cli/check-client/build.ts delete mode 100644 packages/qwik/src/cli/check-client/check-client-command.ts delete mode 100644 packages/qwik/src/cli/check-client/check-disk.ts delete mode 100644 packages/qwik/src/cli/check-client/constant.ts delete mode 100644 packages/qwik/src/cli/check-client/get-latest-m-time.ts delete mode 100644 packages/qwik/src/cli/check-client/get-manifest-file.ts create mode 100644 packages/qwik/src/cli/check-client/index.ts delete mode 100644 packages/qwik/src/cli/check-client/run-qwik-client-command.ts diff --git a/packages/qwik/src/cli/check-client/build.ts b/packages/qwik/src/cli/check-client/build.ts deleted file mode 100644 index fcf8b2f3542..00000000000 --- a/packages/qwik/src/cli/check-client/build.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { log } from '@clack/prompts'; -import type { AppCommand } from '../utils/app-command'; -import { runInPkg } from '../utils/install-deps'; -import { getPackageManager } from '../utils/utils'; -import { BUILD_ARGS } from './constant'; - -/** - * Builds the application using the appropriate package manager. - * - * @param {AppCommand} app - The application command object containing app details. - * @param {string} manifestPath - The path to the manifest file (though it's not used in the current - * function). - * @throws {Error} Throws an error if the build process encounters any issues. - */ - -export async function goBuild(app: AppCommand, manifestPath: string) { - const pkgManager = getPackageManager(); - try { - const { install } = await runInPkg(pkgManager, BUILD_ARGS, app.rootDir); - if (!(await install)) { - throw new Error('Client build command reported failure.'); - } - } catch (buildError: any) { - log.error(`Build error: ${buildError.message}`); - throw new Error('Client build process encountered an error.'); - } -} diff --git a/packages/qwik/src/cli/check-client/check-client-command.ts b/packages/qwik/src/cli/check-client/check-client-command.ts deleted file mode 100644 index a318e6fa239..00000000000 --- a/packages/qwik/src/cli/check-client/check-client-command.ts +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env node'; -import { intro, log, outro } from '@clack/prompts'; -import { bgBlue, bgMagenta, bold, cyan } from 'kleur/colors'; -import type { AppCommand } from '../utils/app-command'; -import { isCheckDisk } from './check-disk'; -import { DISK_DIR, SRC_DIR } from './constant'; -import { getLatestMtime } from './get-latest-m-time'; -import { getManifestFile } from './get-manifest-file'; -import { goBuild } from './build'; - -/** - * Handles the core logic for the 'check-client' command. Exports this function so other modules can - * import and call it. - * - * @param {AppCommand} app - Application command context (assuming structure). - */ -export async function checkClientCommand(app: AppCommand, manifestPath: string): Promise { - intro(`🚀 ${bgBlue(bold(' Qiwk Client Check '))}`); - - log.step(`Checking Disk directory: ${cyan(DISK_DIR)}`); - - if (await isCheckDisk()) { - await goBuild(app, manifestPath); - } else { - log.step(`Checking q-manifest.json file: ${cyan(DISK_DIR)}`); - const manifest = await getManifestFile(manifestPath); - if (!manifest) { - await goBuild(app, manifestPath); - } else { - log.step(`Compare source modification time with q-manifest.json modification time`); - if (await getLatestMtime(SRC_DIR, manifest)) { - await goBuild(app, manifestPath); - } - } - } - - outro(`✅ ${bgMagenta(bold(' Check complete '))}`); -} diff --git a/packages/qwik/src/cli/check-client/check-disk.ts b/packages/qwik/src/cli/check-client/check-disk.ts deleted file mode 100644 index 923d1bf0fbd..00000000000 --- a/packages/qwik/src/cli/check-client/check-disk.ts +++ /dev/null @@ -1,26 +0,0 @@ -import fs from 'fs/promises'; - -import { log } from '@clack/prompts'; -import { panic } from '../utils/utils'; -import { yellow } from 'kleur/colors'; -import { DISK_DIR } from './constant'; - -/** - * Checks if the specified disk directory exists and is accessible. - * - * @returns {Promise} Returns true if the directory exists and can be accessed, returns - * false if it doesn't exist or an error occurs. - */ -export async function isCheckDisk(): Promise { - try { - await fs.access(DISK_DIR); - return true; // Directory exists - } catch (err: any) { - if (err.code === 'ENOENT') { - log.warn(`Disk directory not found: ${yellow(DISK_DIR)}`); - } else { - panic(`Error accessing disk directory ${DISK_DIR}: ${err.message}`); - } - return false; // Directory doesn't exist or there was an error - } -} diff --git a/packages/qwik/src/cli/check-client/constant.ts b/packages/qwik/src/cli/check-client/constant.ts deleted file mode 100644 index d25d1ab2041..00000000000 --- a/packages/qwik/src/cli/check-client/constant.ts +++ /dev/null @@ -1,6 +0,0 @@ -import path from 'path'; - -export const DISK_DIR: string = path.resolve('dist'); -export const SRC_DIR: string = path.resolve('src'); -export const BUILD_ARGS: string[] = ['run', 'build.client']; -export const MANIFEST_PATH: string = path.resolve(DISK_DIR, 'q-manifest.json'); diff --git a/packages/qwik/src/cli/check-client/get-latest-m-time.ts b/packages/qwik/src/cli/check-client/get-latest-m-time.ts deleted file mode 100644 index 17329c6dd1b..00000000000 --- a/packages/qwik/src/cli/check-client/get-latest-m-time.ts +++ /dev/null @@ -1,44 +0,0 @@ -import path from 'path'; -import fs from 'fs/promises'; - -/** - * Recursively finds the latest modification time (mtime) of any file in the given directory. - * - * @param {string} directoryPath - The directory path to search. - * @returns {Promise} Returns the latest mtime (Unix timestamp in milliseconds), or 0 if the - * directory doesn't exist or is empty. - */ -export async function getLatestMtime(directoryPath: string, timestamp: number): Promise { - let returnValue = false; - async function traverse(dir: string): Promise { - let items: Array; - try { - items = await fs.readdir(dir, { withFileTypes: true }); - } catch (err: any) { - if (err.code !== 'ENOENT') { - console.warn(`Cannot read directory ${dir}: ${err.message}`); - } - return; - } - - for (const item of items) { - const fullPath = path.join(dir, item.name); - try { - if (item.isDirectory()) { - await traverse(fullPath); - } else if (item.isFile()) { - const stats = await fs.stat(fullPath); - if (stats.mtimeMs > timestamp) { - returnValue = true; - return; - } - } - } catch (err: any) { - console.warn(`Cannot access ${fullPath}: ${err.message}`); - } - } - } - - await traverse(directoryPath); - return returnValue; -} diff --git a/packages/qwik/src/cli/check-client/get-manifest-file.ts b/packages/qwik/src/cli/check-client/get-manifest-file.ts deleted file mode 100644 index 5050be9a4a1..00000000000 --- a/packages/qwik/src/cli/check-client/get-manifest-file.ts +++ /dev/null @@ -1,30 +0,0 @@ -import fs from 'fs/promises'; -import type { Stats } from 'fs'; - -import { log } from '@clack/prompts'; -import { panic } from '../utils/utils'; -import { yellow } from 'kleur/colors'; -import { DISK_DIR, MANIFEST_PATH } from './constant'; - -/** - * Retrieves the last modified timestamp of the manifest file. - * - * @param {string} manifestPath - The path to the manifest file. - * @returns {Promise} Returns the last modified timestamp (in milliseconds) of the - * manifest file, or null if an error occurs. - */ -export async function getManifestFile(manifestPath: string = MANIFEST_PATH) { - try { - // Get stats for the manifest file - const stats: Stats = await fs.stat(manifestPath); - return stats.mtimeMs; - } catch (err: any) { - // Handle errors accessing the manifest file - if (err.code === 'ENOENT') { - log.warn(`q-manifest.json file not found: ${yellow(DISK_DIR)}`); - } else { - panic(`Error accessing manifest file ${manifestPath}: ${err.message}`); - } - return null; - } -} diff --git a/packages/qwik/src/cli/check-client/index.ts b/packages/qwik/src/cli/check-client/index.ts new file mode 100644 index 00000000000..38ee884224b --- /dev/null +++ b/packages/qwik/src/cli/check-client/index.ts @@ -0,0 +1,155 @@ +import type { AppCommand } from '../utils/app-command'; +import { intro, log, outro } from '@clack/prompts'; +import { bgBlue, bgMagenta, bold, cyan, yellow, red } from 'kleur/colors'; +import { runInPkg } from '../utils/install-deps'; +import { getPackageManager, panic } from '../utils/utils'; +import fs from 'fs/promises'; +import type { Stats } from 'fs'; +import path from 'path'; + +const DISK_DIR: string = path.resolve('dist'); +const SRC_DIR: string = path.resolve('src'); +const MANIFEST_PATH: string = path.resolve(DISK_DIR, 'q-manifest.json'); + +export async function runQwikClientCommand(app: AppCommand) { + try { + const manifestPath = app.args[1]; + await checkClientCommand(app, manifestPath); + } catch (e) { + console.error(`❌ ${red(String(e))}\n`); + process.exit(1); + } +} + +/** + * Handles the core logic for the 'check-client' command. Exports this function so other modules can + * import and call it. + * + * @param {AppCommand} app - Application command context (assuming structure). + */ +async function checkClientCommand(app: AppCommand, manifestPath: string): Promise { + if (!(await clientDirExists())) { + intro(`🚀 ${bgBlue(bold(' Qwik Client Check '))}`); + log.step(`Checking Disk directory: ${cyan(DISK_DIR)}`); + await goBuild(app, manifestPath); + } else { + log.step(`Checking q-manifest.json file: ${cyan(DISK_DIR)}`); + const manifest = await getManifestTs(manifestPath); + if (manifest === null) { + await goBuild(app, manifestPath); + } else { + log.step(`Compare source modification time with q-manifest.json modification time`); + if (await isNewerThan(SRC_DIR, manifest)) { + await goBuild(app, manifestPath); + } + } + } +} + +/** + * Builds the application using the appropriate package manager. + * + * @param {AppCommand} app - The application command object containing app details. + * @param {string} manifestPath - The path to the manifest file (though it's not used in the current + * function). + * @throws {Error} Throws an error if the build process encounters any issues. + */ + +async function goBuild(app: AppCommand, manifestPath: string) { + const pkgManager = getPackageManager(); + log.step('Building client (manifest missing or outdated)...'); + try { + const { install } = await runInPkg(pkgManager, ['run', 'build.client'], app.rootDir); + if (!(await install)) { + throw new Error('Client build command reported failure.'); + } + } catch (buildError: any) { + log.error(`Build error: ${buildError.message}`); + throw new Error('Client build process encountered an error.'); + } + outro(`✅ ${bgMagenta(bold(' Check complete '))}`); +} + +/** + * Retrieves the last modified timestamp of the manifest file. + * + * @param {string} manifestPath - The path to the manifest file. + * @returns {Promise} Returns the last modified timestamp (in milliseconds) of the + * manifest file, or null if an error occurs. + */ +async function getManifestTs(manifestPath: string = MANIFEST_PATH) { + try { + // Get stats for the manifest file + const stats: Stats = await fs.stat(manifestPath); + return stats.mtimeMs; + } catch (err: any) { + // Handle errors accessing the manifest file + if (err.code === 'ENOENT') { + log.warn(`q-manifest.json file not found: ${yellow(DISK_DIR)}`); + } else { + panic(`Error accessing manifest file ${manifestPath}: ${err.message}`); + } + return null; + } +} + +/** + * Checks if the specified disk directory exists and is accessible. + * + * @returns {Promise} Returns true if the directory exists and can be accessed, returns + * false if it doesn't exist or an error occurs. + */ +export async function clientDirExists(): Promise { + try { + await fs.access(DISK_DIR); + return true; // Directory exists + } catch (err: any) { + panic(`Error accessing disk directory ${DISK_DIR}: ${err.message}`); + return false; // Directory doesn't exist or there was an error + } +} + +/** + * Recursively finds the latest modification time (mtime) of any file in the given directory. + * + * @param {string} directoryPath - The directory path to search. + * @returns {Promise} Returns the latest mtime (Unix timestamp in milliseconds), or 0 if the + * directory doesn't exist or is empty. + */ +export async function isNewerThan(directoryPath: string, timestamp: number): Promise { + let returnValue = false; + async function traverse(dir: string): Promise { + if (returnValue) { + return; + } + let items: Array; + try { + items = await fs.readdir(dir, { withFileTypes: true }); + } catch (err: any) { + if (err.code !== 'ENOENT') { + console.warn(`Cannot read directory ${dir}: ${err.message}`); + } + return; + } + + for (const item of items) { + const fullPath = path.join(dir, item.name); + try { + if (item.isDirectory()) { + await traverse(fullPath); + } else if (item.isFile()) { + const stats = await fs.stat(fullPath); + if (stats.mtimeMs > timestamp) { + returnValue = true; + return; + } + } + } catch (err: any) { + console.warn(`Cannot access ${fullPath}: ${err.message}`); + } + } + } + + await traverse(directoryPath); + return returnValue; +} diff --git a/packages/qwik/src/cli/check-client/run-qwik-client-command.ts b/packages/qwik/src/cli/check-client/run-qwik-client-command.ts deleted file mode 100644 index 7259cf372bc..00000000000 --- a/packages/qwik/src/cli/check-client/run-qwik-client-command.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { AppCommand } from '../utils/app-command'; -import { red } from 'kleur/colors'; -import { checkClientCommand } from './check-client-command'; - -export async function runQwikClientCommand(app: AppCommand) { - try { - const manifestPath = app.args[1]; - await checkClientCommand(app, manifestPath); - } catch (e) { - console.error(`❌ ${red(String(e))}\n`); - process.exit(1); - } -} diff --git a/packages/qwik/src/cli/run.ts b/packages/qwik/src/cli/run.ts index 40ee04a3940..bdd6159bd49 100644 --- a/packages/qwik/src/cli/run.ts +++ b/packages/qwik/src/cli/run.ts @@ -8,7 +8,7 @@ import { note, panic, pmRunCmd, printHeader, bye } from './utils/utils'; import { runBuildCommand } from './utils/run-build-command'; import { intro, isCancel, select, confirm } from '@clack/prompts'; import { runV2Migration } from './migrate-v2/run-migration'; -import { runQwikClientCommand } from './check-client/run-qwik-client-command'; +import { runQwikClientCommand } from './check-client'; const SPACE_TO_HINT = 18; const COMMANDS = [ From 970334a966cfec7bf4b94684d4487f3771cf6a3e Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 28 Apr 2025 14:36:13 +0800 Subject: [PATCH 06/10] refactor(cli): update check-client command to accept src and dist paths Modify the check-client command to accept `src` and `dist` as arguments instead of hardcoding them. This change enhances flexibility and allows for more dynamic usage of the command across different adapters. The corresponding build scripts in various adapters' `package.json` files have been updated to pass these arguments. --- packages/qwik/src/cli/check-client/index.ts | 54 +++++++++---------- starters/adapters/aws-lambda/package.json | 2 +- starters/adapters/azure-swa/package.json | 2 +- starters/adapters/bun/package.json | 2 +- starters/adapters/cloud-run/package.json | 2 +- .../adapters/cloudflare-pages/package.json | 2 +- starters/adapters/deno/package.json | 2 +- starters/adapters/express/package.json | 2 +- starters/adapters/fastify/package.json | 2 +- starters/adapters/firebase/package.json | 2 +- starters/adapters/netlify-edge/package.json | 2 +- starters/adapters/node-server/package.json | 2 +- starters/adapters/static/package.json | 2 +- starters/adapters/vercel-edge/package.json | 2 +- 14 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/qwik/src/cli/check-client/index.ts b/packages/qwik/src/cli/check-client/index.ts index 38ee884224b..85a95d1bb38 100644 --- a/packages/qwik/src/cli/check-client/index.ts +++ b/packages/qwik/src/cli/check-client/index.ts @@ -1,20 +1,21 @@ import type { AppCommand } from '../utils/app-command'; import { intro, log, outro } from '@clack/prompts'; -import { bgBlue, bgMagenta, bold, cyan, yellow, red } from 'kleur/colors'; +import { bgBlue, bgMagenta, bold, cyan, red } from 'kleur/colors'; import { runInPkg } from '../utils/install-deps'; import { getPackageManager, panic } from '../utils/utils'; import fs from 'fs/promises'; import type { Stats } from 'fs'; import path from 'path'; -const DISK_DIR: string = path.resolve('dist'); -const SRC_DIR: string = path.resolve('src'); -const MANIFEST_PATH: string = path.resolve(DISK_DIR, 'q-manifest.json'); +const getDiskPath = (dist: string) => path.resolve(dist); +const getSrcPath = (src: string) => path.resolve(src); +const getManifestPath = (dist: string) => path.resolve(dist, 'q-manifest.json'); export async function runQwikClientCommand(app: AppCommand) { try { - const manifestPath = app.args[1]; - await checkClientCommand(app, manifestPath); + const src = app.args[1]; + const dist = app.args[2]; + await checkClientCommand(app, src, dist); } catch (e) { console.error(`❌ ${red(String(e))}\n`); process.exit(1); @@ -27,20 +28,20 @@ export async function runQwikClientCommand(app: AppCommand) { * * @param {AppCommand} app - Application command context (assuming structure). */ -async function checkClientCommand(app: AppCommand, manifestPath: string): Promise { - if (!(await clientDirExists())) { +async function checkClientCommand(app: AppCommand, src: string, dist: string): Promise { + if (!(await clientDirExists(dist))) { intro(`🚀 ${bgBlue(bold(' Qwik Client Check '))}`); - log.step(`Checking Disk directory: ${cyan(DISK_DIR)}`); - await goBuild(app, manifestPath); + log.step(`Checking Disk directory: ${cyan(dist)}`); + await goBuild(app); } else { - log.step(`Checking q-manifest.json file: ${cyan(DISK_DIR)}`); - const manifest = await getManifestTs(manifestPath); + log.step(`Checking q-manifest.json file: ${cyan(dist)}`); + const manifest = await getManifestTs(getManifestPath(dist)); if (manifest === null) { - await goBuild(app, manifestPath); + await goBuild(app); } else { log.step(`Compare source modification time with q-manifest.json modification time`); - if (await isNewerThan(SRC_DIR, manifest)) { - await goBuild(app, manifestPath); + if (await isNewerThan(getSrcPath(src), manifest)) { + await goBuild(app); } } } @@ -49,13 +50,12 @@ async function checkClientCommand(app: AppCommand, manifestPath: string): Promis /** * Builds the application using the appropriate package manager. * - * @param {AppCommand} app - The application command object containing app details. - * @param {string} manifestPath - The path to the manifest file (though it's not used in the current - * function). + * @param {AppCommand} app - The application command object containing app details.e path to the + * manifest file (though it's not used in the current function). * @throws {Error} Throws an error if the build process encounters any issues. */ -async function goBuild(app: AppCommand, manifestPath: string) { +async function goBuild(app: AppCommand) { const pkgManager = getPackageManager(); log.step('Building client (manifest missing or outdated)...'); try { @@ -77,7 +77,7 @@ async function goBuild(app: AppCommand, manifestPath: string) { * @returns {Promise} Returns the last modified timestamp (in milliseconds) of the * manifest file, or null if an error occurs. */ -async function getManifestTs(manifestPath: string = MANIFEST_PATH) { +async function getManifestTs(manifestPath: string) { try { // Get stats for the manifest file const stats: Stats = await fs.stat(manifestPath); @@ -85,7 +85,7 @@ async function getManifestTs(manifestPath: string = MANIFEST_PATH) { } catch (err: any) { // Handle errors accessing the manifest file if (err.code === 'ENOENT') { - log.warn(`q-manifest.json file not found: ${yellow(DISK_DIR)}`); + log.warn(`q-manifest.json file not found`); } else { panic(`Error accessing manifest file ${manifestPath}: ${err.message}`); } @@ -99,12 +99,12 @@ async function getManifestTs(manifestPath: string = MANIFEST_PATH) { * @returns {Promise} Returns true if the directory exists and can be accessed, returns * false if it doesn't exist or an error occurs. */ -export async function clientDirExists(): Promise { +export async function clientDirExists(dist: string): Promise { try { - await fs.access(DISK_DIR); + await fs.access(getDiskPath(dist)); return true; // Directory exists } catch (err: any) { - panic(`Error accessing disk directory ${DISK_DIR}: ${err.message}`); + panic(`Error accessing disk directory ${dist}: ${err.message}`); return false; // Directory doesn't exist or there was an error } } @@ -112,11 +112,11 @@ export async function clientDirExists(): Promise { /** * Recursively finds the latest modification time (mtime) of any file in the given directory. * - * @param {string} directoryPath - The directory path to search. + * @param {string} srcPath - The directory path to search. * @returns {Promise} Returns the latest mtime (Unix timestamp in milliseconds), or 0 if the * directory doesn't exist or is empty. */ -export async function isNewerThan(directoryPath: string, timestamp: number): Promise { +export async function isNewerThan(srcPath: string, timestamp: number): Promise { let returnValue = false; async function traverse(dir: string): Promise { if (returnValue) { @@ -150,6 +150,6 @@ export async function isNewerThan(directoryPath: string, timestamp: number): Pro } } - await traverse(directoryPath); + await traverse(srcPath); return returnValue; } diff --git a/starters/adapters/aws-lambda/package.json b/starters/adapters/aws-lambda/package.json index df22718258d..1ffdfa9e252 100755 --- a/starters/adapters/aws-lambda/package.json +++ b/starters/adapters/aws-lambda/package.json @@ -1,7 +1,7 @@ { "description": "AWS Lambda", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/aws-lambda/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/aws-lambda/vite.config.ts", "serve": "qwik build && serverless offline", "deploy": "serverless deploy" }, diff --git a/starters/adapters/azure-swa/package.json b/starters/adapters/azure-swa/package.json index 4979977b32e..9fbe555e229 100644 --- a/starters/adapters/azure-swa/package.json +++ b/starters/adapters/azure-swa/package.json @@ -1,7 +1,7 @@ { "description": "Azure Static Web Apps", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/azure-swa/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/azure-swa/vite.config.ts", "serve": "swa start" }, "devDependencies": { diff --git a/starters/adapters/bun/package.json b/starters/adapters/bun/package.json index 8d0a3f17486..118eb8a04ef 100644 --- a/starters/adapters/bun/package.json +++ b/starters/adapters/bun/package.json @@ -1,7 +1,7 @@ { "description": "Bun server", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/bun/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/bun/vite.config.ts", "serve": "bun server/entry.bun.js" }, "__qwik__": { diff --git a/starters/adapters/cloud-run/package.json b/starters/adapters/cloud-run/package.json index 54d69971260..caef0f535ba 100644 --- a/starters/adapters/cloud-run/package.json +++ b/starters/adapters/cloud-run/package.json @@ -1,7 +1,7 @@ { "description": "Google Cloud Run server", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/cloud-run/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/cloud-run/vite.config.ts", "deploy": "gcloud run deploy my-cloud-run-app --source ." }, "__qwik__": { diff --git a/starters/adapters/cloudflare-pages/package.json b/starters/adapters/cloudflare-pages/package.json index 2d9a8cd1bd9..7a8c5e8ca29 100644 --- a/starters/adapters/cloudflare-pages/package.json +++ b/starters/adapters/cloudflare-pages/package.json @@ -1,7 +1,7 @@ { "description": "Cloudflare Pages", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/cloudflare-pages/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/cloudflare-pages/vite.config.ts", "deploy": "wrangler pages deploy ./dist", "serve": "wrangler pages dev ./dist --compatibility-flags=nodejs_als" }, diff --git a/starters/adapters/deno/package.json b/starters/adapters/deno/package.json index f6f4704a7a7..b572cfa3181 100644 --- a/starters/adapters/deno/package.json +++ b/starters/adapters/deno/package.json @@ -1,7 +1,7 @@ { "description": "Deno server", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/deno/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/deno/vite.config.ts", "serve": "deno run --allow-net --allow-read --allow-env server/entry.deno.js" }, "__qwik__": { diff --git a/starters/adapters/express/package.json b/starters/adapters/express/package.json index ebbc3a4ae1b..564e917f520 100644 --- a/starters/adapters/express/package.json +++ b/starters/adapters/express/package.json @@ -1,7 +1,7 @@ { "description": "Express.js server", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/express/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/express/vite.config.ts", "serve": "node server/entry.express" }, "dependencies": { diff --git a/starters/adapters/fastify/package.json b/starters/adapters/fastify/package.json index a10d280ce3a..6e97e6da402 100644 --- a/starters/adapters/fastify/package.json +++ b/starters/adapters/fastify/package.json @@ -1,7 +1,7 @@ { "description": "Fastify server", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/fastify/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/fastify/vite.config.ts", "serve": "node server/entry.fastify" }, "dependencies": { diff --git a/starters/adapters/firebase/package.json b/starters/adapters/firebase/package.json index 44f9268a744..d4212c5beee 100644 --- a/starters/adapters/firebase/package.json +++ b/starters/adapters/firebase/package.json @@ -1,7 +1,7 @@ { "description": "Firebase", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/firebase/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/firebase/vite.config.ts", "serve": "qwik build && firebase emulators:start", "deploy": "firebase deploy" }, diff --git a/starters/adapters/netlify-edge/package.json b/starters/adapters/netlify-edge/package.json index 64980f9ba97..2febbffebcf 100644 --- a/starters/adapters/netlify-edge/package.json +++ b/starters/adapters/netlify-edge/package.json @@ -1,7 +1,7 @@ { "description": "Netlify Edge Functions", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/netlify-edge/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/netlify-edge/vite.config.ts", "deploy": "netlify deploy --build" }, "devDependencies": { diff --git a/starters/adapters/node-server/package.json b/starters/adapters/node-server/package.json index 654c0b4c645..2b97a3a85a9 100644 --- a/starters/adapters/node-server/package.json +++ b/starters/adapters/node-server/package.json @@ -1,7 +1,7 @@ { "description": "Vanilla Node server", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/node-server/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/node-server/vite.config.ts", "serve": "node server/entry.node-server" }, "__qwik__": { diff --git a/starters/adapters/static/package.json b/starters/adapters/static/package.json index 59c670d1a22..ebe4f7a3432 100644 --- a/starters/adapters/static/package.json +++ b/starters/adapters/static/package.json @@ -1,7 +1,7 @@ { "description": "Static Site Generator", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/static/vite.config.ts" + "build.server": "qwik check-client src dist && vite build -c adapters/static/vite.config.ts" }, "__qwik__": { "priority": 10, diff --git a/starters/adapters/vercel-edge/package.json b/starters/adapters/vercel-edge/package.json index 1b3e76216db..fbf007e12fc 100644 --- a/starters/adapters/vercel-edge/package.json +++ b/starters/adapters/vercel-edge/package.json @@ -1,7 +1,7 @@ { "description": "Vercel Edge Functions", "scripts": { - "build.server": "qwik check-client dist/q-manifest.json && vite build -c adapters/vercel-edge/vite.config.ts", + "build.server": "qwik check-client src dist && vite build -c adapters/vercel-edge/vite.config.ts", "deploy": "vercel deploy" }, "devDependencies": { From 9ae8462cfbcdd3ceb93c011f9df7c84d359d486a Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Tue, 29 Apr 2025 09:57:11 +0800 Subject: [PATCH 07/10] refactor(cli): simplify client check and build logging Remove redundant log steps and improve error handling in the client check process. This change focuses on reducing noise in the logs and ensuring clearer error messages. --- packages/qwik/src/cli/check-client/index.ts | 30 +++++++++------------ 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/packages/qwik/src/cli/check-client/index.ts b/packages/qwik/src/cli/check-client/index.ts index 85a95d1bb38..97d9df78691 100644 --- a/packages/qwik/src/cli/check-client/index.ts +++ b/packages/qwik/src/cli/check-client/index.ts @@ -30,16 +30,12 @@ export async function runQwikClientCommand(app: AppCommand) { */ async function checkClientCommand(app: AppCommand, src: string, dist: string): Promise { if (!(await clientDirExists(dist))) { - intro(`🚀 ${bgBlue(bold(' Qwik Client Check '))}`); - log.step(`Checking Disk directory: ${cyan(dist)}`); await goBuild(app); } else { - log.step(`Checking q-manifest.json file: ${cyan(dist)}`); const manifest = await getManifestTs(getManifestPath(dist)); if (manifest === null) { await goBuild(app); } else { - log.step(`Compare source modification time with q-manifest.json modification time`); if (await isNewerThan(getSrcPath(src), manifest)) { await goBuild(app); } @@ -57,17 +53,12 @@ async function checkClientCommand(app: AppCommand, src: string, dist: string): P async function goBuild(app: AppCommand) { const pkgManager = getPackageManager(); - log.step('Building client (manifest missing or outdated)...'); - try { - const { install } = await runInPkg(pkgManager, ['run', 'build.client'], app.rootDir); - if (!(await install)) { - throw new Error('Client build command reported failure.'); - } - } catch (buildError: any) { - log.error(`Build error: ${buildError.message}`); - throw new Error('Client build process encountered an error.'); + intro('Building client (manifest missing or outdated)...'); + const { install } = await runInPkg(pkgManager, ['run', 'build.client'], app.rootDir); + if (!(await install)) { + throw new Error('Client build command reported failure.'); } - outro(`✅ ${bgMagenta(bold(' Check complete '))}`); + outro('Client build complete'); } /** @@ -104,7 +95,9 @@ export async function clientDirExists(dist: string): Promise { await fs.access(getDiskPath(dist)); return true; // Directory exists } catch (err: any) { - panic(`Error accessing disk directory ${dist}: ${err.message}`); + if (!(err.code === 'ENOENT')) { + panic(`Error accessing disk directory ${dist}: ${err.message}`); + } return false; // Directory doesn't exist or there was an error } } @@ -127,12 +120,15 @@ export async function isNewerThan(srcPath: string, timestamp: number): Promise Date: Tue, 29 Apr 2025 10:00:29 +0800 Subject: [PATCH 08/10] fix test --- packages/qwik/src/cli/check-client/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/qwik/src/cli/check-client/index.ts b/packages/qwik/src/cli/check-client/index.ts index 97d9df78691..041f23cb7d3 100644 --- a/packages/qwik/src/cli/check-client/index.ts +++ b/packages/qwik/src/cli/check-client/index.ts @@ -1,6 +1,6 @@ import type { AppCommand } from '../utils/app-command'; import { intro, log, outro } from '@clack/prompts'; -import { bgBlue, bgMagenta, bold, cyan, red } from 'kleur/colors'; +import { red } from 'kleur/colors'; import { runInPkg } from '../utils/install-deps'; import { getPackageManager, panic } from '../utils/utils'; import fs from 'fs/promises'; From fecbd125e8d39f4e82e74ca40a6b0db568a3a65a Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Tue, 29 Apr 2025 10:07:12 +0800 Subject: [PATCH 09/10] rename argument --- packages/qwik/src/cli/check-client/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/qwik/src/cli/check-client/index.ts b/packages/qwik/src/cli/check-client/index.ts index 041f23cb7d3..06deecce8df 100644 --- a/packages/qwik/src/cli/check-client/index.ts +++ b/packages/qwik/src/cli/check-client/index.ts @@ -90,13 +90,13 @@ async function getManifestTs(manifestPath: string) { * @returns {Promise} Returns true if the directory exists and can be accessed, returns * false if it doesn't exist or an error occurs. */ -export async function clientDirExists(dist: string): Promise { +export async function clientDirExists(path: string): Promise { try { - await fs.access(getDiskPath(dist)); + await fs.access(getDiskPath(path)); return true; // Directory exists } catch (err: any) { if (!(err.code === 'ENOENT')) { - panic(`Error accessing disk directory ${dist}: ${err.message}`); + panic(`Error accessing disk directory ${path}: ${err.message}`); } return false; // Directory doesn't exist or there was an error } From 449eb302f80677f3ed7bd7632e1e9ec3d31e81e1 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Wed, 7 May 2025 16:57:52 +0800 Subject: [PATCH 10/10] refactor(cli): rename isNewerThan to hasNewer for clarity The function name `isNewerThan` was renamed to `hasNewer` to better reflect its purpose of checking if any files in the directory are newer than the given timestamp. This improves code readability and understanding. --- packages/qwik/src/cli/check-client/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/qwik/src/cli/check-client/index.ts b/packages/qwik/src/cli/check-client/index.ts index 06deecce8df..08fe9811d89 100644 --- a/packages/qwik/src/cli/check-client/index.ts +++ b/packages/qwik/src/cli/check-client/index.ts @@ -36,7 +36,7 @@ async function checkClientCommand(app: AppCommand, src: string, dist: string): P if (manifest === null) { await goBuild(app); } else { - if (await isNewerThan(getSrcPath(src), manifest)) { + if (await hasNewer(getSrcPath(src), manifest)) { await goBuild(app); } } @@ -109,7 +109,7 @@ export async function clientDirExists(path: string): Promise { * @returns {Promise} Returns the latest mtime (Unix timestamp in milliseconds), or 0 if the * directory doesn't exist or is empty. */ -export async function isNewerThan(srcPath: string, timestamp: number): Promise { +export async function hasNewer(srcPath: string, timestamp: number): Promise { let returnValue = false; async function traverse(dir: string): Promise { if (returnValue) {