UNPKG

@storm-stack/core

Version:

A build toolkit and runtime used by Storm Software in TypeScript applications

314 lines (311 loc) 12.7 kB
import { build } from './chunk-7X6KWZIM.js'; import { clean } from './chunk-NSHO7RIH.js'; import { docs } from './chunk-4I46RBIP.js'; import { finalize } from './chunk-I6YSWAI3.js'; import { init } from './chunk-JGDCG6II.js'; import { lint } from './chunk-G4UQR4T2.js'; import { _new } from './chunk-QQJKWQU7.js'; import { prepare } from './chunk-FN2K3AHM.js'; import { createContext, getPersistedMeta, getChecksum } from './chunk-22M3FAZZ.js'; import { __name } from './chunk-43IZMM3W.js'; import { LogLevelLabel } from '@storm-software/config-tools/types'; import { install } from '@stryke/fs/install'; import { isPackageExists } from '@stryke/fs/package-fns'; import { camelCase } from '@stryke/string-format/camel-case'; import { isError } from '@stryke/type-checks/is-error'; import { isNumber } from '@stryke/type-checks/is-number'; import { isSetObject } from '@stryke/type-checks/is-set-object'; import chalk from 'chalk'; import defu from 'defu'; import { createHooks } from 'hookable'; var Engine = class { static { __name(this, "Engine"); } inlineConfig; workspaceConfig; #initialized = false; /** * The engine hooks - these allow the plugins to hook into the engines processing */ #hooks; /** * The plugins provided in the options */ #plugins = []; /** * The options provided to Storm Stack */ // protected options: TOptions; /** * The resolved options provided to Storm Stack */ context; /** * Create a new Storm Stack Engine instance * * @param inlineConfig - The inline configuration for the Storm Stack engine * @param workspaceConfig - The workspace configuration for the Storm Stack engine */ constructor(inlineConfig, workspaceConfig) { this.inlineConfig = inlineConfig; this.workspaceConfig = workspaceConfig; } /** * Initialize the engine */ async init(inlineConfig) { this.#hooks = createHooks(); this.context = await createContext(defu(inlineConfig, this.inlineConfig), this.workspaceConfig); this.context.log(LogLevelLabel.TRACE, "Initializing Storm Stack engine"); for (const plugin of this.context.options.userConfig.plugins ?? []) { await this.addPlugin(plugin); } if (this.#plugins.length === 0) { this.context.log(LogLevelLabel.WARN, "No Storm Stack plugins or presets were specified in the options. Please ensure this is correct, as it is generally not recommended."); } else { for (const plugin of this.#plugins) { plugin.addHooks(this.#hooks); } } await init(this.context, this.#hooks); this.context.log(LogLevelLabel.INFO, "Storm Stack engine has been initialized"); this.#initialized = true; return this.context; } /** * Create a new Storm Stack project * * @remarks * This method will create a new Storm Stack project in the current directory. * * @param inlineConfig - The inline configuration for the new command * @returns A promise that resolves when the project has been created */ async new(inlineConfig = { command: "new" }) { if (!this.#initialized) { await this.init(inlineConfig); } this.context.log(LogLevelLabel.INFO, "\u{1F195} Creating a new Storm Stack project"); await _new(this.context, this.#hooks); this.context.log(LogLevelLabel.TRACE, "Storm Stack - New command completed"); } /** * Clean any previously prepared artifacts * * @remarks * This method will remove the previous Storm Stack artifacts from the project. * * @param inlineConfig - The inline configuration for the clean command * @returns A promise that resolves when the clean command has completed */ async clean(inlineConfig = { command: "clean" }) { if (!this.#initialized) { await this.init(inlineConfig); } this.context.log(LogLevelLabel.INFO, "\u{1F9F9} Cleaning the previous Storm Stack artifacts"); await clean(this.context, this.#hooks); this.context.log(LogLevelLabel.TRACE, "Storm Stack - Clean command completed"); } /** * Prepare the Storm Stack project prior to building * * @remarks * This method will create the necessary directories, and write the artifacts files to the project. * * @param inlineConfig - The inline configuration for the prepare command * @returns A promise that resolves when the prepare command has completed */ async prepare(inlineConfig = { command: "prepare" }) { if (!this.#initialized) { await this.init(inlineConfig); } this.context.log(LogLevelLabel.INFO, "Preparing the Storm Stack project"); await prepare(this.context, this.#hooks); this.context.log(LogLevelLabel.TRACE, "Storm Stack preparation completed"); } /** * Lint the project * * @param inlineConfig - The inline configuration for the lint command * @returns A promise that resolves when the lint command has completed */ async lint(inlineConfig = { command: "lint" }) { if (!this.#initialized) { await this.init(inlineConfig); } if (this.context.persistedMeta?.checksum !== this.context.meta.checksum) { this.context.log(LogLevelLabel.INFO, "The Storm Stack project has been modified since the last time `prepare` was ran. Re-preparing the project."); await this.prepare(inlineConfig); } this.context.log(LogLevelLabel.INFO, "Linting the Storm Stack project"); await lint(this.context, this.#hooks); this.context.log(LogLevelLabel.TRACE, "Storm Stack linting completed"); } /** * Build the project * * @remarks * This method will build the Storm Stack project, generating the necessary artifacts. * * @param inlineConfig - The inline configuration for the build command * @returns A promise that resolves when the build command has completed */ async build(inlineConfig = { command: "build" }) { if (!this.#initialized) { await this.init(inlineConfig); } const persistedMeta = await getPersistedMeta(this.context); const checksum = await getChecksum(this.context.options.projectRoot); if (persistedMeta?.checksum !== checksum) { this.context.log(LogLevelLabel.INFO, "The Storm Stack project has been modified since the last time `prepare` was ran. Re-preparing the project."); await this.prepare(inlineConfig); } this.context.log(LogLevelLabel.INFO, "Building the Storm Stack project"); await build(this.context, this.#hooks); this.context.log(LogLevelLabel.TRACE, "Storm Stack build completed"); } /** * Generate the documentation for the project * * @param inlineConfig - The inline configuration for the docs command * @returns A promise that resolves when the documentation generation has completed */ async docs(inlineConfig = { command: "docs" }) { if (!this.#initialized) { await this.init(inlineConfig); } if (this.context.persistedMeta?.checksum !== this.context.meta.checksum) { this.context.log(LogLevelLabel.INFO, "The Storm Stack project has been modified since the last time `prepare` was ran. Re-preparing the project."); await this.prepare(inlineConfig); } this.context.log(LogLevelLabel.INFO, "Generating documentation for the Storm Stack project"); await docs(this.context, this.#hooks); this.context.log(LogLevelLabel.TRACE, "Storm Stack documentation generation completed"); } /** * Finalization process * * @remarks * This step includes any final processes or clean up required by Storm Stack. It will be run after each Storm Stack command. * * @param inlineConfig - The inline configuration for the Storm Stack engine * @returns A promise that resolves when the finalization process has completed */ async finalize(inlineConfig) { if (!this.#initialized) { await this.init(inlineConfig); } this.context.log(LogLevelLabel.TRACE, "Storm Stack finalize execution started"); await finalize(this.context, this.#hooks); this.context.log(LogLevelLabel.TRACE, "Storm Stack finalize execution completed"); } /** * Add a Storm Stack plugin used in the build process * * @param config - The import path of the plugin to add */ async addPlugin(config) { if (config) { const instance = await this.initPlugin(config); if (!instance) { return; } if (instance.dependencies) { for (const dependency of instance.dependencies) { await this.addPlugin(dependency); } } this.context.log(LogLevelLabel.DEBUG, `Successfully initialized the ${chalk.bold.cyanBright(instance.name)} plugin`); this.#plugins.push(instance); } } /** * Initialize a Storm Stack plugin * * @param plugin - The import path of the plugin to add */ async initPlugin(plugin) { const pluginConfig = typeof plugin === "string" ? [ plugin, {} ] : plugin; let installPath = pluginConfig[0]; if (installPath.startsWith("@") && installPath.split("/").filter(Boolean).length > 2) { const splits = installPath.split("/").filter(Boolean); installPath = `${splits[0]}/${splits[1]}`; } const isInstalled = isPackageExists(installPath, { paths: [ this.context.options.workspaceConfig.workspaceRoot, this.context.options.projectRoot ] }); if (!isInstalled && this.context.options.skipInstalls !== true) { this.context.log(LogLevelLabel.WARN, `The plugin package "${installPath}" is not installed. It will be installed automatically.`); const result = await install(installPath, { cwd: this.context.options.projectRoot }); if (isNumber(result.exitCode) && result.exitCode > 0) { this.context.log(LogLevelLabel.ERROR, result.stderr); throw new Error(`An error occurred while installing the build plugin package "${installPath}" `); } } let pluginInstance; try { const module = await this.context.resolver.import(this.context.resolver.esmResolve(pluginConfig[0])); const PluginConstructor = module.default; pluginInstance = new PluginConstructor({ ...pluginConfig[1] ?? {}, log: this.context.log }); } catch (error) { if (!isInstalled) { throw new Error(`The plugin package "${pluginConfig[0]}" is not installed. Please install the package using the command: "npm install ${pluginConfig[0]} --save-dev"`); } else { throw new Error(`An error occurred while importing the build plugin package "${pluginConfig[0]}": ${isError(error) ? error.message : String(error)} Note: Please ensure the plugin package's default export is a class that extends \`Plugin\` with a constructor that excepts a single arguments of type \`PluginOptions\`.`); } } if (!pluginInstance) { throw new Error(`The plugin package "${pluginConfig[0]}" does not export a valid module.`); } if (!pluginInstance.name) { throw new Error(`The module in the build plugin package "${pluginConfig[0]}" must export a \`name\` string value.`); } if (!pluginInstance.identifier) { throw new Error(`The module in the build plugin package "${pluginConfig[0]}" must export a \`identifier\` string value.`); } if (!pluginInstance.addHooks) { throw new Error(`The module in the build plugin package "${pluginConfig[0]}" must export a \`addHooks\` function value.`); } pluginInstance.options ??= pluginConfig[1] ?? {}; this.context.options.plugins[pluginInstance.identifier] = defu(pluginInstance.options ?? {}, isSetObject(this.context.options.plugins[pluginInstance.identifier]) ? this.context.options.plugins[pluginInstance.identifier] : {}, camelCase(pluginInstance.name) !== camelCase(pluginInstance.identifier) && isSetObject(this.context.options.plugins[pluginInstance.name]) ? this.context.options.plugins[pluginInstance.name] : {}); pluginInstance.options = this.context.options.plugins[pluginInstance.identifier]; const duplicatePlugin = this.#plugins.find((plugin2) => plugin2.isSame(pluginInstance)); if (duplicatePlugin) { this.context.log(LogLevelLabel.TRACE, `Duplicate ${chalk.bold.cyanBright(duplicatePlugin.identifier)} plugin dependency detected - Skipping initialization.`); duplicatePlugin.options = defu(duplicatePlugin.options ?? {}, this.context.options.plugins[pluginInstance.identifier]); this.context.options.plugins[duplicatePlugin.identifier] = duplicatePlugin.options; return null; } this.context.log(LogLevelLabel.TRACE, `Initializing the ${chalk.bold.cyanBright(pluginInstance.name)} plugin...`); return pluginInstance; } }; export { Engine }; //# sourceMappingURL=chunk-7U3POOTC.js.map //# sourceMappingURL=chunk-7U3POOTC.js.map