diff --git a/stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/build-app.sh b/stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/build-app.sh index 55fa7749..1b3c3cc3 100755 --- a/stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/build-app.sh +++ b/stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/build-app.sh @@ -1,13 +1,16 @@ #!/bin/bash -set -euo pipefail - -if [ -n "${CERC_SCRIPT_DEBUG:-}" ]; then +if [ -n "$CERC_SCRIPT_DEBUG" ]; then set -x fi -# Determine the build tool -CERC_BUILD_TOOL="${CERC_BUILD_TOOL:-}" +CERC_MIN_NEXTVER=13.4.2 +CERC_DEFAULT_WEBPACK_VER="5.93.0" + +CERC_NEXT_VERSION="${CERC_NEXT_VERSION:-keep}" +CERC_WEBPACK_VERSION="${CERC_WEBPACK_VERSION:-keep}" + +CERC_BUILD_TOOL="${CERC_BUILD_TOOL}" if [ -z "$CERC_BUILD_TOOL" ]; then if [ -f "pnpm-lock.yaml" ]; then CERC_BUILD_TOOL=pnpm @@ -20,50 +23,11 @@ if [ -z "$CERC_BUILD_TOOL" ]; then fi fi -SCRIPT_DIR=$(dirname "$(readlink -f "$0")") +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) WORK_DIR="${1:-/app}" cd "${WORK_DIR}" || exit 1 -# Get the app's Next.js version -CERC_NEXT_VERSION=$(jq -r '.dependencies.next' package.json) -echo "Using Next.js version: $CERC_NEXT_VERSION" - -# Determine webpack version based on Next.js version (unused if already installed) -determine_webpack_version() { - local next_version=$1 - local major_version=${next_version%%.*} - case $major_version in - 13) - echo "5.75.0" - ;; - 14) - echo "5.88.0" - ;; - *) - echo "5.93.0" # Default to latest stable version if unknown - ;; - esac -} - -# Check if webpack is already in package.json -if CERC_WEBPACK_VERSION=$(jq -r '.dependencies.webpack // .devDependencies.webpack // empty' package.json); then - if [ -n "$CERC_WEBPACK_VERSION" ]; then - echo "Using existing webpack version: $CERC_WEBPACK_VERSION" - else - CERC_WEBPACK_VERSION=$(determine_webpack_version "$CERC_NEXT_VERSION") - echo "Determined webpack version based on Next.js: $CERC_WEBPACK_VERSION" - # Add webpack to devDependencies - jq ".devDependencies.webpack = \"$CERC_WEBPACK_VERSION\"" package.json > package.json.tmp && mv package.json.tmp package.json - fi -else - CERC_WEBPACK_VERSION=$(determine_webpack_version "$CERC_NEXT_VERSION") - echo "Determined webpack version based on Next.js: $CERC_WEBPACK_VERSION" - # Add webpack to devDependencies - jq ".devDependencies.webpack = \"$CERC_WEBPACK_VERSION\"" package.json > package.json.tmp && mv package.json.tmp package.json -fi - -# Determine the Next.js config file if [ -f "next.config.mjs" ]; then NEXT_CONFIG_JS="next.config.mjs" IMPORT_OR_REQUIRE="import" @@ -77,125 +41,203 @@ if [ ! -f "${NEXT_CONFIG_JS}" ]; then touch ${NEXT_CONFIG_JS} fi -# Backup the original config if not already done if [ ! -f "next.config.dist" ]; then - cp "$NEXT_CONFIG_JS" next.config.dist + cp $NEXT_CONFIG_JS next.config.dist fi -# Install js-beautify if not present -command -v js-beautify >/dev/null || npm i -g js-beautify +which js-beautify >/dev/null +if [ $? -ne 0 ]; then + npm i -g js-beautify +fi -# Beautify the config file -js-beautify "$NEXT_CONFIG_JS" > "${NEXT_CONFIG_JS}.pretty" -mv "${NEXT_CONFIG_JS}.pretty" "$NEXT_CONFIG_JS" +# js-beautify formats NEXTJS_CONFIG_FILE (ie next.config.js / next.config.mjs) so we can reliably transformable later +js-beautify next.config.dist > ${NEXT_CONFIG_JS} +echo "" >> ${NEXT_CONFIG_JS} -# Add webpack import/require if not present -if [ "$IMPORT_OR_REQUIRE" = "import" ]; then - if ! grep -q "import.*webpack" "$NEXT_CONFIG_JS"; then - sed -i '1iimport webpack from "webpack";' "$NEXT_CONFIG_JS" - fi - if ! grep -q "import.*createRequire" "$NEXT_CONFIG_JS"; then - sed -i '2iimport { createRequire } from "module";\nconst require = createRequire(import.meta.url);' "$NEXT_CONFIG_JS" +if [ "${IMPORT_OR_REQUIRE}" == "require" ]; then + WEBPACK_REQ_LINE=$(grep -n "require([\'\"]webpack[\'\"])" ${NEXT_CONFIG_JS} | cut -d':' -f1) + if [ -z "$WEBPACK_REQ_LINE" ]; then + cat > ${NEXT_CONFIG_JS}.0 < ${NEXT_CONFIG_JS}.0 <> ${NEXT_CONFIG_JS}.0 <> "$NEXT_CONFIG_JS" - +cat > ${NEXT_CONFIG_JS}.1 < { - a[v] = JSON.stringify(`CERC_RUNTIME_ENV_${v.split(/\./).pop()}`); + a[v] = \`"CERC_RUNTIME_ENV_\${v.split(/\./).pop()}"\`; return a; }, {}); } catch (e) { - console.error('Error loading .env-list.json:', e); + console.error(e); + // If .env-list.json cannot be loaded, we are probably running in dev mode, so use process.env instead. envMap = Object.keys(process.env).reduce((a, v) => { if (v.startsWith('CERC_')) { - a[`process.env.${v}`] = JSON.stringify(process.env[v]); + a[\`process.env.\${v}\`] = JSON.stringify(process.env[v]); } return a; }, {}); } -console.log('Environment map:', envMap); +console.log(envMap); EOF -fi -# Add or update webpack configuration -if ! grep -q '__xCfg__' "$NEXT_CONFIG_JS"; then - cat << 'EOF' >> "$NEXT_CONFIG_JS" +grep 'withPWA' ${NEXT_CONFIG_JS} >/dev/null && HAS_WITHPWA=true || HAS_WITHPWA=false -const __xCfg__ = (nextConfig) => { - return { - ...nextConfig, - webpack: (config, options) => { +if [ "$HAS_WITHPWA" == "true" ]; then + if [ "$IMPORT_OR_REQUIRE" == "import" ]; then + cat > ${NEXT_CONFIG_JS}.2 < { + const realPWA = withPWA(p); + return (nextConfig) => { + const modConfig = {...nextConfig}; + + modConfig.webpack = (config) => { config.plugins.push(new webpack.DefinePlugin(envMap)); - if (typeof nextConfig.webpack === 'function') { - return nextConfig.webpack(config, options); - } - return config; - }, + return nextConfig.webpack ? nextConfig.webpack(config) : config; + }; + + return realPWA(modConfig); }; }; - EOF - - # Update the export/module.exports line - if [ "$IMPORT_OR_REQUIRE" = "import" ]; then - sed -i 's/export default/const __orig_cfg__ =/' "$NEXT_CONFIG_JS" - echo "export default __xCfg__(__orig_cfg__);" >> "$NEXT_CONFIG_JS" else - sed -i 's/module.exports =/const __orig_cfg__ =/' "$NEXT_CONFIG_JS" - echo "module.exports = __xCfg__(__orig_cfg__);" >> "$NEXT_CONFIG_JS" + cat > ${NEXT_CONFIG_JS}.3 < { + const modConfig = {...nextConfig}; + + modConfig.webpack = (config) => { + config.plugins.push(new webpack.DefinePlugin(envMap)); + return nextConfig.webpack ? nextConfig.webpack(config) : config; + }; + + return withPWA(modConfig); +}; +EOF + fi + + cat ${NEXT_CONFIG_JS} | js-beautify | sed 's/withPWA(/__xPWA__(/g' > ${NEXT_CONFIG_JS}.4 +else + cat > ${NEXT_CONFIG_JS}.3 < { + const modConfig = {...nextConfig}; + + modConfig.webpack = (config) => { + config.plugins.push(new webpack.DefinePlugin(envMap)); + return nextConfig.webpack ? nextConfig.webpack(config) : config; + }; + + return modConfig; + }; +EOF + if [ "$IMPORT_OR_REQUIRE" == "import" ]; then + cat ${NEXT_CONFIG_JS} | js-beautify | sed 's/export\s\+default\s\+/const __orig_cfg__ = /g' > ${NEXT_CONFIG_JS}.4 + echo "export default __xCfg__(__orig_cfg__);" > ${NEXT_CONFIG_JS}.5 + else + cat ${NEXT_CONFIG_JS} | js-beautify | sed 's/module.exports\s\+=\s\+/const __orig_cfg__ = /g' > ${NEXT_CONFIG_JS}.4 + echo "module.exports = __xCfg__(__orig_cfg__);" > ${NEXT_CONFIG_JS}.5 fi fi -# Clean up the config file -js-beautify "$NEXT_CONFIG_JS" > "${NEXT_CONFIG_JS}.pretty" -mv "${NEXT_CONFIG_JS}.pretty" "$NEXT_CONFIG_JS" -# Generate .env-list.json +rm -f ${NEXT_CONFIG_JS} +for ((i=0; i <= 10; i++)); do + if [ -s "${NEXT_CONFIG_JS}.${i}" ]; then + if [ $i -le 2 ] ; then + cat ${NEXT_CONFIG_JS}.${i} >> ${NEXT_CONFIG_JS} + else + cat ${NEXT_CONFIG_JS}.${i} | sed 's/^ *//g' | js-beautify | grep -v 'process\.\env\.' | js-beautify >> ${NEXT_CONFIG_JS} + fi + fi +done +rm ${NEXT_CONFIG_JS}.* +cat ${NEXT_CONFIG_JS} | js-beautify > ${NEXT_CONFIG_JS}.pretty +mv ${NEXT_CONFIG_JS}.pretty ${NEXT_CONFIG_JS} + "${SCRIPT_DIR}/find-env.sh" "$(pwd)" > .env-list.json -# Update package.json -[ ! -f "package.dist" ] && cp package.json package.dist - -# Install dependencies -$CERC_BUILD_TOOL install || exit 1 - -# Get the build command from package.json -BUILD_COMMAND=`jq -r '.scripts.build // ""' package.json` - -if [ -z "$BUILD_COMMAND" ]; then - echo "No build command found in package.json. Using default 'next build'." - BUILD_COMMAND="next build" +if [ ! -f "package.dist" ]; then + cp package.json package.dist fi -# Determine the appropriate build commands based on the Next.js version -if semver -p -r ">=14.2.0" "$CERC_NEXT_VERSION"; then +CUR_NEXT_VERSION="`jq -r '.dependencies.next' package.json`" + +if [ "$CERC_NEXT_VERSION" != "keep" ] && [ "$CUR_NEXT_VERSION" != "$CERC_NEXT_VERSION" ]; then + echo "Changing 'next' version specifier from '$CUR_NEXT_VERSION' to '$CERC_NEXT_VERSION' (set with '--extra-build-args \"--build-arg CERC_NEXT_VERSION=$CERC_NEXT_VERSION\"')" + cat package.json | jq ".dependencies.next = \"$CERC_NEXT_VERSION\"" > package.json.$$ + mv package.json.$$ package.json +fi + +CUR_WEBPACK_VERSION="`jq -r '.dependencies.webpack' package.json`" +if [ -z "$CUR_WEBPACK_VERSION" ]; then + CUR_WEBPACK_VERSION="`jq -r '.devDependencies.webpack' package.json`" +fi +if [ "${CERC_WEBPACK_VERSION}" != "keep" ] || [ "${CUR_WEBPACK_VERSION}" == "null" ]; then + if [ -z "$CERC_WEBPACK_VERSION" ] || [ "$CERC_WEBPACK_VERSION" == "keep" ]; then + CERC_WEBPACK_VERSION="${CERC_DEFAULT_WEBPACK_VER}" + fi + echo "Webpack is required for env variable substitution. Adding to webpack@$CERC_WEBPACK_VERSION to dependencies..." 1>&2 + cat package.json | jq ".dependencies.webpack = \"$CERC_WEBPACK_VERSION\"" > package.json.$$ + mv package.json.$$ package.json +fi + +time $CERC_BUILD_TOOL install || exit 1 + +CUR_NEXT_VERSION=`jq -r '.version' node_modules/next/package.json` + +# See https://github.com/vercel/next.js/discussions/46544 +semver -p -r ">=14.2.0" "$CUR_NEXT_VERSION" +if [ $? -eq 0 ]; then # For >= 14.2.0 - CERC_NEXT_COMPILE_COMMAND="${BUILD_COMMAND/next build/next build --experimental-build-mode compile}" - CERC_NEXT_GENERATE_COMMAND="${BUILD_COMMAND/next build/next build --experimental-build-mode generate}" -elif semver -p -r ">=13.4.2" "$CERC_NEXT_VERSION"; then - # For 13.4.2 to 14.1.x - CERC_NEXT_COMPILE_COMMAND="${BUILD_COMMAND/next build/next experimental-compile}" - CERC_NEXT_GENERATE_COMMAND="${BUILD_COMMAND/next build/next experimental-generate}" + CERC_NEXT_COMPILE_COMMAND="next build --experimental-build-mode compile" + CERC_NEXT_GENERATE_COMMAND="next build --experimental-build-mode generate" else - # For versions before 13.4.2 - CERC_NEXT_COMPILE_COMMAND="$BUILD_COMMAND" - CERC_NEXT_GENERATE_COMMAND="$BUILD_COMMAND" + # For 13.4.2 to 14.1.x + CERC_NEXT_COMPILE_COMMAND="next experimental-compile" + CERC_NEXT_GENERATE_COMMAND="next experimental-generate" fi -# Update package.json with the appropriate scripts cat package.json | jq ".scripts.cerc_compile = \"$CERC_NEXT_COMPILE_COMMAND\"" | jq ".scripts.cerc_generate = \"$CERC_NEXT_GENERATE_COMMAND\"" > package.json.$$ mv package.json.$$ package.json -# Run the compile command +semver -p -r ">=$CERC_MIN_NEXTVER" $CUR_NEXT_VERSION +if [ $? -ne 0 ]; then + cat <" + +############################################################################### + +EOF + cat package.json | jq ".dependencies.next = \"^$CERC_MIN_NEXTVER\"" > package.json.$$ + mv package.json.$$ package.json + time $CERC_BUILD_TOOL install || exit 1 +fi + time $CERC_BUILD_TOOL run cerc_compile || exit 1 exit 0 \ No newline at end of file