Skip to content

Commit 7f19259

Browse files
authored
fix: base url env & refactor [LIBS-635] (#872)
* fix: missing base URL from env; refactor env and shell bootstrap * fix: add public URL default * fix: new env format for start script * chore: clean up unused files * fix: base url prefix * refactor: parameter format * chore: last comment
1 parent b5bdfe6 commit 7f19259

File tree

14 files changed

+701
-926
lines changed

14 files changed

+701
-926
lines changed

cli/config/makeViteConfig.mjs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,17 @@ const handleAssetFileNames = ({ name }) => {
7878
* and don't need to use `define`; they just need the envPrefix config.
7979
*/
8080
const getDefineOptions = (env) => {
81-
const defineOptions = {
82-
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
83-
}
81+
const defineOptions = {}
8482
Object.entries(env).forEach(([key, val]) => {
8583
// 'DHIS2_'-prefixed vars go on import.meta.env
8684
if (key.startsWith('DHIS2_')) {
8785
defineOptions[`import.meta.env.${key}`] = JSON.stringify(val)
8886
return
8987
}
9088
// For backwards compatibility, add REACT_APP_DHIS2_... and other env
91-
// vars to process.env. They will be statically replaced at build time.
92-
// This will be removed in future versions
89+
// vars to process.env. These env vars have been filtered by getEnv().
90+
// They will be statically replaced at build time.
91+
// Env vars in this format will be removed in future versions
9392
// todo: deprecate in favor of import.meta.env
9493
defineOptions[`process.env.${key}`] = JSON.stringify(val)
9594
})
@@ -110,14 +109,15 @@ const getBuildInputs = (config, paths) => {
110109
// https://vitejs.dev/config/
111110
export default ({ paths, config, env, host }) => {
112111
return defineConfig({
113-
// Need to specify the location of the app root, since we're not using
114-
// the Vite CLI from the app root
112+
// Need to specify the location of the app root, since this CLI command
113+
// gets run in a different directory than the bootstrapped app
115114
root: paths.shell,
116115

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

123123
// Expose env vars with DHIS2_ prefix in index.html and on

cli/src/commands/build.js

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
const path = require('path')
22
const { reporter, chalk } = require('@dhis2/cli-helpers-engine')
33
const fs = require('fs-extra')
4+
const bootstrapShell = require('../lib/bootstrapShell')
45
const { compile } = require('../lib/compiler')
6+
const { loadEnvFiles, getEnv } = require('../lib/env')
57
const exitOnCatch = require('../lib/exitOnCatch')
68
const generateManifests = require('../lib/generateManifests')
79
const i18n = require('../lib/i18n')
8-
const loadEnvFiles = require('../lib/loadEnvFiles')
910
const parseConfig = require('../lib/parseConfig')
1011
const { isApp } = require('../lib/parseConfig')
1112
const makePaths = require('../lib/paths')
1213
const { injectPrecacheManifest, compileServiceWorker } = require('../lib/pwa')
13-
const makeShell = require('../lib/shell')
1414
const { validatePackage } = require('../lib/validatePackage')
1515
const { handler: pack } = require('./pack.js')
1616

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

3737
if (
3838
standalone === false ||
3939
(typeof standalone === 'undefined' && !config.standalone)
4040
) {
4141
const defaultBase = config.coreApp ? `..` : `../../..`
42-
process.env.DHIS2_BASE_URL = process.env.DHIS2_BASE_URL || defaultBase
42+
const baseUrl = process.env.DHIS2_BASE_URL || defaultBase
4343

44-
printBuildParam('DHIS2_BASE_URL', process.env.DHIS2_BASE_URL)
44+
printBuildParam('DHIS2_BASE_URL', baseUrl)
45+
return { publicUrl, baseUrl }
4546
} else {
4647
printBuildParam('DHIS2_BASE_URL', '<standalone>')
48+
return { publicUrl }
4749
}
4850
}
4951

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

7072
const config = parseConfig(paths)
71-
const shell = makeShell({ config, paths })
72-
73-
if (isApp(config.type)) {
74-
setAppParameters(standalone, config)
75-
}
73+
const appParameters = isApp(config.type)
74+
? getAppParameters(standalone, config)
75+
: null
7676

7777
await fs.remove(paths.buildOutput)
7878

@@ -107,7 +107,7 @@ const handler = async ({
107107

108108
if (isApp(config.type)) {
109109
reporter.info('Bootstrapping local appShell...')
110-
await shell.bootstrap({ shell: shellSource, force })
110+
await bootstrapShell({ paths, shell: shellSource, force })
111111
}
112112

113113
reporter.info(
@@ -135,16 +135,13 @@ const handler = async ({
135135
const { default: createConfig } = await import(
136136
'../../config/makeViteConfig.mjs'
137137
)
138-
const viteConfig = createConfig({
139-
paths,
140-
config,
141-
env: shell.env,
142-
})
138+
const env = getEnv({ config, ...appParameters })
139+
const viteConfig = createConfig({ paths, config, env })
143140
await build(viteConfig)
144141

145142
if (config.pwa.enabled) {
146143
reporter.info('Compiling service worker...')
147-
await compileServiceWorker({ config, paths, mode })
144+
await compileServiceWorker({ env, paths, mode })
148145

149146
reporter.info(
150147
'Injecting supplementary precache manifest...'

cli/src/commands/start.js

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
const { reporter, chalk } = require('@dhis2/cli-helpers-engine')
22
const detectPort = require('detect-port')
3+
const bootstrapShell = require('../lib/bootstrapShell')
34
const { compile } = require('../lib/compiler')
5+
const { loadEnvFiles, getEnv } = require('../lib/env')
46
const exitOnCatch = require('../lib/exitOnCatch')
57
const generateManifests = require('../lib/generateManifests')
68
const i18n = require('../lib/i18n')
7-
const loadEnvFiles = require('../lib/loadEnvFiles')
89
const parseConfig = require('../lib/parseConfig')
910
const { isApp } = require('../lib/parseConfig')
1011
const makePaths = require('../lib/paths')
1112
const createProxyServer = require('../lib/proxy')
1213
const { compileServiceWorker } = require('../lib/pwa')
13-
const makeShell = require('../lib/shell')
1414
const { validatePackage } = require('../lib/validatePackage')
1515

1616
const defaultPort = 3000
@@ -31,7 +31,6 @@ const handler = async ({
3131
loadEnvFiles(paths, mode)
3232

3333
const config = parseConfig(paths)
34-
const shell = makeShell({ config, paths })
3534

3635
if (!isApp(config.type)) {
3736
reporter.error(
@@ -92,7 +91,7 @@ const handler = async ({
9291
})
9392

9493
reporter.info('Bootstrapping local appShell...')
95-
await shell.bootstrap({ shell: shellSource, force })
94+
await bootstrapShell({ paths, shell: shellSource, force })
9695

9796
reporter.info(`Building app ${chalk.bold(config.name)}...`)
9897
await compile({
@@ -113,9 +112,11 @@ const handler = async ({
113112
)
114113
}
115114

115+
const env = getEnv({ config, publicUrl: '.' })
116+
116117
if (config.pwa.enabled) {
117118
reporter.info('Compiling service worker...')
118-
await compileServiceWorker({ config, paths, mode })
119+
await compileServiceWorker({ env, paths, mode })
119120
// don't need to inject precache manifest because no precaching
120121
// is done in development environments
121122
}
@@ -130,12 +131,7 @@ const handler = async ({
130131
const { default: createConfig } = await import(
131132
'../../config/makeViteConfig.mjs'
132133
)
133-
const viteConfig = createConfig({
134-
config,
135-
paths,
136-
env: shell.env,
137-
host,
138-
})
134+
const viteConfig = createConfig({ config, paths, env, host })
139135
const server = await createServer(viteConfig)
140136

141137
const location = config.entryPoints.plugin

cli/src/commands/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ const path = require('path')
22
const { reporter } = require('@dhis2/cli-helpers-engine')
33
const { runCLI } = require('@jest/core')
44
const fs = require('fs-extra')
5+
const { loadEnvFiles } = require('../lib/env')
56
const exitOnCatch = require('../lib/exitOnCatch')
6-
const loadEnvFiles = require('../lib/loadEnvFiles')
77
const makePaths = require('../lib/paths')
88

99
const getAppJestConfig = ({ jestConfigPath, paths }) => {

cli/src/lib/shell/bootstrap.js renamed to cli/src/lib/bootstrapShell.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const getShellVersion = (shellDir) => {
1717
return '0'
1818
}
1919

20-
const bootstrapShell = async (paths, { shell, force = false } = {}) => {
20+
const bootstrapShell = async ({ paths, shell, force = false }) => {
2121
const source = shell ? path.resolve(shell) : paths.shellSource,
2222
dest = paths.shell
2323

cli/src/lib/env/getEnv.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
const { reporter } = require('@dhis2/cli-helpers-engine')
2+
const getPWAEnvVars = require('./getPWAEnvVars')
3+
4+
/**
5+
* Filter process.env for just keys that start with DHIS2_
6+
* to avoid leaking env
7+
*/
8+
const filterEnv = () =>
9+
Object.keys(process.env)
10+
.filter((key) => key.indexOf('DHIS2_') === 0)
11+
.reduce(
12+
(out, key) => ({
13+
...out,
14+
[key]: process.env[key],
15+
}),
16+
{}
17+
)
18+
19+
/**
20+
* Deprecated -- CRA did its own filtering;
21+
* only env vars prefixed with REACT_APP_ would get passed to the app
22+
*/
23+
const prefixEnvForCRA = (env) =>
24+
Object.keys(env).reduce(
25+
(out, key) => ({
26+
...out,
27+
[`REACT_APP_${key}`]: env[key],
28+
}),
29+
{}
30+
)
31+
32+
const getShellEnv = (config) => {
33+
const shellEnv = {
34+
name: config.title,
35+
version: config.version,
36+
loginApp: config.type === 'login_app' || undefined,
37+
direction: config.direction,
38+
// NB: 'IS_PLUGIN' is added by string replacement in
39+
// compiler/entrypoints.js, since env is shared between app and plugin
40+
requiredProps: config.requiredProps?.join(),
41+
skipPluginLogic: config.skipPluginLogic,
42+
...getPWAEnvVars(config),
43+
}
44+
45+
// Remove undefined values and prefix with DHIS2_APP_
46+
const filteredAndPrefixedShellEnv = Object.entries(shellEnv).reduce(
47+
(newEnv, [key, value]) => {
48+
if (typeof value === 'undefined') {
49+
return newEnv
50+
}
51+
return {
52+
...newEnv,
53+
[`DHIS2_APP_${key.toUpperCase()}`]: value,
54+
}
55+
},
56+
{}
57+
)
58+
return filteredAndPrefixedShellEnv
59+
}
60+
61+
module.exports = ({ config, baseUrl, publicUrl }) => {
62+
const filteredEnv = filterEnv()
63+
const shellEnv = getShellEnv(config)
64+
const DHIS2_BASE_URL = baseUrl
65+
66+
const env = {
67+
// Legacy env vars; deprecated
68+
...prefixEnvForCRA({
69+
DHIS2_BASE_URL,
70+
...filteredEnv,
71+
...shellEnv,
72+
}),
73+
// New form for env vars: import.meta.env.DHIS2_etc
74+
...filteredEnv,
75+
...shellEnv,
76+
NODE_ENV: process.env.NODE_ENV,
77+
DHIS2_BASE_URL,
78+
// todo: deprecated; migrate to import.meta.env.BASE_URL
79+
PUBLIC_URL: publicUrl || '.',
80+
}
81+
82+
if (env.REACT_APP_DHIS2_API_VERSION) {
83+
reporter.warn(
84+
'Passing an explicit API version to the DHIS2 App Platform is not recommended.\n' +
85+
'By default, the app platform will now use the latest API version available in the DHIS2 instance.\n' +
86+
'Some API functionality may be unreliable when using an explicit API version.\n' +
87+
'Support for the DHIS2_API_VERSION environment variable may be removed in a future release of the DHIS2 App Platform.'
88+
)
89+
}
90+
91+
reporter.debug('Env passed to app-shell:', env)
92+
return env
93+
}
File renamed without changes.

cli/src/lib/env/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const getEnv = require('./getEnv')
2+
const loadEnvFiles = require('./loadEnvFiles')
3+
4+
module.exports = {
5+
getEnv,
6+
loadEnvFiles,
7+
}
File renamed without changes.

cli/src/lib/pwa/compileServiceWorker.js

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
const path = require('path')
22
const { reporter } = require('@dhis2/cli-helpers-engine')
33
const webpack = require('webpack')
4-
const getEnv = require('../shell/env')
5-
const getPWAEnvVars = require('./getPWAEnvVars')
64

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

31-
// This is part of a bit of a hacky way to provide the same env vars to dev
32-
// SWs as in production by adding them to `process.env` using the plugin
33-
// below.
34-
// TODO: This could be refactored to be simpler now that we're not using
35-
// CRA to build the service worker
36-
const env = getEnv({ name: config.title, ...getPWAEnvVars(config) })
37-
3829
const webpackConfig = {
3930
mode, // "production" or "development"
4031
devtool: isProduction ? false : 'source-map',
@@ -45,12 +36,8 @@ function compileServiceWorker({ config, paths, mode }) {
4536
},
4637
target: 'webworker',
4738
plugins: [
48-
new webpack.DefinePlugin({
49-
'process.env': JSON.stringify({
50-
...env,
51-
NODE_ENV: process.env.NODE_ENV,
52-
}),
53-
}),
39+
// Make sure SW has the same env vars as the app
40+
new webpack.DefinePlugin({ 'process.env': JSON.stringify(env) }),
5441
],
5542
}
5643

@@ -81,7 +68,13 @@ function compileServiceWorker({ config, paths, mode }) {
8168
return
8269
}
8370

84-
reporter.debug('Service Worker compilation successful')
71+
reporter.debug(
72+
'Service Worker compilation successful. Size:',
73+
info.assets[0].size,
74+
'bytes'
75+
)
76+
const outputPath = path.join(info.outputPath, info.assets[0].name)
77+
reporter.debug('Output:', outputPath)
8578
resolve()
8679
})
8780
})

0 commit comments

Comments
 (0)