Merge pull request #32075 from backstage/sennyeya/node-snapshot
feat(cli): provide --no-node-snapshot by default
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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 { runBackend } from './runBackend';
|
||||
import spawn from 'cross-spawn';
|
||||
|
||||
// Mock external dependencies
|
||||
jest.mock('chokidar', () => ({
|
||||
watch: jest.fn(() => ({
|
||||
on: jest.fn().mockReturnThis(),
|
||||
add: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('cross-spawn', () =>
|
||||
jest.fn(() => ({
|
||||
on: jest.fn().mockReturnThis(),
|
||||
once: jest.fn().mockReturnThis(),
|
||||
kill: jest.fn(),
|
||||
killed: false,
|
||||
exitCode: null,
|
||||
pid: 12345,
|
||||
})),
|
||||
);
|
||||
|
||||
jest.mock('../ipc', () => ({
|
||||
IpcServer: jest.fn().mockImplementation(() => ({
|
||||
addChild: jest.fn(),
|
||||
})),
|
||||
ServerDataStore: {
|
||||
bind: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('ctrlc-windows', () => ({
|
||||
ctrlc: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('runBackend', () => {
|
||||
let originalEnv: NodeJS.ProcessEnv;
|
||||
let originalPlatform: string;
|
||||
const mockSpawn = spawn as jest.MockedFunction<typeof spawn>;
|
||||
|
||||
beforeEach(() => {
|
||||
// Use fake timers to control debounce
|
||||
jest.useFakeTimers();
|
||||
|
||||
// Save original environment
|
||||
originalEnv = { ...process.env };
|
||||
process.env = { NODE_ENV: 'test' };
|
||||
originalPlatform = process.platform;
|
||||
|
||||
// Mock process.stdin.on to prevent actual stdin reading
|
||||
jest.spyOn(process.stdin, 'on').mockReturnValue(process.stdin);
|
||||
|
||||
// Mock process.once to prevent actual signal handling
|
||||
jest.spyOn(process, 'once').mockReturnValue(process);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original environment
|
||||
process.env = originalEnv;
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: originalPlatform,
|
||||
});
|
||||
|
||||
jest.clearAllMocks();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
describe('--no-node-snapshot argument handling', () => {
|
||||
it('should pass --no-node-snapshot when NODE_OPTIONS is not set', () => {
|
||||
delete process.env.NODE_OPTIONS;
|
||||
|
||||
runBackend({
|
||||
entry: 'src/index',
|
||||
});
|
||||
|
||||
// Fast-forward past the debounce delay (100ms)
|
||||
jest.advanceTimersByTime(100);
|
||||
|
||||
expect(mockSpawn).toHaveBeenCalled();
|
||||
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
|
||||
expect(spawnArgs).toContain('--no-node-snapshot');
|
||||
});
|
||||
|
||||
it('should pass --no-node-snapshot when NODE_OPTIONS exists without --node-snapshot', () => {
|
||||
process.env.NODE_OPTIONS = '--max-old-space-size=4096';
|
||||
|
||||
runBackend({
|
||||
entry: 'src/index',
|
||||
});
|
||||
|
||||
// Fast-forward past the debounce delay (100ms)
|
||||
jest.advanceTimersByTime(100);
|
||||
|
||||
expect(mockSpawn).toHaveBeenCalled();
|
||||
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
|
||||
expect(spawnArgs).toContain('--no-node-snapshot');
|
||||
});
|
||||
|
||||
it('should not pass --no-node-snapshot when --node-snapshot already exists in NODE_OPTIONS', () => {
|
||||
process.env.NODE_OPTIONS = '--node-snapshot --max-old-space-size=4096';
|
||||
|
||||
runBackend({
|
||||
entry: 'src/index',
|
||||
});
|
||||
|
||||
// Fast-forward past the debounce delay (100ms)
|
||||
jest.advanceTimersByTime(100);
|
||||
|
||||
expect(mockSpawn).toHaveBeenCalled();
|
||||
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
|
||||
expect(spawnArgs).not.toContain('--no-node-snapshot');
|
||||
});
|
||||
|
||||
it('should not pass --no-node-snapshot when --node-snapshot exists in the middle of NODE_OPTIONS', () => {
|
||||
process.env.NODE_OPTIONS =
|
||||
'--max-old-space-size=4096 --node-snapshot --inspect';
|
||||
|
||||
runBackend({
|
||||
entry: 'src/index',
|
||||
});
|
||||
|
||||
// Fast-forward past the debounce delay (100ms)
|
||||
jest.advanceTimersByTime(100);
|
||||
|
||||
expect(mockSpawn).toHaveBeenCalled();
|
||||
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
|
||||
expect(spawnArgs).not.toContain('--no-node-snapshot');
|
||||
});
|
||||
|
||||
it('should pass --no-node-snapshot even with trailing spaces in NODE_OPTIONS', () => {
|
||||
process.env.NODE_OPTIONS = '--max-old-space-size=4096 ';
|
||||
|
||||
runBackend({
|
||||
entry: 'src/index',
|
||||
});
|
||||
|
||||
// Fast-forward past the debounce delay (100ms)
|
||||
jest.advanceTimersByTime(100);
|
||||
|
||||
expect(mockSpawn).toHaveBeenCalled();
|
||||
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
|
||||
expect(spawnArgs).toContain('--no-node-snapshot');
|
||||
});
|
||||
|
||||
it('should pass --no-node-snapshot alongside other option args like --inspect', () => {
|
||||
delete process.env.NODE_OPTIONS;
|
||||
|
||||
runBackend({
|
||||
entry: 'src/index',
|
||||
inspectEnabled: true,
|
||||
});
|
||||
|
||||
// Fast-forward past the debounce delay (100ms)
|
||||
jest.advanceTimersByTime(100);
|
||||
|
||||
expect(mockSpawn).toHaveBeenCalled();
|
||||
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
|
||||
expect(spawnArgs).toContain('--no-node-snapshot');
|
||||
expect(spawnArgs).toContain('--inspect');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -47,7 +47,7 @@ export type RunBackendOptions = {
|
||||
};
|
||||
|
||||
export async function runBackend(options: RunBackendOptions) {
|
||||
const envEnv = process.env as { NODE_ENV: string };
|
||||
const envEnv = process.env as { NODE_ENV: string; NODE_OPTIONS?: string };
|
||||
if (!envEnv.NODE_ENV) {
|
||||
envEnv.NODE_ENV = 'development';
|
||||
}
|
||||
@@ -115,6 +115,12 @@ export async function runBackend(options: RunBackendOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
// Unless the user explicitly toggles node-snapshot, default to provide --no-node-snapshot to reduce number of steps to run scaffolder
|
||||
// on Node LTS.
|
||||
if (!envEnv.NODE_OPTIONS?.includes('--node-snapshot')) {
|
||||
optionArgs.push('--no-node-snapshot');
|
||||
}
|
||||
|
||||
const userArgs = process.argv
|
||||
.slice(['node', 'backstage-cli', 'package', 'start'].length)
|
||||
.filter(arg => !optionArgs.includes(arg));
|
||||
|
||||
@@ -78,6 +78,14 @@ export default async (_opts: OptionValues, cmd: Command) => {
|
||||
process.env.TZ = 'UTC';
|
||||
}
|
||||
|
||||
// Unless the user explicitly toggles node-snapshot, default to provide --no-node-snapshot to reduce number of steps to run scaffolder
|
||||
// on Node LTS.
|
||||
if (!process.env.NODE_OPTIONS?.includes('--node-snapshot')) {
|
||||
process.env.NODE_OPTIONS = `${
|
||||
process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ` : ''
|
||||
}--no-node-snapshot`;
|
||||
}
|
||||
|
||||
// This ensures that the process doesn't exit too early before stdout is flushed
|
||||
if (args.includes('--help')) {
|
||||
(process.stdout as any)._handle.setBlocking(true);
|
||||
|
||||
@@ -289,6 +289,14 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
|
||||
process.env.TZ = 'UTC';
|
||||
}
|
||||
|
||||
// Unless the user explicitly toggles node-snapshot, default to provide --no-node-snapshot to reduce number of steps to run scaffolder
|
||||
// on Node LTS.
|
||||
if (!process.env.NODE_OPTIONS?.includes('--node-snapshot')) {
|
||||
process.env.NODE_OPTIONS = `${
|
||||
process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ` : ''
|
||||
}--no-node-snapshot`;
|
||||
}
|
||||
|
||||
// This ensures that the process doesn't exit too early before stdout is flushed
|
||||
if (args.includes('--jest-help')) {
|
||||
removeOptionArg(args, '--jest-help');
|
||||
|
||||
Reference in New Issue
Block a user