UNPKG

@quasar/app-webpack

Version:

Quasar Framework App CLI with Webpack

338 lines (298 loc) 8.53 kB
const semver = require('semver') const { merge } = require('webpack-merge') const { fatal } = require('../../utils/logger.js') const { getPackageJson } = require('../../utils/get-package-json.js') const { getCallerPath } = require('../../utils/get-caller-path.js') const { getBackwardCompatiblePackageName } = require('../utils.app-extension.js') const { BaseAPI } = require('./BaseAPI.js') /** * API for extension's /index.js script */ module.exports.IndexAPI = class IndexAPI extends BaseAPI { prompts constructor (opts, appExtJson) { super(opts) this.prompts = opts.prompts this.#appExtJson = appExtJson } /** * Get the internal persistent config of this extension. * Returns empty object if it has none. * * @return {object} cfg */ getPersistentConf () { return this.#appExtJson.getInternal(this.extId) } /** * Set the internal persistent config of this extension. * If it already exists, it is overwritten. * * @param {object} cfg */ setPersistentConf (cfg) { this.#appExtJson.setInternal(this.extId, cfg || {}) } /** * Deep merge into the internal persistent config of this extension. * If extension does not have any config already set, this is * essentially equivalent to setting it for the first time. * * @param {object} cfg */ mergePersistentConf (cfg = {}) { const currentCfg = this.getPersistentConf() this.setPersistentConf(merge({}, currentCfg, cfg)) } /** * Ensure the App Extension is compatible with * host app package through a * semver condition. * * If the semver condition is not met, then * @quasar/app-webpack errors out and halts execution * * Example of semver condition: * '1.x || >=2.5.0 || 5.0.0 - 7.2.3' * * @param {string} packageName * @param {string} semverCondition */ compatibleWith (packageName, semverCondition) { const name = getBackwardCompatiblePackageName(packageName) const json = getPackageJson(name, this.appDir) if (json === void 0) { fatal(`Extension(${ this.extId }): Dependency not found - ${ name }. Please install it.`) } if (!semver.satisfies(json.version, semverCondition)) { fatal(`Extension(${ this.extId }): is not compatible with ${ name } v${ json.version }. Required version: ${ semverCondition }`) } } /** * Check if a host app package is installed. Can also * check its version against specific semver condition. * * Example of semver condition: * '1.x || >=2.5.0 || 5.0.0 - 7.2.3' * * @param {string} packageName * @param {string} semverCondition * @return {boolean} package is installed and meets optional semver condition */ hasPackage (packageName, semverCondition) { const name = getBackwardCompatiblePackageName(packageName) const json = getPackageJson(name, this.appDir) if (json === void 0) { return false } return semverCondition !== void 0 ? semver.satisfies(json.version, semverCondition) : true } /** * Check if another app extension is installed * (app extension npm package is installed and it was invoked) * * @param {string} extId * @return {boolean} has the extension installed & invoked */ hasExtension (extId) { return this.#appExtJson.has(extId) } /** * Get the version of a host app package. * * @param {string} packageName * @return {string|undefined} version of app's package */ getPackageVersion (packageName) { const name = getBackwardCompatiblePackageName(packageName) const json = getPackageJson(name, this.appDir) return json !== void 0 ? json.version : void 0 } /** * Extend quasar.config file * * @param {function} fn * (cfg: Object, ctx: Object) => undefined */ extendQuasarConf (fn) { this.#addHook('extendQuasarConf', fn) } /** * Chain webpack config * * @param {function} fn * (cfg: ChainObject, invoke: Object {isClient, isServer}) => undefined */ chainWebpack (fn) { this.#addHook('chainWebpack', fn) } /** * Extend webpack config * * @param {function} fn * (cfg: Object, invoke: Object {isClient, isServer}) => undefined */ extendWebpack (fn) { this.#addHook('extendWebpack', fn) } /** * Extend Bex scripts (background/content-script) Esbuild config * * @param {function} fn * (cfg: Object, api) => undefined */ extendBexScriptsConf (fn) { this.#addHook('extendBexScriptsConf', fn) } /** * Extend Electron Main thread Esbuild config * * @param {function} fn * (cfg: Object, api) => undefined */ extendElectronMainConf (fn) { this.#addHook('extendElectronMainConf', fn) } /** * Extend Electron Preload thread Esbuild config * * @param {function} fn * (cfg: Object, api) => undefined */ extendElectronPreloadConf (fn) { this.#addHook('extendElectronPreloadConf', fn) } /** * Extend PWA custom service worker Esbuild config * (when using Workbox InjectManifest mode) * * @param {function} fn * (cfg: Object, api) => undefined */ extendPWACustomSWConf (fn) { this.#addHook('extendPWACustomSWConf', fn) } /** * Extend SSR Webserver Esbuild config * * @param {function} fn * (cfg: Object, api) => undefined */ extendSSRWebserverConf (fn) { this.#addHook('extendSSRWebserverConf', fn) } /** * Register a command that will become available as * `quasar run <ext-id> <cmd> [args]` and `quasar <ext-id> <cmd> [args]` * * @param {string} commandName * @param {function} fn * ({ args: [ string, ... ], params: {object} }) => ?Promise */ registerCommand (commandName, fn) { this.#hooks.commands[ commandName ] = fn } /** * Register an API file for "quasar describe" command * * @param {string} name * @param {string} relativePath (or node_modules reference if it starts with "~") * (relative path to Api file) */ registerDescribeApi (name, relativePath) { const callerPath = getCallerPath() this.#hooks.describeApi[ name ] = { callerPath, relativePath } } /** * Prepare external services before dev command runs. * * @param {function} fn * (api, { quasarConf }) => ?Promise */ beforeDev (fn) { this.#addHook('beforeDev', fn) } /** * Run hook after Quasar dev server is started ($ quasar dev). * At this point, the dev server has been started and is available * should you wish to do something with it. * * @param {function} fn * (api, { quasarConf }) => ?Promise */ afterDev (fn) { this.#addHook('afterDev', fn) } /** * Run hook before Quasar builds app for production ($ quasar build). * At this point, the distributables folder hasn't been created yet. * * @param {function} fn * (api, { quasarConf }) => ?Promise */ beforeBuild (fn) { this.#addHook('beforeBuild', fn) } /** * Run hook after Quasar built app for production ($ quasar build). * At this point, the distributables folder has been created and is available * should you wish to do something with it. * * @param {function} fn * (api, { quasarConf }) => ?Promise */ afterBuild (fn) { this.#addHook('afterBuild', fn) } /** * Run hook if publishing was requested ("$ quasar build -P"), * after Quasar built app for production and the afterBuild * hook (if specified) was executed. * * @param {function} fn * ({ arg, ...}) => ?Promise * * arg - argument supplied to "--publish"/"-P" parameter * * quasarConf - quasar.config file config object * * distDir - folder where distributables were built */ onPublish (fn) { this.#addHook('onPublish', fn) } /** * Private stuff; to NOT be used in devland */ #appExtJson #hooks = { extendQuasarConf: [], extendWebpack: [], chainWebpack: [], extendSSRWebserverConf: [], extendElectronMainConf: [], extendElectronPreloadConf: [], extendPWACustomSWConf: [], extendBexScriptsConf: [], beforeDev: [], afterDev: [], beforeBuild: [], afterBuild: [], onPublish: [], commands: {}, describeApi: {} } __getHooks (appExtJson) { // protect against external access if (appExtJson === this.#appExtJson) { return this.#hooks } } #addHook (name, fn) { this.#hooks[ name ].push({ fn, api: this }) } }