├── .husky ├── .gitignore └── pre-commit ├── .prettierrc.json ├── integs ├── test-files │ ├── not_a_config │ ├── test_main.js │ ├── invalid_config.json │ ├── test_main.ts │ ├── test_lib.ts │ └── other_config.json └── e2e.test.ts ├── .gitignore ├── .prettierignore ├── src ├── meow.ts ├── config.test.ts ├── constants.ts ├── main.ts ├── validate.ts ├── validate.test.ts └── config.ts ├── jest.config.js ├── rollup.config.js ├── .github ├── dependabot.yml └── workflows │ ├── node.js.yml │ └── codeql-analysis.yml ├── tsconfig.json ├── LICENSE ├── package.json ├── README.md └── dist └── cli.js /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /integs/test-files/not_a_config: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | integs/tmp/* 3 | coverage 4 | -------------------------------------------------------------------------------- /integs/test-files/test_main.js: -------------------------------------------------------------------------------- 1 | console.log("Test"); 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | build 3 | coverage 4 | dist/* 5 | -------------------------------------------------------------------------------- /integs/test-files/invalid_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "invalid": "key" 3 | } 4 | -------------------------------------------------------------------------------- /src/meow.ts: -------------------------------------------------------------------------------- 1 | import M from "meow"; 2 | 3 | export const meow = M; 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /integs/test-files/test_main.ts: -------------------------------------------------------------------------------- 1 | import { greet } from "./test_lib"; 2 | 3 | greet("This is a greeting"); 4 | -------------------------------------------------------------------------------- /integs/test-files/test_lib.ts: -------------------------------------------------------------------------------- 1 | export function greet(name: string) { 2 | console.log(`Hello, ${name}!`); 3 | } 4 | -------------------------------------------------------------------------------- /integs/test-files/other_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Other test script", 3 | "namespace": "http://tampermonkey.net/", 4 | "version": "0.1", 5 | "description": "Gorilla-built, rock-solid, Monkey script", 6 | "author": "You" 7 | } 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "node", 4 | coverageThreshold: { 5 | global: { 6 | branches: 85, 7 | functions: 90, 8 | lines: 90, 9 | }, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | // Rollup config for CLI package 2 | import typescript from "rollup-plugin-typescript"; 3 | export default { 4 | input: "src/main.ts", 5 | output: { 6 | file: "dist/cli.js", 7 | format: "cjs", 8 | banner: "#!/usr/bin/env node", //Required for node commands 9 | }, 10 | plugins: [typescript()], 11 | }; 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "lib": ["esnext"], 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "experimentalDecorators": true, 13 | "emitDecoratorMetadata": true, 14 | "declarationDir": "./dist", 15 | "outDir": "./dist", 16 | "typeRoots": ["node_modules/@types"] 17 | }, 18 | "include": ["src/**/*.ts"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /src/config.test.ts: -------------------------------------------------------------------------------- 1 | import { getBanner } from "./config"; 2 | import { ERROR_MSG } from "./constants"; 3 | 4 | test("handles empty config", () => { 5 | const config = {}; 6 | 7 | const output = getBanner(config); 8 | expect(output).toEqual(` 9 | // ==UserScript== 10 | 11 | // 12 | // Created with love using Gorilla 13 | // ==/UserScript== 14 | `); 15 | }); 16 | 17 | test("handles invalid config key", () => { 18 | const config = { 19 | invalid: "key", 20 | }; 21 | 22 | try { 23 | getBanner(config, true); 24 | } catch (err) { 25 | expect(err).toContain(ERROR_MSG.EXPECT_VALID_KEY); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const HELP_MENU = ` 2 | Usage 3 | $ gorilla 4 | 5 | Options 6 | --config, -c Custom GreaseMonkey config 7 | --input, -i (required) Input filename 8 | --output, -o (required) Output filename 9 | 10 | Examples 11 | $ gorilla --input ./my-script.ts --output ./my-script.user.js 12 | `; 13 | 14 | export const ERROR_MSG = { 15 | EXPECT_JSON_FILE: "Gorilla configs must be a JSON file", 16 | EXPECT_VALID_KEY: "Invalid gorilla config key(s):", 17 | }; 18 | 19 | export const WARN_MSG = { 20 | EXPECT_TYPESCRIPT: 21 | "Gorilla recommends that your input files be written in TypeScript", 22 | EXPECT_GM_EXTENSION: 23 | "GreaseMonkey scripts must end in '.user.js'. Consider renaming your output file.", 24 | EXPECT_GM_KEYS: 25 | "GreaseMonkey script includes keys that GreaseMonkey does not support: ", 26 | }; 27 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ mainline ] 9 | pull_request: 10 | branches: [ mainline ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [12.x, 14.x, 16.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm ci 28 | - run: npm run build --if-present 29 | - run: npm test 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © Alexander King 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | const typescript = require("rollup-plugin-typescript"); 2 | import { rollup, RollupOptions, OutputOptions } from "rollup"; 3 | import { nodeResolve } from "@rollup/plugin-node-resolve"; 4 | import validate from "./validate"; 5 | import { getConfig, getBanner } from "./config"; 6 | 7 | //Validate config input 8 | const { input, output, config, minify } = validate(); 9 | 10 | // Get config values 11 | const configJSON = getConfig(config); 12 | 13 | // Create banner text from config 14 | const banner = getBanner(configJSON); 15 | 16 | // Create Rollup config 17 | const outputConfig: OutputOptions = { 18 | file: output, 19 | banner: banner, 20 | format: "iife", 21 | compact: minify, 22 | }; 23 | const rollupConfig: RollupOptions = { 24 | input, 25 | output: outputConfig, 26 | plugins: [typescript(), nodeResolve()], 27 | }; 28 | 29 | // Call rollup 30 | rollup(rollupConfig) 31 | .then(async (bundle) => { 32 | await bundle.generate(outputConfig); 33 | return await bundle.write(outputConfig); 34 | }) 35 | .then(() => console.log("Gorilla smash complete!")); 36 | -------------------------------------------------------------------------------- /src/validate.ts: -------------------------------------------------------------------------------- 1 | import { meow } from "./meow"; 2 | import { HELP_MENU, ERROR_MSG, WARN_MSG } from "./constants"; 3 | 4 | const validate = () => { 5 | //Use Meow for arg parsing and validation 6 | const cli = meow(HELP_MENU, { 7 | flags: { 8 | config: { 9 | type: "string", 10 | alias: "c", 11 | }, 12 | quiet: { 13 | type: "boolean", 14 | alias: "q", 15 | default: false, 16 | }, 17 | minify: { 18 | type: "boolean", 19 | alias: "m", 20 | default: false, 21 | }, 22 | input: { 23 | type: "string", 24 | alias: "i", 25 | isRequired: true, 26 | }, 27 | output: { 28 | type: "string", 29 | alias: "o", 30 | isRequired: true, 31 | }, 32 | }, 33 | }); 34 | 35 | const { input, output, config, quiet } = cli.flags; 36 | 37 | // Validate expected filetypes 38 | if (config && !config.endsWith(".json")) { 39 | throw ERROR_MSG.EXPECT_JSON_FILE; 40 | } 41 | 42 | if (!quiet && !input.endsWith(".ts")) { 43 | console.warn(WARN_MSG.EXPECT_TYPESCRIPT); 44 | } 45 | 46 | //Provide warning on output 47 | if (!quiet && !output.endsWith("user.js")) { 48 | console.warn(WARN_MSG.EXPECT_GM_EXTENSION); 49 | } 50 | 51 | return cli.flags; 52 | }; 53 | 54 | export default validate; 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gorilla-build", 3 | "version": "0.1.16", 4 | "description": "Gorilla: Stop monkeying around and build better scripts.", 5 | "bin": { 6 | "gorilla": "dist/cli.js" 7 | }, 8 | "scripts": { 9 | "build": "rollup --config", 10 | "test": "npx jest ./src", 11 | "test:c": "npx jest --coverage ./src", 12 | "test:w": "npx jest --watchAll ./src", 13 | "integ": "npx jest ./integs", 14 | "integ:w": "npx jest --watchAll ./integs", 15 | "prepare": "husky install" 16 | }, 17 | "author": "Alex King", 18 | "license": "MIT", 19 | "dependencies": { 20 | "@rollup/plugin-node-resolve": "^15.0.1", 21 | "meow": "^9.0.0", 22 | "rollup": "^2.33.3", 23 | "rollup-plugin-typescript": "^1.0.1", 24 | "tslib": "^2.0.3", 25 | "typescript": "^4.0.5" 26 | }, 27 | "devDependencies": { 28 | "@rollup/plugin-typescript": "^11.0.0", 29 | "@types/jest": "^27.0.1", 30 | "@types/node": "^18.11.18", 31 | "@types/npm": "^7.19.0", 32 | "@types/sinon": "^10.0.2", 33 | "husky": "^8.0.1", 34 | "jest": "^27.5.1", 35 | "lint-staged": "^13.0.0", 36 | "npm": "^8.5.2", 37 | "prettier": "2.8.4", 38 | "sinon": "^14.0.0", 39 | "ts-jest": "^27.1.3" 40 | }, 41 | "repository": { 42 | "type": "git", 43 | "url": "git+https://github.com/apsking/gorilla.git" 44 | }, 45 | "keywords": [ 46 | "greasemonkey", 47 | "tampermonkey", 48 | "violentmonkey", 49 | "script", 50 | "userscript", 51 | "typescript", 52 | "build", 53 | "tool-chain", 54 | "cli" 55 | ], 56 | "bugs": { 57 | "url": "https://github.com/apsking/gorilla/issues" 58 | }, 59 | "homepage": "https://github.com/apsking/gorilla#readme", 60 | "lint-staged": { 61 | "*.{ts,js,css,md}": "prettier --write" 62 | }, 63 | "coveragePathIgnorePatterns": [ 64 | "/node_modules/", 65 | "/integs/tmp/**" 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ mainline ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ mainline ] 20 | schedule: 21 | - cron: '30 15 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /src/validate.test.ts: -------------------------------------------------------------------------------- 1 | import * as M from "./meow"; // = require('meow'); 2 | import validate from "./validate"; 3 | import sinon from "sinon"; 4 | import { ERROR_MSG, WARN_MSG } from "./constants"; 5 | 6 | const sandbox = sinon.createSandbox(); 7 | let stubMeow: sinon.SinonStub; 8 | let spyConsole: sinon.SinonStub; 9 | 10 | beforeEach(() => { 11 | stubMeow = sandbox.stub(M, "meow"); 12 | spyConsole = sandbox.stub(console, "warn"); 13 | }); 14 | 15 | afterEach(() => { 16 | sandbox.restore(); 17 | }); 18 | 19 | test("validates input is a ts file", () => { 20 | stubMeow.returns({ 21 | flags: { 22 | input: "test.js", 23 | output: "test.user.js", 24 | }, 25 | input: [], 26 | unnormalizedFlags: {}, 27 | pkg: {}, 28 | help: "", 29 | showHelp: () => {}, 30 | showVersion: () => {}, 31 | }); 32 | 33 | validate(); 34 | 35 | expect(spyConsole.args[0][0]).toEqual(WARN_MSG.EXPECT_TYPESCRIPT); 36 | }); 37 | 38 | test("validates output is is a GreaseMonkey extension", () => { 39 | stubMeow.returns({ 40 | flags: { 41 | input: "test.ts", 42 | output: "test.other.extension", 43 | }, 44 | input: [], 45 | unnormalizedFlags: {}, 46 | pkg: {}, 47 | help: "", 48 | showHelp: () => {}, 49 | showVersion: () => {}, 50 | }); 51 | 52 | validate(); 53 | 54 | expect(spyConsole.args[0][0]).toEqual(WARN_MSG.EXPECT_GM_EXTENSION); 55 | }); 56 | 57 | test("throws exception with invalid config", () => { 58 | stubMeow.returns({ 59 | flags: { 60 | input: "test.ts", 61 | output: "test.user.js", 62 | config: "test.other.extension", 63 | }, 64 | input: [], 65 | unnormalizedFlags: {}, 66 | pkg: {}, 67 | help: "", 68 | showHelp: () => {}, 69 | showVersion: () => {}, 70 | }); 71 | 72 | try { 73 | validate(); 74 | } catch (e) { 75 | expect(e).toEqual(ERROR_MSG.EXPECT_JSON_FILE); 76 | } 77 | }); 78 | 79 | test("validates quietly", () => { 80 | stubMeow.returns({ 81 | flags: { 82 | input: "test.js", 83 | output: "test.other.extension", 84 | quiet: true, 85 | }, 86 | input: [], 87 | unnormalizedFlags: {}, 88 | pkg: {}, 89 | help: "", 90 | showHelp: () => {}, 91 | showVersion: () => {}, 92 | }); 93 | 94 | validate(); 95 | 96 | expect(spyConsole.called).toEqual(false); 97 | }); 98 | 99 | test("validates output w/o any warnings", () => { 100 | stubMeow.returns({ 101 | flags: { 102 | input: "test.ts", 103 | output: "test.user.js", 104 | }, 105 | input: [], 106 | unnormalizedFlags: {}, 107 | pkg: {}, 108 | help: "", 109 | showHelp: () => {}, 110 | showVersion: () => {}, 111 | }); 112 | 113 | validate(); 114 | 115 | expect(spyConsole.called).toEqual(false); 116 | }); 117 | -------------------------------------------------------------------------------- /integs/e2e.test.ts: -------------------------------------------------------------------------------- 1 | const { exec } = require("child_process"); 2 | import * as fs from "fs"; 3 | import { ERROR_MSG, WARN_MSG } from "../src/constants"; 4 | 5 | const execPromise = ( 6 | execStr: string 7 | ): Promise<{ 8 | err: string; 9 | stdout: string; 10 | stderr: string; 11 | resolve: any; 12 | reject: any; 13 | }> => { 14 | return new Promise((resolve, reject) => { 15 | exec(execStr, (err: string, stdout: string, stderr: string) => { 16 | resolve({ err, stdout, stderr, resolve, reject }); 17 | }); 18 | }); 19 | }; 20 | 21 | // Remove all temp files before each run 22 | beforeEach(async () => { 23 | await execPromise("rm ./integs/tmp/*"); 24 | }); 25 | 26 | test("should show help menu", async () => { 27 | const { stdout } = await execPromise("gorilla --help"); 28 | 29 | expect(stdout).toContain( 30 | "Gorilla: Stop monkeying around and build better scripts." 31 | ); 32 | }); 33 | 34 | test("should throw error on unknown file", async () => { 35 | const { stderr } = await execPromise( 36 | "gorilla --input ./integs/test-files/not_a_file.ts --output ./integs/tmp/out.js" 37 | ); 38 | expect(stderr).toContain("Could not resolve entry module"); 39 | }); 40 | 41 | test("should throw error on bad config JSON filetype", async () => { 42 | const { stderr } = await execPromise( 43 | "gorilla --input ./integs/test-files/not_a_file.ts --output ./integs/tmp/out.js --config ./integs/test-files/not_a_config" 44 | ); 45 | expect(stderr).toContain(ERROR_MSG.EXPECT_JSON_FILE); 46 | }); 47 | 48 | test("should warn on bad gorilla config key", async () => { 49 | const { stderr } = await execPromise( 50 | "gorilla --input ./integs/test-files/test_main.ts --output ./integs/tmp/out.js --config ./integs/test-files/invalid_config.json" 51 | ); 52 | expect(stderr).toContain(WARN_MSG.EXPECT_GM_KEYS); 53 | }); 54 | 55 | test("should show warning for output filename", async () => { 56 | const { stderr } = await execPromise( 57 | "gorilla --input ./integs/test-files/test_main.ts --output ./integs/tmp/out.js" 58 | ); 59 | expect(stderr).toContain(WARN_MSG.EXPECT_GM_EXTENSION); 60 | }); 61 | 62 | test("should show warning for non-TypeScript input", async () => { 63 | const { stderr } = await execPromise( 64 | "gorilla --input ./integs/test-files/test_main.js --output ./integs/tmp/out.js" 65 | ); 66 | expect(stderr).toContain(WARN_MSG.EXPECT_TYPESCRIPT); 67 | }); 68 | 69 | test("should create script with package.json info", async () => { 70 | await execPromise( 71 | "gorilla --input ./integs/test-files/test_main.ts --output ./integs/tmp/out.js" 72 | ); 73 | const file = fs.readFileSync("./integs/tmp/out.js", "utf8"); 74 | expect(file).toContain("gorilla-build"); 75 | expect(file).toContain("Alex King"); 76 | expect(file).toContain("MIT"); 77 | }); 78 | 79 | test("should create script with custom config", async () => { 80 | await execPromise( 81 | "gorilla --config ./integs/test-files/other_config.json --input ./integs/test-files/test_main.ts --output ./integs/tmp/out.js" 82 | ); 83 | const file = fs.readFileSync("./integs/tmp/out.js", "utf8"); 84 | expect(file).toContain("Other test script"); //Just assert name of config 85 | }); 86 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import { WARN_MSG } from "./constants"; 3 | 4 | const PACKAGE_JSON_LOCATION = "./package.json"; 5 | const PACKAGE_JSON_GORILLA_KEY = "gorilla"; 6 | const PACKAGE_JSON_KEYS = [ 7 | "name", 8 | "version", 9 | "description", 10 | "author", 11 | "homepage", 12 | "copyright", 13 | "license", 14 | ]; 15 | 16 | /* 17 | * Attributes for all Metadata Block items: 18 | * https://wiki.greasespot.net/Metadata_Block 19 | */ 20 | export type GorillaConfig = { 21 | author?: string; 22 | description?: string; 23 | exclude?: string[]; 24 | grant?: string[]; 25 | icon?: string; 26 | include?: string[]; 27 | match?: string[]; 28 | name?: string; 29 | namespace?: string; 30 | noframes?: string; 31 | require?: string[]; 32 | resource?: string[]; 33 | updateURL?: string; 34 | downloadURL?: string; 35 | version?: string; 36 | [key: string]: undefined | string | string[]; //needed for lookup 37 | }; 38 | 39 | export const VALID_GORILLA_CONFIG_KEYS = [ 40 | "author", 41 | "description", 42 | "exclude", 43 | "grant", 44 | "icon", 45 | "include", 46 | "match", 47 | "name", 48 | "namespace", 49 | "noframes", 50 | "require", 51 | "resource", 52 | "version", 53 | "updateURL", 54 | "downloadURL", 55 | ]; 56 | 57 | /** 58 | * Priority: 59 | * 1. Input config 60 | * 2. package.json 61 | * @returns current GorillaConfig 62 | */ 63 | export const getConfig = (inputConfigLocation?: string): GorillaConfig => { 64 | const tmpConfig: GorillaConfig = {}; 65 | 66 | // Config passed in by input 67 | if (inputConfigLocation && inputConfigLocation !== "") { 68 | try { 69 | const inputConfig = JSON.parse( 70 | fs.readFileSync(inputConfigLocation, "utf8") 71 | ); 72 | Object.keys(inputConfig).forEach((key) => { 73 | tmpConfig[key] = inputConfig[key]; 74 | }); 75 | } catch (err) { 76 | console.error("Failed to parse input config", err); 77 | } 78 | } 79 | 80 | // Read `package.json` 81 | if (fs.existsSync(PACKAGE_JSON_LOCATION)) { 82 | const packageJSON = JSON.parse( 83 | fs.readFileSync(PACKAGE_JSON_LOCATION, "utf8") 84 | ); 85 | 86 | // Read common keys 87 | PACKAGE_JSON_KEYS.forEach((key) => { 88 | if (packageJSON[key]) { 89 | if (!tmpConfig[key]) { 90 | tmpConfig[key] = packageJSON[key]; 91 | } 92 | } 93 | }); 94 | 95 | // Read valid Gorilla keys 96 | if (packageJSON[PACKAGE_JSON_GORILLA_KEY]) { 97 | VALID_GORILLA_CONFIG_KEYS.forEach((key) => { 98 | if (packageJSON[PACKAGE_JSON_GORILLA_KEY][key]) { 99 | tmpConfig[key] = packageJSON[PACKAGE_JSON_GORILLA_KEY][key]; 100 | } 101 | }); 102 | } 103 | } 104 | 105 | return tmpConfig; 106 | }; 107 | 108 | /* 109 | * Fetch a GreaseMonkey-formatted banner text, which will 110 | * prepend the script itself. 111 | */ 112 | export const getBanner = ( 113 | config: GorillaConfig, 114 | quiet: boolean = false 115 | ): string => { 116 | const invalidItems = Object.keys(config).filter( 117 | (key) => !VALID_GORILLA_CONFIG_KEYS.includes(key) 118 | ); 119 | 120 | if (invalidItems.length > 0 && !quiet) { 121 | const msg = `${WARN_MSG.EXPECT_GM_KEYS} ${invalidItems.join(", ")}`; 122 | console.warn(msg); 123 | } 124 | 125 | const items = Object.keys(config) 126 | .map((key) => ({ key, value: config[key] })) 127 | .map((item) => 128 | Array.isArray(item.value) 129 | ? item.value.map((inner) => ({ key: item.key, value: inner })) 130 | : item 131 | ) 132 | .flatMap((i) => i); 133 | 134 | const scriptLines = items 135 | .map(({ key, value }) => { 136 | const tabs = key.length < 8 ? "\t\t\t" : "\t\t"; 137 | return `// @${key}${value ? `${tabs}${value}` : ""}`; 138 | }) 139 | .join("\n"); 140 | return ` 141 | // ==UserScript== 142 | ${scriptLines} 143 | // 144 | // Created with love using Gorilla 145 | // ==/UserScript== 146 | `; 147 | }; 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
5 |
6 | 🙈 🙉 🙊
7 |
8 |
9 | 🍌 GreaseMonkey · TamperMonkey 🍌
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |