Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: base url env & refactor [LIBS-635] #872

Merged
merged 7 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions cli/config/makeViteConfig.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,17 @@ const handleAssetFileNames = ({ name }) => {
* and don't need to use `define`; they just need the envPrefix config.
*/
const getDefineOptions = (env) => {
const defineOptions = {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}
const defineOptions = {}
Object.entries(env).forEach(([key, val]) => {
// 'DHIS2_'-prefixed vars go on import.meta.env
if (key.startsWith('DHIS2_')) {
defineOptions[`import.meta.env.${key}`] = JSON.stringify(val)
return
}
// For backwards compatibility, add REACT_APP_DHIS2_... and other env
// vars to process.env. They will be statically replaced at build time.
// This will be removed in future versions
// vars to process.env. These env vars have been filtered by getEnv().
// They will be statically replaced at build time.
// Env vars in this format will be removed in future versions
// todo: deprecate in favor of import.meta.env
defineOptions[`process.env.${key}`] = JSON.stringify(val)
})
Expand All @@ -110,14 +109,15 @@ const getBuildInputs = (config, paths) => {
// https://vitejs.dev/config/
export default ({ paths, config, env, host }) => {
return defineConfig({
// Need to specify the location of the app root, since we're not using
// the Vite CLI from the app root
// Need to specify the location of the app root, since this CLI command
// gets run in a different directory than the bootstrapped app
root: paths.shell,

// By default, assets are resolved to the root of the domain ('/'), but
// deployed apps aren't served from there.
// This option is basically the same as PUBLIC_URL for CRA and Parcel.
// Works for both dev and production.
// Gets applied to import.meta.env.BASE_URL in the runtime code
base: './',

// Expose env vars with DHIS2_ prefix in index.html and on
Expand Down
36 changes: 17 additions & 19 deletions cli/src/commands/build.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
const path = require('path')
const { reporter, chalk } = require('@dhis2/cli-helpers-engine')
const fs = require('fs-extra')
const bootstrapShell = require('../lib/bootstrapShell')
const { compile } = require('../lib/compiler')
const { loadEnvFiles, getEnv } = require('../lib/env')
const exitOnCatch = require('../lib/exitOnCatch')
const generateManifests = require('../lib/generateManifests')
const i18n = require('../lib/i18n')
const loadEnvFiles = require('../lib/loadEnvFiles')
const parseConfig = require('../lib/parseConfig')
const { isApp } = require('../lib/parseConfig')
const makePaths = require('../lib/paths')
const { injectPrecacheManifest, compileServiceWorker } = require('../lib/pwa')
const makeShell = require('../lib/shell')
const { validatePackage } = require('../lib/validatePackage')
const { handler: pack } = require('./pack.js')

Expand All @@ -30,20 +30,22 @@ const getNodeEnv = () => {
const printBuildParam = (key, value) => {
reporter.print(chalk.green(` - ${key} :`), chalk.yellow(value))
}
const setAppParameters = (standalone, config) => {
process.env.PUBLIC_URL = process.env.PUBLIC_URL || '.'
printBuildParam('PUBLIC_URL', process.env.PUBLIC_URL)
const getAppParameters = (standalone, config) => {
const publicUrl = process.env.PUBLIC_URL || '.'
printBuildParam('PUBLIC_URL', publicUrl)

if (
standalone === false ||
(typeof standalone === 'undefined' && !config.standalone)
) {
const defaultBase = config.coreApp ? `..` : `../../..`
process.env.DHIS2_BASE_URL = process.env.DHIS2_BASE_URL || defaultBase
const baseUrl = process.env.DHIS2_BASE_URL || defaultBase

printBuildParam('DHIS2_BASE_URL', process.env.DHIS2_BASE_URL)
printBuildParam('DHIS2_BASE_URL', baseUrl)
return { publicUrl, baseUrl }
} else {
printBuildParam('DHIS2_BASE_URL', '<standalone>')
return { publicUrl }
}
}

Expand All @@ -68,11 +70,9 @@ const handler = async ({
printBuildParam('Mode', mode)

const config = parseConfig(paths)
const shell = makeShell({ config, paths })

if (isApp(config.type)) {
setAppParameters(standalone, config)
}
const appParameters = isApp(config.type)
? getAppParameters(standalone, config)
: null

await fs.remove(paths.buildOutput)

Expand Down Expand Up @@ -107,7 +107,8 @@ const handler = async ({

if (isApp(config.type)) {
reporter.info('Bootstrapping local appShell...')
await shell.bootstrap({ shell: shellSource, force })
// todo: args format?
await bootstrapShell(paths, { shell: shellSource, force })
}

reporter.info(
Expand Down Expand Up @@ -135,16 +136,13 @@ const handler = async ({
const { default: createConfig } = await import(
'../../config/makeViteConfig.mjs'
)
const viteConfig = createConfig({
paths,
config,
env: shell.env,
})
const env = getEnv({ config, ...appParameters })
const viteConfig = createConfig({ paths, config, env })
await build(viteConfig)

if (config.pwa.enabled) {
reporter.info('Compiling service worker...')
await compileServiceWorker({ config, paths, mode })
await compileServiceWorker({ env, paths, mode })

reporter.info(
'Injecting supplementary precache manifest...'
Expand Down
19 changes: 8 additions & 11 deletions cli/src/commands/start.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
const { reporter, chalk } = require('@dhis2/cli-helpers-engine')
const detectPort = require('detect-port')
const bootstrapShell = require('../lib/bootstrapShell')
const { compile } = require('../lib/compiler')
const { loadEnvFiles, getEnv } = require('../lib/env')
const exitOnCatch = require('../lib/exitOnCatch')
const generateManifests = require('../lib/generateManifests')
const i18n = require('../lib/i18n')
const loadEnvFiles = require('../lib/loadEnvFiles')
const parseConfig = require('../lib/parseConfig')
const { isApp } = require('../lib/parseConfig')
const makePaths = require('../lib/paths')
const createProxyServer = require('../lib/proxy')
const { compileServiceWorker } = require('../lib/pwa')
const makeShell = require('../lib/shell')
const { validatePackage } = require('../lib/validatePackage')

const defaultPort = 3000
Expand All @@ -31,7 +31,7 @@ const handler = async ({
loadEnvFiles(paths, mode)

const config = parseConfig(paths)
const shell = makeShell({ config, paths })
// const shell = makeShell({ config, paths })

if (!isApp(config.type)) {
reporter.error(
Expand Down Expand Up @@ -92,7 +92,9 @@ const handler = async ({
})

reporter.info('Bootstrapping local appShell...')
await shell.bootstrap({ shell: shellSource, force })
await bootstrapShell(paths, { shell: shellSource, force })
// await shell.bootstrap({ shell: shellSource, force })
const env = getEnv({ config, publicUrl: '.' })

reporter.info(`Building app ${chalk.bold(config.name)}...`)
await compile({
Expand All @@ -115,7 +117,7 @@ const handler = async ({

if (config.pwa.enabled) {
reporter.info('Compiling service worker...')
await compileServiceWorker({ config, paths, mode })
await compileServiceWorker({ env, paths, mode })
// don't need to inject precache manifest because no precaching
// is done in development environments
}
Expand All @@ -130,12 +132,7 @@ const handler = async ({
const { default: createConfig } = await import(
'../../config/makeViteConfig.mjs'
)
const viteConfig = createConfig({
config,
paths,
env: shell.env,
host,
})
const viteConfig = createConfig({ config, paths, env, host })
const server = await createServer(viteConfig)

const location = config.entryPoints.plugin
Expand Down
2 changes: 1 addition & 1 deletion cli/src/commands/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const path = require('path')
const { reporter } = require('@dhis2/cli-helpers-engine')
const { runCLI } = require('@jest/core')
const fs = require('fs-extra')
const { loadEnvFiles } = require('../lib/env')
const exitOnCatch = require('../lib/exitOnCatch')
const loadEnvFiles = require('../lib/loadEnvFiles')
const makePaths = require('../lib/paths')

const getAppJestConfig = ({ jestConfigPath, paths }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const getShellVersion = (shellDir) => {
return '0'
}

// todo: new param format?
const bootstrapShell = async (paths, { shell, force = false } = {}) => {
const source = shell ? path.resolve(shell) : paths.shellSource,
dest = paths.shell
Expand Down
91 changes: 91 additions & 0 deletions cli/src/lib/env/getEnv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const { reporter } = require('@dhis2/cli-helpers-engine')
const getPWAEnvVars = require('./getPWAEnvVars')

/**
* Filter process.env for just keys that start with DHIS2_
* to avoid leaking env
*/
const filterEnv = () =>
Object.keys(process.env)
.filter((key) => key.indexOf('DHIS2_') === 0)
.reduce(
(out, key) => ({
...out,
[key]: process.env[key],
}),
{}
)

/**
* Deprecated -- CRA did its own filtering;
* only env vars prefixed with REACT_APP_ would get passed to the app
*/
const prefixEnvForCRA = (env) =>
Object.keys(env).reduce(
(out, key) => ({
...out,
[`REACT_APP_${key}`]: env[key],
}),
{}
)

const getShellEnv = ({ config, baseUrl }) => {
const shellEnv = {
name: config.title,
version: config.version,
base_url: baseUrl,
loginApp: config.type === 'login_app' || undefined,
direction: config.direction,
// NB: 'IS_PLUGIN' is added by string replacement in
// compiler/entrypoints.js, since env is shared between app and plugin
requiredProps: config.requiredProps?.join(),
skipPluginLogic: config.skipPluginLogic,
...getPWAEnvVars(config),
}

// Remove undefined values and prefix with DHIS2_APP_
const filteredAndPrefixedShellEnv = Object.entries(shellEnv).reduce(
(newEnv, [key, value]) => {
if (typeof value === 'undefined') {
return newEnv
}
return {
...newEnv,
[`DHIS2_APP_${key.toUpperCase()}`]: value,
}
},
{}
)
return filteredAndPrefixedShellEnv
}

module.exports = ({ config, baseUrl, publicUrl }) => {
const filteredEnv = filterEnv()
const shellEnv = getShellEnv({ config, baseUrl })

const env = {
// Legacy env vars; deprecated
...prefixEnvForCRA({
...filteredEnv,
...shellEnv,
}),
// New form for env vars: import.meta.env.DHIS2_etc
...filteredEnv,
...shellEnv,
NODE_ENV: process.env.NODE_ENV,
// todo: deprecated; migrate to import.meta.env.BASE_URL
PUBLIC_URL: publicUrl || '.',
}

if (env.REACT_APP_DHIS2_API_VERSION) {
reporter.warn(
'Passing an explicit API version to the DHIS2 App Platform is not recommended.\n' +
'By default, the app platform will now use the latest API version available in the DHIS2 instance.\n' +
'Some API functionality may be unreliable when using an explicit API version.\n' +
'Support for the DHIS2_API_VERSION environment variable may be removed in a future release of the DHIS2 App Platform.'
)
}

reporter.debug('Env passed to app-shell:', env)
return env
}
File renamed without changes.
7 changes: 7 additions & 0 deletions cli/src/lib/env/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const getEnv = require('./getEnv')
const loadEnvFiles = require('./loadEnvFiles')

module.exports = {
getEnv,
loadEnvFiles,
}
File renamed without changes.
27 changes: 10 additions & 17 deletions cli/src/lib/pwa/compileServiceWorker.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
const path = require('path')
const { reporter } = require('@dhis2/cli-helpers-engine')
const webpack = require('webpack')
const getEnv = require('../shell/env')
const getPWAEnvVars = require('./getPWAEnvVars')

/**
* Uses webpack to bundle a service worker. If used in development mode,
Expand All @@ -20,21 +18,14 @@ const getPWAEnvVars = require('./getPWAEnvVars')
* @param {String} param0.mode - `'production'` or `'development'` (or other valid webpack `mode`s)
* @returns {Promise}
*/
function compileServiceWorker({ config, paths, mode }) {
function compileServiceWorker({ env, paths, mode }) {
// Choose appropriate destination for compiled SW based on 'mode'
const isProduction = mode === 'production'
const outputPath = isProduction
? paths.shellBuildServiceWorker
: paths.shellPublicServiceWorker
const { dir: outputDir, base: outputFilename } = path.parse(outputPath)

// This is part of a bit of a hacky way to provide the same env vars to dev
// SWs as in production by adding them to `process.env` using the plugin
// below.
// TODO: This could be refactored to be simpler now that we're not using
// CRA to build the service worker
const env = getEnv({ name: config.title, ...getPWAEnvVars(config) })

const webpackConfig = {
mode, // "production" or "development"
devtool: isProduction ? false : 'source-map',
Expand All @@ -45,12 +36,8 @@ function compileServiceWorker({ config, paths, mode }) {
},
target: 'webworker',
plugins: [
new webpack.DefinePlugin({
'process.env': JSON.stringify({
...env,
NODE_ENV: process.env.NODE_ENV,
}),
}),
// Make sure SW has the same env vars as the app
new webpack.DefinePlugin({ 'process.env': JSON.stringify(env) }),
],
}

Expand Down Expand Up @@ -81,7 +68,13 @@ function compileServiceWorker({ config, paths, mode }) {
return
}

reporter.debug('Service Worker compilation successful')
reporter.debug(
'Service Worker compilation successful. Size:',
info.assets[0].size,
'bytes'
)
const outputPath = path.join(info.outputPath, info.assets[0].name)
reporter.debug('Output:', outputPath)
resolve()
})
})
Expand Down
2 changes: 0 additions & 2 deletions cli/src/lib/pwa/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
const compileServiceWorker = require('./compileServiceWorker')
const getPWAEnvVars = require('./getPWAEnvVars')
const injectPrecacheManifest = require('./injectPrecacheManifest')

module.exports = {
compileServiceWorker,
getPWAEnvVars,
injectPrecacheManifest,
}
Loading
Loading