UNPKG

@ima/plugin-cli

Version:

IMA.js Plugin CLI tool to build, link, develop IMA.js plugins.

244 lines 10.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parsePkgJson = parsePkgJson; exports.build = build; exports.watch = watch; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const logger_1 = require("@ima/dev-utils/logger"); const anymatch_1 = __importDefault(require("anymatch")); const chalk_1 = __importDefault(require("chalk")); const chokidar_1 = require("chokidar"); const globby_1 = __importDefault(require("globby")); const process_1 = require("./process"); const utils_1 = require("./utils"); function parseArgs(args) { const [command] = args._; return { ...args, command }; } /** * Returns the upmost common part of output dir path of all output options. */ function getBaseCommonOutputDir(output) { const [firstOutputDirParts, ...otherOutputDirsParts] = output.map(output => output.dir.split('/')); return firstOutputDirParts .reduce((acc, cur, index) => { if (otherOutputDirsParts.every(part => part[index] === cur) && cur) { acc.push(cur); } return acc; }, []) .join('/'); } /** * Loads and parses package.json at given working directory. */ async function parsePkgJson(basePath) { return JSON.parse(await (await fs_1.default.promises.readFile(path_1.default.join(basePath, 'package.json'))).toString()); } function errorHandler(error) { logger_1.logger.error('An error occurred while watching files'); console.error(error); } /** * Build command function handler. */ async function build(args) { const parsedArgs = parseArgs(args); const elapsed = (0, logger_1.time)(); const cwd = process.cwd(); const [pkgJson, configurations] = await Promise.all([ parsePkgJson(cwd), (0, process_1.parseConfigFile)(cwd, parsedArgs), ]); logger_1.logger.setSilent(parsedArgs.silent); logger_1.logger.info(`Building ${chalk_1.default.bold.magenta(pkgJson.name)}`); // Spawn compilation for each config await Promise.all(configurations.map(async (config) => { const inputDir = path_1.default.resolve(cwd, config.inputDir); config.output.forEach(({ dir, format }) => { logger_1.logger.info(`${config.inputDir} ${chalk_1.default.blue('→')} ${chalk_1.default.green(dir)} ${chalk_1.default.red(`[${format}]`)}`); }); // Clean output directories await (0, utils_1.cleanOutput)(config, cwd); // Get file paths at input directory let files = await (0, globby_1.default)(path_1.default.posix.join(inputDir.replace(/\\/g, '/'), './**/*'), { cwd: cwd.replace(/\\/g, '/'), }); // Filter files using exclude settings if (config?.exclude) { const matcher = typeof config.exclude === 'function' ? config.exclude : (0, anymatch_1.default)(config.exclude); files = files.filter(filePath => !matcher(filePath)); } const context = { command: 'build', inputDir, config, cwd, }; // Init processing pipeline const process = await (0, process_1.createProcessingPipeline)(context); // Process each file with loaded pipeline files.forEach(process); // Run plugins await (0, process_1.runPlugins)(context); })).catch(error => { logger_1.logger.error(error); process.exit(1); }); logger_1.logger.success('Finished', { elapsed }); } /** * Dev/link command function handler */ async function watch(args) { const cwd = process.cwd(); const parsedArgs = parseArgs(args); const { command, path: linkPath } = parsedArgs; const [pkgJson, configurations] = await Promise.all([ parsePkgJson(cwd), (0, process_1.parseConfigFile)(cwd, parsedArgs), ]); let title = `Watching ${chalk_1.default.bold.magenta(pkgJson.name)}`; if (command === 'link' && linkPath) { const linkedPkgJson = await parsePkgJson(path_1.default.resolve(linkPath)); title = `Linking ${chalk_1.default.bold.magenta(pkgJson.name)} ${chalk_1.default.white('→')} ${chalk_1.default.cyan(linkedPkgJson.name)}`; } // Batch file events const batch = (0, utils_1.createBatcher)(title); logger_1.logger.setSilent(parsedArgs.silent); // Spawn watch for each config configurations.forEach(async (config) => { const inputDir = path_1.default.resolve(cwd, config.inputDir); // Cleanup await (0, utils_1.cleanOutput)(config, cwd); // Processing pipeline context const context = { command, inputDir, config, cwd, }; // Init processing pipeline const process = await (0, process_1.createProcessingPipeline)(context); // Dev watcher (0, chokidar_1.watch)([path_1.default.resolve(inputDir)], { ignoreInitial: false, ignored: config.exclude, persistent: true, }) .on('error', errorHandler) .on('all', async (eventName, filePath) => { const contextPath = path_1.default.relative(inputDir, filePath); const fileName = path_1.default.basename(filePath); // Skip unsupported event names if (eventName === 'all' || eventName === 'ready' || eventName === 'raw' || eventName === 'error') { return; } batch(async () => { switch (eventName) { case 'add': case 'change': await process(filePath); break; case 'unlink': case 'unlinkDir': await (0, utils_1.processOutput)(config, async (outputPath) => { const outputContextPath = path_1.default.join(outputPath, contextPath); if (fs_1.default.existsSync(outputContextPath)) { logger_1.logger.write(`${(0, logger_1.printTime)()} ${chalk_1.default.green('Unlink')}: ${fileName}`); await fs_1.default.promises.rm(outputContextPath, { recursive: true, }); } }, cwd); break; default: return; } }, eventName); }) .on('ready', async () => { await (0, process_1.runPlugins)(context); }); /** * Link watcher, there's only one instance per pkg, it watches the root dist * directory and copies changes to the linked path. */ if (command === 'link' && linkPath) { const distBaseDir = getBaseCommonOutputDir(config.output); const outputDir = path_1.default.join(cwd, distBaseDir); const linkedPath = path_1.default.resolve(linkPath); const linkedBasePath = path_1.default.resolve(linkedPath, 'node_modules', pkgJson.name); const linkedDistPath = path_1.default.join(linkedBasePath, distBaseDir); // Create package.json link to be able to link new packages or changes package keys (like bin, main, etc.) if (!config.additionalWatchPaths) { config.additionalWatchPaths = ['./package.json']; } else { config.additionalWatchPaths = [ ...config.additionalWatchPaths, './package.json', ]; } (0, chokidar_1.watch)([ path_1.default.join(cwd, distBaseDir), Array.isArray(parsedArgs?.additionalWatchPaths) && parsedArgs?.additionalWatchPaths, Array.isArray(config?.additionalWatchPaths) && config?.additionalWatchPaths, ].filter(Boolean), { ignoreInitial: false, persistent: true, ignored: [ '**/tsconfig.build.tsbuildinfo/**', '**/node_modules/**', '**/.DS_Store/**', ], }) .on('error', errorHandler) .on('all', (eventName, filePath) => { // Handler to link additional non-dist files const isAdditionalFile = !filePath.startsWith(outputDir); const contextPath = path_1.default.relative(isAdditionalFile ? cwd : outputDir, filePath); const linkedOutputPath = path_1.default.join(isAdditionalFile ? linkedBasePath : linkedDistPath, contextPath); const linkedOutputDir = path_1.default.dirname(linkedOutputPath); // Skip unsupported event names if (eventName === 'all' || eventName === 'ready' || eventName === 'raw' || eventName === 'error') { return; } batch(async () => { switch (eventName) { case 'add': case 'change': if (!fs_1.default.existsSync(linkedOutputDir)) { await fs_1.default.promises.mkdir(linkedOutputDir, { recursive: true, }); } await fs_1.default.promises.copyFile(filePath, linkedOutputPath); break; case 'unlink': case 'unlinkDir': await fs_1.default.promises.rm(linkedOutputPath, { recursive: true }); break; default: return; } }, eventName); }); } }); } //# sourceMappingURL=commands.js.map