diff --git a/.github/workflows/common-test.yml b/.github/workflows/common-test.yml index 48202802..10030de5 100644 --- a/.github/workflows/common-test.yml +++ b/.github/workflows/common-test.yml @@ -37,7 +37,9 @@ jobs: node-version: ${{ env.node_version }} registry-url: 'https://registry.npmjs.org' - name: Install dependencies - run: npm ci + run: | + node prepareForTest.js cdk-basic + npm i - name: Download build artifact uses: actions/download-artifact@v4 if: ${{ inputs.mode == 'build' }} @@ -84,7 +86,9 @@ jobs: node-version: ${{ env.node_version }} registry-url: 'https://registry.npmjs.org' - name: Install dependencies - run: npm ci + run: | + node prepareForTest.js cdk-esm + npm i - name: Download build artifact uses: actions/download-artifact@v4 if: ${{ inputs.mode == 'build' }} @@ -131,7 +135,9 @@ jobs: node-version: ${{ env.node_version }} registry-url: 'https://registry.npmjs.org' - name: Install dependencies - run: npm ci + run: | + node prepareForTest.js sls-basic + npm i - name: Download build artifact uses: actions/download-artifact@v4 if: ${{ inputs.mode == 'build' }} @@ -179,7 +185,9 @@ jobs: node-version: ${{ env.node_version }} registry-url: 'https://registry.npmjs.org' - name: Install dependencies - run: npm ci + run: | + node prepareForTest.js sls-esbuild-cjs + npm i - name: Download build artifact uses: actions/download-artifact@v4 if: ${{ inputs.mode == 'build' }} @@ -227,7 +235,9 @@ jobs: node-version: ${{ env.node_version }} registry-url: 'https://registry.npmjs.org' - name: Install dependencies - run: npm ci + run: | + node prepareForTest.js sls-esbuild-esm + npm i - name: Download build artifact uses: actions/download-artifact@v4 if: ${{ inputs.mode == 'build' }} @@ -263,6 +273,156 @@ jobs: - name: Test - observability mode run: OBSERVABLE_MODE=true npx vitest --retry 1 test/sls-esbuild-esm.test.ts + test-osls-basic: + runs-on: ubuntu-latest + concurrency: + group: test-osls-basic + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.node_version }} + registry-url: 'https://registry.npmjs.org' + - name: Install dependencies + run: | + node prepareForTest.js osls-basic + npm i + - name: Download build artifact + uses: actions/download-artifact@v4 + if: ${{ inputs.mode == 'build' }} + with: + name: dist + path: dist + - name: Install lambda-live-debugger globally + if: ${{ inputs.mode == 'global' }} + run: | + npm i lambda-live-debugger -g + npm i osls@3.41.0 -g + working-directory: test + - name: Install lambda-live-debugger locally + if: ${{ inputs.mode == 'local' }} + run: | + npm i lambda-live-debugger + working-directory: test + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: eu-west-1 + role-to-assume: ${{ secrets.AWS_ROLE }} + role-session-name: GitHubActions + - name: Destroy + run: npm run destroy + working-directory: test/osls-basic + continue-on-error: true + - name: Deploy + run: npm run deploy + working-directory: test/osls-basic + - name: Test + run: npx vitest --retry 1 test/osls-basic.test.ts + - name: Test - observability mode + run: OBSERVABLE_MODE=true npx vitest --retry 1 test/osls-basic.test.ts + + test-osls-esbuild-cjs: + runs-on: ubuntu-latest + concurrency: + group: test-osls-esbuild-cjs + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.node_version }} + registry-url: 'https://registry.npmjs.org' + - name: Install dependencies + run: | + node prepareForTest.js osls-esbuild-cjs + npm i + - name: Download build artifact + uses: actions/download-artifact@v4 + if: ${{ inputs.mode == 'build' }} + with: + name: dist + path: dist + - name: Install lambda-live-debugger globally + if: ${{ inputs.mode == 'global' }} + run: | + npm i lambda-live-debugger -g + npm i osls@3.41.0 -g + working-directory: test + - name: Install lambda-live-debugger locally + if: ${{ inputs.mode == 'local' }} + run: | + npm i lambda-live-debugger + working-directory: test + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: eu-west-1 + role-to-assume: ${{ secrets.AWS_ROLE }} + role-session-name: GitHubActions + - name: Destroy + run: npm run destroy + working-directory: test/osls-esbuild-cjs + continue-on-error: true + - name: Deploy + run: npm run deploy + working-directory: test/osls-esbuild-cjs + - name: Test + run: npx vitest --retry 1 test/osls-esbuild-cjs.test.ts + - name: Test - observability mode + run: OBSERVABLE_MODE=true npx vitest --retry 1 test/osls-esbuild-cjs.test.ts + + test-osls-esbuild-esm: + runs-on: ubuntu-latest + concurrency: + group: test-osls-esbuild-esm + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.node_version }} + registry-url: 'https://registry.npmjs.org' + - name: Install dependencies + run: | + node prepareForTest.js osls-esbuild-esm + npm i + - name: Download build artifact + uses: actions/download-artifact@v4 + if: ${{ inputs.mode == 'build' }} + with: + name: dist + path: dist + - name: Install lambda-live-debugger globally + if: ${{ inputs.mode == 'global' }} + run: | + npm i lambda-live-debugger -g + npm i osls@3.41.0 -g + working-directory: test + - name: Install lambda-live-debugger locally + if: ${{ inputs.mode == 'local' }} + run: | + npm i lambda-live-debugger + working-directory: test + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: eu-west-1 + role-to-assume: ${{ secrets.AWS_ROLE }} + role-session-name: GitHubActions + - name: Destroy + run: npm run destroy + working-directory: test/osls-esbuild-esm + continue-on-error: true + - name: Deploy + run: npm run deploy + working-directory: test/osls-esbuild-esm + - name: Test + run: npx vitest --retry 1 test/osls-esbuild-esm.test.ts + - name: Test - observability mode + run: OBSERVABLE_MODE=true npx vitest --retry 1 test/osls-esbuild-esm.test.ts + test-sam-basic: runs-on: ubuntu-latest concurrency: @@ -279,7 +439,9 @@ jobs: node-version: ${{ env.node_version }} registry-url: 'https://registry.npmjs.org' - name: Install dependencies - run: npm ci + run: | + node prepareForTest.js sam-basic + npm i - name: Download build artifact uses: actions/download-artifact@v4 if: ${{ inputs.mode == 'build' }} @@ -330,7 +492,9 @@ jobs: node-version: ${{ env.node_version }} registry-url: 'https://registry.npmjs.org' - name: Install dependencies - run: npm ci + run: | + node prepareForTest.js sam-alt + npm i - name: Download build artifact uses: actions/download-artifact@v4 if: ${{ inputs.mode == 'build' }} @@ -377,7 +541,9 @@ jobs: node-version: ${{ env.node_version }} registry-url: 'https://registry.npmjs.org' - name: Install dependencies - run: npm ci + run: | + node prepareForTest.js terraform-basic + npm i - name: Download build artifact uses: actions/download-artifact@v4 if: ${{ inputs.mode == 'build' }} diff --git a/.vscode/launch.json b/.vscode/launch.json index 5bcfd200..f853065e 100755 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -130,6 +130,66 @@ "type": "node", "cwd": "${workspaceRoot}/test/sls-esbuild-esm" }, + { + "name": "LLDebugger - OSLS basic", + "program": "${workspaceRoot}/node_modules/tsx/dist/cli.mjs", + "args": ["../../src/lldebugger.ts", "--stage=test"], + "request": "launch", + "skipFiles": ["/**"], + "console": "integratedTerminal", + "type": "node", + "cwd": "${workspaceRoot}/test/osls-basic" + }, + { + "name": "LLDebugger - OSLS basic - observability", + "program": "${workspaceRoot}/node_modules/tsx/dist/cli.mjs", + "args": ["../../src/lldebugger.ts", "--stage=test", "-o"], + "request": "launch", + "skipFiles": ["/**"], + "console": "integratedTerminal", + "type": "node", + "cwd": "${workspaceRoot}/test/osls-basic" + }, + { + "name": "LLDebugger - OSLS EsBuild CJS", + "program": "${workspaceRoot}/node_modules/tsx/dist/cli.mjs", + "args": ["../../src/lldebugger.ts"], + "request": "launch", + "skipFiles": ["/**"], + "console": "integratedTerminal", + "type": "node", + "cwd": "${workspaceRoot}/test/osls-esbuild-cjs" + }, + { + "name": "LLDebugger - OSLS EsBuild CJS - observability", + "program": "${workspaceRoot}/node_modules/tsx/dist/cli.mjs", + "args": ["../../src/lldebugger.ts", "-o"], + "request": "launch", + "skipFiles": ["/**"], + "console": "integratedTerminal", + "type": "node", + "cwd": "${workspaceRoot}/test/osls-esbuild-cjs" + }, + { + "name": "LLDebugger - OSLS EsBuild ESM", + "program": "${workspaceRoot}/node_modules/tsx/dist/cli.mjs", + "args": ["../../src/lldebugger.ts"], + "request": "launch", + "skipFiles": ["/**"], + "console": "integratedTerminal", + "type": "node", + "cwd": "${workspaceRoot}/test/osls-esbuild-esm" + }, + { + "name": "LLDebugger - OSLS EsBuild ESM - observability", + "program": "${workspaceRoot}/node_modules/tsx/dist/cli.mjs", + "args": ["../../src/lldebugger.ts", "-o"], + "request": "launch", + "skipFiles": ["/**"], + "console": "integratedTerminal", + "type": "node", + "cwd": "${workspaceRoot}/test/osls-esbuild-esm" + }, { "name": "LLDebugger - SAM basic", "program": "${workspaceRoot}/node_modules/tsx/dist/cli.mjs", diff --git a/README.md b/README.md index d4edadd5..d7021c06 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This tool offers similar functionality to [SST](https://sst.dev/) and [Serverles It supports the following frameworks: - AWS CDK v2 -- Serverless Framework v3 (SLS) +- Serverless Framework v3 (SLS) and [`osls` fork](https://github.com/oss-serverless/serverless) - AWS Serverless Application Model (SAM) - Terraform - Any other framework or setup by implementing a simple function in TypeScript diff --git a/package-lock.json b/package-lock.json index 7f005770..ed3cbafe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,10 @@ "test/cdk-config", "test/sls-basic", "test/sls-esbuild", + "test/sls-esbuild-cjs", + "test/osls-basic", + "test/osls-esbuild", + "test/osls-esbuild-cjs", "test/sam-basic", "test/sam-alt", "test/terraform-basic" @@ -78,6 +82,7 @@ "eslint-config-prettier": "^9.1.0", "globals": "^15.9.0", "husky": "^9.1.4", + "osls": "^3.41.0", "prettier": "^3.3.3", "semantic-release": "^23.0.8", "serverless": "^3.38.0", @@ -3436,21 +3441,6 @@ "node": ">=10" } }, - "node_modules/@serverless/dashboard-plugin/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@serverless/dashboard-plugin/node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -9907,6 +9897,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -13990,21 +13995,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/open/node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -14094,6 +14084,228 @@ "node": ">=0.10.0" } }, + "node_modules/osls": { + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/osls/-/osls-3.41.0.tgz", + "integrity": "sha512-GcrMSRYZFLuk60/pYPoF6bSwkLKZw5Fd8Cc+KTYWIDj8GmgY8V5Cb1YJgxJoYO6IbZgxzwXAcozE/MXd5o8wnA==", + "dev": true, + "dependencies": { + "@aws-sdk/client-api-gateway": "^3.588.0", + "@aws-sdk/client-cognito-identity-provider": "^3.588.0", + "@aws-sdk/client-eventbridge": "^3.588.0", + "@aws-sdk/client-iam": "^3.588.0", + "@aws-sdk/client-lambda": "^3.588.0", + "@aws-sdk/client-s3": "^3.588.0", + "@serverless/utils": "^6.13.1", + "ajv": "^8.12.0", + "ajv-formats": "^2.1.1", + "archiver": "^5.3.1", + "aws-sdk": "^2.1404.0", + "bluebird": "^3.7.2", + "cachedir": "^2.3.0", + "chalk": "^4.1.2", + "child-process-ext": "^2.1.1", + "ci-info": "^3.9.0", + "cli-progress-footer": "^2.3.2", + "d": "^1.0.1", + "dayjs": "^1.11.8", + "decompress": "^4.2.1", + "dotenv": "^16.3.1", + "dotenv-expand": "^10.0.0", + "essentials": "^1.2.0", + "ext": "^1.7.0", + "fastest-levenshtein": "^1.0.16", + "filesize": "^10.0.7", + "fs-extra": "^10.1.0", + "get-stdin": "^8.0.0", + "globby": "^11.1.0", + "graceful-fs": "^4.2.11", + "https-proxy-agent": "^5.0.1", + "is-docker": "^2.2.1", + "js-yaml": "^4.1.0", + "json-cycle": "^1.5.0", + "json-refs": "^3.0.15", + "lodash": "^4.17.21", + "memoizee": "^0.4.15", + "micromatch": "^4.0.5", + "node-fetch": "^2.6.11", + "object-hash": "^3.0.0", + "open": "^8.4.2", + "process-utils": "^4.0.0", + "promise-queue": "^2.2.5", + "require-from-string": "^2.0.2", + "semver": "^7.5.3", + "signal-exit": "^3.0.7", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "timers-ext": "^0.1.7", + "type": "^2.7.2", + "untildify": "^4.0.0", + "uuid": "^9.0.0", + "ws": "^7.5.9", + "yaml-ast-parser": "0.0.43" + }, + "bin": { + "serverless": "bin/serverless.js", + "sls": "bin/serverless.js" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/osls-basic": { + "resolved": "test/osls-basic", + "link": true + }, + "node_modules/osls-esbuild": { + "resolved": "test/osls-esbuild-cjs", + "link": true + }, + "node_modules/osls/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/osls/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/osls/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/osls/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/osls/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/osls/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/osls/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/osls/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/osls/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/osls/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/osls/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -15796,21 +16008,6 @@ "node": ">= 6" } }, - "node_modules/serverless/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/serverless/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -16083,6 +16280,10 @@ "resolved": "test/sls-basic", "link": true }, + "node_modules/sls-esbuild": { + "resolved": "test/sls-esbuild-cjs", + "link": true + }, "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -18569,13 +18770,10 @@ "license": "ISC", "dependencies": { "@aws-sdk/client-iam": "^3.577.0", - "@tsconfig/node20": "^20.1.4", - "esbuild": "^0.20.1" + "@tsconfig/node20": "^20.1.4" }, "devDependencies": { "@aws-sdk/client-lambda": "^3.577.0", - "serverless": "^3.38.0", - "serverless-esbuild": "^1.52.1", "typescript": "~5.4.3", "vitest": "^2.0.4" } @@ -18656,6 +18854,29 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "test/osls-basic": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-sts": "^3.577.0" + }, + "devDependencies": { + "osls": "^3.41.0" + } + }, + "test/osls-esbuild-cjs": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-sts": "^3.577.0" + }, + "devDependencies": { + "@tsconfig/node20": "^20.1.4", + "esbuild": "^0.20.2", + "osls": "^3.41.0", + "serverless-esbuild": "^1.52.1" + } + }, "test/sam-alt": { "version": "0.0.1", "dependencies": { @@ -18686,6 +18907,20 @@ "serverless": "^3.38.0" } }, + "test/sls-esbuild-cjs": { + "name": "sls-esbuild", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-sts": "^3.577.0" + }, + "devDependencies": { + "@tsconfig/node20": "^20.1.4", + "esbuild": "^0.20.2", + "serverless": "^3.38.0", + "serverless-esbuild": "^1.52.1" + } + }, "test/terraform-basic": { "version": "1.0.0", "license": "ISC", diff --git a/package.json b/package.json index b42e0728..336a56b0 100755 --- a/package.json +++ b/package.json @@ -60,6 +60,12 @@ "test-sls-esbuild-cjs-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/sls-esbuild-cjs.test.ts", "test-sls-esbuild-esm": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/sls-esbuild-esm.test.ts", "test-sls-esbuild-esm-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/sls-esbuild-esm.test.ts", + "test-osls-basic": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/osls-basic.test.ts", + "test-osls-basic-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/osls-basic.test.ts", + "test-osls-esbuild-cjs": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/osls-esbuild-cjs.test.ts", + "test-osls-esbuild-cjs-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/osls-esbuild-cjs.test.ts", + "test-osls-esbuild-esm": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/osls-esbuild-esm.test.ts", + "test-osls-esbuild-esm-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/osls-esbuild-esm.test.ts", "test-sam-basic": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/sam-basic.test.ts", "test-sam-basic-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/sam-basic.test.ts", "test-sam-alt": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/sam-alt.test.ts", @@ -89,6 +95,7 @@ "prettier": "^3.3.3", "semantic-release": "^23.0.8", "serverless": "^3.38.0", + "osls": "^3.41.0", "serverless-esbuild": "^1.52.1", "tsx": "^4.7.1", "typescript-eslint": "^8.0.0", @@ -141,6 +148,10 @@ "test/cdk-config", "test/sls-basic", "test/sls-esbuild", + "test/sls-esbuild-cjs", + "test/osls-basic", + "test/osls-esbuild", + "test/osls-esbuild-cjs", "test/sam-basic", "test/sam-alt", "test/terraform-basic" diff --git a/prepareForTest.js b/prepareForTest.js new file mode 100644 index 00000000..4a1c84ed --- /dev/null +++ b/prepareForTest.js @@ -0,0 +1,33 @@ +import { readFile, writeFile } from 'fs/promises'; +import { argv } from 'process'; + +/** + * Prepare the package.json file for testing. + * Remove everything that is not needed for testing. + * @param {*} testCase + */ +async function modifyPackageJson(testCase) { + const filePath = 'package.json'; + + const data = await readFile(filePath, 'utf-8'); + const packageJson = JSON.parse(data); + + // Delete scripts and devDependencies nodes + delete packageJson.scripts; + delete packageJson.devDependencies; + + // Replace workspaces node with the test and the test case workspaces + // With this all the necessary npm packages will be installed + packageJson.workspaces = ['test', `test/${testCase}`]; + + await writeFile(filePath, JSON.stringify(packageJson, null, 2), 'utf-8'); + console.log(`Modified ${filePath} successfully!`); +} + +const [testCase] = argv.slice(2); +if (!testCase) { + console.error('Usage: node prepareForTest.js '); + process.exit(1); +} + +void modifyPackageJson(testCase).catch(console.error); diff --git a/src/frameworks/slsFramework.ts b/src/frameworks/slsFramework.ts index d18c69f0..1d559f77 100755 --- a/src/frameworks/slsFramework.ts +++ b/src/frameworks/slsFramework.ts @@ -62,72 +62,38 @@ export class SlsFramework implements IFramework { let resolveVariablesMeta: any; let sources: any; let Serverless: any; + let error1: any | undefined; try { - // lazy load modules - resolveConfigurationPath = ( - await import( - //@ts-ignore - 'serverless/lib/cli/resolve-configuration-path.js' - ) - ).default; - readConfiguration = ( - await import( - //@ts-ignore - 'serverless/lib/configuration/read.js' - ) - ).default; - resolveVariables = ( - await import( - //@ts-ignore - 'serverless/lib/configuration/variables/resolve.js' - ) - ).default; - resolveVariablesMeta = ( - await import( - //@ts-ignore - 'serverless/lib/configuration/variables/resolve-meta.js' - ) - ).default; - const env = await import( - //@ts-ignore - 'serverless/lib/configuration/variables/sources/env.js' - ); - const file = await import( - //@ts-ignore - 'serverless/lib/configuration/variables/sources/file.js' - ); - const opt = await import( - //@ts-ignore - 'serverless/lib/configuration/variables/sources/opt.js' - ); - const self = await import( - //@ts-ignore - 'serverless/lib/configuration/variables/sources/self.js' - ); - const strToBool = await import( - //@ts-ignore - 'serverless/lib/configuration/variables/sources/str-to-bool.js' - ); - const sls = await import( - //@ts-ignores - 'serverless/lib/configuration/variables/sources/instance-dependent/get-sls.js' - ); - - sources = { - env: env.default, - file: file.default, - opt: opt.default, - self: self.default, - strToBool: strToBool.default, - sls: sls.default(), - }; - - Serverless = (await import('serverless')).default; - } catch (error: any) { - Logger.error('Error loading serverless modules', error); + try { + const frameworkFunctions = await loadFramework('serverless'); + resolveConfigurationPath = frameworkFunctions.resolveConfigurationPath; + readConfiguration = frameworkFunctions.readConfiguration; + resolveVariables = frameworkFunctions.resolveVariables; + resolveVariablesMeta = frameworkFunctions.resolveVariablesMeta; + sources = frameworkFunctions.sources; + Serverless = frameworkFunctions.Serverless; + + Logger.verbose(`[SLS] Npm module 'serverless' loaded`); + } catch (error: any) { + Logger.verbose(`[SLS] Failed to load npm module 'serverless'`, error); + + error1 = error; + const frameworkFunctions = await loadFramework('osls'); + resolveConfigurationPath = frameworkFunctions.resolveConfigurationPath; + readConfiguration = frameworkFunctions.readConfiguration; + resolveVariables = frameworkFunctions.resolveVariables; + resolveVariablesMeta = frameworkFunctions.resolveVariablesMeta; + sources = frameworkFunctions.sources; + Serverless = frameworkFunctions.Serverless; + + Logger.verbose(`[SLS] Npm module 'osls' loaded`); + } + } catch (error2: any) { + const error = error1 ?? error2; + Logger.error('Error loading serverless (or osls) module', error); Logger.log( - 'If you are running Lambda Live Debugger from a global installation, install Serverless Framework globally as well. If you are using monorepo, install Serverless Framework also in the project root folder.', + 'If you are running Lambda Live Debugger from a global installation, install Serverless Framework globally as well. If you are using monorepo, install Serverless Framework also in the project root folder. The fork of Serverless Framework https://github.com/oss-serverless/serverless is also supported.', ); throw new Error(`Error loading serverless modules. ${error.message}`, { cause: error, @@ -315,3 +281,73 @@ export class SlsFramework implements IFramework { } export const slsFramework = new SlsFramework(); +async function loadFramework(npmName: string) { + // lazy load modules + const resolveConfigurationPath = ( + await import( + //@ts-ignore + `${npmName}/lib/cli/resolve-configuration-path.js` + ) + ).default; + const readConfiguration = ( + await import( + //@ts-ignore + `${npmName}/lib/configuration/read.js` + ) + ).default; + const resolveVariables = ( + await import( + //@ts-ignore + `${npmName}/lib/configuration/variables/resolve.js` + ) + ).default; + const resolveVariablesMeta = ( + await import( + //@ts-ignore + `${npmName}/lib/configuration/variables/resolve-meta.js` + ) + ).default; + const env = await import( + //@ts-ignore + `${npmName}/lib/configuration/variables/sources/env.js` + ); + const file = await import( + //@ts-ignore + `${npmName}/lib/configuration/variables/sources/file.js` + ); + const opt = await import( + //@ts-ignore + `${npmName}/lib/configuration/variables/sources/opt.js` + ); + const self = await import( + //@ts-ignore + `${npmName}/lib/configuration/variables/sources/self.js` + ); + const strToBool = await import( + //@ts-ignore + `${npmName}/lib/configuration/variables/sources/str-to-bool.js` + ); + const sls = await import( + //@ts-ignores + `${npmName}/lib/configuration/variables/sources/instance-dependent/get-sls.js` + ); + + const sources = { + env: env.default, + file: file.default, + opt: opt.default, + self: self.default, + strToBool: strToBool.default, + sls: sls.default(), + }; + + const Serverless = (await import(npmName)).default; + return { + resolveConfigurationPath, + readConfiguration, + resolveVariablesMeta, + resolveVariables, + sources, + Serverless, + }; +} diff --git a/test/cdk-basic/package.json b/test/cdk-basic/package.json index 50a05a5c..2f141970 100644 --- a/test/cdk-basic/package.json +++ b/test/cdk-basic/package.json @@ -7,7 +7,7 @@ "scripts": { "deploy": "cdk deploy --all -c environment=test --require-approval never --outputs-file cdk-outputs.json", "build": "cdk synth -c environment=test", - "destroy": "cdk destroy -c environment=test --force" + "destroy": "cdk destroy --all -c environment=test --force" }, "devDependencies": { "@types/node": "20.11.30", diff --git a/test/cdk-esm/package.json b/test/cdk-esm/package.json index 788a5d79..c95455a4 100644 --- a/test/cdk-esm/package.json +++ b/test/cdk-esm/package.json @@ -8,7 +8,7 @@ "scripts": { "deploy": "cdk deploy --all -c environment=test --require-approval never --outputs-file cdk-outputs.json", "build": "cdk synth -c environment=test", - "destroy": "cdk destroy -c environment=test --force" + "destroy": "cdk destroy --all -c environment=test --force" }, "devDependencies": { "@tsconfig/node20": "^20.1.4", diff --git a/test/osls-basic.test.ts b/test/osls-basic.test.ts new file mode 100644 index 00000000..60b4f36c --- /dev/null +++ b/test/osls-basic.test.ts @@ -0,0 +1,71 @@ +import { expect, test, describe, beforeAll, afterAll } from 'vitest'; +import { ChildProcess } from 'child_process'; +import { startDebugger } from './utils/startDebugger.js'; +import { expectInfraRemoved } from './utils/expectInfraRemoved.js'; +import { expectInfraDeployed } from './utils/expectInfraDeployed.js'; +import { removeInfra } from './utils/removeInfra.js'; +import { exec } from 'child_process'; +import { promisify } from 'util'; +import { callLambda } from './utils/callLambda.js'; +import { getSamplePayload } from './utils/getSamplePayload.js'; +import { validateLocalResponse } from './utils/validateLocalResponse.js'; +import { getTestProjectFolder } from './utils/getTestProjectFolder.js'; + +export const execAsync = promisify(exec); + +const observableMode = process.env.OBSERVABLE_MODE === 'true'; + +describe('osls-basic', async () => { + const folder = await getTestProjectFolder('osls-basic'); + let lldProcess: ChildProcess | undefined; + + beforeAll(async () => { + if (process.env.CI === 'true' || process.env.RUN_TEST_FROM_CLI === 'true') { + lldProcess = await startDebugger(folder, ['--stage=test']); + } + }); + + afterAll(async () => { + // stop the debugger + lldProcess?.kill(); + }); + + test('check infra', async () => { + const lambdaName = 'lls-osls-basic-test-testJsCommonJs'; + await expectInfraDeployed(lambdaName); + }); + + test('call Lambda - testJsCommonJs', async () => { + const lambdaName = 'lls-osls-basic-test-testJsCommonJs'; + + const payload = getSamplePayload(lambdaName); + const response = await callLambda(lambdaName, payload); + + expect(response.inputEvent).toEqual(payload); + expect(response.runningLocally).toEqual(!observableMode); + if (observableMode) { + await validateLocalResponse(lambdaName, payload); + } + }); + + test('call Lambda - testJsEsModule', async () => { + const lambdaName = 'lls-osls-basic-test-testJsEsModule'; + + const payload = getSamplePayload(lambdaName); + const response = await callLambda(lambdaName, payload); + + expect(response.inputEvent).toEqual(payload); + expect(response.runningLocally).toEqual(!observableMode); + if (observableMode) { + await validateLocalResponse(lambdaName, payload); + } + }); + + test('remove infra', async () => { + if (process.env.CI === 'true' || process.env.RUN_TEST_FROM_CLI === 'true') { + await removeInfra(lldProcess, folder, ['--stage=test']); + const lambdaName = 'lls-osls-basic-test-testJsCommonJs'; + await expectInfraRemoved(lambdaName); + } + }); +}); diff --git a/test/osls-basic/.gitignore b/test/osls-basic/.gitignore new file mode 100644 index 00000000..2b48c8bd --- /dev/null +++ b/test/osls-basic/.gitignore @@ -0,0 +1,6 @@ +# package directories +node_modules +jspm_packages + +# Serverless directories +.serverless \ No newline at end of file diff --git a/test/osls-basic/functions1.yml b/test/osls-basic/functions1.yml new file mode 100644 index 00000000..7226b5a0 --- /dev/null +++ b/test/osls-basic/functions1.yml @@ -0,0 +1,2 @@ +testJsEsModule: + handler: services/testJsEsModule/lambda.lambdaHandler diff --git a/test/osls-basic/functions2.yml b/test/osls-basic/functions2.yml new file mode 100644 index 00000000..52f57d79 --- /dev/null +++ b/test/osls-basic/functions2.yml @@ -0,0 +1,2 @@ +testJsCommonJs: + handler: services/testJsCommonJs/lambda.lambdaHandler diff --git a/test/osls-basic/package.json b/test/osls-basic/package.json new file mode 100644 index 00000000..d2f8a92a --- /dev/null +++ b/test/osls-basic/package.json @@ -0,0 +1,19 @@ +{ + "name": "osls-basic", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "deploy": "serverless deploy --stage=test", + "destroy": "serverless remove --stage=test" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "osls": "^3.41.0" + }, + "dependencies": { + "@aws-sdk/client-sts": "^3.577.0" + } +} diff --git a/test/osls-basic/serverless.yml b/test/osls-basic/serverless.yml new file mode 100644 index 00000000..601cb316 --- /dev/null +++ b/test/osls-basic/serverless.yml @@ -0,0 +1,11 @@ +service: lls-osls-basic +frameworkVersion: '3' + +provider: + name: aws + runtime: nodejs20.x + region: eu-west-1 + +functions: + - ${file(./functions1.yml)} + - ${file(./functions2.yml)} diff --git a/test/osls-basic/services/testJsCommonJs/lambda.js b/test/osls-basic/services/testJsCommonJs/lambda.js new file mode 100755 index 00000000..4e37462d --- /dev/null +++ b/test/osls-basic/services/testJsCommonJs/lambda.js @@ -0,0 +1,40 @@ +const { STSClient, GetCallerIdentityCommand } = require('@aws-sdk/client-sts'); + +const stsClient = new STSClient({}); + +const lambdaHandler = async (event, context) => { + // Check context + const remainingTime = context.getRemainingTimeInMillis(); + if (remainingTime === undefined) { + throw new Error('Remaining time is undefined'); + } + + // check if SDK works + const command = new GetCallerIdentityCommand({}); + const identity = await stsClient.send(command); + + const response = { + inputEvent: event, + accountId: identity.Account, + runningLocally: process.env.IS_LOCAL === 'true', + }; + + if (process.env.IS_LOCAL === 'true') { + const fs = require('fs'); + const path = require('path'); + const filePath = path.join( + '..', + 'local_lambda_responses', + `${context.functionName}.json`, + ); + + fs.writeFileSync(filePath, JSON.stringify(response, null, 2)); + } + + return response; +}; + +// Export the lambda handler if needed, e.g., for unit testing +module.exports = { + lambdaHandler, +}; diff --git a/test/osls-basic/services/testJsCommonJs/package.json b/test/osls-basic/services/testJsCommonJs/package.json new file mode 100644 index 00000000..9a7eb492 --- /dev/null +++ b/test/osls-basic/services/testJsCommonJs/package.json @@ -0,0 +1,8 @@ +{ + "name": "sls-basic-test-js-commonjs", + "version": "1.0.0", + "type": "commonjs", + "dependencies": { + "@aws-sdk/client-sts": "^3.577.0" + } +} diff --git a/test/osls-basic/services/testJsEsModule/lambda.js b/test/osls-basic/services/testJsEsModule/lambda.js new file mode 100755 index 00000000..e3508208 --- /dev/null +++ b/test/osls-basic/services/testJsEsModule/lambda.js @@ -0,0 +1,35 @@ +import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts'; + +const stsClient = new STSClient({}); + +export const lambdaHandler = async (event, context) => { + // check context + const remainingTime = context.getRemainingTimeInMillis(); + if (remainingTime === undefined) { + throw new Error('Remaining time is undefined'); + } + + // check SDK works + const command = new GetCallerIdentityCommand({}); + const identity = await stsClient.send(command); + + const response = { + inputEvent: event, + accountId: identity.Account, + runningLocally: process.env.IS_LOCAL === 'true', + }; + + if (process.env.IS_LOCAL === 'true') { + const fs = await import('fs'); + const path = await import('path'); + const filePath = path.join( + '..', + 'local_lambda_responses', + `${context.functionName}.json`, + ); + + fs.writeFileSync(filePath, JSON.stringify(response, null, 2)); + } + + return response; +}; diff --git a/test/osls-basic/services/testJsEsModule/package.json b/test/osls-basic/services/testJsEsModule/package.json new file mode 100644 index 00000000..97225145 --- /dev/null +++ b/test/osls-basic/services/testJsEsModule/package.json @@ -0,0 +1,8 @@ +{ + "name": "sls-basic-test-js-esmodule", + "version": "1.0.0", + "type": "module", + "dependencies": { + "@aws-sdk/client-sts": "^3.577.0" + } +} diff --git a/test/osls-esbuild-cjs.test.ts b/test/osls-esbuild-cjs.test.ts new file mode 100644 index 00000000..3bf2cca9 --- /dev/null +++ b/test/osls-esbuild-cjs.test.ts @@ -0,0 +1,58 @@ +import { expect, test, describe, beforeAll, afterAll } from 'vitest'; +import { ChildProcess } from 'child_process'; +import { startDebugger } from './utils/startDebugger.js'; +import { expectInfraRemoved } from './utils/expectInfraRemoved.js'; +import { expectInfraDeployed } from './utils/expectInfraDeployed.js'; +import { removeInfra } from './utils/removeInfra.js'; +import { exec } from 'child_process'; +import { promisify } from 'util'; +import { callLambda } from './utils/callLambda.js'; +import { getSamplePayload } from './utils/getSamplePayload.js'; +import { validateLocalResponse } from './utils/validateLocalResponse.js'; +import { getTestProjectFolder } from './utils/getTestProjectFolder.js'; + +export const execAsync = promisify(exec); + +const observableMode = process.env.OBSERVABLE_MODE === 'true'; + +describe('osls-esbuild-cjs', async () => { + const folder = await getTestProjectFolder('osls-esbuild-cjs'); + let lldProcess: ChildProcess | undefined; + + beforeAll(async () => { + if (process.env.CI === 'true' || process.env.RUN_TEST_FROM_CLI === 'true') { + lldProcess = await startDebugger(folder); + } + }); + + afterAll(async () => { + // stop the debugger + lldProcess?.kill(); + }); + + test('check infra', async () => { + const lambdaName = 'lls-osls-esbuild-cjs-test-testTsCommonJs'; + await expectInfraDeployed(lambdaName); + }); + + test('call Lambda - testTsCommonJs', async () => { + const lambdaName = 'lls-osls-esbuild-cjs-test-testTsCommonJs'; + + const payload = getSamplePayload(lambdaName); + const response = await callLambda(lambdaName, payload); + + expect(response.inputEvent).toEqual(payload); + expect(response.runningLocally).toEqual(!observableMode); + if (observableMode) { + await validateLocalResponse(lambdaName, payload); + } + }); + + test('remove infra', async () => { + if (process.env.CI === 'true' || process.env.RUN_TEST_FROM_CLI === 'true') { + await removeInfra(lldProcess, folder); + const lambdaName = 'lls-osls-esbuild-cjs-test-testTsCommonJs'; + await expectInfraRemoved(lambdaName); + } + }); +}); diff --git a/test/osls-esbuild-cjs/.gitignore b/test/osls-esbuild-cjs/.gitignore new file mode 100644 index 00000000..2b48c8bd --- /dev/null +++ b/test/osls-esbuild-cjs/.gitignore @@ -0,0 +1,6 @@ +# package directories +node_modules +jspm_packages + +# Serverless directories +.serverless \ No newline at end of file diff --git a/test/osls-esbuild-cjs/package.json b/test/osls-esbuild-cjs/package.json new file mode 100644 index 00000000..44e909f2 --- /dev/null +++ b/test/osls-esbuild-cjs/package.json @@ -0,0 +1,23 @@ +{ + "name": "osls-esbuild", + "version": "1.0.0", + "description": "", + "type": "module", + "main": "index.js", + "scripts": { + "deploy": "serverless deploy", + "destroy": "serverless remove" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "esbuild": "^0.20.2", + "osls": "^3.41.0", + "serverless-esbuild": "^1.52.1", + "@tsconfig/node20": "^20.1.4" + }, + "dependencies": { + "@aws-sdk/client-sts": "^3.577.0" + } +} diff --git a/test/osls-esbuild-cjs/serverless.yml b/test/osls-esbuild-cjs/serverless.yml new file mode 100644 index 00000000..718a6fcf --- /dev/null +++ b/test/osls-esbuild-cjs/serverless.yml @@ -0,0 +1,20 @@ +service: lls-osls-esbuild-cjs +frameworkVersion: '3' + +plugins: + - serverless-esbuild + +provider: + name: aws + runtime: nodejs20.x + region: eu-west-1 + stage: test + +custom: + esbuild: + bundle: true + minify: false + +functions: + testTsCommonJs: + handler: services/testTsCommonJs/lambda.lambdaHandler diff --git a/test/osls-esbuild-cjs/services/testTsCommonJs/lambda.ts b/test/osls-esbuild-cjs/services/testTsCommonJs/lambda.ts new file mode 100755 index 00000000..70bd2e6e --- /dev/null +++ b/test/osls-esbuild-cjs/services/testTsCommonJs/lambda.ts @@ -0,0 +1,36 @@ +import { Handler } from 'aws-lambda'; +import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts'; + +const stsClient = new STSClient({}); + +export const lambdaHandler: Handler = async (event, context) => { + // check context + const remainingTime = context.getRemainingTimeInMillis(); + if (remainingTime === undefined) { + throw new Error('Remaining time is undefined'); + } + + // check SDK works + const command = new GetCallerIdentityCommand({}); + const identity = await stsClient.send(command); + + const response = { + inputEvent: event, + accountId: identity.Account, + runningLocally: process.env.IS_LOCAL === 'true', + }; + + if (process.env.IS_LOCAL === 'true') { + const fs = await import('fs'); + const path = await import('path'); + const filePath = path.join( + '..', + 'local_lambda_responses', + `${context.functionName}.json`, + ); + + fs.writeFileSync(filePath, JSON.stringify(response, null, 2)); + } + + return response; +}; diff --git a/test/osls-esbuild-cjs/services/testTsCommonJs/package.json b/test/osls-esbuild-cjs/services/testTsCommonJs/package.json new file mode 100644 index 00000000..df873a2f --- /dev/null +++ b/test/osls-esbuild-cjs/services/testTsCommonJs/package.json @@ -0,0 +1,8 @@ +{ + "name": "sls-esbuild-test-ts-commonjs", + "version": "1.0.0", + "type": "commonjs", + "dependencies": { + "@aws-sdk/client-sts": "^3.577.0" + } +} diff --git a/test/osls-esbuild-cjs/services/testTsCommonJs/tsconfig.json b/test/osls-esbuild-cjs/services/testTsCommonJs/tsconfig.json new file mode 100755 index 00000000..f48ef0ec --- /dev/null +++ b/test/osls-esbuild-cjs/services/testTsCommonJs/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "compilerOptions": { + "moduleResolution": "node", + "module": "CommonJS" + } +} diff --git a/test/osls-esbuild-esm.test.ts b/test/osls-esbuild-esm.test.ts new file mode 100644 index 00000000..5677e63f --- /dev/null +++ b/test/osls-esbuild-esm.test.ts @@ -0,0 +1,58 @@ +import { expect, test, describe, beforeAll, afterAll } from 'vitest'; +import { ChildProcess } from 'child_process'; +import { startDebugger } from './utils/startDebugger.js'; +import { expectInfraRemoved } from './utils/expectInfraRemoved.js'; +import { expectInfraDeployed } from './utils/expectInfraDeployed.js'; +import { removeInfra } from './utils/removeInfra.js'; +import { exec } from 'child_process'; +import { promisify } from 'util'; +import { callLambda } from './utils/callLambda.js'; +import { getSamplePayload } from './utils/getSamplePayload.js'; +import { validateLocalResponse } from './utils/validateLocalResponse.js'; +import { getTestProjectFolder } from './utils/getTestProjectFolder.js'; + +export const execAsync = promisify(exec); + +const observableMode = process.env.OBSERVABLE_MODE === 'true'; + +describe('osls-esbuild-esm', async () => { + const folder = await getTestProjectFolder('osls-esbuild-esm'); + let lldProcess: ChildProcess | undefined; + + beforeAll(async () => { + if (process.env.CI === 'true' || process.env.RUN_TEST_FROM_CLI === 'true') { + lldProcess = await startDebugger(folder); + } + }); + + afterAll(async () => { + // stop the debugger + lldProcess?.kill(); + }); + + test('check infra', async () => { + const lambdaName = 'lls-osls-esbuild-esm-test-testTsEsModule'; + await expectInfraDeployed(lambdaName); + }); + + test('call Lambda - testTsEsModule', async () => { + const lambdaName = 'lls-osls-esbuild-esm-test-testTsEsModule'; + + const payload = getSamplePayload(lambdaName); + const response = await callLambda(lambdaName, payload); + + expect(response.inputEvent).toEqual(payload); + expect(response.runningLocally).toEqual(!observableMode); + if (observableMode) { + await validateLocalResponse(lambdaName, payload); + } + }); + + test('remove infra', async () => { + if (process.env.CI === 'true' || process.env.RUN_TEST_FROM_CLI === 'true') { + await removeInfra(lldProcess, folder); + const lambdaName = 'lls-osls-esbuild-esm-test-testTsEsModule'; + await expectInfraRemoved(lambdaName); + } + }); +}); diff --git a/test/osls-esbuild-esm/.gitignore b/test/osls-esbuild-esm/.gitignore new file mode 100644 index 00000000..2b48c8bd --- /dev/null +++ b/test/osls-esbuild-esm/.gitignore @@ -0,0 +1,6 @@ +# package directories +node_modules +jspm_packages + +# Serverless directories +.serverless \ No newline at end of file diff --git a/test/osls-esbuild-esm/package.json b/test/osls-esbuild-esm/package.json new file mode 100644 index 00000000..5d512fa4 --- /dev/null +++ b/test/osls-esbuild-esm/package.json @@ -0,0 +1,24 @@ +{ + "name": "osls-esbuild", + "version": "1.0.0", + "description": "", + "type": "module", + "main": "index.js", + "scripts": { + "deploy": "serverless deploy", + "destroy": "serverless remove" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "esbuild": "^0.20.2", + "osls": "^3.41.0", + "serverless-esbuild": "^1.52.1", + "@types/aws-lambda": "^8.10.137" + }, + "dependencies": { + "@tsconfig/node20": "^20.1.4", + "@aws-sdk/client-sts": "^3.577.0" + } +} diff --git a/test/osls-esbuild-esm/serverless.yml b/test/osls-esbuild-esm/serverless.yml new file mode 100644 index 00000000..cd96d6d2 --- /dev/null +++ b/test/osls-esbuild-esm/serverless.yml @@ -0,0 +1,24 @@ +service: lls-osls-esbuild-esm +frameworkVersion: '3' + +plugins: + - serverless-esbuild + +provider: + name: aws + runtime: nodejs20.x + region: eu-west-1 + stage: test + +custom: + esbuild: + bundle: true + minify: true + format: esm + outputFileExtension: .mjs + banner: + js: "import { createRequire } from 'module';const require = createRequire(import.meta.url);" + +functions: + testTsEsModule: + handler: services/testTsEsModule/lambda.lambdaHandler diff --git a/test/osls-esbuild-esm/services/testTsEsModule/lambda.ts b/test/osls-esbuild-esm/services/testTsEsModule/lambda.ts new file mode 100755 index 00000000..5e17de6c --- /dev/null +++ b/test/osls-esbuild-esm/services/testTsEsModule/lambda.ts @@ -0,0 +1,35 @@ +import { Handler } from 'aws-lambda'; +import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts'; + +const stsClient = new STSClient({}); + +export const lambdaHandler: Handler = async (event, context) => { + // check context + const remainingTime = context.getRemainingTimeInMillis(); + if (remainingTime === undefined) { + throw new Error('Remaining time is undefined'); + } + + // check SDK works + const command = new GetCallerIdentityCommand({}); + const identity = await stsClient.send(command); + + const response = { + inputEvent: event, + accountId: identity.Account, + runningLocally: process.env.IS_LOCAL === 'true', + }; + + if (process.env.IS_LOCAL === 'true') { + const fs = await import('fs'); + const path = await import('path'); + const filePath = path.join( + '..', + 'local_lambda_responses', + `${context.functionName}.json`, + ); + fs.writeFileSync(filePath, JSON.stringify(response, null, 2)); + } + + return response; +}; diff --git a/test/osls-esbuild-esm/services/testTsEsModule/package.json b/test/osls-esbuild-esm/services/testTsEsModule/package.json new file mode 100644 index 00000000..c2b49e2b --- /dev/null +++ b/test/osls-esbuild-esm/services/testTsEsModule/package.json @@ -0,0 +1,11 @@ +{ + "name": "sls-esbuild-test-ts-esmodule", + "version": "1.0.0", + "type": "module", + "devDependencies": { + "@tsconfig/node20": "^20.1.4" + }, + "dependencies": { + "@aws-sdk/client-sts": "^3.577.0" + } +} diff --git a/test/osls-esbuild-esm/services/testTsEsModule/tsconfig.json b/test/osls-esbuild-esm/services/testTsEsModule/tsconfig.json new file mode 100755 index 00000000..fca5c83e --- /dev/null +++ b/test/osls-esbuild-esm/services/testTsEsModule/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node" + } +} diff --git a/test/osls-esbuild-esm/tsconfig.json b/test/osls-esbuild-esm/tsconfig.json new file mode 100755 index 00000000..fca5c83e --- /dev/null +++ b/test/osls-esbuild-esm/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node" + } +} diff --git a/test/package.json b/test/package.json index 05695aa1..6683a679 100644 --- a/test/package.json +++ b/test/package.json @@ -11,12 +11,9 @@ "devDependencies": { "@aws-sdk/client-lambda": "^3.577.0", "vitest": "^2.0.4", - "serverless": "^3.38.0", - "serverless-esbuild": "^1.52.1", "typescript": "~5.4.3" }, "dependencies": { - "esbuild": "^0.20.1", "@aws-sdk/client-iam": "^3.577.0", "@tsconfig/node20": "^20.1.4" }