UNPKG

@ima/plugin-cli

Version:

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

222 lines 8.68 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseConfigFile = parseConfigFile; exports.emitSource = emitSource; exports.processTransformers = processTransformers; exports.createProcessingPipeline = createProcessingPipeline; exports.runPlugins = runPlugins; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const logger_1 = require("@ima/dev-utils/logger"); const chalk_1 = __importDefault(require("chalk")); const configurations_1 = require("./configurations"); const preprocessTransformer_1 = require("../transformers/preprocessTransformer"); const swcTransformer_1 = require("../transformers/swcTransformer"); const CONFIG_BASENAME = 'ima-plugin.config'; /** * Parses ima.build.js file, initializing the build pipeline. */ async function parseConfigFile(cwd, args) { const configDir = path_1.default.resolve(cwd); const configFile = await fs_1.default.promises .readdir(configDir) .then(files => files.find(fileName => fileName.startsWith(CONFIG_BASENAME))); // Recursively try to find file up to file system root if (!configFile) { const newConfigDir = path_1.default.resolve(cwd, '..'); if (newConfigDir !== configDir) { return parseConfigFile(path_1.default.resolve(cwd, '..'), args); } } // Define default config let loadedConfig = []; if (args.clientServerConfig) { loadedConfig.push(configurations_1.clientServerConfig); } else if (args.nodeConfig) { loadedConfig.push(configurations_1.nodeConfig); } else { loadedConfig.push(configurations_1.defaultConfig); } // Override with custom configuration if (configFile) { let configPath = path_1.default.join(configDir, configFile); if (!configPath.startsWith('/')) { configPath = 'file:///' + configPath.replace(/\\/g, '/'); } loadedConfig = (await import(configPath)).default; loadedConfig = Array.isArray(loadedConfig) ? loadedConfig : [loadedConfig]; } // Override jsx runtime option from CLI args if (args.jsxRuntime) { loadedConfig = loadedConfig.map(config => { config.jsxRuntime = args.jsxRuntime; return config; }); } return loadedConfig; } /** * Helper function to emit source. If it's not undefined, the source is * written to the output path. If it is undefined, the original file * is simply copied. */ async function emitSource(source, context, outputDir) { // Create output directory if (!fs_1.default.existsSync(outputDir)) { await fs_1.default.promises.mkdir(outputDir, { recursive: true }); } // Emit source if (source) { const outputPath = path_1.default.join(outputDir, source?.fileName); return Promise.all([ fs_1.default.promises.writeFile(outputPath, source.code), source.map && fs_1.default.promises.writeFile(`${outputPath}.map`, source.map), ]); } else { // Just copy files without source await fs_1.default.promises.copyFile(context.filePath, path_1.default.join(outputDir, context.fileName)); } } /** * Load source file contents and runs transformers on it, provided * in the ima.build.js config. */ async function processTransformers(source, transformers, context) { if (!transformers) { return source; } const { filePath } = context; for (const transformerDefinition of transformers) { const [transform, options] = Array.isArray(transformerDefinition) ? transformerDefinition : [transformerDefinition]; try { if (!options) { source = await transform({ source, context }); continue; } if (options.test && !options.test.test(filePath)) { continue; } source = await transform({ source, context }); } catch (err) { logger_1.logger.error(`at ${chalk_1.default.magenta(context.filePath)}`); console.error(err); // Don't continue in build command if (context.command === 'build') { process.exit(1); } } } return source; } /** * Creates processing pipeline used in build, link and dev scripts. * It is constructed to run on each file separately. */ async function createProcessingPipeline(ctx) { const { config } = ctx; const transformersMap = new Map(); const isDevelopment = ctx.command !== 'build' && process.env.NODE_ENV !== 'production'; // Create map of transformers for each output option config.output.forEach(output => { // Create default transformers set based on config options const defaultTransformers = [ typeof output.bundle !== 'undefined' && (0, preprocessTransformer_1.preprocessTransformer)({ context: { client: output.bundle === 'client', server: output.bundle === 'server', }, }), [ (0, swcTransformer_1.createSwcTransformer)({ target: config.target, sourceMaps: config.sourceMaps, development: isDevelopment, type: output.format, jsxRuntime: config.jsxRuntime, }), { test: /\.(js|jsx)$/ }, ], [ (0, swcTransformer_1.createSwcTransformer)({ target: config.target, sourceMaps: config.sourceMaps, development: isDevelopment, type: output.format, jsxRuntime: config.jsxRuntime, syntax: 'typescript', }), { test: /\.(ts|tsx)$/ }, ], ].filter(Boolean); // Check for custom transformers in config if (Array.isArray(config.transformers)) { const hasDefaultsPlaceholder = config.transformers.find(t => typeof t === 'string' && t === '...'); transformersMap.set(output.dir, config.transformers .map(t => hasDefaultsPlaceholder && typeof t === 'string' && t === '...' ? defaultTransformers : t) .flat() .filter(Boolean)); } else { transformersMap.set(output.dir, defaultTransformers); } }); return async (filePath) => { const fileName = path_1.default.basename(filePath); const contextDir = path_1.default.dirname(path_1.default.relative(ctx.inputDir, filePath)); // Read file contents let source = { fileName, code: await fs_1.default.promises.readFile(filePath, 'utf8'), }; // Run transformers for each output and emit await Promise.all(config.output.map(async ({ dir, include, exclude }) => { if ((include && ((typeof include === 'function' && !include(filePath)) || (typeof include === 'object' && !include?.test(filePath)))) || (exclude && ((typeof exclude === 'function' && exclude(filePath)) || (typeof exclude === 'object' && exclude?.test(filePath))))) { return; } const outputDir = path_1.default.resolve(ctx.cwd, dir); const context = { ...ctx, contextDir, fileName, filePath, outputDir, }; const outputFilename = path_1.default.join(outputDir, fileName); ctx.command === 'link' && logger_1.logger.write(`${(0, logger_1.printTime)()} ${chalk_1.default.green('Linking')}: ${outputFilename}`); // Process transforms source = await processTransformers(source, transformersMap.get(dir), context); // Write new source await emitSource(source, context, path_1.default.resolve(dir, contextDir)); })); }; } /** * Runs plugins defined in config file. */ async function runPlugins(context) { if (!context.config?.plugins?.length) { return; } for (const plugin of context.config.plugins) { await plugin(context); } } //# sourceMappingURL=process.js.map