UNPKG

@capawesome/cli

Version:

The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.

131 lines (130 loc) 6 kB
import { isInteractive } from '../../../utils/environment.js'; import { directoryContainsSourceMaps, directoryContainsSymlinks, isDirectory, isReadable, pathExists, } from '../../../utils/file.js'; import { generateManifestJson } from '../../../utils/manifest.js'; import { prompt } from '../../../utils/prompt.js'; import zip from '../../../utils/zip.js'; import { defineCommand, defineOptions } from '@robingenz/zli'; import consola from 'consola'; import fs from 'fs'; import pathModule from 'path'; import { z } from 'zod'; export default defineCommand({ description: 'Generate manifest file and compress locally built web assets into a bundle.zip file.', options: defineOptions(z.object({ inputPath: z.string().optional().describe('Path to the web assets directory.'), outputPath: z .string() .optional() .default('./bundle.zip') .describe('Output path for the generated artifact file (default: ./bundle.zip).'), overwrite: z .boolean() .optional() .default(false) .describe('Overwrite output file if it already exists (default: false).'), skipManifest: z.boolean().optional().default(false).describe('Skip manifest file generation (default: false).'), })), action: async (options, args) => { let { inputPath, outputPath, overwrite, skipManifest } = options; // 1. Input path resolution if (!inputPath) { if (!isInteractive()) { consola.error('You must provide an input path when running in non-interactive environment.'); process.exit(1); } consola.warn('Make sure you have built your web assets before creating a bundle (e.g., `npm run build`).'); inputPath = await prompt('Enter the path to the web assets directory (e.g., `dist` or `www`):', { type: 'text', }); if (!inputPath) { consola.error('You must provide an input path.'); process.exit(1); } } // Convert to absolute path inputPath = pathModule.resolve(inputPath); // Validate input path exists const inputPathReadable = await isReadable(inputPath); if (!inputPathReadable) { consola.error(`The input path does not exist or is not accessible: ${inputPath}`); process.exit(1); } // Validate input is a directory const inputIsDirectory = await isDirectory(inputPath); if (!inputIsDirectory) { consola.error(`Input path must be a directory, not a file: ${inputPath}`); process.exit(1); } // Validate directory contains index.html const indexHtmlPath = pathModule.join(inputPath, 'index.html'); const indexHtmlReadable = await isReadable(indexHtmlPath); if (!indexHtmlReadable) { consola.error(`The index.html file does not exist or is not accessible: ${indexHtmlPath}`); process.exit(1); } // Check for symlinks const containsSymlinks = await directoryContainsSymlinks(inputPath); if (containsSymlinks) { consola.warn('Symbolic links were detected in the specified path. Symbolic links are skipped during bundling.'); } // Check for source maps const containsSourceMaps = await directoryContainsSourceMaps(inputPath); if (containsSourceMaps) { consola.warn('Source map files were detected in the specified path. Source maps should not be distributed to end users as they expose your original source code and increase the download size. Consider excluding source map files from your build output.'); } // 2. Output path resolution if (!outputPath) { outputPath = './bundle.zip'; } outputPath = pathModule.resolve(outputPath); // 3. Check if output exists and handle overwrite const outputPathExists = await pathExists(outputPath); if (outputPathExists) { if (!overwrite) { if (!isInteractive()) { consola.error(`Output file already exists: ${outputPath}. Use --overwrite flag to skip confirmation or run in interactive mode.`); process.exit(1); } const shouldOverwrite = await prompt('Output file already exists. Overwrite?', { type: 'confirm', initial: false, }); if (!shouldOverwrite) { consola.info('Operation cancelled.'); process.exit(0); } } } // Validate parent directory exists const outputDir = pathModule.dirname(outputPath); const outputDirExists = await pathExists(outputDir); if (!outputDirExists) { consola.error(`The output directory does not exist: ${outputDir}`); process.exit(1); } // 4. Generate bundle consola.start('Generating bundle...'); try { // Generate manifest (unless skipped) if (!skipManifest) { consola.info('Generating manifest file...'); await generateManifestJson(inputPath); } // Compress directory consola.info('Compressing directory...'); const buffer = await zip.zipFolder(inputPath); // Write output consola.info(`Writing output to ${outputPath}...`); await fs.promises.writeFile(outputPath, buffer); // Success output consola.success(`Bundle created successfully!`); } catch (error) { consola.error('Failed to generate bundle:'); if (error instanceof Error) { consola.error(error.message); } process.exit(1); } }, });