Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for TypeScript scripts #477

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
chore: Change back to the function-style operation
We lose the more normal looking export in favor of the `return` statement already traditional to this action, and thus can handle await statements without an ESM conversion.

This is a separate commit from the former because I think the next major version of this action should switch to ESM, revert this commit, and use the more standard export notation in all supported languages.
  • Loading branch information
Ricky C committed Jul 6, 2024
commit f27b40e6b7c7d0e475b6cbfe8c5bacd9349b782c
16 changes: 7 additions & 9 deletions __test__/interpret-script.test.ts
Original file line number Diff line number Diff line change
@@ -11,12 +11,12 @@ const scripts: Record<SupportedLanguage, string> = {
[SupportedLanguage.cts]: `
const FS = require('node:fs') // Proof that we are in CommonJS.
let a: string // Proof that we are in TypeScript.
exports = foo // Proof that we executed correctly.
return foo // Proof that we executed correctly.
`,
[SupportedLanguage.mts]: `
import FS from 'node:fs' // Proof that we are in an ES Module.
let a: string // Proof that we are in TypeScript.
export default foo // Proof that we executed correctly.
return foo // Proof that we executed correctly.
`
}

@@ -108,7 +108,7 @@ describe(interpretScript.name, () => {
{require} as any,
`
const {test} = require('../test/requireable')
exports = test()
return test()
`
)
return expect(result()).resolves.toEqual('hello')
@@ -165,13 +165,12 @@ describe(interpretScript.name, () => {
const result = await interpretScript(
SupportedLanguage.mts,
{} as any,
`export default {a: 'b'}`
`return {a: 'b'}`
)
return expect(result()).resolves.toEqual({a: 'b'})
})

test.skip(`a script that uses a root level await`, async () => {
// Will not work until we can actually run in ESM. Current code is transpiling the mts to cjs, so we don't get root level awaits yet.
test(`a script that uses a root level await`, async () => {
const result = await interpretScript(
SupportedLanguage.mts,
{} as any,
@@ -180,14 +179,13 @@ describe(interpretScript.name, () => {
return expect(result()).resolves
})

test.skip(`a script imports a script from disk`, async () => {
// Will not work until we can actually run in ESM. Current code is transpiling the mts to cjs, so we don't get root level awaits yet.
test(`a script imports a script from disk`, async () => {
const result = await interpretScript(
SupportedLanguage.mts,
{require} as any,
`
const {test} = await import('../test/importable')
export default test()
return test()
`
)
return expect(result()).resolves.toEqual('hello')
29 changes: 10 additions & 19 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -213408,21 +213408,23 @@ var io = __nccwpck_require__(7436);
var dist_node = __nccwpck_require__(8883);
// EXTERNAL MODULE: ./node_modules/@octokit/plugin-retry/dist-node/index.js
var plugin_retry_dist_node = __nccwpck_require__(6298);
;// CONCATENATED MODULE: external "node:vm"
const external_node_vm_namespaceObject = require("node:vm");
// EXTERNAL MODULE: ./node_modules/typescript/lib/typescript.js
var typescript = __nccwpck_require__(7414);
;// CONCATENATED MODULE: ./src/async-function.ts
const AsyncFunction = Object.getPrototypeOf(async () => null).constructor;
function callAsyncFunction(args, source) {
const fn = new AsyncFunction(...Object.keys(args), source);
return fn(...Object.values(args));
const commonJsArgs = {
...args,
module: { exports: {} },
exports: {}
};
const fn = new AsyncFunction(...Object.keys(commonJsArgs), source);
return fn(...Object.values(commonJsArgs));
}

;// CONCATENATED MODULE: ./src/interpret-script.ts



var SupportedLanguage;
(function (SupportedLanguage) {
SupportedLanguage["cjs"] = "cjs";
@@ -213431,31 +213433,20 @@ var SupportedLanguage;
})(SupportedLanguage || (SupportedLanguage = {}));
async function interpretScript(language, context, script) {
switch (language) {
case SupportedLanguage.cjs:
return async () => callAsyncFunction(context, script);
case SupportedLanguage.cts:
case SupportedLanguage.mts: {
const fileName = `github-script.${language}`;
const compilerResult = (0,typescript.transpileModule)(script, {
script = (0,typescript.transpileModule)(script, {
compilerOptions: {
module: typescript.ModuleKind.CommonJS,
target: typescript.ScriptTarget.Latest,
strict: true
},
fileName
});
return async () => {
const runContext = {
module: { exports: {} },
exports: {},
process,
...context
};
const runResult = external_node_vm_namespaceObject.runInNewContext(compilerResult.outputText, runContext);
return runResult;
};
}).outputText;
}
}
return async () => callAsyncFunction(context, script);
}

;// CONCATENATED MODULE: ./src/retry-options.ts
9 changes: 7 additions & 2 deletions src/async-function.ts
Original file line number Diff line number Diff line change
@@ -22,6 +22,11 @@ export function callAsyncFunction<T>(
args: AsyncFunctionArguments,
source: string
): Promise<T> {
const fn = new AsyncFunction(...Object.keys(args), source)
return fn(...Object.values(args))
const commonJsArgs = {
...args,
module: {exports: {}},
exports: {}
}
const fn = new AsyncFunction(...Object.keys(commonJsArgs), source)
return fn(...Object.values(commonJsArgs))
}
25 changes: 4 additions & 21 deletions src/interpret-script.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as VM from 'node:vm'

import * as core from '@actions/core'
import * as exec from '@actions/exec'
import {Context} from '@actions/github/lib/context'
@@ -36,35 +34,20 @@ export async function interpretScript<T>(
script: string
): Promise<() => Promise<T>> {
switch (language) {
case SupportedLanguage.cjs:
return async () => callAsyncFunction(context, script)
case SupportedLanguage.cts:
case SupportedLanguage.mts: {
const fileName = `github-script.${language}`

const compilerResult = transpileModule(script, {
script = transpileModule(script, {
compilerOptions: {
module: ModuleKind.CommonJS, // Take the incoming TypeScript and compile it to CommonJS to run in the CommonJS environment of this action.
target: ScriptTarget.Latest,
strict: true
},
fileName
})

return async () => {
const runContext: CjsContext & Record<string, unknown> = {
module: {exports: {}},
exports: {},
process,
...context
}
const runResult = VM.runInNewContext(
compilerResult.outputText,
runContext
)

return runResult
}
}).outputText
}
}

return async () => callAsyncFunction(context, script)
}