Merge pull request #31255 from backstage/cli-package-start-entry-dir
[CLI] Package Start Entrypoint Option
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Added a new `--entrypoint` option to the `package start` command, which allows you to specify a custom entry directory/file for development applications. This is particularly useful when maintaining separate dev apps for different versions of your plugin (e.g., stable and alpha).
|
||||
|
||||
**Example usage:**
|
||||
|
||||
Consider the following plugin dev folder structure:
|
||||
|
||||
```
|
||||
dev/
|
||||
index.tsx
|
||||
alpha/
|
||||
index.ts
|
||||
```
|
||||
|
||||
- The default `yarn package start` command uses the `dev/` folder as the entry point and executes `dev/index.tsx` file;
|
||||
- Running `yarn package start --entrypoint dev/alpha` will instead use `dev/alpha/` as the entry point and execute `dev/alpha/index.ts` file.
|
||||
@@ -177,11 +177,12 @@ Usage: backstage-cli package start [options]
|
||||
Start a package for local development
|
||||
|
||||
Options:
|
||||
--config <path> Config files to load instead of app-config.yaml (default: [])
|
||||
--role <name> Run the command with an explicit package role
|
||||
--check Enable type checking and linting if available
|
||||
--inspect Enable debugger in Node.js environments
|
||||
--inspect-brk Enable debugger in Node.js environments, breaking before code starts
|
||||
--config <path> Config files to load instead of app-config.yaml (default: [])
|
||||
--role <name> Run the command with an explicit package role
|
||||
--check Enable type checking and linting if available
|
||||
--inspect Enable debugger in Node.js environments
|
||||
--inspect-brk Enable debugger in Node.js environments, breaking before code starts
|
||||
--entrypoint <path> Entry directory path (uses index file) or entry file path (without extension). Defaults to "dev"
|
||||
```
|
||||
|
||||
## package build
|
||||
|
||||
@@ -312,6 +312,7 @@ Usage: program [options]
|
||||
Options:
|
||||
--check
|
||||
--config <path>
|
||||
--entrypoint <path>
|
||||
--inspect [host]
|
||||
--inspect-brk [host]
|
||||
--link <path>
|
||||
|
||||
@@ -23,6 +23,7 @@ import { paths } from '../../../../../lib/paths';
|
||||
export async function command(opts: OptionValues): Promise<void> {
|
||||
await startPackage({
|
||||
role: await findRoleFromCommand(opts),
|
||||
entrypoint: opts.entrypoint,
|
||||
targetDir: paths.targetDir,
|
||||
configPaths: opts.config as string[],
|
||||
checksEnabled: Boolean(opts.check),
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2020 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 { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import { resolveEntryPath } from './startPackage';
|
||||
|
||||
describe('resolveEntryPath', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
afterEach(() => {
|
||||
mockDir.clear();
|
||||
});
|
||||
|
||||
it('should remove file extensions', () => {
|
||||
mockDir.setContent({
|
||||
'dev/custom.tsx': '// dev app code',
|
||||
});
|
||||
|
||||
const result = resolveEntryPath('dev/custom.tsx', mockDir.path);
|
||||
|
||||
expect(result).toBe('dev/custom');
|
||||
});
|
||||
|
||||
it('should remove trailing slashes', () => {
|
||||
mockDir.setContent({
|
||||
'dev/alpha.ts': '// dev app code',
|
||||
});
|
||||
|
||||
const result = resolveEntryPath('dev/alpha/', mockDir.path);
|
||||
|
||||
expect(result).toBe('dev/alpha');
|
||||
});
|
||||
|
||||
it('should handle multiple dots in filename', () => {
|
||||
mockDir.setContent({
|
||||
'index.alpha.ts': 'export const data = {};',
|
||||
});
|
||||
|
||||
const result = resolveEntryPath('index.alpha.ts', mockDir.path);
|
||||
|
||||
expect(result).toBe('index.alpha');
|
||||
});
|
||||
|
||||
it('should handle simple directory names', () => {
|
||||
mockDir.setContent({
|
||||
'dev/index.ts': '// dev app code',
|
||||
});
|
||||
|
||||
const result = resolveEntryPath('dev', mockDir.path);
|
||||
|
||||
expect(result).toBe('dev/index');
|
||||
});
|
||||
|
||||
it('should handle nested directory paths', () => {
|
||||
mockDir.setContent({
|
||||
'dev/alpha/index.ts': '// dev app code',
|
||||
});
|
||||
|
||||
const result = resolveEntryPath('dev/alpha', mockDir.path);
|
||||
|
||||
expect(result).toBe('dev/alpha/index');
|
||||
});
|
||||
|
||||
it('should return the file when there is a directory with the same name', () => {
|
||||
mockDir.setContent({
|
||||
'dev/alpha.ts': '// dev app code',
|
||||
'dev/app-config.yaml': '// dev app config',
|
||||
'dev/alpha/index.ts': '// dev app code',
|
||||
'dev/alpha/app-config.yaml': '// dev app config',
|
||||
});
|
||||
|
||||
const result = resolveEntryPath('dev/alpha', mockDir.path);
|
||||
|
||||
expect(result).toBe('dev/alpha');
|
||||
});
|
||||
});
|
||||
@@ -17,9 +17,24 @@
|
||||
import { PackageRole } from '@backstage/cli-node';
|
||||
import { startBackend, startBackendPlugin } from './startBackend';
|
||||
import { startFrontend } from './startFrontend';
|
||||
import { parse, resolve, join } from 'path';
|
||||
import { glob } from 'glob';
|
||||
|
||||
export function resolveEntryPath(
|
||||
entrypoint: string = 'dev',
|
||||
targetDir: string,
|
||||
): string {
|
||||
const { dir: entryDir, name: entryName } = parse(entrypoint);
|
||||
const [entryFile] = glob.sync(`${resolve(targetDir, entryDir, entryName)}.*`);
|
||||
if (entryFile) {
|
||||
return join(entryDir, entryName);
|
||||
}
|
||||
return join(entryDir, entryName, 'index');
|
||||
}
|
||||
|
||||
export async function startPackage(options: {
|
||||
role: PackageRole;
|
||||
entrypoint?: string;
|
||||
targetDir: string;
|
||||
configPaths: string[];
|
||||
checksEnabled: boolean;
|
||||
@@ -45,8 +60,8 @@ export async function startPackage(options: {
|
||||
case 'frontend-plugin':
|
||||
case 'frontend-plugin-module':
|
||||
return startFrontend({
|
||||
entry: 'dev/index',
|
||||
...options,
|
||||
entry: resolveEntryPath(options.entrypoint, options.targetDir),
|
||||
});
|
||||
case 'frontend-dynamic-container' as PackageRole: // experimental
|
||||
return startFrontend({
|
||||
|
||||
@@ -137,6 +137,10 @@ export const buildPlugin = createCliPlugin({
|
||||
'--link <path>',
|
||||
'Link an external workspace for module resolution',
|
||||
)
|
||||
.option(
|
||||
'--entrypoint <path>',
|
||||
'The entrypoint to start from, relative to the package root. Can point to either a file (without extension) or a directory (in which case the index file in that directory is used). Defaults to "dev"',
|
||||
)
|
||||
.action(lazy(() => import('./commands/package/start'), 'command'));
|
||||
|
||||
await defaultCommand.parseAsync(args, { from: 'user' });
|
||||
|
||||
Reference in New Issue
Block a user