Skip to content
Merged
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
17 changes: 17 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { validate } from './validate';
import { deploy } from './deploy';
import { template } from './template';
import { destroyStack } from './destroy';
import { runLocal } from './local';

const program = new Command();

Expand Down Expand Up @@ -101,4 +102,20 @@ program
},
);

program
.command('local <stackName>')
.description('run Serverless application locally for debugging')
.option('-s, --stage <stage>', 'specify the stage', 'default')
.option('-p, --port <port>', 'specify the port', '3000')
.option('-d, --debug', 'enable debug mode')
.option('-w, --watch', 'enable file watch', true)
.action(async (stackName, { stage, port, debug, watch }) => {
await runLocal(stackName, {
stage,
port: Number(port) || 3000,
debug: !!debug,
watch: typeof watch === 'boolean' ? watch : true,
});
});

program.parse();
45 changes: 45 additions & 0 deletions src/commands/local.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { logger, setContext } from '../common';
import { startLocalStack } from '../stack/localStack';

export interface RunLocalOptions {
stage: string;
port: number;
debug: boolean;
watch: boolean;
}

export const runLocal = async (stackName: string, opts: RunLocalOptions) => {
const { stage, port, debug, watch } = opts;

await setContext({ stage });

logger.info(
`run-local starting: stack=${stackName} stage=${stage} port=${port} debug=${debug} watch=${watch}`,
);

await startLocalStack();

// if (watch) {
// const cwd = process.cwd();
// try {
// fs.watch(cwd, { recursive: true }, (eventType, filename) => {
// if (!filename) return;
// const filePath = path.join(cwd, filename);
// logger.info(`file change detected: ${eventType} ${filePath}`);
// });
// logger.info(`watching files under ${process.cwd()}`);
// } catch (err) {
// logger.warn(`file watch not available: ${String(err)}`);
// }
// }
//
// const shutdown = () => {
// logger.info('shutting down run-local server');
// server.close(() => process.exit(0));
// };
// process.on('SIGINT', shutdown);
// process.on('SIGTERM', shutdown);
//
// // return server for tests if needed
// return { server, port, stage, debug, watch };
};
38 changes: 38 additions & 0 deletions src/stack/localStack/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import http from 'node:http';
import { logger } from '../../common';
import { EventDomain, EventTypes } from '../../types';
import { isEmpty } from 'lodash';

const startApiGatewayServer = (event: EventDomain) => {
const server = http.createServer((req, res) => {
const matchedTrigger = event.triggers.find(
(trigger) => trigger.method === req.method && trigger.path === req.url,
);
if (!matchedTrigger) {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Not Found\n');
logger.warn(`API Gateway Event - ${req.method} ${req.url} -> Not Found`);
return;
}

res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`Invoked backend: ${matchedTrigger.backend}\n`);
logger.info(`API Gateway Event - ${req.method} ${req.url} -> ${matchedTrigger.backend}`);
});

const port = 3000 + Math.floor(Math.random() * 1000);
server.listen(port, () => {
logger.info(`API Gateway "${event.name}" listening on http://localhost:${port}`);
});
};

export const startEvents = (events: Array<EventDomain> | undefined) => {
const apiGateways = events?.filter((event) => event.type === EventTypes.API_GATEWAY);
if (isEmpty(apiGateways)) {
return;
}

apiGateways!.forEach((gateway) => {
startApiGatewayServer(gateway);
});
};
6 changes: 6 additions & 0 deletions src/stack/localStack/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './event';

export const startLocalStack = async () => {
// Placeholder for starting local stack logic
console.log('Local stack started');
};