styleguide
This commit is contained in:
parent
aed7f7207b
commit
82b8aae3ec
2 changed files with 135 additions and 1 deletions
|
|
@ -3,7 +3,7 @@ memory_config:
|
||||||
disabled: false
|
disabled: false
|
||||||
code_review:
|
code_review:
|
||||||
disable: false
|
disable: false
|
||||||
comment_severity_threshold: MEDIUM
|
comment_severity_threshold: LOW
|
||||||
max_review_comments: -1
|
max_review_comments: -1
|
||||||
pull_request_opened:
|
pull_request_opened:
|
||||||
help: false
|
help: false
|
||||||
|
|
|
||||||
134
.gemini/styleguide.md
Normal file
134
.gemini/styleguide.md
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
title: Equicord Review Guidelines
|
||||||
|
|
||||||
|
rules:
|
||||||
|
critical:
|
||||||
|
licenseHeader: NEVER edit existing license headers, new files use 2026
|
||||||
|
checkDeprecated: always verify components/functions arent deprecated before using
|
||||||
|
noAnyTypes: NEVER use any for Discord objects, import proper types from @vencord/discord-types
|
||||||
|
minimalChanges: fix with smallest possible change, no refactoring unless explicitly asked
|
||||||
|
noOverengineering: solve the problem at hand only, no patterns for patterns sake
|
||||||
|
humanText: write natural human text in errors, descriptions, messages. No dashes or robotic formatting. Write like "Module not found" not "Module - not found". Keep it simple and conversational
|
||||||
|
code:
|
||||||
|
deleteNotComment: DELETE dead code, dont comment it, git preserves history
|
||||||
|
noHardcoded: no hardcoded values, unused imports, magic numbers
|
||||||
|
useClassNameFactory: import classNameFactory from @utils/css (NOT @api/Styles which is deprecated), ALWAYS use cl() for class names, never hardcode strings like "vc-plugin-name-class"
|
||||||
|
useClasses: when combining multiple class names use classes() from @utils/misc, not template strings like `${a} ${b}`
|
||||||
|
useExisting: use utilities from @utils/, @api/, @components/
|
||||||
|
logger: use Logger from @utils/Logger, not console.log
|
||||||
|
descriptions: capital first letter, end with period
|
||||||
|
minimal: less code with same functionality is ALWAYS better
|
||||||
|
noComments: NEVER add comments unless explicitly asked
|
||||||
|
preserveComments: keep existing comments, they contain important context
|
||||||
|
performance: use Map/Set, .some/.find, no spread in loops, Promise.all
|
||||||
|
simple: KISS over clever, flat over nested, explicit over implicit
|
||||||
|
typescript:
|
||||||
|
prefer: optional chaining (?.), nullish coalescing (??), const, arrow functions
|
||||||
|
use: destructuring, template literals, object shorthand, array methods
|
||||||
|
style: early returns, trust inference, inline single-use variables
|
||||||
|
react:
|
||||||
|
errorBoundary: wrap complex components with ErrorBoundary.wrap(Component, { noop: true })
|
||||||
|
nullNotUndefined: return null for conditional rendering not undefined
|
||||||
|
forbidden: React.cloneElement, React.isValidElement, React.memo(), React.lazy, React.Children
|
||||||
|
cleanup: always return cleanup functions in useEffect
|
||||||
|
forbidden:
|
||||||
|
DOM: direct DOM manipulation (use patches)
|
||||||
|
cssOnly: CSS-only plugins
|
||||||
|
emptyCatch: empty catch blocks
|
||||||
|
hardcodedVars: hardcoded minified vars in patches (e,t,n)
|
||||||
|
settingsArrays: NEVER use settings.use() with arrays, mutate then reassign
|
||||||
|
antiPatterns{bad ⟹ good}:
|
||||||
|
"value !== null && value !== undefined" ⟹ "value"
|
||||||
|
"array && array.length > 0" ⟹ "array.length"
|
||||||
|
"settings?.store?.value" ⟹ "settings.store.value"
|
||||||
|
"value || defaultValue" ⟹ "value ?? defaultValue"
|
||||||
|
|
||||||
|
patching:
|
||||||
|
core:
|
||||||
|
surgical: minimum code touched, stability over cleverness, one patch per concern
|
||||||
|
iterate: try multiple approaches, keep simplifying until you find the cleanest solution
|
||||||
|
minimize: remove any regex character that doesnt change the match result
|
||||||
|
balanced: not overengineered, not overlooked, just clean and future-proof
|
||||||
|
find:
|
||||||
|
preferIntl: use #{intl::KEY} in find when possible, most stable anchor
|
||||||
|
makeUnique: if find matches multiple modules, add nearby stable string like "),icon:" or function name
|
||||||
|
combine: "#{intl::PIN_MESSAGE}),icon:" is better than generic strings
|
||||||
|
match:
|
||||||
|
shortestPath: match only what you need to replace, let find do the targeting
|
||||||
|
intlInMatch: can use #{intl::KEY} in match too, gets canonicalized to hash
|
||||||
|
simpleWins: /#{intl::KEY}\)/ beats /label:\i\.pinned\?.{0,60}#{intl::KEY}\)/
|
||||||
|
useAmpersand: $& keeps original, append/prepend to it
|
||||||
|
boundedGaps: .{0,50} not .+? or .*?
|
||||||
|
captureOnlyIfNeeded: only use capture groups if reusing in replace
|
||||||
|
NEVER:
|
||||||
|
hardcodedMinified: e,t,n,r,i,o,s,l,c,u,$_,xx,eD,eH,eW ⟹ use \i instead
|
||||||
|
minifiedChains: \i\.\i alone ⟹ surround with stable strings
|
||||||
|
unboundedGaps: .+? or .*? ⟹ use .{0,N}
|
||||||
|
genericPatterns: /className:\i/ alone ⟹ add anchor
|
||||||
|
rawIntlHash: .aA4Vce ⟹ #{intl::KEY_NAME}
|
||||||
|
examples:
|
||||||
|
clean: |
|
||||||
|
find: "#{intl::PIN_MESSAGE}),icon:"
|
||||||
|
match: /#{intl::PIN_MESSAGE}\)/
|
||||||
|
replace: "$self.getPinLabel(arguments[0]))"
|
||||||
|
append: |
|
||||||
|
find: "#{intl::VIEW_AS_ROLES_MENTIONS_WARNING}"
|
||||||
|
match: /#{intl::VIEW_AS_ROLES_MENTIONS_WARNING}.{0,100}(?=])/
|
||||||
|
replace: "$&,$self.renderTooltip(arguments[0].guild)"
|
||||||
|
wrap: |
|
||||||
|
find: "#{intl::THREE_USERS_TYPING}"
|
||||||
|
match: /(?<="aria-atomic":!0,children:)\i/
|
||||||
|
replace: "$self.renderTypingUsers({ children: $& })"
|
||||||
|
|
||||||
|
intl:
|
||||||
|
syntax: "#{intl::KEY}" or "#{intl::HASH::raw}" if key unknown
|
||||||
|
workflow: hash in code → reverse to get key → use #{intl::KEY} or fallback to #{intl::HASH::raw}
|
||||||
|
|
||||||
|
pluginInterop:
|
||||||
|
checkEnabled: isPluginEnabled(name) from @api/PluginManager takes a string, checks required/isDependency/enabled
|
||||||
|
import: import the plugin directly from its path to access .name and functions
|
||||||
|
avoid: dont use Vencord.Plugins.plugins or plugin.started, dont use "as unknown as" casting
|
||||||
|
example: |
|
||||||
|
import { isPluginEnabled } from "@api/PluginManager";
|
||||||
|
import otherPlugin from "@equicordplugins/otherPlugin";
|
||||||
|
if (!isPluginEnabled(otherPlugin.name)) return null;
|
||||||
|
otherPlugin.someFunction();
|
||||||
|
|
||||||
|
reference:
|
||||||
|
types: Channel, Guild, GuildMember, User, Role, Message from @vencord/discord-types
|
||||||
|
components: Paragraph, BaseText, Flex, Button, ErrorCard from @components/
|
||||||
|
settings: use definePluginSettings from @api/Settings, not inline settings object
|
||||||
|
utilities:
|
||||||
|
clipboard: copyToClipboard from @utils/clipboard
|
||||||
|
discord: insertTextIntoChatInputBox, getCurrentChannel, getCurrentGuild, getIntlMessage, openUserProfile, openPrivateChannel, sendMessage, copyWithToast, getUniqueUsername, fetchUserProfile from @utils/discord
|
||||||
|
css: classNameFactory, classNameToSelector from @utils/css
|
||||||
|
misc: classes, sleep, isObject, isObjectEmpty, pluralise, parseUrl, identity from @utils/misc
|
||||||
|
text: formatDuration, formatDurationMs, humanFriendlyJoin, makeCodeblock, toInlineCode, escapeRegExp from @utils/text
|
||||||
|
modal: openModal, closeModal, ModalRoot, ModalHeader, ModalContent, ModalCloseButton from @utils/modal
|
||||||
|
margins: Margins from @utils/margins (Margins.top8, Margins.bottom16, etc.)
|
||||||
|
guards: isTruthy, isNonNullish from @utils/guards
|
||||||
|
web: saveFile, chooseFile from @utils/web
|
||||||
|
webpackCommon:
|
||||||
|
icons: IconUtils from @webpack/common, NEVER hardcode cdn.discordapp.com URLs
|
||||||
|
getUserAvatarURL: IconUtils.getUserAvatarURL(user, canAnimate?, size?)
|
||||||
|
getDefaultAvatarURL: IconUtils.getDefaultAvatarURL(id)
|
||||||
|
getUserBannerURL: IconUtils.getUserBannerURL({ id, banner, canAnimate?, size })
|
||||||
|
getGuildIconURL: IconUtils.getGuildIconURL({ id, icon, size?, canAnimate? })
|
||||||
|
getGuildBannerURL: IconUtils.getGuildBannerURL(guild, canAnimate?)
|
||||||
|
getChannelIconURL: IconUtils.getChannelIconURL({ id, icon })
|
||||||
|
getEmojiURL: IconUtils.getEmojiURL({ id, animated, size })
|
||||||
|
getApplicationIconURL: IconUtils.getApplicationIconURL(data)
|
||||||
|
getGameAssetURL: IconUtils.getGameAssetURL(data)
|
||||||
|
stores: UserStore, GuildStore, ChannelStore, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, PresenceStore, RelationshipStore, MessageStore, EmojiStore, ThemeStore, PermissionStore, VoiceStateStore from @webpack/common
|
||||||
|
actions: RestAPI, FluxDispatcher, MessageActions, NavigationRouter, ChannelRouter, ChannelActionCreators, SettingsRouter from @webpack/common
|
||||||
|
utils: Constants (Constants.Endpoints), SnowflakeUtils, Parser, PermissionsBits, moment, lodash, ColorUtils, ImageUtils, DateUtils, UsernameUtils, DisplayProfileUtils from @webpack/common
|
||||||
|
components: Tooltip, TextInput, TextArea, Select, Slider, Avatar, Menu, Popout, ScrollerThin, Timestamp from @webpack/common
|
||||||
|
toasts: Toasts, showToast from @webpack/common
|
||||||
|
imports:
|
||||||
|
lazy: findByPropsLazy, findStoreLazy, findComponentByCodeLazy, findExportedComponentLazy from @webpack
|
||||||
|
common: useState, useEffect, useCallback, useStateFromStores from @webpack/common
|
||||||
|
neverHardcode:
|
||||||
|
cdnURLs: NEVER use "cdn.discordapp.com/avatars/", "cdn.discordapp.com/banners/", "cdn.discordapp.com/emojis/", "cdn.discordapp.com/icons/", "cdn.discordapp.com/channel-icons/" ⟹ use IconUtils
|
||||||
|
apiEndpoints: NEVER hardcode "/api/v9", "/users/@me" ⟹ use Constants.Endpoints or RestAPI
|
||||||
|
console: NEVER use console.log/warn/error ⟹ use Logger from @utils/Logger
|
||||||
|
classConcat: NEVER use `${a} ${b}` for class names ⟹ use classes(a, b) from @utils/misc
|
||||||
|
defaultAvatars: NEVER hardcode "/assets/*.png" for default avatars ⟹ use IconUtils.getDefaultAvatarURL(id)
|
||||||
Loading…
Add table
Reference in a new issue