Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
41 changes: 24 additions & 17 deletions packages/portal-proto/next.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
// Optional config overrides supplied at runtime as a JSON object via
// NEXT_CONFIG_OVERRIDES. Its `turbopack`, `connectSrc`, and `env` properties are
// spread into the corresponding entries below (overrides take precedence). This
// env var is unset in normal `next dev`, `next build`, `next start`, and the
// Docker image, so the Data Portal build depends on no external config code.
// Local tooling can inject overrides — e.g. src/features/proteinpaint/dev.sh runs
// ppNextConfig.mjs to point Turbopack at a local proteinpaint client build.
let overrides = {};
if (process.env.NEXT_CONFIG_OVERRIDES) {
try {
overrides = JSON.parse(process.env.NEXT_CONFIG_OVERRIDES);
} catch (e) {
throw new Error(`Invalid NEXT_CONFIG_OVERRIDES JSON: ${e.message}`);
}
}

/**
* This basePath defines root of the application. This must match
* the intended deployment path. For example, the basePath of "/v2"
Expand All @@ -14,21 +30,9 @@ const connectSrc = [
"https://storage.googleapis.com/idc-index-data-artifacts/",
// Uncomment to use mock server for testing
//"https://localhost:3100",
...(overrides.connectSrc || []),
];

if (process.env.NODE_ENV == "development") {
// in SJ dev environment, this would point to a local PP server instance
const PROTEINPAINT_API =
process.env.PROTEINPAINT_API ||
process.env.NEXT_PUBLIC_PROTEINPAINT_API ||
"";
const PROTEINPAINT_HOST =
PROTEINPAINT_API.split("://")[1]?.split("/")[0] || "";

if (PROTEINPAINT_HOST && !connectSrc.includes(`https://${PROTEINPAINT_HOST}`))
connectSrc.push(`https://${PROTEINPAINT_HOST}`);
}

// Fallback if Docker is not run: This calls git directly
const buildHash = () => {
try {
Expand Down Expand Up @@ -56,7 +60,6 @@ const cspHeader = `
frame-ancestors 'none';
upgrade-insecure-requests;
`;

// @ts-check
/**
* @type {import('next').NextConfig}
Expand All @@ -69,6 +72,7 @@ module.exports = {
as: "*.js",
},
},
...(overrides.turbopack || {}),
},
i18n: {
locales: ["en"],
Expand All @@ -82,9 +86,12 @@ module.exports = {
},
allowedDevOrigins: ["localhost.gdc.cancer.gov"],
env: {
// passed via command line, `PROTEINPAINT_API=... npm run dev`
PROTEINPAINT_API:
process.env.PROTEINPAINT_API || process.env.NEXT_PUBLIC_PROTEINPAINT_API,
// e.g. PROTEINPAINT_API, supplied via NEXT_CONFIG_OVERRIDES.env or process.env
...(overrides.env || {
PROTEINPAINT_API:
process.env.PROTEINPAINT_API ||
process.env.NEXT_PUBLIC_PROTEINPAINT_API,
}),
NEXT_PUBLIC_APP_VERSION: process.env.npm_package_version,
// NEXT_PUBLIC_BUILD_SHORT_SHA is passed from gitlab to docker when docker is not run it tries to get it directly from git
NEXT_PUBLIC_APP_HASH:
Expand Down
63 changes: 39 additions & 24 deletions packages/portal-proto/src/features/proteinpaint/dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,47 @@
# assumes that the proteinpaint folder is a sibling dir of gff

if [[ "$1" == "unlink" ]]; then
# to test the published client package before submitting a PR with an updated pp-client version
npm unlink ../proteinpaint/client
# to test the published client package before submitting a PR with an updated pp-client version.
# Clear any local-dev vars inherited from the shell so next.config.js does NOT apply the
# local client override — unlink mode must resolve the published package.
unset PP_CLIENT_DIST NEXT_CONFIG_OVERRIDES

npm uninstall @sjcrh/proteinpaint-client --save --workspace=packages/portal-proto
npm install @sjcrh/proteinpaint-client --save --save-exact --workspace=packages/portal-proto

# sometimes the nextjs bundle cache is stale after changing the client package
rm -rf packages/portal-proto/.next

# run the following in a separate tab
# local-ssl-proxy --config ssl-proxy.json --cert localhost.pem --key localhost-key.pem
# then from the gff dir
PROTEINPAINT_API=https://localhost.gdc.cancer.gov:3011 PORT=3001 npm run dev
Comment thread
siosonel marked this conversation as resolved.
else
# to test the local PP client code -
# !!! NOTE: turbopack used to find the @sjrch/proteintpaint under node_mmodules, but has stopped working !!!
# a temporary fix is to do the following:
# 1. run `npm run dev` from the sjpp or proteinpaint repo/directory
# 2. on rebundling from the client dir, run `cp -r dist ~/dev/gdc-frontend-framework/node_modules/"@sjcrh"/proteinpaint-client/`
#
npm unlink ../proteinpaint/client # change back to `npm link` when fixed
# An issue with npm link and workspaces: the non-linked @sjcrh/proteinpaint-client package
# may be moved to portal-proto/node_modules, creating 2 separate modules of the same package,
# must ensure only the linked module is used for bundling so delete
rm -rf packages/portal-proto/node_modules/@sjcrh
# also not able to do a simpler
# `cd packages/portal-proto && npm link path/to/proteinpaint/client`,
# where the linked module would be in portal-proto/node_modules instead of the
# other way around
fi
# to test the local PP client code:
# 1. build the client in watch mode from the sjpp repo which has proteinpaint as a submodule:
# cd ../../sjpp && npm run dev
# 2. ppNextConfig.mjs (run below) prints a NEXT_CONFIG_OVERRIDES JSON object that
# next.config.js spreads: `turbopack` points Turbopack at the local client dist,
# and `env`/`connectSrc` supply PROTEINPAINT_API and allow its host in the CSP
# (see next.config.js). No npm link or manual `cp -r dist ...` into node_modules is
# needed. A browser refresh after a client rebuild is usually enough to pick up changes.
# PP_CLIENT_DIST may point at the client repo root, its dist dir, or app.js itself.
# Resolve to an absolute path (via a subshell) so it is unaffected by the cwd
# that `lerna run dev` uses for the next process.
if ! PP_CLIENT_DIST="$(cd ../proteinpaint/client && pwd -P)"; then
echo "error: ../proteinpaint/client not found (expected sibling of this repo)" >&2
exit 1
fi
export PP_CLIENT_DIST

# sometimes the nextjs bundle cache is stale after switching the client source
rm -rf packages/portal-proto/.next

# sometimes the nextjs bundle cache are stale after npm link
rm -rf packages/portal-proto/.next
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"

# run the following tab in a separate tab
# local-ssl-proxy --config ssl-proxy.json --cert localhost.pem --key localhost-key.pem
# then from the gff dir
PROTEINPAINT_API=https://localhost.gdc.cancer.gov:3011 PORT=3001 npm run dev
# run the following in a separate tab
# local-ssl-proxy --config ssl-proxy.json --cert localhost.pem --key localhost-key.pem
# then from the gff dir
PROTEINPAINT_API=https://localhost.gdc.cancer.gov:3011 NEXT_CONFIG_OVERRIDES="$("$SCRIPT_DIR/ppNextConfig.mjs")" \
PORT=3001 npm run dev
fi
96 changes: 96 additions & 0 deletions packages/portal-proto/src/features/proteinpaint/ppNextConfig.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env node
import path from "path";
import { existsSync } from "fs";
import { fileURLToPath } from "url";

const CLIENT_PKG = "@sjcrh/proteinpaint-client";

// Default proteinpaint API for local SJ development; override with PROTEINPAINT_API.
const DEFAULT_PROTEINPAINT_API = "https://localhost.gdc.cancer.gov:3011";

// Deepest directory shared by two absolute paths.
const commonAncestor = (a, b) => {
const as = a.split(path.sep);
const bs = b.split(path.sep);
const out = [];
for (let i = 0; i < Math.min(as.length, bs.length) && as[i] === bs[i]; i++)
out.push(as[i]);
return out.join(path.sep) || path.sep;
};

// Absolute path to the client's built entry (dist/app.js) from a base that may
// point at the client repo root, the dist directory, or app.js itself. Prefer
// whichever candidate exists; otherwise infer from the directory name so it also
// works before a first build.
const clientAppJs = (base) => {
const abs = path.resolve(base);
if (abs.endsWith(`${path.sep}app.js`)) return abs;
const asDistDir = path.join(abs, "app.js"); // base is .../client/dist
const asRepoRoot = path.join(abs, "dist", "app.js"); // base is .../client
if (existsSync(asRepoRoot)) return asRepoRoot;
if (existsSync(asDistDir)) return asDistDir;
return path.basename(abs) === "dist" ? asDistDir : asRepoRoot;
};

/**
* Turbopack config fragment that bundles a local proteinpaint/client build
* instead of the published proteinpaint-client dependency, replacing the old
* `npm link` + `cp -r dist` workflow that Turbopack no longer supports in a
* workspace. Nested under `turbopack` in the JSON printed by this CLI and passed
* to next.config.js via NEXT_CONFIG_OVERRIDES (see dev.sh), so the Data Portal
* build never depends on this dev-only module.
*
* - resolveAlias: aliased to dist/app.js (not the package dir) so its sibling
* chunk-*.js files resolve from the real repo dist and rebuilds are live.
* The value must be project-root-relative — Turbopack rejects absolute paths.
* - root: widened to the ancestor shared with the client, since the client
* typically lives outside this repo and Turbopack won't resolve files outside
* its root.
*
* @param projectDir - absolute path to the Next project root (the directory
* containing next.config.js).
* @param clientDir - path to the local client (repo root, dist dir, or app.js).
* @returns a fragment to spread into next.config.js `turbopack`.
*/
export function turbopackConfig(projectDir, clientDir) {
if (!clientDir) return {};

const appJs = clientAppJs(clientDir);
let rel = path.relative(projectDir, appJs);
if (!rel.startsWith(".")) rel = "./" + rel;

return {
root: commonAncestor(projectDir, appJs),
resolveAlias: { [CLIENT_PKG]: rel },
};
}

// CLI: print a NEXT_CONFIG_OVERRIDES JSON object for next.config.js, e.g.
// NEXT_CONFIG_OVERRIDES="$(node ppNextConfig.mjs)" npm run dev
// It supplies everything local proteinpaint development needs:
// - turbopack: alias to the local client build (PP_CLIENT_DIST, else the
// conventional sibling ../proteinpaint/client)
// - env: PROTEINPAINT_API (PROTEINPAINT_API env, else the SJ dev default)
// - connectSrc: the API host, added to the CSP connect-src directive
if (
process.argv[1] &&
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
) {
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const projectDir = path.resolve(scriptDir, "../../.."); // packages/portal-proto
const clientDir =
process.env.PP_CLIENT_DIST ||
path.resolve(projectDir, "../../../proteinpaint/client");

const proteinpaintApi =
process.env.PROTEINPAINT_API || DEFAULT_PROTEINPAINT_API;
const apiHost = proteinpaintApi.split("://")[1]?.split("/")[0] || "";

process.stdout.write(
JSON.stringify({
turbopack: turbopackConfig(projectDir, clientDir),
connectSrc: apiHost ? [`https://${apiHost}`] : [],
env: { PROTEINPAINT_API: proteinpaintApi },
}),
);
}