diff --git a/.github/workflows/automated-test.yml b/.github/workflows/automated-test.yml new file mode 100644 index 0000000..cb89963 --- /dev/null +++ b/.github/workflows/automated-test.yml @@ -0,0 +1,19 @@ +name: Node.js CI + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: '20.x' + - uses: pnpm/action-setup@v3 + with: + version: 8 + - run: pnpm i + - run: pnpm run build + - run: pnpm test diff --git a/package.json b/package.json index 6a6eb49..bb72c78 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,10 @@ "discord-mod-compiler": "dist/cli.js" }, "scripts": { - "test": "npm run cli ./test/sample/index.js RePlugged", + "test": "tsc && node ./test/check-converters.js", "cli": "tsc && node dist/cli.js", "cli-debug": "tsc && node --inspect dist/cli.js", + "cli-nobuild": "node dist/cli.js", "build": "tsc" }, "keywords": [], diff --git a/src/api/ModImplementation.ts b/src/api/ModImplementation.ts new file mode 100644 index 0000000..5ffae34 --- /dev/null +++ b/src/api/ModImplementation.ts @@ -0,0 +1,9 @@ +import { WebpackApi } from "./Webpack.js"; + +export interface IModImplementation { + WebpackApi: typeof WebpackApi, + /** + * shall be true when a mod requires the Dev to bundle their code into single file + */ + importsForbidden?: boolean, +} diff --git a/src/api/index.ts b/src/api/RuntimeGenerators.ts similarity index 77% rename from src/api/index.ts rename to src/api/RuntimeGenerators.ts index 18cd219..f7ecb4d 100644 --- a/src/api/index.ts +++ b/src/api/RuntimeGenerators.ts @@ -1,14 +1,3 @@ -import IBaseWebpackApi, { WebpackApi } from "./Webpack/index.js"; - -interface ModImplementation { - WebpackApi: typeof WebpackApi, - /** - * shall be true when a mod requires the Dev to bundle their code into single file - */ - importsForbidden?: boolean, -} -export { IBaseWebpackApi, WebpackApi, ModImplementation }; - /** * Creates a function from a given path that gets resolved at runtime. * @param path - The path to create the function from. diff --git a/src/api/Webpack/index.ts b/src/api/Webpack.ts similarity index 85% rename from src/api/Webpack/index.ts rename to src/api/Webpack.ts index 15afbb3..7909bbc 100644 --- a/src/api/Webpack/index.ts +++ b/src/api/Webpack.ts @@ -1,7 +1,6 @@ -interface IBaseWebpackApi { +export interface IBaseWebpackApi { getModule(filter: (match: any) => boolean): any; } -export default IBaseWebpackApi; class DummyWebpackApi implements IBaseWebpackApi { // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/src/cli.ts b/src/cli.ts index e841548..a56a074 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -6,10 +6,10 @@ import converter from "./converter.js"; import { File } from "@babel/types"; import { myPackageName } from "./utils.js"; import { transformSync } from "@babel/core"; -import { ModImplementation } from "./api/index.js"; +import { IModImplementation } from "./api/ModImplementation.js"; if (process.argv.length != 4) { - console.log("Usage:\n\t" + myPackageName + " \nExample:\n\t" + myPackageName + " ./index.js BetterDiscord"); + console.error(`Usage:\n\t${myPackageName} \nExample:\n\t${myPackageName} ./index.js BetterDiscord`); process.exit(1); } @@ -42,7 +42,7 @@ if (!isClientModSupported) { const filler = import(url.pathToFileURL(`${__dirname}/converters/${targetDiscordMod}.js`).href); // eslint-disable-next-line @typescript-eslint/no-unused-vars -filler.then((x: { default: ModImplementation }) => { +filler.then((x: { default: IModImplementation }) => { if (x.default.importsForbidden) console.warn('\x1b[33m%s\x1b[0m', `Warning: Target mod ${targetDiscordMod} requires your code to be bundled into single file`); const out = converter(ast as File & { errors: [] }, x); diff --git a/src/converter.ts b/src/converter.ts index eced9f6..059f3a8 100644 --- a/src/converter.ts +++ b/src/converter.ts @@ -2,7 +2,7 @@ import { ParseResult } from "@babel/parser"; import { File, Identifier, ImportDeclaration, ImportSpecifier, MemberExpression, Statement } from "@babel/types"; import { NonFunctionType, myPackageName } from "./utils.js"; -import { ModImplementation } from "./api/index.js"; +import { IModImplementation } from "./api/ModImplementation"; function removeASTLocation(ast: Statement[] | Statement) { if (Array.isArray(ast)) { @@ -95,7 +95,7 @@ function deepFind(obj: any, path: string): K | undefined { const getKeyValue = (obj: T, key: K): T[K] => obj[key]; -export default function (ast: ParseResult, targetedDiscordModApiLibrary: { default: ModImplementation }): Statement[] { +export default function (ast: ParseResult, targetedDiscordModApiLibrary: { default: IModImplementation }): Statement[] { const parsedBody = ast.program.body; const importStatements = parsedBody.filter(x => x.type == "ImportDeclaration"); const importsToBake = []; @@ -134,11 +134,11 @@ export default function (ast: ParseResult, targetedDiscordModApiLibrary: { console.log(trueObj); if (trueObj != undefined && importsToBake.includes((trueObj.object as Identifier).name)) { removeASTLocation(trueObj as unknown as Statement); - const propDesc = Object.getOwnPropertyDescriptor(targetedDiscordModApiLibrary.default, (trueObj.object as Identifier).name as keyof ModImplementation); + const propDesc = Object.getOwnPropertyDescriptor(targetedDiscordModApiLibrary.default, (trueObj.object as Identifier).name as keyof IModImplementation); if (!propDesc) continue; // const targetClass = targetedDiscordModApiLibrary.default[(trueObj.object as Identifier).name]; - const targetClass: ModImplementation[keyof ModImplementation] = propDesc.value ?? propDesc.get!(); // TODO: don't make value `any` + const targetClass: IModImplementation[keyof IModImplementation] = propDesc.value ?? propDesc.get!(); // TODO: don't make value `any` if (targetClass == undefined) continue; const replacementObject = getKeyValue(targetClass, (trueObj.property as Identifier).name as keyof typeof targetClass) as { object: string, property: string }; diff --git a/src/converters/betterdiscord.ts b/src/converters/betterdiscord.ts index f8f2500..783d0ac 100644 --- a/src/converters/betterdiscord.ts +++ b/src/converters/betterdiscord.ts @@ -1,4 +1,6 @@ -import { IBaseWebpackApi, ModImplementation, createFunctionFromObjectProperty } from "../api/index.js"; +import { IModImplementation } from "../api/ModImplementation.js"; +import { createFunctionFromObjectProperty } from "../api/RuntimeGenerators.js"; +import { IBaseWebpackApi } from "../api/Webpack.js"; class BDWebpackApi implements IBaseWebpackApi { get getModule() { @@ -9,4 +11,4 @@ class BDWebpackApi implements IBaseWebpackApi { export default { WebpackApi: new BDWebpackApi(), importsForbidden: true, -} as ModImplementation; +} as IModImplementation; diff --git a/src/converters/replugged.ts b/src/converters/replugged.ts index 6559957..702d4b5 100644 --- a/src/converters/replugged.ts +++ b/src/converters/replugged.ts @@ -1,4 +1,6 @@ -import { IBaseWebpackApi, ModImplementation, createFunctionFromObjectProperty } from "../api/index.js"; +import { IModImplementation } from "../api/ModImplementation.js"; +import { createFunctionFromObjectProperty } from "../api/RuntimeGenerators.js"; +import { IBaseWebpackApi } from "../api/Webpack.js"; class RPWebpackApi implements IBaseWebpackApi { get getModule() { @@ -8,4 +10,4 @@ class RPWebpackApi implements IBaseWebpackApi { export default { WebpackApi: new RPWebpackApi(), -} as ModImplementation; +} as IModImplementation; diff --git a/src/index.ts b/src/index.ts index d9e95da..3c7b07e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export * from "./api/index.js"; +export * from './api/Webpack.js'; \ No newline at end of file diff --git a/test/check-converters.js b/test/check-converters.js new file mode 100644 index 0000000..1275392 --- /dev/null +++ b/test/check-converters.js @@ -0,0 +1,20 @@ +import fs from 'fs'; +import path from 'path'; +import { execSync } from 'child_process'; + +// Read the files in ./src/converters +const files = fs.readdirSync('./src/converters'); + +// Filter .ts files and remove extension +const tsFiles = files.filter(file => path.extname(file) === '.ts').map(file => path.basename(file, '.ts')); + +for (const file of tsFiles) { + console.log(`Testing ${file}`); + + try { + execSync(`npm run cli-nobuild ./test/sample/index.js ${file}`, { stdio: 'inherit' }); + } catch (error) { + console.error(`Error in ${file}`); + process.exit(1); + } +} \ No newline at end of file