diff --git a/CHANGELOG.md b/CHANGELOG.md index f9e0da750..3a58932ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,56 +1,30 @@ -# [12.0.0-alpha.1](https://github.com/dhis2/app-platform/compare/v11.2.2...v12.0.0-alpha.1) (2024-06-06) +## [11.4.1](https://github.com/dhis2/app-platform/compare/v11.4.0...v11.4.1) (2024-06-06) -### Features - -* replace CRA with Vite [LIBS-598] ([#847](https://github.com/dhis2/app-platform/issues/847)) ([3dd0e59](https://github.com/dhis2/app-platform/commit/3dd0e5938dda443751cb1a5627226a6ecf13377c)) - - -### BREAKING CHANGES - -* Supported Node versions are 18.x or 20+ - -* ci: upgrade Node version - -* fix: always add PWA_ENABLED to app env for better static analysis - -* chore: pause precache manifest injection - -* fix: building SW without CRA - -* chore: comment update - -* fix: group moment locales in their own dir - -* refactor: clean up precache injection - -* fix: locale utils and handling moment in jest +### Bug Fixes -* fix: compile correctly after merging changes +* clean up for plugins [LIBS-620] ([#851](https://github.com/dhis2/app-platform/issues/851)) ([13af3b5](https://github.com/dhis2/app-platform/commit/13af3b5ee862ea4b7952c6a9199505cfe6a1bdaa)) -* chore: comment in compile.js +# [11.4.0](https://github.com/dhis2/app-platform/compare/v11.3.1...v11.4.0) (2024-06-04) -* chore: some clean-up -* chore: comments +### Features -* fix: use port 3000 for the dev server +* parse pluginType from d2 config to add to manifest.webapp ([#849](https://github.com/dhis2/app-platform/issues/849)) ([c1dae23](https://github.com/dhis2/app-platform/commit/c1dae238b92183922962811a52ab50d1b73e7995)) -* fix: improve moment locale chunk naming +## [11.3.1](https://github.com/dhis2/app-platform/compare/v11.3.0...v11.3.1) (2024-06-03) -* chore: remove CRA -* fix: use mjs build of Vite +### Bug Fixes -* fix: bump cli-style for CRA fix +* don't start plugins for apps without a plugin entrypoint ([#850](https://github.com/dhis2/app-platform/issues/850)) ([a89d4cf](https://github.com/dhis2/app-platform/commit/a89d4cf348f7edc0a52b8ab9aacf96f2de939de4)) -* feat: use interactive dev server output from Vite +# [11.3.0](https://github.com/dhis2/app-platform/compare/v11.2.2...v11.3.0) (2024-05-30) -* fix: make dev server port configurable -* chore: remove old index.html +### Features -* fix: env tweaks +* start plugin and app separately [LIBS-391] [LIBS-392] ([#848](https://github.com/dhis2/app-platform/issues/848)) ([82003e7](https://github.com/dhis2/app-platform/commit/82003e73fce995a83318c623da6028d9975e6686)) ## [11.2.2](https://github.com/dhis2/app-platform/compare/v11.2.1...v11.2.2) (2024-05-06) diff --git a/adapter/src/index.js b/adapter/src/index.js index 1b80cf2de..5f8e00920 100644 --- a/adapter/src/index.js +++ b/adapter/src/index.js @@ -92,7 +92,7 @@ AppAdapter.propTypes = { parentAlertsAdd: PropTypes.func, plugin: PropTypes.bool, pwaEnabled: PropTypes.bool, - showAlertsInPlugin: PropTypes.func, + showAlertsInPlugin: PropTypes.bool, url: PropTypes.string, onPluginError: PropTypes.func, } diff --git a/cli/config/plugin.webpack.config.js b/cli/config/plugin.webpack.config.js index 96f7a6984..81ee88635 100644 --- a/cli/config/plugin.webpack.config.js +++ b/cli/config/plugin.webpack.config.js @@ -103,7 +103,7 @@ module.exports = ({ env: webpackEnv, config, paths }) => { path: paths.shellBuildOutput, filename: isProduction ? 'static/js/plugin-[name].[contenthash:8].js' - : 'static/js/plugin.bundle.js', + : 'static/js/plugin-[name].bundle.js', chunkFilename: isProduction ? 'static/js/plugin-[name].[contenthash:8].chunk.js' : 'static/js/plugin-[name].chunk.js', @@ -192,9 +192,7 @@ module.exports = ({ env: webpackEnv, config, paths }) => { // dhis2: Inject plugin static assets to the existing SW's precache // manifest. Don't need to do in dev because precaching isn't done // in dev environments. - // Check the actual NODE_ENV because `isProduction` is currently - // always true due to a bug (see src/lib/plugin/start.js) - process.env.NODE_ENV === 'production' && + isProduction && new WorkboxWebpackPlugin.InjectManifest({ swSrc: paths.shellBuildServiceWorker, injectionPoint: 'self.__WB_PLUGIN_MANIFEST', diff --git a/cli/src/commands/start.js b/cli/src/commands/start.js index 0b28c475b..041c5508c 100644 --- a/cli/src/commands/start.js +++ b/cli/src/commands/start.js @@ -3,6 +3,7 @@ const detectPort = require('detect-port') const { compile } = require('../lib/compiler') const exitOnCatch = require('../lib/exitOnCatch') const generateManifests = require('../lib/generateManifests') +const { getOriginalEntrypoints } = require('../lib/getOriginalEntrypoints') const i18n = require('../lib/i18n') const loadEnvFiles = require('../lib/loadEnvFiles') const parseConfig = require('../lib/parseConfig') @@ -23,6 +24,8 @@ const handler = async ({ shell: shellSource, proxy, proxyPort, + app: shouldStartOnlyApp, + plugin: shouldStartOnlyPlugin, }) => { const paths = makePaths(cwd) @@ -123,30 +126,49 @@ const handler = async ({ reporter.print('') reporter.info('Starting development server...') - reporter.print( - `The app ${chalk.bold( - config.name - )} is now available on port ${newPort}` - ) - reporter.print('') - // todo: split up app and plugin starts - const shellStartPromise = shell.start({ port: newPort }) + // start app and/or plugin, depending on flags + + // entryPoints.app is populated by default -- get the app's raw + // entry points from the d2.config.js file to tell if there is an + // app entrypoint configured (and fall back to the whatever we do + // have available) + const entryPoints = + getOriginalEntrypoints(paths) || config.entryPoints + + const noFlagsPassed = !shouldStartOnlyApp && !shouldStartOnlyPlugin + const shouldStartApp = + entryPoints.app && (noFlagsPassed || shouldStartOnlyApp) + const shouldStartPlugin = + entryPoints.plugin && (noFlagsPassed || shouldStartOnlyPlugin) + + if (!shouldStartApp && !shouldStartPlugin) { + throw new Error( + 'The requested app/plugin is not configured to start. Check the flags passed to the start script and the entrypoints configured in d2.config.js, then try again.' + ) + } - if (config.entryPoints.plugin) { - const pluginPort = await detectPort(newPort + 1) + const startPromises = [] + if (shouldStartApp) { reporter.print( - `The plugin is now available on port ${pluginPort} at /${paths.pluginLaunchPath}` + `The app ${chalk.bold( + config.name + )} is now available on port ${newPort}` ) reporter.print('') + startPromises.push(shell.start({ port: newPort })) + } - await Promise.all([ - shellStartPromise, - plugin.start({ port: pluginPort }), - ]) - } else { - await shellStartPromise + if (shouldStartPlugin) { + const pluginPort = shouldStartApp ? newPort + 1 : newPort + reporter.print( + `The plugin is now available on port ${pluginPort} at /${paths.pluginLaunchPath}` + ) + reporter.print('') + startPromises.push(plugin.start({ port: pluginPort })) } + + await Promise.all(startPromises) }, { name: 'start', @@ -178,6 +200,16 @@ const command = { description: 'The port to use when running the proxy', default: 8080, }, + // todo: change with Vite + app: { + type: 'boolean', + description: + 'Start a dev server for just the app entrypoint (instead of both app and plugin, if this app has a plugin)', + }, + plugin: { + type: 'boolean', + description: 'Start a dev server for just the plugin entrypoint', + }, }, handler, } diff --git a/cli/src/lib/generateManifests.js b/cli/src/lib/generateManifests.js index 6ba6db624..5685049e9 100644 --- a/cli/src/lib/generateManifests.js +++ b/cli/src/lib/generateManifests.js @@ -1,28 +1,7 @@ const { reporter, chalk } = require('@dhis2/cli-helpers-engine') const fs = require('fs-extra') +const { getOriginalEntrypoints } = require('./getOriginalEntrypoints') -/** - * Gets the original `entrypoints` property in d2.config.js - * without applying defaults. Used to detect if there is actually - * supposed to be an app entrypoint for this... app. Temporary until - * the build process is redesigned to allow building plugins without - * apps (LIBS-479) - */ -const getOriginalEntrypoints = (paths) => { - try { - if (fs.existsSync(paths.config)) { - reporter.debug('Loading d2 config at', paths.config) - // NB: this import can be confounded by previous object mutations - const originalConfig = require(paths.config) - reporter.debug('loaded', originalConfig) - return originalConfig.entryPoints // may be undefined - } - } catch (e) { - reporter.error('Failed to load d2 config!') - reporter.error(e) - process.exit(1) - } -} const parseCustomAuthorities = (authorities) => { if (!authorities) { return undefined @@ -141,6 +120,7 @@ module.exports = (paths, config, publicUrl) => { launch_path: shouldIncludeAppLaunchPath ? paths.launchPath : undefined, plugin_launch_path: includesPlugin ? paths.pluginLaunchPath : undefined, + plugin_type: includesPlugin ? config.pluginType : undefined, default_locale: 'en', activities: { dhis: { diff --git a/cli/src/lib/getOriginalEntrypoints.js b/cli/src/lib/getOriginalEntrypoints.js new file mode 100644 index 000000000..5efa0a48d --- /dev/null +++ b/cli/src/lib/getOriginalEntrypoints.js @@ -0,0 +1,26 @@ +const { reporter } = require('@dhis2/cli-helpers-engine') +const fs = require('fs-extra') + +/** + * Gets the original `entrypoints` property in d2.config.js + * without applying defaults. Used to detect if there is actually + * supposed to be an app entrypoint for this... app. Temporary until + * the build process is redesigned to allow building plugins without + * apps (LIBS-479) + */ +const getOriginalEntrypoints = (paths) => { + try { + if (fs.existsSync(paths.config)) { + reporter.debug('Loading d2 config at', paths.config) + // NB: this import can be confounded by previous object mutations + const originalConfig = require(paths.config) + reporter.debug('loaded', originalConfig) + return originalConfig.entryPoints // may be undefined + } + } catch (e) { + reporter.error('Failed to load d2 config!') + reporter.error(e) + process.exit(1) + } +} +exports.getOriginalEntrypoints = getOriginalEntrypoints diff --git a/cli/src/lib/parseConfig.js b/cli/src/lib/parseConfig.js index d1b5a40d0..a77f8318f 100644 --- a/cli/src/lib/parseConfig.js +++ b/cli/src/lib/parseConfig.js @@ -48,6 +48,18 @@ const validateConfig = (config) => { ) } }) + + const { pluginType } = config + if (pluginType && !/^[A-Z0-9-_]+$/.test(pluginType)) { + throw new Error( + `Field ${chalk.bold( + 'pluginType' + )} must contain only the characters A-Z (uppercase), 0-9, -, or _. Got: ${chalk.bold( + `"${pluginType}"` + )}` + ) + } + return true } diff --git a/cli/src/lib/plugin/start.js b/cli/src/lib/plugin/start.js index 09d378262..75ba7ab57 100644 --- a/cli/src/lib/plugin/start.js +++ b/cli/src/lib/plugin/start.js @@ -7,9 +7,7 @@ const webpackConfigFactory = require('../../../config/plugin.webpack.config') module.exports = async ({ port, config, paths }) => { const webpackConfig = webpackConfigFactory({ - // todo: change to development, but this creates a compilation error - // can read more here: https://github.com/dhis2/app-platform/pull/740/files/69411d9b61845cbd0d053f2313bdbd4e80fdf2ac#r1031576956 - env: 'production', + env: 'development', config, paths, }) diff --git a/docs/config/d2-config-js-reference.md b/docs/config/d2-config-js-reference.md index dc80b93b9..78752ede0 100644 --- a/docs/config/d2-config-js-reference.md +++ b/docs/config/d2-config-js-reference.md @@ -24,6 +24,7 @@ The following configuration properties are supported: | **entryPoints.plugin** | _string_ | | The path to the application's plugin entrypoint (not used for libraries) | | **entryPoints.lib** | _string_ or _object_ | **./src/index** | The path to the library entrypoint(s) (not used for applications). Supports [conditional exports](https://nodejs.org/dist/latest-v16.x/docs/api/packages.html#packages_conditional_exports) | | **skipPluginLogic** | _boolean_ | **false** | By default, plugin entry points will be wrapped with logic to allow the passing of properties and resizing between the parent app and the child plugin. This logic will allow users to use the plugin inside an app when wrapped in `` component from app-runtime. If set to true, this logic will not be loaded. | +| **pluginType** | _string_ | | Gets added to the `plugin_type` field for this app in the `/api/apps` response -- an example is `pluginType: 'DASHBOARD'` for a plugin meant to be consumed by the Dashboard app. Must be contain only characters from the set A-Z (uppercase), 0-9, `-` and `_`; i.e., it's tested against the regex `/^[A-Z0-9-_]+$/`. | | **dataStoreNamespace** | _string_ | | The DataStore and UserDataStore namespace to reserve for this application. The reserved namespace **must** be suitably unique, as other apps will fail to install if they attempt to reserve the same namespace - see the [webapp manifest docs](https://docs.dhis2.org/en/develop/loading-apps.html) | | **customAuthorities** | _Array(string)_ | | An array of custom authorities to create when installing the app, these do not provide security protections in the DHIS2 REST API but can be assigned to user roles and used to modify the interface displayed to a user - see the [webapp manifest docs](https://docs.dhis2.org/en/develop/loading-apps.html) | | **minDHIS2Version** | _string_ | | The minimum DHIS2 version the App supports (eg. '2.35'). Required when uploading an app to the App Hub. The app's major version in the app's package.json needs to be increased when changing this property. | diff --git a/docs/scripts/start.md b/docs/scripts/start.md index 696f8c6be..a9cb6d575 100644 --- a/docs/scripts/start.md +++ b/docs/scripts/start.md @@ -23,4 +23,7 @@ Options: --port, -p The port to use when running the development server [number] --proxy, -P The remote DHIS2 instance the proxy should point to [string] --proxyPort The port to use when running the proxy [number] [default: 8080] + --app Start a dev server for just the app entrypoint (instead of both + app and plugin, if this app has a plugin) [boolean] + --plugin Start a dev server for just the plugin entrypoint [boolean] ``` diff --git a/examples/pwa-app/d2.config.js b/examples/pwa-app/d2.config.js index 2b6efe325..1a95dbe42 100644 --- a/examples/pwa-app/d2.config.js +++ b/examples/pwa-app/d2.config.js @@ -16,6 +16,9 @@ const config = { // Uncomment this to test plugin builds: // plugin: './src/components/VisualizationsList.js', }, + + // pluginType: 'DASHBOARD', + skipPluginLogic: true, } module.exports = config diff --git a/shell/src/PluginLoader.jsx b/shell/src/PluginLoader.jsx index ee7c900ec..426fca57d 100644 --- a/shell/src/PluginLoader.jsx +++ b/shell/src/PluginLoader.jsx @@ -50,7 +50,7 @@ const PluginInner = ({ // inner div disables margin collapsing which would prevent computing correct height return (
-
+
{ > +