Compare commits

...

49 Commits

Author SHA1 Message Date
Alyxia Sother 80d9974c05
[autoTag] Fixed age-old bug 2021-07-05 23:49:03 +02:00
Alyxia Sother bd4c9d266a
[siteGen] Fix replaceAll bug 2021-07-05 22:59:24 +02:00
Alyxia Sother 4879b52251
[authorGen] Fix missing performance bug??? 2021-07-05 22:32:27 +02:00
Alyxia Sother 3b0b6e7974
[Ignore] env.json 2021-07-05 22:15:13 +02:00
Alyxia Sother 7b70fcd297
[Fixes] Fixed all issues 2021-07-05 22:15:05 +02:00
Alyxia Sother 6a71b42e66
[Index] Fixed some issues caused by leftover merge conflicts 2021-07-05 21:51:25 +02:00
Ducko 255062d5d5
[Doc] Add subheader for self usage 2021-07-05 21:44:04 +02:00
Ducko b8320bb167
[GM > Plugin] Add commands wrapper 2021-07-05 21:43:58 +02:00
Ducko 4b0de84b00
[SiteGen] Always show scrollbar in container 2021-07-05 21:43:53 +02:00
Ducko 2cac769291
[SiteGen] Use max-height in containers instead of height 2021-07-05 21:43:44 +02:00
Ducko 55578d0773
[SiteGen] Fix another browser bug caused by last fix 2021-07-05 21:43:40 +02:00
Ducko ae6c36373f
[SiteGen] Fix random browser bug relating to order by rewrite 2021-07-05 21:43:25 +02:00
Ducko 1a1e3fcbae
[SiteGen] Rewrite to use sidebar, add repos 2021-07-05 21:43:07 +02:00
Ducko 4e3f301bd6
[SiteGen] Better meta description 2021-07-05 21:42:53 +02:00
Ducko cf61e63df0
[SiteGen] Add meta 2021-07-05 21:42:21 +02:00
Ducko 758810cfe8
[SiteGen] Initial add (also remove deprecated modules.json) 2021-07-05 21:42:15 +02:00
Ducko 65c93cd135
[AuthorGen] Just use prop, template literal no longer needed 2021-07-05 21:41:10 +02:00
Ducko abdad45d2d
[AuthorGen] Don't append discrim 2021-07-05 21:41:07 +02:00
Ducko c349fd6ccc
[AuthorGen] Add ratelimit, fix trying to use on authors not array 2021-07-05 21:41:02 +02:00
Ducko ea51962e77
[Various] New env JS file, AuthorGen 2021-07-05 21:37:48 +02:00
Ducko f2ac90299c
[ImageCDN] Copy if failed to use ImageMagick 2021-07-05 21:34:34 +02:00
PandaDriver ae21d6d6cc
Fix error on Windows (#9) 2021-07-05 21:34:27 +02:00
Ducko 0300abe06e
[Feat] Local images building 2021-07-05 21:33:45 +02:00
Ducko 4adf688ee8
[*Theme] Don't convert hash color 2021-07-05 21:31:31 +02:00
Ducko d05b6c17c5
[*Theme] Escape $, move escaping to generic instead of in each preproc 2021-07-05 21:30:56 +02:00
Ducko 42e2c02719
[GM > Plugin] Add addCSS(css), source tweaks 2021-07-05 21:30:33 +02:00
Ducko 8b347972ac
[GM > Plugin] Initial Add 2021-07-05 21:30:27 +02:00
Ducko 0d68aa88c2
[PCCompat > Commands] Only destructure if not empty object (no args given) 2021-07-05 21:30:22 +02:00
Ducko 9bcf6d59f8
[PCCompat > Webpack] Fix not actually calling toLowerCase 2021-07-05 21:30:19 +02:00
Ducko 5fe0bdc257
[PCCompat > Webpack] Fix last commit not erroring if displayName was not defined 2021-07-05 21:30:14 +02:00
Ducko 865a105079
[PCCompat > Webpack] Make display name getting case insensitive 2021-07-05 21:30:11 +02:00
Ducko 549bb1afec
[PCCompat > Settings] Properly show SwitchItem changes, return values for settings store 2021-07-05 21:30:07 +02:00
Ducko da87247f5d
[PCCompatCheck] Better module.exports ignoring (accept no value inline) 2021-07-05 21:29:58 +02:00
Ducko 2db8ae01c4
[PCCompatCheck] Add module.exports ignoring 2021-07-05 21:29:55 +02:00
Ducko e2f529d7fc
[PCPlugin] Accept all valid file extensions, not just js 2021-07-05 21:29:52 +02:00
Ducko 9b36c9d799
[PCCompat > Plugin] Add manifest 2021-07-05 21:29:49 +02:00
Ducko c999320da0
[PCCompatCheck] Add reactUtils ignoring 2021-07-05 21:29:46 +02:00
Ducko e18c019720
[ElectronCompat > Shell] Add openExternal 2021-07-05 21:29:42 +02:00
Ducko 41aec3cb61
[Index] Fix caching errors on null objects 2021-07-05 21:29:37 +02:00
Ducko 102f7ab662
[GenericTheme > Settings] Remove oninput logging 2021-07-05 21:29:24 +02:00
Ducko 833819c63f
[GenericTheme > Settings] Fix hex color checking when not color type causing bad values oninput 2021-07-05 21:29:05 +02:00
Ducko d4b6b0a142
[GooseMod] Add Webpack common (@goosemod/webpack/common) 2021-07-05 21:28:29 +02:00
Ducko d24d0c5b6c
[Scripts > PCCompatCheck] Add imports logging when no alias 2021-07-05 21:28:26 +02:00
Ducko bec681af2d
[Index] Fix caching issues with multiple BD themes in one repo 2021-07-05 21:28:22 +02:00
Ducko 768db5ae3f
[*Theme] Stricter character set for variable extraction regex 2021-07-05 21:28:15 +02:00
Ducko 8aa61cf30b
[Index] Undo cache tweaks 2021-07-05 21:26:48 +02:00
Ducko 1a2a0e454c
[Index] Add null checking for modules in repo JSON out 2021-07-05 21:26:43 +02:00
Ducko 1147791ce8
[Index] Add lastUpdated to manifests, tweak caching 2021-07-05 21:26:35 +02:00
Alyxia Sother d209c0665e
[Add Theme (PCTheme)] lexisother/dotfiles-discord-theme 2021-07-05 20:56:16 +02:00
29 changed files with 1476 additions and 136 deletions

6
.gitignore vendored
View File

@ -1,9 +1,7 @@
node_modules
.cache
clones
src/gh_pat.json
# dist
src/env.json
dist/*
devDist
devTemp

View File

@ -2,4 +2,16 @@
Builder for Module Store v2.
**To run this yourself, you need to [get your own GitHub Personal Access Token](https://docs.github.com/en/articles/creating-a-personal-access-token-for-the-command-line) and export it in `src/gh_pat.js`.**
## Self usage
To run this yourself, you **need** to:
1. [Get your own GitHub Personal Access Token](https://docs.github.com/en/articles/creating-a-personal-access-token-for-the-command-line)
2. Get a Discord bot token
3. Make a `src/env.js` file like:
```js
export default {
github: '<your github pat>',
discord: '<your discord bot token>
}
```

1
dist/.gitkeep vendored
View File

@ -0,0 +1 @@

View File

@ -1 +1,2 @@
export * as clipboard from './clipboard';
export * as clipboard from './clipboard';
export * as shell from './shell';

View File

@ -0,0 +1,5 @@
export const openExternal = (url) => {
// Discord makes new tabs / windows (via window.open) use electron.shell.openExternal internally, so just use window.open
window.open(url);
};

View File

@ -0,0 +1,51 @@
import showToast from '@goosemod/toast';
import { commands } from '@goosemod/patcher';
export default class Plugin {
constructor() {
this.patches = [];
this.commands = [];
this.stylesheets = [];
}
command(...args) {
this.commands.push(args[0]);
commands.add(...args);
}
enqueueUnpatch(unpatch) {
this.patches.push(unpatch);
}
addCss(css) {
const el = document.createElement('style');
el.appendChild(document.createTextNode(css)); // Load the stylesheet via style element w/ CSS text
document.head.appendChild(el);
this.stylesheets.push(el); // Push to internal array so we can remove the elements on unload
}
toast(content, options) {
showToast(content, {
subtext: this.name,
...options
});
}
goosemodHandlers = {
onImport: () => {
this.onImport();
},
onRemove: () => {
this.patches.forEach((x) => x());
this.stylesheets.forEach((x) => x.remove());
this.commands.forEach((x) => commands.remove(x));
this.onRemove();
}
}
}

View File

@ -0,0 +1 @@
module.exports = goosemodScope.webpackModules.common;

View File

@ -4,5 +4,4 @@ export { default as SliderInput } from './sliderInput';
export { default as Divider } from './divider';
export { default as ButtonItem } from './buttonItem';
export { default as Category } from './category';
export const SwitchItem = goosemodScope.webpackModules.findByDisplayName('SwitchItem');
export { default as SwitchItem } from './switchItem';

View File

@ -0,0 +1,22 @@
const { React } = goosemodScope.webpackModules.common;
const SwitchItem = goosemodScope.webpackModules.findByDisplayName('SwitchItem');
export default class SwitchItemContainer extends React.Component {
constructor(props) {
const originalHandler = props.onChange;
props.onChange = (e) => {
originalHandler(e);
this.props.value = e;
this.forceUpdate();
};
super(props);
}
render() {
return React.createElement(SwitchItem, {
...this.props
});
}
}

View File

@ -22,6 +22,18 @@ export class Plugin {
Settings.makeStore(this.entityID);
}
// Supposed to return PC manifest, which we don't store so return a rough one based on GM metadata
get manifest() {
return {
name: this.name,
description: this.description,
version: this.version,
author: this.authors.toString(),
license: 'Unknown'
}
}
get entityID() {
return this.name;
}

View File

@ -5,8 +5,16 @@ export const registerCommand = ({ command, alias, description, usage, executor }
// TODO: implement alias
goosemodScope.patcher.commands.add(command, description,
async ( { args: [ { text } ] } ) => {
const out = await executor(text.split(' ')); // Run original executor func (await incase it's an async function)
async (ret) => {
// Don't just destructure as using without text arguments returns empty object ({})
let textGiven = '';
if (ret.args) {
const { args: [ { text } ] } = ret;
textGiven = text;
}
const out = await executor(textGiven.split(' ')); // Run original executor func (await incase it's an async function)
if (!out.send) {
goosemodScope.patcher.internalMessage(out.result); // PC impl. sends internal message when out.send === false, so we also do the same via our previous Patcher API function

View File

@ -19,10 +19,14 @@ class SimpleStore {
}
this.store[key] = value;
return this.store[key];
}
toggleSetting = (key) => {
this.store[key] = !this.store[key];
return this.store[key];
}
deleteSetting = (key) => {

View File

@ -27,7 +27,8 @@ module.exports = {
},
getModuleByDisplayName: (displayName) => {
return goosemodScope.webpackModules.findByDisplayName(displayName);
// Use custom find instead of GM's findByDisplayName as PC's is case insensitive
return goosemodScope.webpackModules.find((x) => x.displayName && x.displayName.toLowerCase() === displayName.toLowerCase());
},
...goosemodScope.webpackModules.common // Export common modules (eg: React)

274
package-lock.json generated
View File

@ -11,7 +11,8 @@
"dependencies": {
"axios": "^0.21.1",
"glob": "^7.1.6",
"parcel-bundler": "^1.12.4"
"parcel-bundler": "^1.12.4",
"sass": "^1.32.8"
}
},
"node_modules/@babel/code-frame": {
@ -1785,9 +1786,13 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001179",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001179.tgz",
"integrity": "sha512-blMmO0QQujuUWZKyVrD1msR4WNDAqb/UPO1Sw2WWsQ7deoM5bJiicKnWJ1Y0NS/aGINSnKPIWBMw5luX+NDUCA=="
"version": "1.0.30001242",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001242.tgz",
"integrity": "sha512-KvNuZ/duufelMB3w2xtf9gEWCSxJwUgoxOx5b6ScLXC4kPc9xsczUVCPrQU26j5kOsHM4pSUL54tAZt5THQKug==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
}
},
"node_modules/caseless": {
"version": "0.12.0",
@ -4974,6 +4979,17 @@
"resolved": "https://registry.npmjs.org/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz",
"integrity": "sha1-GN4vl+S/epVRrXURlCtUlverpmA="
},
"node_modules/picomatch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
@ -5978,6 +5994,147 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/sass": {
"version": "1.35.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz",
"integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/sass/node_modules/anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/sass/node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/chokidar": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/sass/node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/sass/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/sass/node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/sass/node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/sass/node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -8829,9 +8986,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001179",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001179.tgz",
"integrity": "sha512-blMmO0QQujuUWZKyVrD1msR4WNDAqb/UPO1Sw2WWsQ7deoM5bJiicKnWJ1Y0NS/aGINSnKPIWBMw5luX+NDUCA=="
"version": "1.0.30001242",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001242.tgz",
"integrity": "sha512-KvNuZ/duufelMB3w2xtf9gEWCSxJwUgoxOx5b6ScLXC4kPc9xsczUVCPrQU26j5kOsHM4pSUL54tAZt5THQKug=="
},
"caseless": {
"version": "0.12.0",
@ -11454,6 +11611,11 @@
"resolved": "https://registry.npmjs.org/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz",
"integrity": "sha1-GN4vl+S/epVRrXURlCtUlverpmA="
},
"picomatch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw=="
},
"pn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
@ -12279,11 +12441,101 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sass": {
"version": "1.32.8",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.32.8.tgz",
"integrity": "sha512-Sl6mIeGpzjIUZqvKnKETfMf0iDAswD9TNlv13A7aAF3XZlRPMq4VvJWBC2N2DXbp94MQVdNSFG6LfF/iOXrPHQ==",
"version": "1.35.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz",
"integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==",
"requires": {
"chokidar": ">=2.0.0 <4.0.0"
"chokidar": ">=3.0.0 <4.0.0"
},
"dependencies": {
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"requires": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
}
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"requires": {
"fill-range": "^7.0.1"
}
},
"chokidar": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
"requires": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"requires": {
"to-regex-range": "^5.0.1"
}
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"optional": true
},
"glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"requires": {
"is-glob": "^4.0.1"
}
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"requires": {
"binary-extensions": "^2.0.0"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
},
"readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"requires": {
"picomatch": "^2.2.1"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"requires": {
"is-number": "^7.0.0"
}
}
}
},
"sax": {

View File

@ -2,52 +2,43 @@
"name": "ms2builder",
"version": "1.0.0",
"description": "Builder for Module Store v2.",
"main": "src/index.js",
"scripts": {
"theme": "node ./scripts/add.js theme",
"pccompatcheck": "node ./scripts/pccompatCheck.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/GooseMod/MS2Builder.git"
},
"author": "GooseMod",
"license": "MIT",
"bugs": {
"url": "https://github.com/GooseMod/MS2Builder/issues"
},
"homepage": "https://github.com/GooseMod/MS2Builder#readme",
"dependencies": {
"axios": "^0.21.1",
"glob": "^7.1.6",
"parcel-bundler": "^1.12.4",
"sass": "^1.32.8"
},
"alias": {
"@goosemod/patcher": "./moduleWrappers/goosemod/patcher.js",
"@goosemod/webpack": "./moduleWrappers/goosemod/webpack.js",
"@goosemod/webpack/common": "./moduleWrappers/goosemod/webpackCommon.js",
"@goosemod/logger": "./moduleWrappers/goosemod/logger.js",
"@goosemod/reactUtils": "./moduleWrappers/goosemod/reactUtils.js",
"@goosemod/toast": "./moduleWrappers/goosemod/toast.js",
"@goosemod/settings": "./moduleWrappers/goosemod/settings.js",
"@goosemod/plugin": "./moduleWrappers/goosemod/plugin.js",
"powercord/entities": "./moduleWrappers/powercord/entities.js",
"powercord/injector": "./moduleWrappers/powercord/injector.js",
"powercord/webpack": "./moduleWrappers/powercord/webpack.js",
"powercord/util": "./moduleWrappers/powercord/util.js",
"powercord/components/settings": "./moduleWrappers/powercord/components/settings/index.js",
"_powercord/global": "./moduleWrappers/powercord/global/index.js",
"electron": "./moduleWrappers/electron/index.js"
},
"type": "module"
}

View File

@ -47,7 +47,7 @@ for (const jsFile of glob.sync(`${cloneDir}/**/*.js`).concat(glob.sync(`${cloneD
const aliasPath = alias[requirePath];
if (!aliasPath) {
console.log('No alias', requirePath);
console.log('No alias', requirePath, '(', imports, ')');
continue;
}
@ -56,9 +56,10 @@ for (const jsFile of glob.sync(`${cloneDir}/**/*.js`).concat(glob.sync(`${cloneD
for (let imp of imports.split(',')) {
imp = imp.trim().split(':')[0];
const searchRegex = new RegExp(`^export.*${imp}.*$`, 'gm');
const searchRegex1 = new RegExp(`^export.*${imp}.*$`, 'gm');
const searchRegex2 = new RegExp(`^ *${imp}[:,]`, 'gm');
const searchMatch = aliasJS.match(searchRegex);
const searchMatch = aliasJS.match(searchRegex1) || aliasJS.match(searchRegex2);
if (!searchMatch) {
if (requirePath === 'powercord/webpack') {
@ -69,6 +70,14 @@ for (const jsFile of glob.sync(`${cloneDir}/**/*.js`).concat(glob.sync(`${cloneD
}
}
if (requirePath === 'powercord/util') {
const webpackJS = readFileSync(join(__dirname, '..', '..', 'GooseMod', 'src', 'util', 'react.js'), 'utf8');
if (webpackJS.includes(`export function ${imp}`)) {
continue;
}
}
if (requirePath === 'powercord/injector') {
continue;
}
@ -79,4 +88,4 @@ for (const jsFile of glob.sync(`${cloneDir}/**/*.js`).concat(glob.sync(`${cloneD
}
console.log();
}
}

50
src/authorGen.js Normal file
View File

@ -0,0 +1,50 @@
import { performance } from 'perf_hooks';
import { readFileSync } from 'fs';
import axios from 'axios';
let lastRequest = 0;
const userCache = {};
let file;
let botToken;
try {
file = JSON.parse(readFileSync('./env.json'));
botToken = file.dtoken;
} catch (error) {
if (error.code !== 'ENOENT') throw error;
botToken = process.env.BOTTOKEN;
}
const getUser = async (id) => {
if (userCache[id]) return userCache[id];
while (performance.now() - 500 < lastRequest) {
// Has been less than 500ms since last request
await new Promise((res) => setTimeout(res, 100));
}
lastRequest = performance.now();
return (userCache[id] = (
await axios.get(`https://discord.com/api/v9/users/${id}`, {
headers: {
Authorization: `Bot ${botToken}`,
},
})
).data);
};
export default async (id) => {
const user = await getUser(id);
console.log(user);
return {
// Use semi-minified keys as to avoid size
i: id, // id
n: user.username, // name
a: user.avatar, // avatar
};
};

View File

@ -1,15 +1,19 @@
const basicIncludes = (bundleCode, code, display = (code[0].toUpperCase() + code.substring(1))) => {
const basicIncludes = (bundleCode, code, display = code[0].toUpperCase() + code.substring(1)) => {
if (bundleCode.includes(code)) return display;
};
export default (bundleCode) => { // Scans final bundle JS to try and auto-detect things, then adding tags
export default (bundleCode, currentTags) => {
// Scans final bundle JS to try and auto-detect things, then adding tags
const tags = [
basicIncludes(bundleCode, 'react'),
basicIncludes(bundleCode, 'document', 'DOM'),
(bundleCode.includes('document.createTextNode') && /document\.createElement\(['"`]style['"`]\)/.test(bundleCode)) ? 'CSS' : undefined,
/document\.createElement\(['"`]style['"`]\)/.test(bundleCode) ? 'CSS' : undefined,
].filter((x) => x !== undefined);
currentTags.includes('themes') ? 'theme' : undefined,
]
.filter((x) => x !== undefined)
.concat(currentTags.filter((x) => x !== 'themes'));
return tags;
};
};

50
src/imageCdn.js Normal file
View File

@ -0,0 +1,50 @@
import { createWriteStream, mkdirSync, copyFileSync, rmSync } from 'fs';
import { join } from 'path';
import { exec } from 'child_process';
import axios from 'axios';
export default (manifest) => {
return manifest.images ? Promise.all(manifest.images.map(async (x, i) => {
const baseDir = join(distDir, 'img', manifest.name);
const rawFile = join(baseDir, x.split('/').pop());
const rawExt = rawFile.split('.').pop();
const finalName = `${i}.${rawExt === 'gif' ? 'gif' : 'png'}`;
const finalFile = join(baseDir, finalName);
mkdirSync(baseDir, { recursive: true });
console.log(x, i, rawFile, finalFile);
const writer = createWriteStream(rawFile);
const resp = await axios.get(x, {
responseType: 'stream'
});
resp.data.pipe(writer);
await new Promise((res, rej) => {
writer.on('finish', res);
writer.on('error', rej);
});
if (rawExt !== 'gif') {
try {
await new Promise((res) => exec(`convert "${rawFile}" -resize x720 -quality 90 "${finalFile}"`, res));
} catch (e) {
console.error('Failed to use ImageMagick to resize and compress, copying file as backup');
copyFileSync(rawFile, finalFile);
}
} else {
copyFileSync(rawFile, finalFile);
}
try {
rmSync(rawFile);
} catch (e) { }
return `/img/${manifest.name}/${finalName}`;
})) : [];
};

View File

@ -1,21 +1,25 @@
import ModuleRepos from './modules/index.js';
import AutoTag from './autoTag.js';
import WebhookSend from './webhook.js';
import ImageCDN from './imageCdn.js';
import AuthorGen from './authorGen.js';
import SiteGen from './siteGen/index.js';
import Parcel from 'parcel-bundler';
import axios from 'axios';
import glob from 'glob';
import { rmSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, existsSync, rmdirSync } from 'fs';
import { rmSync, mkdirSync, readFileSync, writeFileSync, existsSync } from 'fs';
import { createHash } from 'crypto';
import { dirname } from 'path';
import { dirname, sep } from 'path';
import { fileURLToPath } from 'url';
let file;
let githubPAT;
try {
file = JSON.parse(readFileSync('./gh_pat.json'));
file = JSON.parse(readFileSync('./env.json'));
githubPAT = file.token;
} catch (error) {
if (error.code !== 'ENOENT') throw error;
@ -24,9 +28,10 @@ try {
const __dirname = dirname(fileURLToPath(import.meta.url));
const clonesDir = `${__dirname.replace('/src', '')}/clones`;
const clonesDir = `${__dirname.replace(`${sep}src`, '')}/clones`;
const distDir = `${__dirname.replace('/src', '')}/dist`;
const distDir = `${__dirname.replace(`${sep}src`, '')}/dist`;
global.distDir = distDir;
const modulesDir = `${distDir}/module`;
@ -37,7 +42,7 @@ const resetDir = (dir) => {
if (process.argv[2] === '-f') {
resetDir(clonesDir);
resetDir(distDir);
resetDir(modulesDir);
}
@ -46,13 +51,15 @@ let previous = [];
if (existsSync(clonesDir)) {
for (const cloneDir of glob.sync(`${clonesDir}/*/*`)) {
process.chdir(cloneDir);
const currentHash = await new Promise((res) => exec(`git rev-parse HEAD`, (err, stdout) => res(stdout.trim())));
const moduleInRepos = ModuleRepos.map(
(x) => x.modules.filter(
(y) => y[0] === cloneDir.replace(`${clonesDir}/`, '') && (y[1] === currentHash || !y[1])
)
const currentHash = await new Promise((res) =>
exec(`git rev-parse HEAD`, (err, stdout) => res(stdout.trim())),
);
const moduleInRepos = ModuleRepos.map((x) =>
x.modules.filter(
(y) => y[0] === cloneDir.replace(`${clonesDir}/`, '') && (y[1] === currentHash || !y[1]),
),
).find((x) => x.length > 0);
if (moduleInRepos) {
@ -68,46 +75,48 @@ const parcelOptions = {
watch: false,
sourceMaps: false,
outDir: modulesDir,
logLevel: 0
logLevel: 0,
};
const githubCache = {};
const getGithubInfo = async (repo) => {
if (githubCache[repo]) return githubCache[repo];
const info = (await axios.get(`https://api.github.com/repos/${repo}`, {
headers: {
'Authorization': `token ${githubPAT}`
}
})).data;
const info = (
await axios.get(`https://api.github.com/repos/${repo}`, {
headers: {
Authorization: `token ${githubPAT}`,
},
})
).data;
githubCache[repo] = info;
return info;
};
let oldTotalModulesJson = [];
for (const parentRepo of ModuleRepos) {
let moduleJson = {
modules: [],
meta: parentRepo.meta
meta: parentRepo.meta,
};
const repoJsonPath = `${distDir}/${parentRepo.filename}.json`;
const currentRepoJson = existsSync(repoJsonPath) ? JSON.parse(readFileSync(repoJsonPath, 'utf8')) : undefined;
const currentRepoJson = existsSync(repoJsonPath)
? JSON.parse(readFileSync(repoJsonPath, 'utf8'))
: undefined;
for (const repo of parentRepo.modules) {
console.time(repo.slice(0, 2).join(' @ ')+`${repo[2] ? ` ${repo[2]}` : ''}`);
console.time(repo.slice(0, 2).join(' @ ') + `${repo[2] ? ` ${repo[2]}` : ''}`);
const name = repo[0];
const cloneDir = `${clonesDir}/${name}`;
let moduleDir = repo[2] || '';
try {
if (previous.includes(repo)) {
let currentModule = currentRepoJson.modules.filter((x) => x.github.repo === repo[0]);
let currentModule = currentRepoJson.modules.filter((x) => x?.github?.repo === repo[0]);
if (currentModule.length > 1) {
const manifest = JSON.parse(readFileSync(`${cloneDir}${moduleDir}/goosemodModule.json`));
@ -115,123 +124,152 @@ for (const parentRepo of ModuleRepos) {
} else {
currentModule = currentModule[0];
}
moduleJson.modules.push(currentModule);
process.stdout.write('[SKIP] ');
console.timeEnd(repo.slice(0, 2).join(' @ ')+`${repo[2] ? ` ${repo[2]}` : ''}`);
console.timeEnd(repo.slice(0, 2).join(' @ ') + `${repo[2] ? ` ${repo[2]}` : ''}`);
continue;
}
} catch (e) {
console.log('Cache fail', repo[0], e);
}
let githubInfo = getGithubInfo(repo[0]);
// console.log(repo);
const url = `https://github.com/${repo[0]}.git`;
const commitHash = repo[1];
const preprocessor = repo[3];
// resetDir(cloneDir);
// rmSync(cloneDir, { recursive: true, force: true });
if (existsSync(cloneDir)) {
process.chdir(cloneDir);
const currentHash = await new Promise((res) => exec(`git rev-parse HEAD`, (err, stdout) => res(stdout.trim())));
const currentHash = await new Promise((res) =>
exec(`git rev-parse HEAD`, (err, stdout) => res(stdout.trim())),
);
if (currentHash !== repo[1]) rmSync(cloneDir, { recursive: true, force: true });
if (currentHash !== repo[1] && repo[1] !== '')
rmSync(cloneDir, { recursive: true, force: true });
}
process.chdir(distDir); // Incase current wd is broken, in which case exec / git crashes
await new Promise((res) => exec(`git clone ${url} ${cloneDir}`, res));
process.chdir(cloneDir);
const lastHash = await new Promise((res) => exec(`git rev-parse HEAD`, (err, stdout) => res(stdout.trim())));
const lastHash = await new Promise((res) =>
exec(`git rev-parse HEAD`, (err, stdout) => res(stdout.trim())),
);
await new Promise((res) => exec(`git checkout ${commitHash}`, res));
const commitTimestamp = await new Promise((res) =>
exec(`git log -1 --format="%at" | xargs -I{} date -d @{} +%s`, (err, stdout) =>
res(stdout.trim()),
),
);
if (preprocessor) {
const preOut = (await import(`./preprocessors/${preprocessor}.js`)).default(`${cloneDir}${moduleDir}`, repo);
const preOut = (await import(`./preprocessors/${preprocessor}.js`)).default(
`${cloneDir}${moduleDir}`,
repo,
);
if (preOut !== undefined) {
moduleDir = preOut;
}
}
const manifest = JSON.parse(readFileSync(`${cloneDir}${moduleDir}/goosemodModule.json`));
// console.log(manifest);
const outFile = `${manifest.name}.js`;
const bundler = new Parcel(`${cloneDir}${moduleDir}/${manifest.main}`, Object.assign(parcelOptions, {
outFile
}));
const bundler = new Parcel(
`${cloneDir}${moduleDir}/${manifest.main}`,
Object.assign(parcelOptions, {
outFile,
}),
);
const bundle = await bundler.bundle();
const outPath = `${modulesDir}/${outFile}`;
let jsCode = readFileSync(outPath, 'utf8');
jsCode = `${jsCode};parcelRequire('${bundle.entryAsset.basename}').default`; // Make eval return the index module's default export
// console.log(jsCode);
writeFileSync(outPath, jsCode);
const jsHash = createHash('sha512').update(jsCode).digest('hex');
githubInfo = await githubInfo; // GitHub info is gotten async during other stuff to reduce time
const manifestJson = {
name: manifest.name,
description: manifest.description,
version: manifest.version,
tags: AutoTag(jsCode, manifest.tags),
authors: manifest.authors,
hash: jsHash,
github: {
stars: githubInfo.stargazers_count,
repo: repo[0]
repo: repo[0],
},
...repo[4]
lastUpdated: parseInt(commitTimestamp),
...repo[4],
};
if (manifest.images) manifestJson.images = manifest.images;
if (manifest.dependencies) manifestJson.dependencies = manifest.dependencies;
manifestJson.images = await ImageCDN(manifestJson);
if (Array.isArray(manifestJson.authors))
manifestJson.authors = await Promise.all(
manifestJson.authors.map(async (x) => {
if (x.match(/^[0-9]{17,18}$/)) {
return await AuthorGen(x);
}
return x;
}),
);
moduleJson.modules.push(manifestJson);
console.timeEnd(repo.slice(0, 2).join(' @ ')+`${repo[2] ? ` ${repo[2]}` : ''}`);
console.timeEnd(repo.slice(0, 2).join(' @ ') + `${repo[2] ? ` ${repo[2]}` : ''}`);
// console.log(lastHash);
if (commitHash !== '' && lastHash !== commitHash) {
console.log('[Warning] Commit hash in modules does not match latest commit in repo');
}
}
writeFileSync(repoJsonPath, JSON.stringify(moduleJson));
moduleJson.modules = moduleJson.modules.filter((x) => x !== null);
oldTotalModulesJson = oldTotalModulesJson.concat(moduleJson.modules);
writeFileSync(repoJsonPath, JSON.stringify(moduleJson));
}
writeFileSync(`${distDir}/modules.json`, JSON.stringify(oldTotalModulesJson));
copyFileSync(`${__dirname.replace('/src', '')}/_headers`, `${distDir}/_headers`);
WebhookSend();
await SiteGen();

View File

@ -1,8 +1,11 @@
export default [
['RazerMoon/muteNewGuild', 'a8ea528e67089ea00a457623cebbef6a670127fc', '/manifest.json', 'pcPlugin', {
authors: ['162970149857656832'],
}],
['keanuplayz/PC-Clap', 'master', '/manifest.json', 'pcPlugin', {
[
'keanuplayz/PC-Clap',
'master',
'/manifest.json',
'pcPlugin',
{
authors: ['717352467280691331'],
}],
},
],
];

View File

@ -4,7 +4,22 @@ export default [
- No hash is provided as ported themes have no risk of RCE / etc
*/
['keanuplayz/usrbg', '', '/powercord_manifest.json', 'pcTheme', {
authors: ['717352467280691331']
}]
[
'keanuplayz/usrbg',
'',
'/powercord_manifest.json',
'pcTheme',
{
authors: ['717352467280691331'],
},
],
[
'lexisother/dotfiles-discord-theme',
'',
'/powercord_manifest.json',
'pcTheme',
{
authors: ['465702500146610176', '364720702252908544'],
},
],
];

View File

@ -1,13 +1,13 @@
import { readFileSync, writeFileSync, mkdirSync, rmSync } from 'fs';
export default (themePath, repo) => {
const content = readFileSync(themePath);
const content = readFileSync(themePath, 'utf8');
const metaReg = /@([^ ]*) (.*)/g;
let manifest = {
main: 'index.js',
tags: ['theme', 'port']
tags: ['theme', 'port'],
};
let match;
@ -17,7 +17,7 @@ export default (themePath, repo) => {
value = value.trim();
// console.log(key, value);
// console.log(key, value);
switch (key) {
case 'name':
@ -33,11 +33,11 @@ export default (themePath, repo) => {
break;
case 'author':
manifest.authors = [ value ];
manifest.authors = [value];
break;
case 'authorId':
manifest.authors = [ value ];
manifest.authors = [value];
break;
}
}
@ -59,7 +59,7 @@ export default {
)
);
},
onRemove: async () => {
style.remove();
},

View File

@ -0,0 +1,169 @@
const discordVars = [
'--header-primary',
'--header-secondary',
'--text-normal',
'--text-muted',
'--text-link',
'--channels-default',
'--interactive-normal',
'--interactive-hover',
'--interactive-active',
'--interactive-muted',
'--background-primary',
'--background-secondary',
'--background-secondary-alt',
'--background-tertiary',
'--background-accent',
'--background-floating',
'--background-mobile-primary',
'--background-mobile-secondary',
'--background-modifier-hover',
'--background-modifier-active',
'--background-modifier-selected',
'--background-modifier-accent',
'--background-mentioned',
'--background-mentioned-hover',
'--background-message-hover',
'--background-help-warning',
'--background-help-info',
'--scrollbar-thin-thumb',
'--scrollbar-thin-track',
'--scrollbar-auto-thumb',
'--scrollbar-auto-track',
'--scrollbar-auto-scrollbar-color-thumb',
'--scrollbar-auto-scrollbar-color-track',
'--elevation-stroke',
'--elevation-low',
'--elevation-medium',
'--elevation-high',
'--logo-primary',
'--focus-primary',
'--radio-group-dot-foreground',
'--guild-header-text-shadow',
'--channeltextarea-background',
'--activity-card-background',
'--textbox-markdown-syntax',
'--deprecated-card-bg',
'--deprecated-card-editable-bg',
'--deprecated-store-bg',
'--deprecated-quickswitcher-input-background',
'--deprecated-quickswitcher-input-placeholder',
'--deprecated-text-input-bg',
'--deprecated-text-input-border',
'--deprecated-text-input-border-hover',
'--deprecated-text-input-border-disabled',
'--deprecated-text-input-prefix',
];
export default (manifest, _content, repo) => {
const content = _content.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$');
let variables = content.match(/--([^*!\n}]*): ([^*!\n}]*);/g) || [];
if (variables.length > 0)
variables = variables.map((x) => {
const spl = x.split(':');
let name = spl[0].trim();
let val = spl.slice(1).join(':').trim().slice(0, -1).replace(' !important', '');
let type = 'text';
if (val.match(/(#[0-9a-fA-F]{6}|([0-9]{1,3}, ?[0-9]{1,3}, ?[0-9]{1,3}))/)) {
type = 'color';
}
return [name, val, type];
});
// Filter out Discord standard vars, and duplicate names
if (variables.length > 0)
variables = variables.filter(
(x, i, s) =>
!discordVars.includes(x[0]) &&
!x[1].includes('var(') &&
!x[0].includes('glasscord') &&
s.indexOf(s.find((y) => y[0] === x[0])) === i,
);
const toShowSettings = repo[5] === true && variables.length > 0;
console.log(variables, toShowSettings);
return `// Generated by MS2Builder - genericTheme preprocessor / porter
let style;
export default {
goosemodHandlers: {
onImport: async () => {
style = document.createElement("style");
document.head.appendChild(style);
style.appendChild(
document.createTextNode(
\`${content}\`
)
);
if (${toShowSettings} || (goosemodScope.settings.gmSettings.get().allThemeSettings)) goosemodScope.settings.createItem('${
manifest.name
}', [
'',
${variables
.map(
(x) => `{
type: '${x[2] === 'color' ? 'text-and-color' : 'text-input'}',
text: '${x[0]
.replace('--rs-', '--radial-status:-')
.substring(2)
.replace(/-/g, ' ')
.replace(/\w\S*/g, (_) => _[0].toUpperCase() + _.substring(1).toLowerCase())
.replace('Rgb', 'RGB')
.replace('Afk', 'AFK')
.replace('Dnd', 'DND')}',
oninput: (val) => {
${''/* x[2] === 'color' && x[1][0] !== '#' ? `val = 'rgb(' + parseInt(val.substring(1, 3), 16).toString() + ', ' + parseInt(val.substring(3, 5), 16).toString() + ', ' + parseInt(val.substring(5, 7), 16).toString() + ')'` : '' */}
document.body.style.setProperty('${x[0]}', val);
},
initialValue: () => {
let val = document.body.style.getPropertyValue(\`${x[0]}\`) || \`${x[1]}\`;
${
x[2] === 'color'
? `if (val[0] !== '#') { val = '#' + val.split(', ').map((x) => parseInt(x).toString(16).padStart(2, '0')).join(''); }`
: ''
}
console.log(val);
return val;
}
}`,
)
.join(', ')}
])
},
onRemove: async () => {
style.remove();
try {
goosemodScope.settings.removeItem('${manifest.name}');
} catch (e) { }
},
getSettings: async () => [${variables
.map((x) => `['${x[0]}', document.body.style.getPropertyValue(\`${x[0]}\`)]`)
.join(', ')}],
loadSettings: async (settings) => {
settings.forEach((x) => {
document.body.style.setProperty(x[0], x[1]);
});
}
}
};`;
};
<<<<<<< HEAD
=======
>>>>>>> 39784f0 ([GenericTheme > Settings] Fix hex color checking when not color type causing bad values oninput)

View File

@ -41,7 +41,7 @@ export default (manifestPath, repo) => {
writeFileSync(join(baseDir, `index.js`), content);
for (const jsFile of glob.sync(`${baseDir}/**/*.js`)) {
for (const jsFile of glob.sync(`${baseDir}/**/*.{js,jsx,ts,tsx}`)) {
console.log(jsFile);
let content = readFileSync(jsFile, 'utf8');

View File

@ -27,7 +27,7 @@ export default (manifestPath, repo) => {
pcManifest.theme = cssPath;
}
const content = readFileSync(pcManifest.theme, 'utf8').replace(/\\/g, '\\\\').replace(/`/g, '\\`');
const content = readFileSync(pcManifest.theme, 'utf8');
const jsCode = `// Generated by MS2Builder - pcTheme preprocessor / porter
let style;

Binary file not shown.

141
src/siteGen/index.js Normal file
View File

@ -0,0 +1,141 @@
import { readFileSync, writeFileSync, copyFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import glob from 'glob';
const __dirname = dirname(fileURLToPath(import.meta.url));
const genAuthors = (a) => {
let authors = [];
if (typeof a === 'string') {
authors = a.split(', ');
} else if (Array.isArray(a)) {
authors = a;
}
return authors
.map((x, i) => {
if (typeof x === 'object') {
// User object
const pfp = `<img loading=lazy style="display: inline; border-radius: 50%; margin-right: 5px; vertical-align: bottom;" src="https://cdn.discordapp.com/avatars/${x.i}/${x.a}.png?size=32">`;
const name = `<span class="author" style="line-height: 32px;">${x.n}</span>`; //<span class="description-3_Ncsb">#${result.discriminator}</span></span>`;
return i > 1 ? pfp : pfp + name;
}
let idMatch = x.match(/(.*) \(([0-9]{17,18})\)/); // "<name> (<id>)"
if (idMatch === null) return `<span class="author">${x}</span>`; // "<name>"
return `<span class="author">${idMatch[1]}</span>`;
})
.join('<span class="description-3_Ncsb">,</span> ');
};
const makeCard = (m) => {
const authors = genAuthors(m.authors);
return `<div class="gm-store-card ${m.tags.join(' ')}" data-last-updated=${
m.lastUpdated
} data-stars=${m.github.stars}>
<div style="background-image: url('${m.images?.length ? m.images[0] : ''}');"></div>
<div class="title-31JmR4 ${!authors.includes('avatar') ? 'no-pfp' : ''}">${authors}</div>
<div class="title-31JmR4">${m.name}</div>
<div class="colorStandard-2KCXvj size14-e6ZScH default-3nhoK- formText-3fs7AJ description-3_Ncsb formText-3fs7AJ modeDefault-3a2Ph1">${
m.description
}</div>
<div>
<div class="colorStandard-2KCXvj size14-e6ZScH default-3nhoK- formText-3fs7AJ description-3_Ncsb formText-3fs7AJ modeDefault-3a2Ph1">
<span>${m.github.stars}</span>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z"></path></svg>
</div>
<div class="colorStandard-2KCXvj size14-e6ZScH default-3nhoK- formText-3fs7AJ description-3_Ncsb formText-3fs7AJ modeDefault-3a2Ph1">${
m.version === '0' || m.version.toLowerCase().includes('auto') ? '' : `v${m.version}`
}</div>
</div>
</div>`;
};
const makeRepo = (r) => {
return `<a href="${r.filename}" class="repo">
<div>
<div class="title-31JmR4">${r.name}</div>
<div class="colorStandard-2KCXvj size14-e6ZScH default-3nhoK- formText-3fs7AJ description-3_Ncsb formText-3fs7AJ modeDefault-3a2Ph1">${r.description}</div>
</div>
<div class="colorStandard-2KCXvj size14-e6ZScH default-3nhoK- formText-3fs7AJ description-3_Ncsb formText-3fs7AJ modeDefault-3a2Ph1">
<div><strong>${r.themes}</strong> themes</div>
<div><strong>${r.plugins}</strong> plugins</div>
<div><strong>${r.developers}</strong> developers</div>
</div>
</a>`;
};
export default () => {
let template = readFileSync(join(__dirname, 'template.html'), 'utf8');
const repos = glob.sync(join(global.distDir, '*.json'));
let cards = [];
let metas = [];
let name = 'NovaGM Store';
let description = '';
for (const repo of repos) {
console.log(repo);
const json = JSON.parse(readFileSync(repo, 'utf8'));
if (repos.length === 1) {
// 1 repo likely means a custom repo, so use that name and description
name = json.meta.name;
description = `${json.meta.description} `;
}
cards = cards.concat(json.modules);
metas.push({
...json.meta,
themes: json.modules.filter((x) => x.tags.includes('theme')).length,
plugins: json.modules.filter((x) => !x.tags.includes('theme')).length,
developers: Object.keys(
json.modules.reduce((acc, x) => {
(!Array.isArray(x.authors) ? [x.authors] : x.authors).forEach(
(a) => (acc[typeof a === 'object' ? a.i : a] = true),
);
return acc;
}, {}),
).length,
filename: repo.split('/').pop(),
});
}
description += `Browse ${cards.filter((x) => x.tags.includes('theme')).length} themes and ${
cards.filter((x) => !x.tags.includes('theme')).length
} plugins from ${
Object.keys(
cards.reduce((acc, x) => {
(!Array.isArray(x.authors) ? [x.authors] : x.authors).forEach(
(a) => (acc[typeof a === 'object' ? a.i : a] = true),
);
return acc;
}, {}),
).length
} developers.`;
cards = cards.sort((a, b) => a.name.localeCompare(b.name)).map((x) => makeCard(x));
template = template
.replace('ALL_CARDS', cards.join('\n'))
.replace('REPOS', metas.map((x) => makeRepo(x)).join('\n'))
.replace(/NAME/g, name)
.replace(/DESCRIPTION/g, description);
writeFileSync(join(global.distDir, 'index.html'), template);
copyFileSync(join(__dirname, 'NotoSans-Medium.ttf'), join(global.distDir, 'NotoSans-Medium.ttf'));
};

503
src/siteGen/template.html Normal file
View File

@ -0,0 +1,503 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>NAME</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta itemprop="name" content="NAME">
<meta itemprop="description" content="DESCRIPTION">
<meta property="og:title" content="NAME">
<meta property="og:description" content="DESCRIPTION">
<meta property="og:type" content="website">
<meta property="og:url" content="/">
<style>
:root {
--elevation-medium: 0 4px 4px rgba(0,0,0,0.16);
--background-secondary: #161A1E;
--background-secondary-alt: #0A0D0F;
--background-tertiary: #181C20;
--interactive-normal: #b9bbbe;
--header-primary: #fff;
--header-secondary: #b9bbbe;
--text-normal: #dcddde;
}
@font-face {
font-family: 'Noto Sans';
src: url('NotoSans-Medium.ttf') format('truetype');
}
* {
font-family: 'Noto Sans', sans-serif;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
background-color: #101418;
overflow: hidden;
}
.sidebar {
display: inline-block;
width: 180px;
height: 100vh;
vertical-align: top;
box-sizing: border-box;
background-color: var(--background-secondary);
}
.sidebar > div {
width: 100%;
padding: 12px 16px;
color: var(--text-normal);
background-color: var(--background-tertiary);
box-sizing: border-box;
cursor: pointer;
}
.sidebar > .sidebar-header {
cursor: default;
color: var(--header-primary);
background-color: transparent;
}
.sidebar > div.active {
background-color: #7289da;
}
.sidebar-button:hover {
background-color: #14181C;
}
.container {
padding: 22px;
width: calc(100vw - 185px);
max-height: 100vh;
overflow-y: scroll;
display: inline-flex;
flex-flow: row wrap;
align-items: center;
justify-content: center;
box-sizing: border-box;
}
@media (max-width: 1300px) {
.container {
width: 100vw;
}
}
.flex-break {
flex-basis: 100%;
}
body.themes .gm-store-card:not(.theme) {
display: none;
}
body.plugins .gm-store-card.theme {
display: none;
}
.container.repos {
display: none;
}
body.repos .container.cards {
display: none;
}
body.repos .container.repos {
display: inline-flex;
}
.container.repos {
align-content: flex-start;
}
.container .repo {
width: 300px;
height: 200px;
justify-content: space-between;
display: flex;
flex-flow: column;
text-decoration: none;
}
.repo * {
cursor: pointer;
}
.repo > :last-child {
display: flex;
justify-content: space-around;
}
.repo > strong {
color: var(--text-normal);
}
.gm-inline-dropdown {
display: flex;
align-items: center;
gap: 8px;
margin-left: 12px;
}
.gm-inline-dropdown > :last-child {
width: 120px;
background-color: var(--background-tertiary);
border: none;
border-radius: 4px;
outline: 0px !important;
padding: 6px;
color: var(--text-normal);
}
.gm-store-search {
flex-grow: 1;
margin-right: 12px;
}
.gm-store-card, .repo {
box-shadow: var(--elevation-medium);
background-color: var(--background-secondary);
border-radius: 8px;
box-sizing: border-box;
padding: 12px;
margin: 10px;
width: 330px;
height: 380px;
position: relative;
}
.gm-store-card > :nth-child(1) {
width: calc(100% + 24px);
height: 200px;
border-radius: 8px 8px 0 0;
margin-top: -12px;
margin-left: -12px;
background-color: var(--background-secondary-alt);
background-repeat: no-repeat;
background-size: contain;
background-position: 50%;
text-align: center;
line-height: 200px;
color: var(--interactive-normal);
font-family: var(--font-display);
font-size: 36px;
}
.gm-store-card > :nth-child(2) {
position: absolute;
top: 152px;
right: 10px;
opacity: 0.95;
border-radius: 16px;
background-color: rgba(0, 0, 0, 0.5);
width: fit-content;
padding-right: 10px;
}
.gm-store-card > :nth-child(2).no-pfp {
padding: 4px 8px;
}
.gm-store-card > :nth-child(3) {
width: 85%;
margin-top: 10px;
overflow: hidden;
display: -webkit-box;
webkit-line-clamp: 1;
webkit-box-orient: vertical;
}
.gm-store-card > :nth-child(4) {
width: 85%;
margin-top: 5px;
overflow: hidden;
display: -webkit-box;
webkit-line-clamp: 3;
webkit-box-orient: vertical;
clear: both;
}
.gm-store-card > :nth-child(5) {
display: flex;
align-items: center;
flex-direction: column;
order: 2;
margin-left: auto;
position: absolute;
top: 208px;
right: 12px;
}
.gm-store-card > :nth-child(5) > :nth-child(1) {
}
.gm-store-card > :nth-child(5) > :nth-child(1) > :nth-child(1) {
position: relative;
top: 7px;
font-size: 18px;
font-weight: 600;
}
.gm-store-card > :nth-child(5) > :nth-child(1) > :nth-child(2) {
position: relative;
top: 8px;
margin-left: 5px;
}
.gm-store-card > :nth-child(5) > :nth-child(2) {
margin-top: 20px;
}
.gm-store-card > :nth-child(6) {
position: absolute;
bottom: 12px;
width: calc(100% - 32px);
display: flex;
gap: 5px;
}
.gm-store-card > :nth-child(6) > :nth-child(1) {
display: inline-flex;
cursor: pointer;
width: 90px;
}
.gm-store-card > :nth-child(6) > :nth-child(2) {
width: auto;
margin-left: 14px;
min-width: 0px;
padding: 2px 5px;
color: rgb(221, 221, 221);
display: inline-flex;
cursor: pointer;
}
.gm-store-card > :nth-child(6) > :nth-child(3) {
margin-top: 4px;
position: absolute;
right: -10px;
}
.gm-store-card > :nth-child(6) > :nth-child(3).hide-toggle {
display: none;
}
.container-cMG81i, .inner-2P4tQO {
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-flex: 1;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
}
.container-cMG81i {
border-radius: 4px;
overflow: hidden;
background-color: var(--background-tertiary);
}
.inner-2P4tQO {
position: relative;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
padding: 1px;
min-width: 0;
}
.medium-1LLV3p .input-3Xdcic, .medium-1LLV3p .tag-2gZFdE {
font-size: 16px;
line-height: 32px;
height: 30px;
padding: 0 8px;
}
.input-3Xdcic {
-webkit-box-sizing: border-box;
box-sizing: border-box;
background: transparent;
border: none;
resize: none;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
min-width: 48px;
margin: 1px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
color: var(--text-normal);
outline: 0 !important;
}
.description-3_Ncsb, .labelDescriptor-1PqHgD {
color: var(--header-secondary) !important;
}
.modeDefault-3a2Ph1 {
cursor: default;
}
.description-3_Ncsb {
font-size: 14px;
line-height: 20px;
font-weight: 400;
}
.size14-e6ZScH {
font-size: 14px;
line-height: 18px;
}
.colorStandard-2KCXvj {
color: var(--text-normal);
}
.title-31JmR4 {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
display: block;
overflow: hidden;
margin-top: 0;
margin-bottom: 0;
color: var(--header-primary);
line-height: 24px;
font-size: 16px;
font-weight: 500;
word-wrap: break-word;
cursor: pointer;
}
</style>
</head>
<body class="themes">
<div class="sidebar">
<div class="sidebar-header">NAME</div>
<div class="sidebar-button active">Themes</div>
<div class="sidebar-button">Plugins</div>
<div class="sidebar-button">Repos</div>
</div>
<div class="container cards">
<div class="gm-store-search container-cMG81i medium-1LLV3p"><div class="inner-2P4tQO"><input id="search-input" class="input-3Xdcic" placeholder="Search" value=""></div></div>
<div class="gm-inline-dropdown">
<div class="colorStandard-2KCXvj size14-e6ZScH default-3nhoK- formText-3fs7AJ description-3_Ncsb formText-3fs7AJ modeDefault-3a2Ph1">Sort by</div>
<select id="sort-select">
<option>Stars</option>
<option>A-Z</option>
<option>Last Updated</option>
</select>
</div>
<div class="flex-break"></div>
ALL_CARDS
</div>
<div class="container repos">
REPOS
</div>
<script>
const cards = [...document.getElementsByClassName('gm-store-card')];
const sortSelectEl = document.getElementById('sort-select');
sortSelectEl.oninput = () => {
switch (sortSelectEl.value) {
case 'Stars': {
cards.forEach((x) => { x.style.order = 999 - parseInt(x.getAttribute('data-stars')); });
break;
}
case 'Last Updated': {
cards.forEach((x) => { x.style.order = 3000000000 - parseInt(x.getAttribute('data-last-updated')); });
break;
}
case 'A-Z': {
cards.forEach((x) => { x.style.order = ''; });
break;
}
}
};
sortSelectEl.oninput();
const searchInputEl = document.getElementById('search-input');
searchInputEl.oninput = () => {
const fuzzyReg = new RegExp(`.*${searchInputEl.value}.*`, 'i');
cards.forEach((x) => { x.style.display = fuzzyReg.test(x.children[2].textContent) || fuzzyReg.test(x.children[3].textContent) ? '' : 'none'; });
};
const sidebarButtons = [...document.getElementsByClassName('sidebar-button')];
sidebarButtons.forEach((x) => x.onclick = () => {
sidebarButtons.forEach((y) => y.classList.remove('active'));
x.classList.add('active');
document.body.className = x.textContent.toLowerCase();
});
</script>
</body>
</html>