From 5d37e4c75d51b5811f8988864f5f7222c87a5fab Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Thu, 22 Aug 2024 17:38:24 -0400 Subject: [PATCH] Add Support for ESM Actions (#90) This PR converts `local-action` to ESM, as well as adds support for JavaScript/TypeScript actions written in ESM syntax. This is accomplished by replacing proxyquire with quibble, which allows for stubbing out both CommonJS and ESM packages with less issues. --- .eslintignore | 4 + .eslintrc.yml | 1 + .node-version | 2 +- __fixtures__/core.ts | 13 + .../javascript-esm/no-import/.env.fixture | 2 + .../javascript-esm/no-import/action.yml | 16 + .../javascript-esm/no-import/package.json | 13 + .../javascript-esm/no-import/src/index.js | 3 + .../javascript-esm/no-import/src/main.js | 7 + .../javascript-esm/success/.env.fixture | 2 + .../success/action.yml} | 4 +- .../javascript-esm/success/package.json | 13 + .../javascript-esm/success/src/index.js | 3 + .../javascript-esm/success/src/main.js | 13 + __fixtures__/javascript/failure/.env.fixture | 8 - __fixtures__/javascript/failure/action.yml | 6 - __fixtures__/javascript/failure/src/index.js | 7 - __fixtures__/javascript/failure/src/main.js | 15 - .../javascript/no-import/.env.fixture | 10 +- __fixtures__/javascript/no-import/action.yml | 2 +- .../javascript/no-import/package.json | 12 + .../javascript/no-import/src/index.js | 4 - __fixtures__/javascript/no-import/src/main.js | 2 - __fixtures__/javascript/success/.env.fixture | 11 +- __fixtures__/javascript/success/action.yml | 2 +- __fixtures__/javascript/success/package.json | 12 + __fixtures__/javascript/success/src/index.js | 4 - __fixtures__/javascript/success/src/main.js | 14 +- __fixtures__/tsconfig-paths.ts | 4 + .../typescript-esm/no-import/.env.fixture | 2 + .../typescript-esm/no-import/action.yml | 17 + .../typescript-esm/no-import/package.json | 17 + .../typescript-esm/no-import/src/index.ts | 3 + .../typescript-esm/no-import/src/main.ts | 3 + .../typescript-esm/no-import/tsconfig.json | 25 + .../typescript-esm/success/.env.fixture | 2 + .../typescript-esm/success/action.yml | 17 + .../typescript-esm/success/package.json | 17 + .../typescript-esm/success/src/index.ts | 3 + .../typescript-esm/success/src/main.ts | 9 + .../typescript-esm/success/tsconfig.json | 25 + __fixtures__/typescript/failure/.env.fixture | 8 - __fixtures__/typescript/failure/action.yml | 6 - __fixtures__/typescript/failure/src/index.ts | 5 - __fixtures__/typescript/failure/src/main.ts | 8 - .../typescript/no-import/.env.fixture | 10 +- __fixtures__/typescript/no-import/action.yml | 2 +- .../typescript/no-import/package.json | 15 + .../typescript/no-import/src/index.ts | 2 - .../typescript/no-import/tsconfig.json | 20 + .../typescript/success-yaml/.env.fixture | 11 - .../typescript/success-yaml/src/index.ts | 5 - .../typescript/success-yaml/src/main.ts | 12 - __fixtures__/typescript/success/.env.fixture | 11 +- __fixtures__/typescript/success/action.yml | 2 +- .../typescript/success/package-lock.json | 103 ++++ __fixtures__/typescript/success/package.json | 15 + __fixtures__/typescript/success/src/index.ts | 2 - __fixtures__/typescript/success/src/main.ts | 5 +- __fixtures__/typescript/success/tsconfig.json | 20 + __mocks__/@actions/core.ts | 67 --- __mocks__/tsconfig-paths.ts | 2 - __tests__/bootstrap.test.ts | 53 -- __tests__/command.test.ts | 95 +--- __tests__/commands/run.test.ts | 118 +++-- __tests__/index.test.ts | 42 +- __tests__/stubs/core-stubs.test.ts | 124 +++-- __tests__/stubs/env-stubs.test.ts | 17 +- __tests__/stubs/summary-stubs.test.ts | 67 +-- __tests__/utils/output.test.ts | 19 +- bin/{local-action => local-action.js} | 18 +- jest.config.ts | 20 +- package-lock.json | 498 +++++++++++------- package.json | 17 +- src/bootstrap.js | 43 -- src/bootstrap.mts | 48 ++ src/command.ts | 11 +- src/commands/run.ts | 87 ++- src/index.ts | 6 +- src/stubs/core-stubs.ts | 48 +- src/stubs/env-stubs.ts | 2 +- src/stubs/summary-stubs.ts | 4 +- src/types/quibble.d.ts | 5 + src/utils/package.ts | 18 + tsconfig.base.json | 29 + tsconfig.eslint.json | 9 +- tsconfig.json | 20 +- 87 files changed, 1202 insertions(+), 896 deletions(-) create mode 100644 __fixtures__/core.ts create mode 100644 __fixtures__/javascript-esm/no-import/.env.fixture create mode 100644 __fixtures__/javascript-esm/no-import/action.yml create mode 100644 __fixtures__/javascript-esm/no-import/package.json create mode 100644 __fixtures__/javascript-esm/no-import/src/index.js create mode 100644 __fixtures__/javascript-esm/no-import/src/main.js create mode 100644 __fixtures__/javascript-esm/success/.env.fixture rename __fixtures__/{typescript/success-yaml/action.yaml => javascript-esm/success/action.yml} (78%) create mode 100644 __fixtures__/javascript-esm/success/package.json create mode 100644 __fixtures__/javascript-esm/success/src/index.js create mode 100644 __fixtures__/javascript-esm/success/src/main.js delete mode 100644 __fixtures__/javascript/failure/.env.fixture delete mode 100644 __fixtures__/javascript/failure/action.yml delete mode 100644 __fixtures__/javascript/failure/src/index.js delete mode 100644 __fixtures__/javascript/failure/src/main.js create mode 100644 __fixtures__/javascript/no-import/package.json create mode 100644 __fixtures__/javascript/success/package.json create mode 100644 __fixtures__/tsconfig-paths.ts create mode 100644 __fixtures__/typescript-esm/no-import/.env.fixture create mode 100644 __fixtures__/typescript-esm/no-import/action.yml create mode 100644 __fixtures__/typescript-esm/no-import/package.json create mode 100644 __fixtures__/typescript-esm/no-import/src/index.ts create mode 100644 __fixtures__/typescript-esm/no-import/src/main.ts create mode 100644 __fixtures__/typescript-esm/no-import/tsconfig.json create mode 100644 __fixtures__/typescript-esm/success/.env.fixture create mode 100644 __fixtures__/typescript-esm/success/action.yml create mode 100644 __fixtures__/typescript-esm/success/package.json create mode 100644 __fixtures__/typescript-esm/success/src/index.ts create mode 100644 __fixtures__/typescript-esm/success/src/main.ts create mode 100644 __fixtures__/typescript-esm/success/tsconfig.json delete mode 100644 __fixtures__/typescript/failure/.env.fixture delete mode 100644 __fixtures__/typescript/failure/action.yml delete mode 100644 __fixtures__/typescript/failure/src/index.ts delete mode 100644 __fixtures__/typescript/failure/src/main.ts create mode 100644 __fixtures__/typescript/no-import/package.json create mode 100644 __fixtures__/typescript/no-import/tsconfig.json delete mode 100644 __fixtures__/typescript/success-yaml/.env.fixture delete mode 100644 __fixtures__/typescript/success-yaml/src/index.ts delete mode 100644 __fixtures__/typescript/success-yaml/src/main.ts create mode 100644 __fixtures__/typescript/success/package-lock.json create mode 100644 __fixtures__/typescript/success/package.json create mode 100644 __fixtures__/typescript/success/tsconfig.json delete mode 100644 __mocks__/@actions/core.ts delete mode 100644 __mocks__/tsconfig-paths.ts delete mode 100644 __tests__/bootstrap.test.ts rename bin/{local-action => local-action.js} (80%) delete mode 100644 src/bootstrap.js create mode 100644 src/bootstrap.mts create mode 100644 src/types/quibble.d.ts create mode 100644 src/utils/package.ts create mode 100644 tsconfig.base.json diff --git a/.eslintignore b/.eslintignore index 05c92ed..4a1bf5f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,7 @@ +__fixtures__/javascript +__fixtures__/javascript-esm +__fixtures__/typescript +__fixtures__/typescript-esm .github/ .vscode/ badges/ diff --git a/.eslintrc.yml b/.eslintrc.yml index d00cef2..99c66da 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -38,6 +38,7 @@ rules: 'camelcase': 'off', 'eslint-comments/no-use': 'off', 'i18n-text/no-en': 'off', + 'import/no-namespace': 'off', 'no-console': 'off', 'no-unused-vars': 'off' } diff --git a/.node-version b/.node-version index 9944ce6..7cc2069 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -21.6.2 +20.5.1 diff --git a/__fixtures__/core.ts b/__fixtures__/core.ts new file mode 100644 index 0000000..dd8793f --- /dev/null +++ b/__fixtures__/core.ts @@ -0,0 +1,13 @@ +import { jest } from '@jest/globals' + +export const debug = jest.fn().mockImplementation(() => {}) +export const error = jest.fn().mockImplementation(() => {}) +export const info = jest.fn().mockImplementation(() => {}) +export const getInput = jest.fn().mockImplementation(() => {}) +export const setOutput = jest.fn().mockImplementation(() => {}) +export const setFailed = jest.fn().mockImplementation(() => {}) +export const warning = jest.fn().mockImplementation(() => {}) +export const summary = { + addRaw: jest.fn().mockImplementation(() => {}), + write: jest.fn().mockImplementation(() => {}) +} diff --git a/__fixtures__/javascript-esm/no-import/.env.fixture b/__fixtures__/javascript-esm/no-import/.env.fixture new file mode 100644 index 0000000..bd69b49 --- /dev/null +++ b/__fixtures__/javascript-esm/no-import/.env.fixture @@ -0,0 +1,2 @@ +ACTIONS_STEP_DEBUG=false +INPUT_MILLISECONDS=2400 diff --git a/__fixtures__/javascript-esm/no-import/action.yml b/__fixtures__/javascript-esm/no-import/action.yml new file mode 100644 index 0000000..8f24325 --- /dev/null +++ b/__fixtures__/javascript-esm/no-import/action.yml @@ -0,0 +1,16 @@ +name: JavaScript (No Import) +description: This action doesn't import any dependencies + +inputs: + myInput: + description: An input + required: true + default: value + +outputs: + myOutput: + description: An output + +runs: + using: node20 + main: dist/index.js diff --git a/__fixtures__/javascript-esm/no-import/package.json b/__fixtures__/javascript-esm/no-import/package.json new file mode 100644 index 0000000..014638e --- /dev/null +++ b/__fixtures__/javascript-esm/no-import/package.json @@ -0,0 +1,13 @@ +{ + "name": "javascript-action", + "description": "GitHub Actions JavaScript template", + "version": "0.0.0", + "type": "module", + "engines": { + "node": ">=20" + }, + "dependencies": { + "@actions/core": "^1.10.1" + }, + "devDependencies": {} +} diff --git a/__fixtures__/javascript-esm/no-import/src/index.js b/__fixtures__/javascript-esm/no-import/src/index.js new file mode 100644 index 0000000..2320541 --- /dev/null +++ b/__fixtures__/javascript-esm/no-import/src/index.js @@ -0,0 +1,3 @@ +const { run } = require('./main') + +run() diff --git a/__fixtures__/javascript-esm/no-import/src/main.js b/__fixtures__/javascript-esm/no-import/src/main.js new file mode 100644 index 0000000..932a702 --- /dev/null +++ b/__fixtures__/javascript-esm/no-import/src/main.js @@ -0,0 +1,7 @@ +async function run() { + return Promise.resolve() +} + +module.exports = { + run +} diff --git a/__fixtures__/javascript-esm/success/.env.fixture b/__fixtures__/javascript-esm/success/.env.fixture new file mode 100644 index 0000000..bd69b49 --- /dev/null +++ b/__fixtures__/javascript-esm/success/.env.fixture @@ -0,0 +1,2 @@ +ACTIONS_STEP_DEBUG=false +INPUT_MILLISECONDS=2400 diff --git a/__fixtures__/typescript/success-yaml/action.yaml b/__fixtures__/javascript-esm/success/action.yml similarity index 78% rename from __fixtures__/typescript/success-yaml/action.yaml rename to __fixtures__/javascript-esm/success/action.yml index 3c183c0..3dd209c 100644 --- a/__fixtures__/typescript/success-yaml/action.yaml +++ b/__fixtures__/javascript-esm/success/action.yml @@ -1,11 +1,11 @@ -name: TypeScript (Success) +name: JavaScript (Success) description: This action returns without error inputs: myInput: description: An input required: true - default: 'default value' + default: value outputs: myOutput: diff --git a/__fixtures__/javascript-esm/success/package.json b/__fixtures__/javascript-esm/success/package.json new file mode 100644 index 0000000..014638e --- /dev/null +++ b/__fixtures__/javascript-esm/success/package.json @@ -0,0 +1,13 @@ +{ + "name": "javascript-action", + "description": "GitHub Actions JavaScript template", + "version": "0.0.0", + "type": "module", + "engines": { + "node": ">=20" + }, + "dependencies": { + "@actions/core": "^1.10.1" + }, + "devDependencies": {} +} diff --git a/__fixtures__/javascript-esm/success/src/index.js b/__fixtures__/javascript-esm/success/src/index.js new file mode 100644 index 0000000..2320541 --- /dev/null +++ b/__fixtures__/javascript-esm/success/src/index.js @@ -0,0 +1,3 @@ +const { run } = require('./main') + +run() diff --git a/__fixtures__/javascript-esm/success/src/main.js b/__fixtures__/javascript-esm/success/src/main.js new file mode 100644 index 0000000..c8b0755 --- /dev/null +++ b/__fixtures__/javascript-esm/success/src/main.js @@ -0,0 +1,13 @@ +const { getInput, info, setOutput } = require('@actions/core') + +async function run() { + const myInput = getInput('myInput') + + setOutput('myOutput', myInput) + + info('JavaScript Action Succeeded!') +} + +module.exports = { + run +} diff --git a/__fixtures__/javascript/failure/.env.fixture b/__fixtures__/javascript/failure/.env.fixture deleted file mode 100644 index 1a3d037..0000000 --- a/__fixtures__/javascript/failure/.env.fixture +++ /dev/null @@ -1,8 +0,0 @@ -# Do not commit your actual .env file to Git! This may contain secrets or other -# private information. - -# GitHub Actions inputs should follow `INPUT_` format (case-insensitive). -INPUT_milliseconds=2400 - -# Enable/disable step debug logs -ACTIONS_STEP_DEBUG=false \ No newline at end of file diff --git a/__fixtures__/javascript/failure/action.yml b/__fixtures__/javascript/failure/action.yml deleted file mode 100644 index bd1f68c..0000000 --- a/__fixtures__/javascript/failure/action.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: JavaScript (Failing) -description: This action sets a failure status - -runs: - using: node20 - main: dist/index.js diff --git a/__fixtures__/javascript/failure/src/index.js b/__fixtures__/javascript/failure/src/index.js deleted file mode 100644 index 9fdf848..0000000 --- a/__fixtures__/javascript/failure/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable import/no-commonjs */ - -const { run } = require('./main') - -run() diff --git a/__fixtures__/javascript/failure/src/main.js b/__fixtures__/javascript/failure/src/main.js deleted file mode 100644 index 8243490..0000000 --- a/__fixtures__/javascript/failure/src/main.js +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable import/no-commonjs */ - -const core = require('@actions/core') - -async function run() { - core.summary.addRaw('JavaScript Action Failed!') - await core.summary.write() - - core.setFailed('JavaScript Action Failed!') -} - -module.exports = { - run -} diff --git a/__fixtures__/javascript/no-import/.env.fixture b/__fixtures__/javascript/no-import/.env.fixture index 1a3d037..bd69b49 100644 --- a/__fixtures__/javascript/no-import/.env.fixture +++ b/__fixtures__/javascript/no-import/.env.fixture @@ -1,8 +1,2 @@ -# Do not commit your actual .env file to Git! This may contain secrets or other -# private information. - -# GitHub Actions inputs should follow `INPUT_` format (case-insensitive). -INPUT_milliseconds=2400 - -# Enable/disable step debug logs -ACTIONS_STEP_DEBUG=false \ No newline at end of file +ACTIONS_STEP_DEBUG=false +INPUT_MILLISECONDS=2400 diff --git a/__fixtures__/javascript/no-import/action.yml b/__fixtures__/javascript/no-import/action.yml index 4328448..8f24325 100644 --- a/__fixtures__/javascript/no-import/action.yml +++ b/__fixtures__/javascript/no-import/action.yml @@ -5,7 +5,7 @@ inputs: myInput: description: An input required: true - default: 'default value' + default: value outputs: myOutput: diff --git a/__fixtures__/javascript/no-import/package.json b/__fixtures__/javascript/no-import/package.json new file mode 100644 index 0000000..903ee2a --- /dev/null +++ b/__fixtures__/javascript/no-import/package.json @@ -0,0 +1,12 @@ +{ + "name": "javascript-action", + "description": "GitHub Actions JavaScript template", + "version": "0.0.0", + "engines": { + "node": ">=20" + }, + "dependencies": { + "@actions/core": "^1.10.1" + }, + "devDependencies": {} +} diff --git a/__fixtures__/javascript/no-import/src/index.js b/__fixtures__/javascript/no-import/src/index.js index 9fdf848..2320541 100644 --- a/__fixtures__/javascript/no-import/src/index.js +++ b/__fixtures__/javascript/no-import/src/index.js @@ -1,7 +1,3 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable import/no-commonjs */ - const { run } = require('./main') run() diff --git a/__fixtures__/javascript/no-import/src/main.js b/__fixtures__/javascript/no-import/src/main.js index f80e0d2..932a702 100644 --- a/__fixtures__/javascript/no-import/src/main.js +++ b/__fixtures__/javascript/no-import/src/main.js @@ -1,5 +1,3 @@ -/* eslint-disable import/no-commonjs */ - async function run() { return Promise.resolve() } diff --git a/__fixtures__/javascript/success/.env.fixture b/__fixtures__/javascript/success/.env.fixture index f1b3926..bd69b49 100644 --- a/__fixtures__/javascript/success/.env.fixture +++ b/__fixtures__/javascript/success/.env.fixture @@ -1,11 +1,2 @@ -# Do not commit your actual .env file to Git! This may contain secrets or other -# private information. - -# GitHub Actions inputs should follow `INPUT_` format (case-insensitive). -INPUT_milliseconds=2400 - -# Enable/disable step debug logs ACTIONS_STEP_DEBUG=false - -# Step summary output location -GITHUB_STEP_SUMMARY='summary.md' \ No newline at end of file +INPUT_MILLISECONDS=2400 diff --git a/__fixtures__/javascript/success/action.yml b/__fixtures__/javascript/success/action.yml index 8cf184e..3dd209c 100644 --- a/__fixtures__/javascript/success/action.yml +++ b/__fixtures__/javascript/success/action.yml @@ -5,7 +5,7 @@ inputs: myInput: description: An input required: true - default: 'default value' + default: value outputs: myOutput: diff --git a/__fixtures__/javascript/success/package.json b/__fixtures__/javascript/success/package.json new file mode 100644 index 0000000..903ee2a --- /dev/null +++ b/__fixtures__/javascript/success/package.json @@ -0,0 +1,12 @@ +{ + "name": "javascript-action", + "description": "GitHub Actions JavaScript template", + "version": "0.0.0", + "engines": { + "node": ">=20" + }, + "dependencies": { + "@actions/core": "^1.10.1" + }, + "devDependencies": {} +} diff --git a/__fixtures__/javascript/success/src/index.js b/__fixtures__/javascript/success/src/index.js index 9fdf848..2320541 100644 --- a/__fixtures__/javascript/success/src/index.js +++ b/__fixtures__/javascript/success/src/index.js @@ -1,7 +1,3 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable import/no-commonjs */ - const { run } = require('./main') run() diff --git a/__fixtures__/javascript/success/src/main.js b/__fixtures__/javascript/success/src/main.js index 975d152..c8b0755 100644 --- a/__fixtures__/javascript/success/src/main.js +++ b/__fixtures__/javascript/success/src/main.js @@ -1,17 +1,11 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable import/no-commonjs */ - -const core = require('@actions/core') +const { getInput, info, setOutput } = require('@actions/core') async function run() { - const myInput = core.getInput('myInput') - - core.setOutput('myOutput', myInput) + const myInput = getInput('myInput') - core.summary.addRaw('JavaScript Action Succeeded!') - await core.summary.write() + setOutput('myOutput', myInput) - core.info('JavaScript Action Succeeded!') + info('JavaScript Action Succeeded!') } module.exports = { diff --git a/__fixtures__/tsconfig-paths.ts b/__fixtures__/tsconfig-paths.ts new file mode 100644 index 0000000..8de1e1c --- /dev/null +++ b/__fixtures__/tsconfig-paths.ts @@ -0,0 +1,4 @@ +import { jest } from '@jest/globals' + +export const loadConfig = jest.fn().mockImplementation(() => {}) +export const register = jest.fn().mockImplementation(() => {}) diff --git a/__fixtures__/typescript-esm/no-import/.env.fixture b/__fixtures__/typescript-esm/no-import/.env.fixture new file mode 100644 index 0000000..bd69b49 --- /dev/null +++ b/__fixtures__/typescript-esm/no-import/.env.fixture @@ -0,0 +1,2 @@ +ACTIONS_STEP_DEBUG=false +INPUT_MILLISECONDS=2400 diff --git a/__fixtures__/typescript-esm/no-import/action.yml b/__fixtures__/typescript-esm/no-import/action.yml new file mode 100644 index 0000000..1850ace --- /dev/null +++ b/__fixtures__/typescript-esm/no-import/action.yml @@ -0,0 +1,17 @@ +name: The name of your action here +description: Provide a description here +author: Your name or organization here + +inputs: + milliseconds: + description: Your input description here + required: true + default: '1000' + +outputs: + time: + description: Your output description here + +runs: + using: node20 + main: dist/index.js diff --git a/__fixtures__/typescript-esm/no-import/package.json b/__fixtures__/typescript-esm/no-import/package.json new file mode 100644 index 0000000..eb4088e --- /dev/null +++ b/__fixtures__/typescript-esm/no-import/package.json @@ -0,0 +1,17 @@ +{ + "name": "typescript-action", + "description": "GitHub Actions TypeScript template", + "version": "0.0.0", + "type": "module", + "engines": { + "node": ">=20" + }, + "dependencies": { + "@actions/core": "^1.10.1" + }, + "devDependencies": { + "@types/node": "^20.14.7", + "ts-node": "^10.9.2", + "typescript": "^5.5.2" + } +} diff --git a/__fixtures__/typescript-esm/no-import/src/index.ts b/__fixtures__/typescript-esm/no-import/src/index.ts new file mode 100644 index 0000000..2804fe8 --- /dev/null +++ b/__fixtures__/typescript-esm/no-import/src/index.ts @@ -0,0 +1,3 @@ +import { run } from './main.js' + +run() diff --git a/__fixtures__/typescript-esm/no-import/src/main.ts b/__fixtures__/typescript-esm/no-import/src/main.ts new file mode 100644 index 0000000..df2965f --- /dev/null +++ b/__fixtures__/typescript-esm/no-import/src/main.ts @@ -0,0 +1,3 @@ +export async function run(): Promise { + console.log('TypeScript ESM Action (no-import)!') +} diff --git a/__fixtures__/typescript-esm/no-import/tsconfig.json b/__fixtures__/typescript-esm/no-import/tsconfig.json new file mode 100644 index 0000000..13d8763 --- /dev/null +++ b/__fixtures__/typescript-esm/no-import/tsconfig.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "declaration": true, + "declarationMap": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2022"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "newLine": "lf", + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "pretty": true, + "resolveJsonModule": true, + "sourceMap": true, + "strict": true, + "strictNullChecks": true, + "target": "ES2022" + }, + "exclude": ["node_modules"], + "include": ["src"] +} diff --git a/__fixtures__/typescript-esm/success/.env.fixture b/__fixtures__/typescript-esm/success/.env.fixture new file mode 100644 index 0000000..bd69b49 --- /dev/null +++ b/__fixtures__/typescript-esm/success/.env.fixture @@ -0,0 +1,2 @@ +ACTIONS_STEP_DEBUG=false +INPUT_MILLISECONDS=2400 diff --git a/__fixtures__/typescript-esm/success/action.yml b/__fixtures__/typescript-esm/success/action.yml new file mode 100644 index 0000000..1850ace --- /dev/null +++ b/__fixtures__/typescript-esm/success/action.yml @@ -0,0 +1,17 @@ +name: The name of your action here +description: Provide a description here +author: Your name or organization here + +inputs: + milliseconds: + description: Your input description here + required: true + default: '1000' + +outputs: + time: + description: Your output description here + +runs: + using: node20 + main: dist/index.js diff --git a/__fixtures__/typescript-esm/success/package.json b/__fixtures__/typescript-esm/success/package.json new file mode 100644 index 0000000..eb4088e --- /dev/null +++ b/__fixtures__/typescript-esm/success/package.json @@ -0,0 +1,17 @@ +{ + "name": "typescript-action", + "description": "GitHub Actions TypeScript template", + "version": "0.0.0", + "type": "module", + "engines": { + "node": ">=20" + }, + "dependencies": { + "@actions/core": "^1.10.1" + }, + "devDependencies": { + "@types/node": "^20.14.7", + "ts-node": "^10.9.2", + "typescript": "^5.5.2" + } +} diff --git a/__fixtures__/typescript-esm/success/src/index.ts b/__fixtures__/typescript-esm/success/src/index.ts new file mode 100644 index 0000000..2804fe8 --- /dev/null +++ b/__fixtures__/typescript-esm/success/src/index.ts @@ -0,0 +1,3 @@ +import { run } from './main.js' + +run() diff --git a/__fixtures__/typescript-esm/success/src/main.ts b/__fixtures__/typescript-esm/success/src/main.ts new file mode 100644 index 0000000..51cab60 --- /dev/null +++ b/__fixtures__/typescript-esm/success/src/main.ts @@ -0,0 +1,9 @@ +import { getInput, info, setOutput } from '@actions/core' + +export async function run(): Promise { + const myInput: string = getInput('myInput') + + setOutput('myOutput', myInput) + + info('TypeScript ESM Action Succeeded!') +} diff --git a/__fixtures__/typescript-esm/success/tsconfig.json b/__fixtures__/typescript-esm/success/tsconfig.json new file mode 100644 index 0000000..13d8763 --- /dev/null +++ b/__fixtures__/typescript-esm/success/tsconfig.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "declaration": true, + "declarationMap": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2022"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "newLine": "lf", + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "pretty": true, + "resolveJsonModule": true, + "sourceMap": true, + "strict": true, + "strictNullChecks": true, + "target": "ES2022" + }, + "exclude": ["node_modules"], + "include": ["src"] +} diff --git a/__fixtures__/typescript/failure/.env.fixture b/__fixtures__/typescript/failure/.env.fixture deleted file mode 100644 index 1a3d037..0000000 --- a/__fixtures__/typescript/failure/.env.fixture +++ /dev/null @@ -1,8 +0,0 @@ -# Do not commit your actual .env file to Git! This may contain secrets or other -# private information. - -# GitHub Actions inputs should follow `INPUT_` format (case-insensitive). -INPUT_milliseconds=2400 - -# Enable/disable step debug logs -ACTIONS_STEP_DEBUG=false \ No newline at end of file diff --git a/__fixtures__/typescript/failure/action.yml b/__fixtures__/typescript/failure/action.yml deleted file mode 100644 index 8cf674c..0000000 --- a/__fixtures__/typescript/failure/action.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: TypeScript (Failing) -description: This action sets a failure status - -runs: - using: node20 - main: dist/index.js diff --git a/__fixtures__/typescript/failure/src/index.ts b/__fixtures__/typescript/failure/src/index.ts deleted file mode 100644 index 1c5eb3f..0000000 --- a/__fixtures__/typescript/failure/src/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ - -import { run } from './main' - -run() diff --git a/__fixtures__/typescript/failure/src/main.ts b/__fixtures__/typescript/failure/src/main.ts deleted file mode 100644 index 757616a..0000000 --- a/__fixtures__/typescript/failure/src/main.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { setFailed, summary } from '@actions/core' - -export async function run(): Promise { - summary.addRaw('TypeScript Action Failed!') - await summary.write() - - setFailed('TypeScript Action Failed!') -} diff --git a/__fixtures__/typescript/no-import/.env.fixture b/__fixtures__/typescript/no-import/.env.fixture index 1a3d037..bd69b49 100644 --- a/__fixtures__/typescript/no-import/.env.fixture +++ b/__fixtures__/typescript/no-import/.env.fixture @@ -1,8 +1,2 @@ -# Do not commit your actual .env file to Git! This may contain secrets or other -# private information. - -# GitHub Actions inputs should follow `INPUT_` format (case-insensitive). -INPUT_milliseconds=2400 - -# Enable/disable step debug logs -ACTIONS_STEP_DEBUG=false \ No newline at end of file +ACTIONS_STEP_DEBUG=false +INPUT_MILLISECONDS=2400 diff --git a/__fixtures__/typescript/no-import/action.yml b/__fixtures__/typescript/no-import/action.yml index ce2ff45..c6e9f71 100644 --- a/__fixtures__/typescript/no-import/action.yml +++ b/__fixtures__/typescript/no-import/action.yml @@ -5,7 +5,7 @@ inputs: myInput: description: An input required: true - default: 'default value' + default: value outputs: myOutput: diff --git a/__fixtures__/typescript/no-import/package.json b/__fixtures__/typescript/no-import/package.json new file mode 100644 index 0000000..0ab84ed --- /dev/null +++ b/__fixtures__/typescript/no-import/package.json @@ -0,0 +1,15 @@ +{ + "name": "typescript-action", + "description": "GitHub Actions TypeScript template", + "version": "0.0.0", + "engines": { + "node": ">=20" + }, + "dependencies": { + "@actions/core": "^1.10.1" + }, + "devDependencies": { + "@types/node": "^22.2.0", + "typescript": "^5.5.4" + } +} diff --git a/__fixtures__/typescript/no-import/src/index.ts b/__fixtures__/typescript/no-import/src/index.ts index 1c5eb3f..f81171b 100644 --- a/__fixtures__/typescript/no-import/src/index.ts +++ b/__fixtures__/typescript/no-import/src/index.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ - import { run } from './main' run() diff --git a/__fixtures__/typescript/no-import/tsconfig.json b/__fixtures__/typescript/no-import/tsconfig.json new file mode 100644 index 0000000..a0a7bc6 --- /dev/null +++ b/__fixtures__/typescript/no-import/tsconfig.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "rootDir": "./src", + "moduleResolution": "NodeNext", + "baseUrl": "./", + "sourceMap": true, + "outDir": "./dist", + "noImplicitAny": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "newLine": "lf" + }, + "exclude": ["node_modules"], + "include": ["src"] +} diff --git a/__fixtures__/typescript/success-yaml/.env.fixture b/__fixtures__/typescript/success-yaml/.env.fixture deleted file mode 100644 index f1b3926..0000000 --- a/__fixtures__/typescript/success-yaml/.env.fixture +++ /dev/null @@ -1,11 +0,0 @@ -# Do not commit your actual .env file to Git! This may contain secrets or other -# private information. - -# GitHub Actions inputs should follow `INPUT_` format (case-insensitive). -INPUT_milliseconds=2400 - -# Enable/disable step debug logs -ACTIONS_STEP_DEBUG=false - -# Step summary output location -GITHUB_STEP_SUMMARY='summary.md' \ No newline at end of file diff --git a/__fixtures__/typescript/success-yaml/src/index.ts b/__fixtures__/typescript/success-yaml/src/index.ts deleted file mode 100644 index 1c5eb3f..0000000 --- a/__fixtures__/typescript/success-yaml/src/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ - -import { run } from './main' - -run() diff --git a/__fixtures__/typescript/success-yaml/src/main.ts b/__fixtures__/typescript/success-yaml/src/main.ts deleted file mode 100644 index 4500783..0000000 --- a/__fixtures__/typescript/success-yaml/src/main.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { getInput, info, setOutput, summary } from '@actions/core' - -export async function run(): Promise { - const myInput: string = getInput('myInput') - - setOutput('myOutput', myInput) - - summary.addRaw('TypeScript Action Succeeded!') - await summary.write() - - info('TypeScript Action Succeeded!') -} diff --git a/__fixtures__/typescript/success/.env.fixture b/__fixtures__/typescript/success/.env.fixture index f1b3926..bd69b49 100644 --- a/__fixtures__/typescript/success/.env.fixture +++ b/__fixtures__/typescript/success/.env.fixture @@ -1,11 +1,2 @@ -# Do not commit your actual .env file to Git! This may contain secrets or other -# private information. - -# GitHub Actions inputs should follow `INPUT_` format (case-insensitive). -INPUT_milliseconds=2400 - -# Enable/disable step debug logs ACTIONS_STEP_DEBUG=false - -# Step summary output location -GITHUB_STEP_SUMMARY='summary.md' \ No newline at end of file +INPUT_MILLISECONDS=2400 diff --git a/__fixtures__/typescript/success/action.yml b/__fixtures__/typescript/success/action.yml index 3c183c0..ad834ec 100644 --- a/__fixtures__/typescript/success/action.yml +++ b/__fixtures__/typescript/success/action.yml @@ -5,7 +5,7 @@ inputs: myInput: description: An input required: true - default: 'default value' + default: value outputs: myOutput: diff --git a/__fixtures__/typescript/success/package-lock.json b/__fixtures__/typescript/success/package-lock.json new file mode 100644 index 0000000..a231960 --- /dev/null +++ b/__fixtures__/typescript/success/package-lock.json @@ -0,0 +1,103 @@ +{ + "name": "typescript-action", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "typescript-action", + "version": "0.0.0", + "dependencies": { + "@actions/core": "^1.10.1" + }, + "devDependencies": { + "@types/node": "^22.2.0", + "typescript": "^5.5.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@actions/core": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", + "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/http-client": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.2.tgz", + "integrity": "sha512-2TvX5LskKQzDDQI+bobIDGAjkn0NJiQlg4MTrKnZ8HfQ7nDEUbtJ1ytxPDb2bfk3Hr2XD99X8oAJISAmIoiSAQ==", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@types/node": { + "version": "22.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.0.tgz", + "integrity": "sha512-49AbMDwYUz7EXxKU/r7mXOsxwFr4BYbvB7tWYxVuLdb2ibd30ijjXINSMAHiEEZk5PCRBmW1gUeisn2VMKt3cQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.6.tgz", + "integrity": "sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==", + "dev": true + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + } + } +} diff --git a/__fixtures__/typescript/success/package.json b/__fixtures__/typescript/success/package.json new file mode 100644 index 0000000..0ab84ed --- /dev/null +++ b/__fixtures__/typescript/success/package.json @@ -0,0 +1,15 @@ +{ + "name": "typescript-action", + "description": "GitHub Actions TypeScript template", + "version": "0.0.0", + "engines": { + "node": ">=20" + }, + "dependencies": { + "@actions/core": "^1.10.1" + }, + "devDependencies": { + "@types/node": "^22.2.0", + "typescript": "^5.5.4" + } +} diff --git a/__fixtures__/typescript/success/src/index.ts b/__fixtures__/typescript/success/src/index.ts index 1c5eb3f..f81171b 100644 --- a/__fixtures__/typescript/success/src/index.ts +++ b/__fixtures__/typescript/success/src/index.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ - import { run } from './main' run() diff --git a/__fixtures__/typescript/success/src/main.ts b/__fixtures__/typescript/success/src/main.ts index 4500783..2f71fd4 100644 --- a/__fixtures__/typescript/success/src/main.ts +++ b/__fixtures__/typescript/success/src/main.ts @@ -1,12 +1,9 @@ -import { getInput, info, setOutput, summary } from '@actions/core' +import { getInput, info, setOutput } from '@actions/core' export async function run(): Promise { const myInput: string = getInput('myInput') setOutput('myOutput', myInput) - summary.addRaw('TypeScript Action Succeeded!') - await summary.write() - info('TypeScript Action Succeeded!') } diff --git a/__fixtures__/typescript/success/tsconfig.json b/__fixtures__/typescript/success/tsconfig.json new file mode 100644 index 0000000..a0a7bc6 --- /dev/null +++ b/__fixtures__/typescript/success/tsconfig.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "rootDir": "./src", + "moduleResolution": "NodeNext", + "baseUrl": "./", + "sourceMap": true, + "outDir": "./dist", + "noImplicitAny": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "newLine": "lf" + }, + "exclude": ["node_modules"], + "include": ["src"] +} diff --git a/__mocks__/@actions/core.ts b/__mocks__/@actions/core.ts deleted file mode 100644 index 1a9307b..0000000 --- a/__mocks__/@actions/core.ts +++ /dev/null @@ -1,67 +0,0 @@ -const addPath = jest.fn() -const debug = jest.fn() -const endGroup = jest.fn() -const error = jest.fn() -const exportVariable = jest.fn() -const getBooleanInput = jest.fn() -const getIDToken = jest.fn() -const getInput = jest.fn() -const getMultilineInput = jest.fn() -const getState = jest.fn() -const group = jest.fn() -const info = jest.fn() -const isDebug = jest.fn() -const notice = jest.fn() -const saveState = jest.fn() -const setCommandEcho = jest.fn() -const setFailed = jest.fn() -const setOutput = jest.fn() -const setSecret = jest.fn() -const startGroup = jest.fn() -const warning = jest.fn() - -const summary = {} -summary['filePath'] = jest.fn().mockReturnValue(summary) -summary['wrap'] = jest.fn().mockReturnValue(summary) -summary['write'] = jest.fn().mockReturnValue(summary) -summary['clear'] = jest.fn().mockReturnValue(summary) -summary['stringify'] = jest.fn().mockReturnValue(summary) -summary['isEmptyBuffer'] = jest.fn().mockReturnValue(summary) -summary['emptyBuffer'] = jest.fn().mockReturnValue(summary) -summary['addRaw'] = jest.fn().mockReturnValue(summary) -summary['addEOL'] = jest.fn().mockReturnValue(summary) -summary['addCodeBlock'] = jest.fn().mockReturnValue(summary) -summary['addList'] = jest.fn().mockReturnValue(summary) -summary['addTable'] = jest.fn().mockReturnValue(summary) -summary['addDetails'] = jest.fn().mockReturnValue(summary) -summary['addImage'] = jest.fn().mockReturnValue(summary) -summary['addHeading'] = jest.fn().mockReturnValue(summary) -summary['addSeparator'] = jest.fn().mockReturnValue(summary) -summary['addBreak'] = jest.fn().mockReturnValue(summary) -summary['addQuote'] = jest.fn().mockReturnValue(summary) -summary['addLink'] = jest.fn().mockReturnValue(summary) - -export { - addPath, - debug, - endGroup, - error, - exportVariable, - getBooleanInput, - getIDToken, - getInput, - getMultilineInput, - getState, - group, - info, - isDebug, - notice, - saveState, - setCommandEcho, - setFailed, - setOutput, - setSecret, - startGroup, - summary, - warning -} diff --git a/__mocks__/tsconfig-paths.ts b/__mocks__/tsconfig-paths.ts deleted file mode 100644 index 2f82618..0000000 --- a/__mocks__/tsconfig-paths.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const loadConfig = jest.fn().mockImplementation() -export const register = jest.fn().mockImplementation() diff --git a/__tests__/bootstrap.test.ts b/__tests__/bootstrap.test.ts deleted file mode 100644 index a3dccb8..0000000 --- a/__tests__/bootstrap.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import fs from 'fs' -import { ResetCoreMetadata } from '../src/stubs/core-stubs' -import { ResetEnvMetadata } from '../src/stubs/env-stubs' - -let fs_existsSyncSpy: jest.SpyInstance -let fs_readFileSyncSpy: jest.SpyInstance - -describe('Bootstrap', () => { - beforeAll(() => { - fs_existsSyncSpy = jest.spyOn(fs, 'existsSync') - fs_readFileSyncSpy = jest.spyOn(fs, 'readFileSync') - }) - - beforeEach(() => { - // Reset metadata - ResetEnvMetadata() - ResetCoreMetadata() - }) - - afterEach(() => { - // Reset all spies - jest.resetAllMocks() - - // Reset module imports - jest.resetModules() - }) - - it('Does nothing if no target action path is provided', async () => { - process.env.TARGET_ACTION_PATH = '' - - await import('../src/bootstrap') - - expect(fs_existsSyncSpy).not.toHaveBeenCalledWith( - `${process.env.TARGET_ACTION_PATH}/tsconfig.json` - ) - expect(fs_readFileSyncSpy).not.toHaveBeenCalledWith( - `${process.env.TARGET_ACTION_PATH}/tsconfig.json` - ) - }) - - it('Does nothing if the target action path does not exist', async () => { - process.env.TARGET_ACTION_PATH = 'non-existent-path' - - await import('../src/bootstrap') - - expect(fs_existsSyncSpy).toHaveBeenCalledWith( - `${process.env.TARGET_ACTION_PATH}/tsconfig.json` - ) - expect(fs_readFileSyncSpy).not.toHaveBeenCalledWith( - `${process.env.TARGET_ACTION_PATH}/tsconfig.json` - ) - }) -}) diff --git a/__tests__/command.test.ts b/__tests__/command.test.ts index 6b5b1d3..2870b38 100644 --- a/__tests__/command.test.ts +++ b/__tests__/command.test.ts @@ -1,23 +1,32 @@ -/* eslint-disable import/no-namespace */ - +import { jest } from '@jest/globals' import { Command } from 'commander' -import { makeProgram } from '../src/command' -import * as run from '../src/commands/run' -import { ResetCoreMetadata } from '../src/stubs/core-stubs' -import { ResetEnvMetadata } from '../src/stubs/env-stubs' +import { ResetCoreMetadata } from '../src/stubs/core-stubs.js' +import { ResetEnvMetadata } from '../src/stubs/env-stubs.js' + +const action = jest.fn() + +jest.unstable_mockModule('../src/commands/run.js', () => { + return { + action + } +}) + +const process_exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => { + throw new Error(`process.exit()`) +}) + +const process_stderrSpy = jest + .spyOn(process.stderr, 'write') + .mockImplementation(() => true) -let process_exitSpy: jest.SpyInstance -let process_stderrSpy: jest.SpyInstance let program: Command -let run_actionSpy: jest.SpyInstance -describe('Commmand', () => { - beforeAll(() => { - // Prevent output during tests - jest.spyOn(console, 'log').mockImplementation() - jest.spyOn(console, 'table').mockImplementation() - }) +// Prevent output during tests +jest.spyOn(console, 'log').mockImplementation(() => {}) +const { makeProgram } = await import('../src/command.js') + +describe('Commmand', () => { beforeEach(async () => { // Reset metadata ResetEnvMetadata() @@ -25,10 +34,6 @@ describe('Commmand', () => { // Create a new program before each test program = await makeProgram() - - // Stub the run action and process.exit - run_actionSpy = jest.spyOn(run, 'action').mockImplementation() - process_exitSpy = jest.spyOn(process, 'exit').mockImplementation() }) afterEach(() => { @@ -63,32 +68,10 @@ describe('Commmand', () => { ) expect(process_exitSpy).not.toHaveBeenCalled() - expect(run_actionSpy).toHaveBeenCalled() - }) - - it('Runs if all arguments are provided (action.yaml)', async () => { - await ( - await makeProgram() - ).parseAsync( - [ - './__fixtures__/typescript/success-yaml', - 'src/index.ts', - './__fixtures__/typescript/success-yaml/.env.fixture' - ], - { - from: 'user' - } - ) - - expect(process_exitSpy).not.toHaveBeenCalled() - expect(run_actionSpy).toHaveBeenCalled() + expect(action).toHaveBeenCalled() }) it('Exits if no path argument is provided', async () => { - process_stderrSpy = jest - .spyOn(process.stderr, 'write') - .mockImplementation() - await (await makeProgram()).parseAsync([], { from: 'user' }) expect(process_exitSpy).toHaveBeenCalled() @@ -97,10 +80,6 @@ describe('Commmand', () => { }) it('Exits if no entrypoint argument is provided', async () => { - process_stderrSpy = jest - .spyOn(process.stderr, 'write') - .mockImplementation() - await ( await makeProgram() ).parseAsync(['./__fixtures__/typescript/success', ''], { from: 'user' }) @@ -111,10 +90,6 @@ describe('Commmand', () => { }) it('Exits if no env-file argument is provided', async () => { - process_stderrSpy = jest - .spyOn(process.stderr, 'write') - .mockImplementation() - await ( await makeProgram() ).parseAsync(['./__fixtures__/typescript/success', 'src/index.ts'], { @@ -127,10 +102,6 @@ describe('Commmand', () => { }) it('Exits if the action path is not a directory', async () => { - process_stderrSpy = jest - .spyOn(process.stderr, 'write') - .mockImplementation() - await expect( (await makeProgram()).parseAsync( ['./package.json', 'src/index.ts', '.env'], @@ -144,10 +115,6 @@ describe('Commmand', () => { }) it('Exits if the action path does not exist', async () => { - process_stderrSpy = jest - .spyOn(process.stderr, 'write') - .mockImplementation() - await expect( (await makeProgram()).parseAsync( ['/test/path/does/not/exist', 'src/index.ts', '.env'], @@ -161,10 +128,6 @@ describe('Commmand', () => { }) it('Exits if the action path does not contain an action.yml or action.yaml', async () => { - process_stderrSpy = jest - .spyOn(process.stderr, 'write') - .mockImplementation() - await expect( (await makeProgram()).parseAsync( ['./__fixtures__', 'src/index.ts', '.env'], @@ -178,10 +141,6 @@ describe('Commmand', () => { }) it('Exits if the entrypoint does not exist', async () => { - process_stderrSpy = jest - .spyOn(process.stderr, 'write') - .mockImplementation() - await expect( (await makeProgram()).parseAsync( ['./__fixtures__/typescript/success', 'src/fake.ts', '.env'], @@ -195,10 +154,6 @@ describe('Commmand', () => { }) it('Throws if the dotenv file does not exist', async () => { - process_stderrSpy = jest - .spyOn(process.stderr, 'write') - .mockImplementation() - await expect( (await makeProgram()).parseAsync( ['./__fixtures__/typescript/success', 'src/index.ts', '.notreal.env'], diff --git a/__tests__/commands/run.test.ts b/__tests__/commands/run.test.ts index 212600b..b1d2893 100644 --- a/__tests__/commands/run.test.ts +++ b/__tests__/commands/run.test.ts @@ -1,23 +1,29 @@ -/* eslint-disable import/no-namespace */ +import { jest } from '@jest/globals' +import * as core from '../../__fixtures__/core.js' +import { ResetCoreMetadata } from '../../src/stubs/core-stubs.js' +import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs.js' -import { setFailed, summary } from '@actions/core' -import { action } from '../../src/commands/run' -import { ResetCoreMetadata } from '../../src/stubs/core-stubs' -import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs' -import * as output from '../../src/utils/output' +const quibbleEsm = jest.fn().mockImplementation(() => {}) +const quibbleDefault = jest.fn().mockImplementation(() => {}) -const summary_writeSpy: jest.SpyInstance = jest - .spyOn(summary, 'write') - .mockImplementation() +// @ts-expect-error - `quibble` is the default, but we need to mock esm() too +quibbleDefault.esm = quibbleEsm -describe('Command: run', () => { - beforeAll(() => { - // Prevent output during tests - jest.spyOn(console, 'log').mockImplementation() - jest.spyOn(console, 'table').mockImplementation() - jest.spyOn(output, 'printTitle').mockImplementation() - }) +jest.unstable_mockModule('@actions/core', () => core) +jest.unstable_mockModule('quibble', () => { + return { default: quibbleDefault } +}) +jest.unstable_mockModule('../../src/utils/output.js', () => { + return { printTitle: jest.fn() } +}) + +const { action } = await import('../../src/commands/run.js') +// Prevent output during tests +jest.spyOn(console, 'log').mockImplementation(() => {}) +jest.spyOn(console, 'table').mockImplementation(() => {}) + +describe('Command: run', () => { beforeEach(() => { // Reset metadata ResetEnvMetadata() @@ -31,85 +37,91 @@ describe('Command: run', () => { describe('TypeScript', () => { it('Action: success', async () => { - process.env.GITHUB_STEP_SUMMARY = 'summary.md' - EnvMeta.actionFile = `./__fixtures__/typescript/success/action.yml` EnvMeta.actionPath = `./__fixtures__/typescript/success` EnvMeta.dotenvFile = `./__fixtures__/typescript/success/.env.fixture` - EnvMeta.entrypoint = `./__fixtures__/typescript/success/src/index.ts` + EnvMeta.entrypoint = `./__fixtures__/typescript/success/src/main.ts` await expect(action()).resolves.toBeUndefined() - - expect(summary_writeSpy).toHaveBeenCalled() - expect(setFailed).not.toHaveBeenCalled() }) - it('Action: failure', async () => { - delete process.env.GITHUB_STEP_SUMMARY + it('Action: no-import', async () => { + EnvMeta.actionFile = `./__fixtures__/typescript/no-import/action.yml` + EnvMeta.actionPath = `./__fixtures__/typescript/no-import` + EnvMeta.dotenvFile = `./__fixtures__/typescript/no-import/.env.fixture` + EnvMeta.entrypoint = `./__fixtures__/typescript/no-import/src/main.ts` - EnvMeta.actionFile = `./__fixtures__/typescript/failure/action.yml` - EnvMeta.actionPath = `./__fixtures__/typescript/failure` - EnvMeta.dotenvFile = `./__fixtures__/typescript/failure/.env.fixture` - EnvMeta.entrypoint = `./__fixtures__/typescript/failure/src/index.ts` + await expect(action()).resolves.toBeUndefined() + }) + }) + + describe('TypeScript ESM', () => { + it('Action: success', async () => { + EnvMeta.actionFile = `./__fixtures__/typescript-esm/success/action.yml` + EnvMeta.actionPath = `./__fixtures__/typescript-esm/success` + EnvMeta.dotenvFile = `./__fixtures__/typescript-esm/success/.env.fixture` + EnvMeta.entrypoint = `./__fixtures__/typescript-esm/success/src/main.ts` await expect(action()).resolves.toBeUndefined() - expect(summary_writeSpy).toHaveBeenCalled() - expect(setFailed).toHaveBeenCalledWith('TypeScript Action Failed!') + expect(core.setFailed).not.toHaveBeenCalled() + expect(quibbleEsm).toHaveBeenCalled() }) it('Action: no-import', async () => { - EnvMeta.actionFile = `./__fixtures__/typescript/no-import/action.yml` - EnvMeta.actionPath = `./__fixtures__/typescript/no-import` - EnvMeta.dotenvFile = `./__fixtures__/typescript/no-import/.env.fixture` - EnvMeta.entrypoint = `./__fixtures__/typescript/no-import/src/index.ts` + EnvMeta.actionFile = `./__fixtures__/typescript-esm/no-import/action.yml` + EnvMeta.actionPath = `./__fixtures__/typescript-esm/no-import` + EnvMeta.dotenvFile = `./__fixtures__/typescript-esm/no-import/.env.fixture` + EnvMeta.entrypoint = `./__fixtures__/typescript-esm/no-import/src/main.ts` await expect(action()).resolves.toBeUndefined() - expect(summary_writeSpy).not.toHaveBeenCalled() - expect(setFailed).not.toHaveBeenCalled() + expect(core.setFailed).not.toHaveBeenCalled() + expect(quibbleEsm).toHaveBeenCalled() }) }) describe('JavaScript', () => { it('Action: success', async () => { - process.env.GITHUB_STEP_SUMMARY = 'summary.md' - EnvMeta.actionFile = `./__fixtures__/javascript/success/action.yml` EnvMeta.actionPath = `./__fixtures__/javascript/success` EnvMeta.dotenvFile = `./__fixtures__/javascript/success/.env.fixture` - EnvMeta.entrypoint = `./__fixtures__/javascript/success/src/index.js` + EnvMeta.entrypoint = `./__fixtures__/javascript/success/src/main.js` await expect(action()).resolves.toBeUndefined() - - expect(summary_writeSpy).toHaveBeenCalled() - expect(setFailed).not.toHaveBeenCalled() }) - it('Action: failure', async () => { - delete process.env.GITHUB_STEP_SUMMARY + it('Action: no-import', async () => { + EnvMeta.actionFile = `./__fixtures__/javascript/no-import/action.yml` + EnvMeta.actionPath = `./__fixtures__/javascript/no-import` + EnvMeta.dotenvFile = `./__fixtures__/javascript/no-import/.env.fixture` + EnvMeta.entrypoint = `./__fixtures__/javascript/no-import/src/main.js` - EnvMeta.actionFile = `./__fixtures__/javascript/failure/action.yml` - EnvMeta.actionPath = `./__fixtures__/javascript/failure` - EnvMeta.dotenvFile = `./__fixtures__/javascript/failure/.env.fixture` - EnvMeta.entrypoint = `./__fixtures__/javascript/failure/src/index.js` + await expect(action()).resolves.toBeUndefined() + }) + }) + + describe('JavaScript (ESM)', () => { + it('Action: success', async () => { + EnvMeta.actionFile = `./__fixtures__/javascript/success/action.yml` + EnvMeta.actionPath = `./__fixtures__/javascript/success` + EnvMeta.dotenvFile = `./__fixtures__/javascript/success/.env.fixture` + EnvMeta.entrypoint = `./__fixtures__/javascript/success/src/main.js` await expect(action()).resolves.toBeUndefined() - expect(summary_writeSpy).toHaveBeenCalled() - expect(setFailed).toHaveBeenCalled() + expect(quibbleDefault).toHaveBeenCalled() }) it('Action: no-import', async () => { EnvMeta.actionFile = `./__fixtures__/javascript/no-import/action.yml` EnvMeta.actionPath = `./__fixtures__/javascript/no-import` EnvMeta.dotenvFile = `./__fixtures__/javascript/no-import/.env.fixture` - EnvMeta.entrypoint = `./__fixtures__/javascript/no-import/src/index.js` + EnvMeta.entrypoint = `./__fixtures__/javascript/no-import/src/main.js` await expect(action()).resolves.toBeUndefined() - expect(summary_writeSpy).not.toHaveBeenCalled() - expect(setFailed).not.toHaveBeenCalled() + expect(quibbleDefault).toHaveBeenCalled() }) }) }) diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index ded3459..71054b2 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -1,29 +1,23 @@ -/* eslint-disable import/no-namespace */ +import { jest } from '@jest/globals' +import { ResetCoreMetadata } from '../src/stubs/core-stubs.js' +import { ResetEnvMetadata } from '../src/stubs/env-stubs.js' + +const makeProgram = jest.fn().mockResolvedValue({ + parse: jest.fn() +} as never) + +jest.unstable_mockModule('../src/command.js', () => { + return { + makeProgram + } +}) -import { Command } from 'commander' -import * as command from '../src/command' -import { run } from '../src/index' -import { ResetCoreMetadata } from '../src/stubs/core-stubs' -import { ResetEnvMetadata } from '../src/stubs/env-stubs' +// Prevent output during tests +jest.spyOn(console, 'log').mockImplementation(() => {}) -let command_makeProgramSpy: jest.SpyInstance +const { run } = await import('../src/index.js') describe('Index', () => { - beforeAll(() => { - // Prevent output during tests - jest.spyOn(console, 'log').mockImplementation() - jest.spyOn(console, 'table').mockImplementation() - - // Stub the command.makeProgram call - command_makeProgramSpy = jest - .spyOn(command, 'makeProgram') - .mockImplementation(async () => { - return Promise.resolve({ - parse: () => {} - } as Command) - }) - }) - beforeEach(() => { // Reset metadata ResetEnvMetadata() @@ -31,15 +25,13 @@ describe('Index', () => { }) afterEach(() => { - // Reset all spies jest.resetAllMocks() }) describe('run()', () => { it('Runs the program', async () => { await run() - - expect(command_makeProgramSpy).toHaveBeenCalled() + expect(makeProgram).toHaveBeenCalled() }) }) }) diff --git a/__tests__/stubs/core-stubs.test.ts b/__tests__/stubs/core-stubs.test.ts index 90d35ca..aba859b 100644 --- a/__tests__/stubs/core-stubs.test.ts +++ b/__tests__/stubs/core-stubs.test.ts @@ -1,35 +1,36 @@ +import { jest } from '@jest/globals' +import path from 'path' import { CoreMeta, ResetCoreMetadata, - exportVariable, - setSecret, addPath, + debug, + endGroup, + error, + exportVariable, + getBooleanInput, + getIDToken, getInput, getMultilineInput, - getBooleanInput, - setOutput, - setCommandEcho, - setFailed, - log, + getState, + group, + info, isDebug, - debug, - error, - warning, + log, notice, - info, - startGroup, - endGroup, - group, saveState, - getState, - getIDToken, - toWin32Path, + setCommandEcho, + setFailed, + setOutput, + setSecret, + startGroup, toPlatformPath, - toPosixPath -} from '../../src/stubs/core-stubs' -import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs' -import type { CoreMetadata } from '../../src/types' -import path from 'path' + toPosixPath, + toWin32Path, + warning +} from '../../src/stubs/core-stubs.js' +import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs.js' +import type { CoreMetadata } from '../../src/types.js' /** Empty CoreMetadata Object */ const empty: CoreMetadata = { @@ -53,13 +54,11 @@ const empty: CoreMetadata = { } } -describe('Core', () => { - beforeAll(() => { - // Prevent output during tests - jest.spyOn(console, 'log').mockImplementation() - jest.spyOn(console, 'table').mockImplementation() - }) +// Prevent output during tests +jest.spyOn(console, 'log').mockImplementation(() => {}) +jest.spyOn(console, 'table').mockImplementation(() => {}) +describe('Core', () => { beforeEach(() => { // Reset metadata ResetEnvMetadata() @@ -287,7 +286,7 @@ describe('Core', () => { describe('setOutput()', () => { it('Sets the action outputs', () => { - jest.spyOn(CoreMeta.colors, 'cyan').mockImplementation() + jest.spyOn(CoreMeta.colors, 'cyan').mockImplementation(() => {}) setOutput('my-output', 'output-value') @@ -295,9 +294,9 @@ describe('Core', () => { }) it('Logs the output to the console', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'cyan') - .mockImplementation() + .mockImplementation(() => {}) setOutput('my-output', 'output-value') @@ -319,7 +318,7 @@ describe('Core', () => { describe('setFailed()', () => { it('Sets the exit code to failure', () => { - jest.spyOn(CoreMeta.colors, 'red').mockImplementation() + jest.spyOn(CoreMeta.colors, 'red').mockImplementation(() => {}) setFailed('test') @@ -348,9 +347,9 @@ describe('Core', () => { }) it('Logs only the color when no message is provided', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'blue') - .mockImplementation() + .mockImplementation(() => {}) log('group') @@ -358,9 +357,9 @@ describe('Core', () => { }) it('Redacts secrets from the output', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'blue') - .mockImplementation() + .mockImplementation(() => {}) // Set a secret to mask CoreMeta.secrets = ['secret-value-1234'] @@ -373,9 +372,9 @@ describe('Core', () => { }) it('Includes annotations in the output', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'blue') - .mockImplementation() + .mockImplementation(() => {}) log('group', 'my message', { title: 'my title', @@ -388,9 +387,9 @@ describe('Core', () => { }) it('Defaults the endLine property to startLine', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'white') - .mockImplementation() + .mockImplementation(() => {}) log('info', 'my message', { startLine: 1 @@ -402,9 +401,9 @@ describe('Core', () => { }) it('Defaults the endColumn property to startColumn', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'white') - .mockImplementation() + .mockImplementation(() => {}) log('info', 'my message', { startColumn: 1 @@ -431,9 +430,9 @@ describe('Core', () => { // Enable step debug logging CoreMeta.stepDebug = true - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'gray') - .mockImplementation() + .mockImplementation(() => {}) debug('test') @@ -444,9 +443,9 @@ describe('Core', () => { // Disable step debug logging CoreMeta.stepDebug = false - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'gray') - .mockImplementation() + .mockImplementation(() => {}) debug('test') @@ -456,9 +455,9 @@ describe('Core', () => { describe('error()', () => { it('Logs to the console', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'red') - .mockImplementation() + .mockImplementation(() => {}) error('test') @@ -468,9 +467,9 @@ describe('Core', () => { describe('warning()', () => { it('Logs to the console', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'yellow') - .mockImplementation() + .mockImplementation(() => {}) warning('test') @@ -480,9 +479,9 @@ describe('Core', () => { describe('notice()', () => { it('Logs to the console', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'magenta') - .mockImplementation() + .mockImplementation(() => {}) notice('test') @@ -492,9 +491,9 @@ describe('Core', () => { describe('info()', () => { it('Logs to the console', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'white') - .mockImplementation() + .mockImplementation(() => {}) info('test') @@ -504,9 +503,9 @@ describe('Core', () => { describe('startGroup()', () => { it('Logs to the console', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'blue') - .mockImplementation() + .mockImplementation(() => {}) startGroup('test') @@ -516,9 +515,9 @@ describe('Core', () => { describe('endGroup()', () => { it('Logs to the console', () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'blue') - .mockImplementation() + .mockImplementation(() => {}) endGroup() @@ -528,14 +527,11 @@ describe('Core', () => { describe('group()', () => { it('Logs grouped messages to the console', async () => { - const core_outputSpy: jest.SpyInstance = jest + const core_outputSpy = jest .spyOn(CoreMeta.colors, 'blue') - .mockImplementation() + .mockImplementation(() => {}) - const core_infoSpy: jest.SpyInstance = jest.spyOn( - CoreMeta.colors, - 'white' - ) + const core_infoSpy = jest.spyOn(CoreMeta.colors, 'white') await group('my-group', async () => { info('test') diff --git a/__tests__/stubs/env-stubs.test.ts b/__tests__/stubs/env-stubs.test.ts index 0a9eefd..5073da5 100644 --- a/__tests__/stubs/env-stubs.test.ts +++ b/__tests__/stubs/env-stubs.test.ts @@ -1,6 +1,7 @@ -import { ResetCoreMetadata } from '../../src/stubs/core-stubs' -import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs' -import type { EnvMetadata } from '../../src/types' +import { jest } from '@jest/globals' +import { ResetCoreMetadata } from '../../src/stubs/core-stubs.js' +import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs.js' +import type { EnvMetadata } from '../../src/types.js' /** Empty EnvMetadata Object */ const empty: EnvMetadata = { @@ -14,13 +15,10 @@ const empty: EnvMetadata = { path: '' } -describe('Env', () => { - beforeAll(() => { - // Prevent output during tests - jest.spyOn(console, 'log').mockImplementation() - jest.spyOn(console, 'table').mockImplementation() - }) +// Prevent output during tests +jest.spyOn(console, 'log').mockImplementation(() => {}) +describe('Env', () => { beforeEach(() => { // Reset metadata ResetEnvMetadata() @@ -28,7 +26,6 @@ describe('Env', () => { }) afterEach(() => { - // Reset all spies jest.resetAllMocks() }) diff --git a/__tests__/stubs/summary-stubs.test.ts b/__tests__/stubs/summary-stubs.test.ts index dad8a52..09c4576 100644 --- a/__tests__/stubs/summary-stubs.test.ts +++ b/__tests__/stubs/summary-stubs.test.ts @@ -1,17 +1,16 @@ +import { jest } from '@jest/globals' import fs from 'fs' import { EOL } from 'os' import path from 'path' -import { Summary } from '../../src/stubs/summary-stubs' -import { CoreMeta, ResetCoreMetadata } from '../../src/stubs/core-stubs' +import { CoreMeta, ResetCoreMetadata } from '../../src/stubs/core-stubs.js' +import { Summary } from '../../src/stubs/summary-stubs.js' let summary: Summary = new Summary() -describe('Summary', () => { - beforeAll(() => { - // Prevent output during tests - jest.spyOn(console, 'log').mockImplementation() - }) +// Prevent output during tests +jest.spyOn(console, 'log').mockImplementation(() => {}) +describe('Summary', () => { beforeEach(() => { // Reset the summary instance summary = new Summary() @@ -20,7 +19,6 @@ describe('Summary', () => { }) afterEach(() => { - // Reset all spies jest.resetAllMocks() // Restore environment metadata @@ -34,12 +32,14 @@ describe('Summary', () => { }) describe('filePath()', () => { - let fs_accessSyncSpy: jest.SpyInstance - let fs_existsSyncSpy: jest.SpyInstance - let path_resolveSpy: jest.SpyInstance + let fs_accessSyncSpy: jest.SpiedFunction + let fs_existsSyncSpy: jest.SpiedFunction + let path_resolveSpy: jest.SpiedFunction beforeEach(() => { - fs_accessSyncSpy = jest.spyOn(fs, 'accessSync').mockImplementation() + fs_accessSyncSpy = jest + .spyOn(fs, 'accessSync') + .mockImplementation(() => {}) fs_existsSyncSpy = jest.spyOn(fs, 'existsSync').mockReturnValue(true) path_resolveSpy = jest .spyOn(path, 'resolve') @@ -115,19 +115,23 @@ describe('Summary', () => { }) describe('write()', () => { - let fs_accessSyncSpy: jest.SpyInstance - let fs_appendFileSyncSpy: jest.SpyInstance - let fs_existsSyncSpy: jest.SpyInstance - let fs_writeFileSyncSpy: jest.SpyInstance - let path_resolveSpy: jest.SpyInstance + let fs_accessSyncSpy: jest.SpiedFunction + let fs_appendFileSyncSpy: jest.SpiedFunction + let fs_existsSyncSpy: jest.SpiedFunction + let fs_writeFileSyncSpy: jest.SpiedFunction + let path_resolveSpy: jest.SpiedFunction beforeEach(() => { - fs_accessSyncSpy = jest.spyOn(fs, 'accessSync').mockImplementation() + fs_accessSyncSpy = jest + .spyOn(fs, 'accessSync') + .mockImplementation(() => {}) fs_appendFileSyncSpy = jest .spyOn(fs, 'appendFileSync') - .mockImplementation() + .mockImplementation(() => {}) fs_existsSyncSpy = jest.spyOn(fs, 'existsSync').mockReturnValueOnce(true) - fs_writeFileSyncSpy = jest.spyOn(fs, 'writeFileSync').mockImplementation() + fs_writeFileSyncSpy = jest + .spyOn(fs, 'writeFileSync') + .mockImplementation(() => {}) path_resolveSpy = jest .spyOn(path, 'resolve') .mockReturnValueOnce('/path/to/summary.md') @@ -192,11 +196,12 @@ describe('Summary', () => { describe('clear()', () => { it('Clears the buffer', async () => { - const summary_emptyBufferSpy = jest.fn().mockReturnValue(summary) - const summary_writeSpy = jest.fn().mockResolvedValue(summary) - - summary.emptyBuffer = summary_emptyBufferSpy - summary.write = summary_writeSpy + const summary_emptyBufferSpy = jest + .spyOn(summary, 'emptyBuffer') + .mockReturnValue(summary) + const summary_writeSpy = jest + .spyOn(summary, 'write') + .mockResolvedValue(summary) await summary.clear() @@ -243,9 +248,9 @@ describe('Summary', () => { }) it('Adds an EOL', () => { - const summary_addEOLSpy = jest.fn().mockReturnValue(summary) - - summary.addEOL = summary_addEOLSpy + const summary_addEOLSpy = jest + .spyOn(summary, 'addEOL') + .mockReturnValue(summary) summary.addRaw('text', true) @@ -256,9 +261,9 @@ describe('Summary', () => { describe('addEOL()', () => { it('Adds an EOL', () => { - const summary_addRawSpy = jest.fn().mockReturnValue(summary) - - summary.addRaw = summary_addRawSpy + const summary_addRawSpy = jest + .spyOn(summary, 'addRaw') + .mockReturnValue(summary) summary.addEOL() diff --git a/__tests__/utils/output.test.ts b/__tests__/utils/output.test.ts index a5db17f..8dd030a 100644 --- a/__tests__/utils/output.test.ts +++ b/__tests__/utils/output.test.ts @@ -1,16 +1,14 @@ -import { ResetCoreMetadata } from '../../src/stubs/core-stubs' -import { ResetEnvMetadata } from '../../src/stubs/env-stubs' -import { printTitle } from '../../src/utils/output' +import { jest } from '@jest/globals' +import { ResetCoreMetadata } from '../../src/stubs/core-stubs.js' +import { ResetEnvMetadata } from '../../src/stubs/env-stubs.js' +import { printTitle } from '../../src/utils/output.js' -let console_logSpy: jest.SpyInstance +// Prevent output during tests +const console_logSpy: jest.SpiedFunction = jest + .spyOn(console, 'log') + .mockImplementation(() => {}) describe('Output', () => { - beforeAll(() => { - // Prevent output during tests - console_logSpy = jest.spyOn(console, 'log').mockImplementation() - jest.spyOn(console, 'table').mockImplementation() - }) - beforeEach(() => { // Reset metadata ResetEnvMetadata() @@ -18,7 +16,6 @@ describe('Output', () => { }) afterEach(() => { - // Reset all spies jest.resetAllMocks() }) diff --git a/bin/local-action b/bin/local-action.js similarity index 80% rename from bin/local-action rename to bin/local-action.js index 08dd5ea..2e6c42b 100755 --- a/bin/local-action +++ b/bin/local-action.js @@ -1,7 +1,9 @@ #!/usr/bin/env node -const fs = require('fs') -const path = require('path') -const { execSync } = require('child_process') +import * as fs from 'fs' +import { execSync } from 'node:child_process' +import { dirname } from 'node:path' +import { fileURLToPath } from 'node:url' +import * as path from 'path' /** * This script is used to run the local action. It sets the NODE_OPTIONS @@ -10,6 +12,8 @@ const { execSync } = require('child_process') */ function entrypoint() { + const __dirname = dirname(fileURLToPath(import.meta.url)) + // Save the current environment and path. const envBackup = { ...process.env } const pathBackup = process.env.PATH @@ -25,8 +29,10 @@ function entrypoint() { // need to be double-escaped so the path resolves correctly. const bootstrapPath = process.platform === 'win32' - ? path.join(packagePath, 'src', 'bootstrap.js').replaceAll('\\', '\\\\') - : path.join(packagePath, 'src', 'bootstrap.js') + ? path + .join(packagePath, 'src', 'bootstrap.mts') + .replaceAll('\\', '\\\\') + : path.join(packagePath, 'src', 'bootstrap.mts') // Require the bootstrap script in NODE_OPTIONS. process.env.NODE_OPTIONS = process.env.NODE_OPTIONS @@ -34,7 +40,7 @@ function entrypoint() { : `--require ${bootstrapPath}` // Start building the command to run local-action. - let command = `npx tsx "${path.join(packagePath, 'src', 'index.ts')}"` + let command = `npx tsx --loader=quibble "${path.join(packagePath, 'src', 'index.ts')}"` // Process the input arguments. if (process.argv.length === 2) { diff --git a/jest.config.ts b/jest.config.ts index 286f786..873c7e4 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,15 +1,15 @@ -import type { Config } from 'jest' +import type { JestConfigWithTsJest } from 'ts-jest' -const config: Config = { - //extensionsToTreatAsEsm: ['.ts'], - // "moduleNameMapper": { - // "^fixtures/(.*)$": "/__fixtures__/$1", - // } +const config: JestConfigWithTsJest = { clearMocks: true, collectCoverage: true, - collectCoverageFrom: ['src/**/*.ts'], + collectCoverageFrom: ['src/**'], coverageDirectory: 'coverage', - coveragePathIgnorePatterns: ['node_modules'], + coveragePathIgnorePatterns: [ + 'node_modules', + 'src/bootstrap.mts', + 'src/types/quibble.d.ts' + ], coverageReporters: ['json-summary', 'lcov', 'text'], coverageThreshold: { global: { @@ -19,12 +19,14 @@ const config: Config = { statements: 100 } }, - moduleDirectories: ['node_modules', 'src'], + extensionsToTreatAsEsm: ['.ts'], moduleFileExtensions: ['js', 'ts'], preset: 'ts-jest', reporters: ['default', 'jest-junit'], resolver: 'ts-jest-resolver', + roots: ['/src/', '/__fixtures__/', '/__tests__/'], testMatch: ['**/*.test.ts'], + testPathIgnorePatterns: ['/dist/', '/megalinter-reports/', '/node_modules/'], transform: { '\\.[jt]sx?$': [ 'ts-jest', diff --git a/package-lock.json b/package-lock.json index 6c4f54c..126152b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@github/local-action", - "version": "1.5.1", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@github/local-action", - "version": "1.5.1", + "version": "2.0.0", "license": "MIT", "dependencies": { "@actions/core": "^1.10.1", @@ -16,14 +16,14 @@ "dotenv": "^16.4.5", "figlet": "^1.7.0", "js-yaml": "^4.1.0", - "proxyquire": "^2.1.3", + "quibble": "^0.9.2", "tsconfig-paths": "^4.2.0", - "tsx": "^4.7.1", - "typescript": "^5.4.5", + "tsx": "^4.16.5", + "typescript": "^5.5.4", "yaml": "^2.4.2" }, "bin": { - "local-action": "bin/local-action" + "local-action": "bin/local-action.js" }, "devDependencies": { "@jest/globals": "^29.7.0", @@ -37,13 +37,13 @@ "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-github": "^5.0.1", "eslint-plugin-jest": "^28.3.0", - "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-prettier": "^5.2.1", "jest": "^29.7.0", "jest-junit": "^16.0.0", "make-coverage-badge": "^1.2.0", - "prettier": "^3.2.4", + "prettier": "^3.3.3", "prettier-eslint": "^16.3.0", - "ts-jest": "^29.1.2", + "ts-jest": "^29.2.4", "ts-jest-resolver": "^2.0.1" }, "engines": { @@ -748,9 +748,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", "cpu": [ "ppc64" ], @@ -759,13 +759,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", "cpu": [ "arm" ], @@ -774,13 +774,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", "cpu": [ "arm64" ], @@ -789,13 +789,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", "cpu": [ "x64" ], @@ -804,13 +804,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", "cpu": [ "arm64" ], @@ -819,13 +819,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", "cpu": [ "x64" ], @@ -834,13 +834,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", "cpu": [ "arm64" ], @@ -849,13 +849,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", "cpu": [ "x64" ], @@ -864,13 +864,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", "cpu": [ "arm" ], @@ -879,13 +879,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", "cpu": [ "arm64" ], @@ -894,13 +894,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", "cpu": [ "ia32" ], @@ -909,13 +909,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", "cpu": [ "loong64" ], @@ -924,13 +924,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", "cpu": [ "mips64el" ], @@ -939,13 +939,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", "cpu": [ "ppc64" ], @@ -954,13 +954,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", "cpu": [ "riscv64" ], @@ -969,13 +969,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", "cpu": [ "s390x" ], @@ -984,13 +984,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", "cpu": [ "x64" ], @@ -999,13 +999,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", "cpu": [ "x64" ], @@ -1014,13 +1014,28 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", "cpu": [ "x64" ], @@ -1029,13 +1044,13 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", "cpu": [ "x64" ], @@ -1044,13 +1059,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", "cpu": [ "arm64" ], @@ -1059,13 +1074,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", "cpu": [ "ia32" ], @@ -1074,13 +1089,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", "cpu": [ "x64" ], @@ -1089,7 +1104,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -2737,6 +2752,12 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, "node_modules/asynciterator.prototype": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", @@ -3458,6 +3479,21 @@ "url": "https://dotenvx.com" } }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.665", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.665.tgz", @@ -3635,40 +3671,41 @@ } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" } }, "node_modules/escalade": { @@ -4128,13 +4165,13 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.9.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4478,16 +4515,25 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/fill-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", - "integrity": "sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA==", + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, "dependencies": { - "is-object": "~1.0.1", - "merge-descriptors": "~1.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, "node_modules/fill-range": { @@ -5237,14 +5283,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", - "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -5473,6 +5511,95 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/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/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -6890,8 +7017,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -7071,14 +7197,6 @@ "tmpl": "1.0.5" } }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7151,11 +7269,6 @@ "node": ">=10" } }, - "node_modules/module-not-found-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", - "integrity": "sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g==" - }, "node_modules/mri": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", @@ -7559,9 +7672,9 @@ } }, "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -7750,16 +7863,6 @@ "node": ">= 6" } }, - "node_modules/proxyquire": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", - "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", - "dependencies": { - "fill-keys": "^1.0.2", - "module-not-found-error": "^1.0.1", - "resolve": "^1.11.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7805,6 +7908,18 @@ } ] }, + "node_modules/quibble": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/quibble/-/quibble-0.9.2.tgz", + "integrity": "sha512-BrL7hrZcbyyt5ZDfePkGFDc3m82uUtxCPOnpRUrkOdtBnmV9ldQKxXORkKL8eIzToRNaCpIPyKyfdfq/tBlFAA==", + "dependencies": { + "lodash": "^4.17.21", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">= 0.14.0" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -8340,9 +8455,9 @@ } }, "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", "dev": true, "dependencies": { "@pkgr/core": "^0.1.0", @@ -8446,12 +8561,13 @@ } }, "node_modules/ts-jest": { - "version": "29.1.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.5.tgz", - "integrity": "sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==", + "version": "29.2.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.4.tgz", + "integrity": "sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==", "dev": true, "dependencies": { "bs-logger": "0.x", + "ejs": "^3.1.10", "fast-json-stable-stringify": "2.x", "jest-util": "^29.0.0", "json5": "^2.2.3", @@ -8568,17 +8684,17 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "dev": true }, "node_modules/tsx": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.16.0.tgz", - "integrity": "sha512-MPgN+CuY+4iKxGoJNPv+1pyo5YWZAQ5XfsyobUG+zoKG7IkvCPLZDEyoIb8yLS2FcWci1nlxAqmvPlFWD5AFiQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.17.0.tgz", + "integrity": "sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg==", "dependencies": { - "esbuild": "~0.21.5", + "esbuild": "~0.23.0", "get-tsconfig": "^4.7.5" }, "bin": { @@ -8698,9 +8814,9 @@ } }, "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index b37134e..69ac40d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "@github/local-action", "description": "Local Debugging for GitHub Actions", - "version": "1.5.1", + "version": "2.0.0", + "type": "module", "author": "Nick Alteen ", "private": false, "homepage": "https://github.com/github/local-action", @@ -24,7 +25,7 @@ ".": "./src/index.ts" }, "bin": { - "local-action": "bin/local-action" + "local-action": "bin/local-action.js" }, "scripts": { "ci-test": "NODE_OPTIONS=\"$NODE_OPTIONS --no-warnings --experimental-vm-modules\" jest --ci --reporters=default --reporters=jest-junit --no-cache", @@ -48,10 +49,10 @@ "dotenv": "^16.4.5", "figlet": "^1.7.0", "js-yaml": "^4.1.0", - "proxyquire": "^2.1.3", + "quibble": "^0.9.2", "tsconfig-paths": "^4.2.0", - "tsx": "^4.7.1", - "typescript": "^5.4.5", + "tsx": "^4.16.5", + "typescript": "^5.5.4", "yaml": "^2.4.2" }, "devDependencies": { @@ -66,13 +67,13 @@ "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-github": "^5.0.1", "eslint-plugin-jest": "^28.3.0", - "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-prettier": "^5.2.1", "jest": "^29.7.0", "jest-junit": "^16.0.0", "make-coverage-badge": "^1.2.0", - "prettier": "^3.2.4", + "prettier": "^3.3.3", "prettier-eslint": "^16.3.0", - "ts-jest": "^29.1.2", + "ts-jest": "^29.2.4", "ts-jest-resolver": "^2.0.1" } } diff --git a/src/bootstrap.js b/src/bootstrap.js deleted file mode 100644 index 87f5334..0000000 --- a/src/bootstrap.js +++ /dev/null @@ -1,43 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-var-requires */ - -/** - * This file is used to bootstrap the environment for the action. - * - * It is added as a --require option to `npx tsx`. The purpose of this file is - * to handle TypeScript configuration options such as path mapping. For example, - * if an action repository makes use of TypeScript paths for module resolution, - * this bootstrap script will parse them and register them so that modules can - * be resolved correctly. - */ -if (process.env.TARGET_ACTION_PATH && process.env.TARGET_ACTION_PATH !== '') { - const fs = require('fs') - - // Check if the action has a `tsconfig.json` file. - if (fs.existsSync(`${process.env.TARGET_ACTION_PATH}/tsconfig.json`)) { - const tsConfigPaths = require('tsconfig-paths') - - // Load the `tsconfig.json` from the action directory. - const actionTsConfig = JSON.parse( - fs.readFileSync( - `${process.env.TARGET_ACTION_PATH}/tsconfig.json`, - 'utf-8' - ) - ) - - // Load the current `tsconfig.json` from the root of this directory. - tsConfigPaths.loadConfig(__dirname) - - // Get the paths from the action's `tsconfig.json`, if any. - const paths = actionTsConfig.compilerOptions.paths ?? {} - - // Add any path mappings from the imported action. Replace the base URL with - // the target action path. - // @todo Should this take into account the previous `baseUrl` value? - tsConfigPaths.register({ - baseUrl: process.env.TARGET_ACTION_PATH, - paths, - addMatchAll: true - }) - } -} diff --git a/src/bootstrap.mts b/src/bootstrap.mts new file mode 100644 index 0000000..335de84 --- /dev/null +++ b/src/bootstrap.mts @@ -0,0 +1,48 @@ +/* eslint-disable github/no-then */ +/* eslint-disable @typescript-eslint/no-floating-promises */ + +/** + * This file is used to bootstrap the environment for the action. + * + * It is added as a --require option to `npx tsx`. The purpose of this file is + * to handle various configuration options. For example, if an action repository + * makes use of TypeScript paths for module resolution, this bootstrap script + * will parse them and register them so that modules can be resolved correctly. + */ +import('fs').then(({ existsSync, readFileSync }) => { + import('tsconfig-paths').then(({ loadConfig, register }) => { + if ( + process.env.TARGET_ACTION_PATH && + process.env.TARGET_ACTION_PATH !== '' + ) { + // Check if the action has a `tsconfig.json` file. + if (existsSync(`${process.env.TARGET_ACTION_PATH}/tsconfig.json`)) { + // Load the `tsconfig.json` from the action directory. + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const actionTsConfig = JSON.parse( + readFileSync( + `${process.env.TARGET_ACTION_PATH}/tsconfig.json`, + 'utf-8' + ) + ) + + // Load the current `tsconfig.json` from the root of this directory. + loadConfig(__dirname) + + // Get the paths from the action's `tsconfig.json`, if any. + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const paths = actionTsConfig.compilerOptions.paths ?? {} + + // Add any path mappings from the imported action. Replace the base URL with + // the target action path. + // @todo Should this take into account the previous `baseUrl` value? + register({ + baseUrl: process.env.TARGET_ACTION_PATH, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + paths, + addMatchAll: true + }) + } + } + }) +}) diff --git a/src/command.ts b/src/command.ts index 66601d7..bb7c8a6 100644 --- a/src/command.ts +++ b/src/command.ts @@ -1,6 +1,8 @@ import { Command, InvalidArgumentError } from 'commander' -import { action as runAction } from './commands/run' -import { EnvMeta } from './stubs/env-stubs' +import { dirname } from 'node:path' +import { fileURLToPath } from 'node:url' +import { action } from './commands/run.js' +import { EnvMeta } from './stubs/env-stubs.js' /** * Creates the program for the CLI @@ -36,6 +38,7 @@ export async function makeProgram(): Promise { // Confirm there is an `action.yml` or `action.yaml` in the directory and // save the path to environment metadata + /* istanbul ignore else */ if (fs.existsSync(path.resolve(actionPath, 'action.yml'))) EnvMeta.actionFile = path.resolve(EnvMeta.actionPath, 'action.yml') else if (fs.existsSync(path.resolve(actionPath, 'action.yaml'))) @@ -86,6 +89,8 @@ export async function makeProgram(): Promise { return dotenvFile } + const __dirname = dirname(fileURLToPath(import.meta.url)) + program .name('local-action') .description('Test a GitHub Action locally') @@ -104,7 +109,7 @@ export async function makeProgram(): Promise { ) .argument('', 'Path to the local .env file', checkDotenvFile) .action(async () => { - await runAction() + await action() }) return program diff --git a/src/commands/run.ts b/src/commands/run.ts index f6a5211..01b99f4 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -1,33 +1,10 @@ import { config } from 'dotenv' -import proxyquire from 'proxyquire' -import { - CoreMeta, - addPath, - debug, - endGroup, - error, - exportVariable, - getBooleanInput, - getIDToken, - getInput, - getMultilineInput, - getState, - group, - info, - isDebug, - notice, - saveState, - setCommandEcho, - setFailed, - setOutput, - setSecret, - startGroup, - warning -} from '../stubs/core-stubs' -import { Summary } from '../stubs/summary-stubs' -import { EnvMeta } from '../stubs/env-stubs' -import type { Action } from '../types' -import { printTitle } from '../utils/output' +import quibble from 'quibble' +import { CORE_STUBS, CoreMeta } from '../stubs/core-stubs.js' +import { EnvMeta } from '../stubs/env-stubs.js' +import type { Action } from '../types.js' +import { printTitle } from '../utils/output.js' +import { isESM } from '../utils/package.js' export async function action(): Promise { const { Chalk } = await import('chalk') @@ -80,13 +57,17 @@ export async function action(): Promise { // Load action settings CoreMeta.stepDebug = process.env.ACTIONS_STEP_DEBUG === 'true' + /* istanbul ignore next */ CoreMeta.stepSummaryPath = process.env.GITHUB_STEP_SUMMARY ?? '' // Read the action.yml file and parse the expected inputs/outputs const actionYaml: Action = YAML.parse( fs.readFileSync(EnvMeta.actionFile, { encoding: 'utf8', flag: 'r' }) ) as Action + + /* istanbul ignore next */ EnvMeta.inputs = actionYaml.inputs || {} + /* istanbul ignore next */ EnvMeta.outputs = actionYaml.outputs || {} // Output the action metadata @@ -109,32 +90,24 @@ export async function action(): Promise { printTitle(CoreMeta.colors.green, 'Running Action') - // Stub the `@actions/toolkit` libraries and run the action - await proxyquire(path.resolve(EnvMeta.entrypoint).toString(), { - '@actions/core': { - '@global': true, - addPath, - debug, - endGroup, - error, - exportVariable, - getBooleanInput, - getIDToken, - getInput, - getMultilineInput, - getState, - group, - info, - isDebug, - notice, - saveState, - setCommandEcho, - setFailed, - setOutput, - setSecret, - startGroup, - summary: new Summary(), - warning - } - }) + // Stub the `@actions/toolkit` libraries and run the action. Quibble requires + // a different approach depending on if this is an ESM action. + if (isESM()) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + await quibble.esm( + `${path.resolve(EnvMeta.actionPath)}/node_modules/@actions/core/lib/core.js`, + CORE_STUBS + ) + } else { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + quibble( + `${path.resolve(EnvMeta.actionPath)}/node_modules/@actions/core`, + CORE_STUBS + ) + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const { run } = await import(path.resolve(EnvMeta.entrypoint)) + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + await run() } diff --git a/src/index.ts b/src/index.ts index 17c3afc..9af835f 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,6 @@ #!/usr/bin/env tsx import type { Command } from 'commander' -import { textSync } from 'figlet' -import { makeProgram } from './command' +import { makeProgram } from './command.js' /** * Runs the CLI program @@ -10,6 +9,7 @@ import { makeProgram } from './command' */ export async function run(): Promise { const chalk = (await import('chalk')).default + const { textSync } = (await import('figlet')).default // Print the header console.log(chalk.blue(textSync('Action Debugger'))) @@ -21,4 +21,4 @@ export async function run(): Promise { /* istanbul ignore next */ // eslint-disable-next-line @typescript-eslint/no-floating-promises -if (require.main === module) run() +run() diff --git a/src/stubs/core-stubs.ts b/src/stubs/core-stubs.ts index e354275..e0df90d 100644 --- a/src/stubs/core-stubs.ts +++ b/src/stubs/core-stubs.ts @@ -1,5 +1,36 @@ -import type { AnnotationProperties, CoreMetadata, InputOptions } from '../types' -import { EnvMeta } from './env-stubs' +import path from 'path' +import type { + AnnotationProperties, + CoreMetadata, + InputOptions +} from '../types.js' +import { EnvMeta } from './env-stubs.js' +import { Summary } from './summary-stubs.js' + +export const CORE_STUBS = { + addPath, + debug, + endGroup, + error, + exportVariable, + getBooleanInput, + getIDToken, + getInput, + getMultilineInput, + getState, + group, + info, + isDebug, + notice, + saveState, + setCommandEcho, + setFailed, + setOutput, + setSecret, + startGroup, + summary: new Summary(), + warning +} /** * Metadata for `@actions/core` @@ -86,10 +117,6 @@ export function setSecret(secret: string): void { * @returns void */ export function addPath(inputPath: string): void { - // Importing with require is necessary to avoid async/await. - // eslint-disable-next-line @typescript-eslint/no-var-requires - const path = require('path') as typeof import('path') - EnvMeta.path = `${inputPath}${path.delimiter}${process.env.PATH}` process.env.PATH = EnvMeta.path } @@ -507,10 +534,9 @@ export function getState(name: string): string { * @param aud The audience for the token * @returns A promise that resolves the OIDC token */ +// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars export async function getIDToken(aud?: string): Promise { - await Promise.reject(new Error('Not implemented')) - /* istanbul ignore next */ - return aud + throw new Error('Not implemented') } //----------------------------------------------------------------------- @@ -547,9 +573,5 @@ export function toWin32Path(pth: string): string { * @return The platform-specific path */ export function toPlatformPath(pth: string): string { - // Importing with require is necessary to avoid async/await. - // eslint-disable-next-line @typescript-eslint/no-var-requires - const path = require('path') as typeof import('path') - return pth.replace(/[/\\]/g, path.sep) } diff --git a/src/stubs/env-stubs.ts b/src/stubs/env-stubs.ts index 3f98b96..c155e58 100644 --- a/src/stubs/env-stubs.ts +++ b/src/stubs/env-stubs.ts @@ -1,4 +1,4 @@ -import type { EnvMetadata } from '../types' +import type { EnvMetadata } from '../types.js' /** * Environment Metdata diff --git a/src/stubs/summary-stubs.ts b/src/stubs/summary-stubs.ts index c80d3ef..245c7b9 100644 --- a/src/stubs/summary-stubs.ts +++ b/src/stubs/summary-stubs.ts @@ -1,12 +1,12 @@ import fs from 'fs' import { EOL } from 'os' import path from 'path' -import { CoreMeta } from './core-stubs' import type { SummaryImageOptions, SummaryTableRow, SummaryWriteOptions -} from '../types' +} from '../types.js' +import { CoreMeta } from './core-stubs.js' /** * A class for creating and writing job step summaries. diff --git a/src/types/quibble.d.ts b/src/types/quibble.d.ts new file mode 100644 index 0000000..39f0e14 --- /dev/null +++ b/src/types/quibble.d.ts @@ -0,0 +1,5 @@ +declare module 'quibble' { + const quibble: any + export default quibble + export const esm: any +} diff --git a/src/utils/package.ts b/src/utils/package.ts new file mode 100644 index 0000000..b2d10a0 --- /dev/null +++ b/src/utils/package.ts @@ -0,0 +1,18 @@ +import fs from 'fs' +import { EnvMeta } from '../stubs/env-stubs.js' + +/** + * Checks if the JavaScript/TypeScript project is an ESM module. + * + * @returns True if the project is an ESM module, false otherwise. + */ +export function isESM(): boolean { + const packageJson = JSON.parse( + fs.readFileSync(`${EnvMeta.actionPath}/package.json`, { + encoding: 'utf8', + flag: 'r' + }) + ) as { type: string } + + return packageJson.type === 'module' +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..7b949d0 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowImportingTsExtensions": true, + "allowJs": true, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "declaration": true, + "declarationMap": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2022"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "newLine": "lf", + "noEmit": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "paths": {}, + "pretty": true, + "resolveJsonModule": true, + "rootDir": ".", + "sourceMap": true, + "strict": true, + "strictNullChecks": true, + "target": "ES2022" + } +} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index f557238..d8c8ac6 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -1,6 +1,13 @@ { "$schema": "https://json.schemastore.org/tsconfig", "extends": "./tsconfig.json", - "include": ["__fixtures__", "__tests__", "src"], + "include": [ + "__fixtures__/core.ts", + "__fixtures__/tsconfig-paths.ts", + "__tests__", + "src", + "types", + "jest.config.ts" + ], "exclude": ["node_modules", "coverage"] } diff --git a/tsconfig.json b/tsconfig.json index 3bdc64a..72b7386 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,18 @@ { "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.base.json", "compilerOptions": { - "allowImportingTsExtensions": true, - "allowJs": true, - "baseUrl": ".", - "esModuleInterop": true, - "lib": ["ES2022"], "module": "NodeNext", "moduleResolution": "NodeNext", "noEmit": true, - "noImplicitAny": false, - "paths": {}, - "resolveJsonModule": true, - "rootDir": ".", - "strict": false, "target": "ES2022" }, - "exclude": ["__fixtures__", "__tests__", "coverage", "node_modules"] + "include": ["src", "types"], + "exclude": [ + "__fixtures__/core.ts", + "__fixtures__/tsconfig-paths.ts", + "__tests__", + "coverage", + "node_modules" + ] }