Update config to handle relative frontend/backend during webpack build.
Signed-off-by: Aramis Sennyey <sennyeya@amazon.com>
This commit is contained in:
@@ -64,10 +64,19 @@ export const apis = [
|
||||
createApiFactory({
|
||||
api: discoveryApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) =>
|
||||
UrlPatternDiscovery.compile(
|
||||
`${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`,
|
||||
),
|
||||
factory: ({ configApi }) => {
|
||||
let baseUrl;
|
||||
try {
|
||||
// Try parsing the url relative to the current document origin, if it fails the URL is misformed.
|
||||
baseUrl = new URL(
|
||||
configApi.getString('backend.baseUrl'),
|
||||
document.location.origin,
|
||||
).href.replace(/\/$/, '');
|
||||
} catch (err) {
|
||||
baseUrl = configApi.getString('backend.baseUrl');
|
||||
}
|
||||
return UrlPatternDiscovery.compile(`${baseUrl}/api/{{ pluginId }}`);
|
||||
},
|
||||
}),
|
||||
createApiFactory({
|
||||
api: alertApiRef,
|
||||
|
||||
@@ -19,15 +19,17 @@ import { resolve as resolvePath } from 'path';
|
||||
import { buildBundle } from '../../lib/bundler';
|
||||
import { getEnvironmentParallelism } from '../../lib/parallel';
|
||||
import { loadCliConfig } from '../../lib/config';
|
||||
import { CliConfigOptions } from '@backstage/config-loader/src/lib/cli';
|
||||
|
||||
interface BuildAppOptions {
|
||||
targetDir: string;
|
||||
writeStats: boolean;
|
||||
cliOptions?: CliConfigOptions;
|
||||
configPaths: string[];
|
||||
}
|
||||
|
||||
export async function buildFrontend(options: BuildAppOptions) {
|
||||
const { targetDir, writeStats, configPaths } = options;
|
||||
const { targetDir, writeStats, configPaths, cliOptions } = options;
|
||||
const { name } = await fs.readJson(resolvePath(targetDir, 'package.json'));
|
||||
await buildBundle({
|
||||
targetDir,
|
||||
@@ -37,6 +39,7 @@ export async function buildFrontend(options: BuildAppOptions) {
|
||||
...(await loadCliConfig({
|
||||
args: configPaths,
|
||||
fromPackage: name,
|
||||
cliOptions,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -39,6 +39,14 @@ export function registerRepoCommand(program: Command) {
|
||||
'--all',
|
||||
'Build all packages, including bundled app and backend packages.',
|
||||
)
|
||||
.option(
|
||||
'--public-path <path>',
|
||||
'Public path for hosting the website on, can be relative.',
|
||||
)
|
||||
.option(
|
||||
'--backend-url <url>',
|
||||
'Backend url, expects just the origin or sub-route. Do not include /api. Can be relative.',
|
||||
)
|
||||
.option(
|
||||
'--since <ref>',
|
||||
'Only build packages and their dev dependents that changed since the specified ref',
|
||||
|
||||
@@ -153,6 +153,10 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
|
||||
return;
|
||||
}
|
||||
await buildFrontend({
|
||||
cliOptions: {
|
||||
publicPath: opts.publicPath,
|
||||
backendUrl: opts.backendUrl,
|
||||
},
|
||||
targetDir: pkg.dir,
|
||||
configPaths: (buildOptions.config as string[]) ?? [],
|
||||
writeStats: Boolean(buildOptions.stats),
|
||||
|
||||
@@ -36,10 +36,12 @@ import { runPlain } from '../run';
|
||||
import ESLintPlugin from 'eslint-webpack-plugin';
|
||||
import pickBy from 'lodash/pickBy';
|
||||
|
||||
const DUMMY_URL = 'http://dummyurl.org';
|
||||
|
||||
export function resolveBaseUrl(config: Config): URL {
|
||||
const baseUrl = config.getString('app.baseUrl');
|
||||
try {
|
||||
return new URL(baseUrl);
|
||||
return new URL(baseUrl, DUMMY_URL);
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid app.baseUrl, ${error}`);
|
||||
}
|
||||
@@ -88,7 +90,7 @@ export async function createConfig(
|
||||
const externalPkgs = packages.filter(p => !isChildPath(paths.root, p.dir));
|
||||
|
||||
const baseUrl = frontendConfig.getString('app.baseUrl');
|
||||
const validBaseUrl = new URL(baseUrl);
|
||||
const validBaseUrl = new URL(baseUrl, DUMMY_URL);
|
||||
const publicPath = validBaseUrl.pathname.replace(/\/$/, '');
|
||||
if (checksEnabled) {
|
||||
plugins.push(
|
||||
|
||||
@@ -60,6 +60,9 @@ export async function serveBundle(options: ServeOptions) {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
|
||||
// The index needs to be rewritten relative to the new public path, including subroutes.
|
||||
index: config.output?.publicPath ?? '/index.html',
|
||||
},
|
||||
https:
|
||||
url.protocol === 'https:'
|
||||
|
||||
@@ -32,6 +32,10 @@ type Options = {
|
||||
withFilteredKeys?: boolean;
|
||||
withDeprecatedKeys?: boolean;
|
||||
fullVisibility?: boolean;
|
||||
cliOptions?: {
|
||||
publicPath?: string;
|
||||
backendUrl?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export async function loadCliConfig(options: Options) {
|
||||
@@ -76,6 +80,7 @@ export async function loadCliConfig(options: Options) {
|
||||
experimentalEnvFunc: options.mockEnv
|
||||
? async name => process.env[name] || 'x'
|
||||
: undefined,
|
||||
cliOptions: options.cliOptions,
|
||||
configRoot: paths.targetRoot,
|
||||
configTargets: configTargets,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AppConfig } from '@backstage/config';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
|
||||
export type CliConfigOptions = {
|
||||
publicPath?: string;
|
||||
backendUrl?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read specific parameters from the CLI and add them to the build config.
|
||||
* @param opts CLI passed parameters.
|
||||
* @returns Array of config, empty if there is no relevant passed in cli options.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function readCliConfig(opts?: CliConfigOptions): AppConfig[] {
|
||||
if (!opts || Object.keys(opts).length === 0) return [];
|
||||
const data: JsonObject = {};
|
||||
|
||||
if (opts.publicPath) {
|
||||
data.app = {
|
||||
baseUrl: opts.publicPath,
|
||||
};
|
||||
}
|
||||
|
||||
if (opts.backendUrl) {
|
||||
data.backend = {
|
||||
baseUrl: opts.backendUrl,
|
||||
};
|
||||
}
|
||||
|
||||
return [{ data, context: 'cli' }];
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
readEnvConfig,
|
||||
} from './lib';
|
||||
import fetch from 'node-fetch';
|
||||
import { CliConfigOptions, readCliConfig } from './lib/cli';
|
||||
|
||||
/** @public */
|
||||
export type ConfigTarget = { path: string } | { url: string };
|
||||
@@ -81,6 +82,11 @@ export type LoadConfigOptions = {
|
||||
* An optional configuration that enables watching of config files.
|
||||
*/
|
||||
watch?: LoadConfigOptionsWatch;
|
||||
|
||||
/**
|
||||
* New options from the CLI that affect the build config.
|
||||
*/
|
||||
cliOptions?: CliConfigOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -230,6 +236,8 @@ export async function loadConfig(
|
||||
}
|
||||
}
|
||||
|
||||
const cliConfigs = readCliConfig(options.cliOptions);
|
||||
|
||||
const envConfigs = readEnvConfig(process.env);
|
||||
|
||||
const watchConfigFile = (watchProp: LoadConfigOptionsWatch) => {
|
||||
@@ -318,7 +326,7 @@ export async function loadConfig(
|
||||
|
||||
return {
|
||||
appConfigs: remote
|
||||
? [...remoteConfigs, ...fileConfigs, ...envConfigs]
|
||||
: [...fileConfigs, ...envConfigs],
|
||||
? [...remoteConfigs, ...fileConfigs, ...envConfigs, ...cliConfigs]
|
||||
: [...fileConfigs, ...envConfigs, ...cliConfigs],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ function getBasePath(configApi: Config) {
|
||||
function readBasePath(configApi: ConfigApi) {
|
||||
let { pathname } = new URL(
|
||||
configApi.getOptionalString('app.baseUrl') ?? '/',
|
||||
'http://dummy.dev', // baseUrl can be specified as just a path
|
||||
document.location.origin, // baseUrl can be specified as just a path
|
||||
);
|
||||
pathname = pathname.replace(/\/*$/, '');
|
||||
return pathname;
|
||||
@@ -332,7 +332,6 @@ export class AppManager implements BackstageApp {
|
||||
routeParents={routing.parents}
|
||||
routeObjects={routing.objects}
|
||||
routeBindings={routeBindings}
|
||||
basePath={getBasePath(loadedConfig.api)}
|
||||
>
|
||||
<InternalAppContext.Provider
|
||||
value={{ routeObjects: routing.objects }}
|
||||
@@ -426,7 +425,7 @@ export class AppManager implements BackstageApp {
|
||||
|
||||
if (isReactRouterBeta()) {
|
||||
return (
|
||||
<RouterComponent>
|
||||
<RouterComponent basename={basePath}>
|
||||
<RouteTracker routeObjects={routeObjects} />
|
||||
<SignInPageWrapper component={SignInPageComponent}>
|
||||
<Routes>
|
||||
|
||||
@@ -60,6 +60,10 @@ export type ErrorBoundaryFallbackProps = {
|
||||
resetError: () => void;
|
||||
};
|
||||
|
||||
export type RouterProps = {
|
||||
basename: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* A set of replaceable core components that are part of every Backstage app.
|
||||
*
|
||||
|
||||
@@ -214,15 +214,13 @@ export class RouteResolver {
|
||||
// Next we figure out the base path, which is the combination of the common parent path
|
||||
// between our current location and our target location, as well as the additional path
|
||||
// that is the difference between the parent path and the base of our target location.
|
||||
const basePath =
|
||||
this.appBasePath +
|
||||
resolveBasePath(
|
||||
targetRef,
|
||||
relativeSourceLocation,
|
||||
this.routePaths,
|
||||
this.routeParents,
|
||||
this.routeObjects,
|
||||
);
|
||||
const basePath = resolveBasePath(
|
||||
targetRef,
|
||||
relativeSourceLocation,
|
||||
this.routePaths,
|
||||
this.routeParents,
|
||||
this.routeObjects,
|
||||
);
|
||||
|
||||
const routeFunc: RouteFunc<Params> = (...[params]) => {
|
||||
return joinPaths(basePath, generatePath(targetPath, params));
|
||||
|
||||
Reference in New Issue
Block a user