mirror of
https://github.com/VibedByKaKi/t3-code-android-nightly.git
synced 2026-10-09 11:51:15 +02:00
395 lines
16 KiB
TypeScript
395 lines
16 KiB
TypeScript
import "vite-plus/test/config";
|
|
import { defineConfig } from "vite-plus";
|
|
import * as NodeURL from "node:url";
|
|
|
|
/** Import restrictions every file keeps, including the one module exempt from the glyph rule. */
|
|
const RESTRICTED_IMPORT_PATHS = [
|
|
{
|
|
name: "@t3tools/client-runtime",
|
|
message:
|
|
"Import from an explicit @t3tools/client-runtime/* subpath. The package has no root export.",
|
|
},
|
|
{
|
|
name: "@pierre/diffs/react",
|
|
importNames: ["CodeView"],
|
|
message: "Use StyledDiffCodeView so web diff surfaces share styling and virtualized geometry.",
|
|
},
|
|
];
|
|
|
|
/**
|
|
* The cva functions behind components/ui exports. They style a foreign element to look
|
|
* like a Button or Toggle, which bypasses the component's variants; render the component
|
|
* instead (`render={<Button …/>}`, or `SelectButton` for a picker trigger).
|
|
*/
|
|
const RESTRICTED_UI_VARIANT_PATTERNS = [
|
|
{
|
|
group: ["**/components/ui/*", "**/ui/*", "./ui/*"],
|
|
importNames: ["buttonVariants", "toggleVariants", "badgeVariants", "selectTriggerVariants"],
|
|
message:
|
|
"Render the components/ui export instead of borrowing its class recipe (render={<Button …/>}, SelectButton, ToggleGroup).",
|
|
},
|
|
];
|
|
|
|
/** Lucide's pull-request glyphs, which only `pullRequestIcons.tsx` may name. */
|
|
const RESTRICTED_PULL_REQUEST_GLYPH_IMPORTS = {
|
|
name: "lucide-react",
|
|
importNames: [
|
|
"GitMerge",
|
|
"GitMergeIcon",
|
|
"GitPullRequest",
|
|
"GitPullRequestIcon",
|
|
"GitPullRequestArrow",
|
|
"GitPullRequestArrowIcon",
|
|
"GitPullRequestClosed",
|
|
"GitPullRequestClosedIcon",
|
|
"GitPullRequestDraft",
|
|
"GitPullRequestDraftIcon",
|
|
"GitPullRequestCreate",
|
|
"GitPullRequestCreateIcon",
|
|
"GitPullRequestCreateArrow",
|
|
"GitPullRequestCreateArrowIcon",
|
|
],
|
|
message:
|
|
"Pick a glyph by meaning from PullRequestGlyph in apps/web/src/components/pullRequest/pullRequestIcons.tsx so every surface draws the same pull request the same way.",
|
|
};
|
|
|
|
export default defineConfig({
|
|
resolve: {
|
|
alias: {
|
|
"~": NodeURL.fileURLToPath(new URL("./apps/web/src", import.meta.url)),
|
|
},
|
|
},
|
|
test: {
|
|
environment: "node",
|
|
exclude: [
|
|
"**/.repos/**",
|
|
"**/.t3/**",
|
|
"**/node_modules/**",
|
|
"**/dist/**",
|
|
"**/dist-electron/**",
|
|
"**/.{idea,git,cache,output,temp}/**",
|
|
],
|
|
hookTimeout: 60_000,
|
|
testTimeout: 60_000,
|
|
setupFiles: [
|
|
NodeURL.fileURLToPath(
|
|
new URL("./packages/shared/src/testing/longTempDir.ts", import.meta.url),
|
|
),
|
|
],
|
|
},
|
|
staged: {
|
|
// Formatter only for now — no lint or typecheck on commit.
|
|
"*": "vp fmt --no-error-on-unmatched-pattern",
|
|
},
|
|
fmt: {
|
|
ignorePatterns: [
|
|
".repos/**",
|
|
// Macroscope's glob-per-line ignore grammar, not Markdown: formatting
|
|
// it rewrites `*` as `_` and joins lines.
|
|
".macroscope/ignore.md",
|
|
".alchemy",
|
|
"dist",
|
|
"dist-electron",
|
|
"node_modules",
|
|
"pnpm-lock.yaml",
|
|
"*.tsbuildinfo",
|
|
"**/routeTree.gen.ts",
|
|
"apps/mobile/android/**",
|
|
"apps/mobile/ios/**",
|
|
"apps/mobile/uniwind-types.d.ts",
|
|
"*.icon/**",
|
|
],
|
|
sortPackageJson: {},
|
|
overrides: [
|
|
{
|
|
files: [".devcontainer/devcontainer.json"],
|
|
options: {
|
|
trailingComma: "none",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
lint: {
|
|
ignorePatterns: [
|
|
".repos",
|
|
".repos/**",
|
|
"dist",
|
|
"dist-electron",
|
|
"node_modules",
|
|
"pnpm-lock.yaml",
|
|
"*.tsbuildinfo",
|
|
"**/routeTree.gen.ts",
|
|
"apps/mobile/android/**",
|
|
"apps/mobile/ios/**",
|
|
"apps/mobile/uniwind-types.d.ts",
|
|
],
|
|
plugins: ["eslint", "oxc", "react", "unicorn", "typescript"],
|
|
jsPlugins: ["./oxlint-plugin-t3code/index.ts", "@shadcn/lint"],
|
|
settings: {
|
|
shadcn: { ui: "~/components/ui" },
|
|
},
|
|
categories: {
|
|
correctness: "warn",
|
|
suspicious: "warn",
|
|
perf: "warn",
|
|
},
|
|
rules: {
|
|
"unicorn/no-array-sort": "off",
|
|
"unicorn/consistent-function-scoping": "off",
|
|
"oxc/no-map-spread": "off",
|
|
"react-in-jsx-scope": "off",
|
|
"react-hooks/exhaustive-deps": "off",
|
|
"eslint/no-shadow": "off",
|
|
"eslint/no-await-in-loop": "off",
|
|
"eslint/no-underscore-dangle": "off",
|
|
"typescript/consistent-return": "off",
|
|
"typescript/no-base-to-string": "off",
|
|
"typescript/no-duplicate-type-constituents": "off",
|
|
"typescript/no-floating-promises": "off",
|
|
"typescript/no-implied-eval": "off",
|
|
"typescript/no-meaningless-void-operator": "off",
|
|
"typescript/no-redundant-type-constituents": "off",
|
|
"typescript/no-unnecessary-boolean-literal-compare": "off",
|
|
"typescript/no-unnecessary-type-conversion": "off",
|
|
"typescript/no-unnecessary-type-arguments": "off",
|
|
"typescript/no-unnecessary-type-assertion": "off",
|
|
"typescript/no-unnecessary-type-parameters": "off",
|
|
"typescript/no-unsafe-type-assertion": "off",
|
|
"typescript/await-thenable": "off",
|
|
"typescript/require-array-sort-compare": "off",
|
|
"typescript/restrict-template-expressions": "off",
|
|
"typescript/unbound-method": "off",
|
|
"eslint/no-restricted-imports": [
|
|
"error",
|
|
{ paths: [...RESTRICTED_IMPORT_PATHS, RESTRICTED_PULL_REQUEST_GLYPH_IMPORTS] },
|
|
],
|
|
"t3code/no-global-process-runtime": "error",
|
|
"t3code/no-inline-schema-compile": "warn",
|
|
"t3code/no-manual-effect-runtime-in-tests": "error",
|
|
"t3code/no-native-title-tooltip": "error",
|
|
"t3code/no-raw-mcp-registration": "error",
|
|
"t3code/no-test-in-loop": "error",
|
|
"t3code/no-rpc-permission-bypass": ["error", { allowRawClientAccess: true }],
|
|
"t3code/no-unscoped-has": "error",
|
|
"t3code/namespace-node-imports": "error",
|
|
"t3code/prefer-catch-tags": "error",
|
|
"t3code/require-centered-scroll-gutter": "error",
|
|
"t3code/require-suppression-reason": "error",
|
|
},
|
|
overrides: [
|
|
{
|
|
files: ["packages/client-runtime/src/state/**", "apps/{web,mobile,desktop}/src/**"],
|
|
rules: { "t3code/no-rpc-permission-bypass": ["error", { allowRawClientAccess: false }] },
|
|
},
|
|
{
|
|
// Only shared command boundaries install the session-backed permission guard.
|
|
files: [
|
|
"packages/client-runtime/src/state/runtime.ts",
|
|
"packages/client-runtime/src/state/vcsAction.ts",
|
|
],
|
|
rules: {
|
|
"t3code/no-rpc-permission-bypass": [
|
|
"error",
|
|
{ allowGuardInstallation: true, allowRawClientAccess: false },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
// These clients are session metadata, device streams, and an Expo update adapter.
|
|
files: [
|
|
"apps/web/src/components/settings/ConnectionsSettings.tsx",
|
|
"apps/mobile/src/features/updates/app-updates.ts",
|
|
"apps/web/src/components/device/DevicePhoneViewport.tsx",
|
|
"apps/web/src/components/device/DeviceDuoViewport.tsx",
|
|
],
|
|
rules: { "t3code/no-rpc-permission-bypass": ["error", { allowRawClientAccess: true }] },
|
|
},
|
|
{
|
|
// Incompatible hosts cannot open a normal session; their updater uses a dedicated socket.
|
|
files: ["packages/client-runtime/src/connection/outdatedHostUpdate.ts"],
|
|
rules: { "t3code/no-rpc-permission-bypass": "off" },
|
|
},
|
|
{
|
|
// RPC implementation and transport test fixtures need the raw client.
|
|
files: [
|
|
"packages/client-runtime/src/rpc/**",
|
|
"**/*.{test,spec}.{ts,tsx,js,jsx,mts,cts,mjs,cjs}",
|
|
],
|
|
rules: { "t3code/no-rpc-permission-bypass": "off" },
|
|
},
|
|
{
|
|
// The one place that reads the host platform to seed the injected references.
|
|
files: ["packages/shared/src/hostProcess.ts"],
|
|
rules: { "t3code/no-global-process-runtime": "off" },
|
|
},
|
|
{
|
|
// The registration helpers that only accept handlers built by McpToolAccess.
|
|
files: ["apps/server/src/mcp/McpHttpServer.ts"],
|
|
rules: { "t3code/no-raw-mcp-registration": "off" },
|
|
},
|
|
{
|
|
files: ["apps/web/src/**"],
|
|
excludeFiles: ["apps/web/src/components/ui/**"],
|
|
rules: {
|
|
"eslint/no-restricted-imports": [
|
|
"error",
|
|
{
|
|
paths: [...RESTRICTED_IMPORT_PATHS, RESTRICTED_PULL_REQUEST_GLYPH_IMPORTS],
|
|
patterns: RESTRICTED_UI_VARIANT_PATTERNS,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
// The one module allowed to name lucide's pull-request glyphs; everything else picks
|
|
// from its vocabulary. The other import restrictions still apply here.
|
|
files: ["apps/web/src/components/pullRequest/pullRequestIcons.tsx"],
|
|
rules: { "eslint/no-restricted-imports": ["error", { paths: RESTRICTED_IMPORT_PATHS }] },
|
|
},
|
|
{
|
|
files: ["apps/mobile/src/**"],
|
|
rules: { "t3code/no-mobile-uniwind-theme-escape-hatches": "error" },
|
|
},
|
|
{
|
|
// Every class in web code must be one Tailwind generates: a typo or a class nothing
|
|
// declares ships silently unstyled. JS hooks use data attributes, not class names.
|
|
files: ["apps/web/src/**"],
|
|
rules: { "shadcn/no-unknown-classes": "error" },
|
|
},
|
|
{
|
|
// Colors come from theme tokens so status tones follow custom themes. components/ui
|
|
// has no findings and stays covered too.
|
|
files: ["apps/web/src/**"],
|
|
rules: { "shadcn/no-raw-colors": "error" },
|
|
},
|
|
{
|
|
// Third-party marks (brand logos, the macOS permission panes, Codex's Computer Use
|
|
// mark) must keep their exact colors, so the files that hold them are exempt.
|
|
files: ["apps/web/src/components/Icons.tsx", "apps/web/src/components/JetBrainsIcons.tsx"],
|
|
rules: { "shadcn/no-raw-colors": "off" },
|
|
},
|
|
{
|
|
// components/ui exports own their look. App code picks a variant or size instead
|
|
// of restyling with className; layout classes (width, flex, margin, position) stay
|
|
// allowed because placement belongs to the parent. components/ui is for generic
|
|
// primitives: a look that belongs to one feature stays in that feature's component.
|
|
files: ["apps/web/src/**"],
|
|
excludeFiles: ["apps/web/src/components/ui/**"],
|
|
rules: {
|
|
// A className built at runtime on a ui component is one no-restyle cannot read.
|
|
"shadcn/require-static-classes": "error",
|
|
// Appearance values come from the theme and Tailwind's scales. Layout stays free
|
|
// (placement belongs to the parent); the other entries are values no scale can hold.
|
|
"shadcn/no-arbitrary-values": [
|
|
"error",
|
|
{
|
|
allow: [
|
|
"layout",
|
|
// Which properties an element animates is per-element behaviour, like layout,
|
|
// not a design value; timing curves and durations still come from the theme.
|
|
"transition",
|
|
// Overlays that follow their frame's corner, which is set at runtime
|
|
// (floating preview) or by the element they decorate (composer outline).
|
|
"rounded-[inherit]",
|
|
// Inline chips size in em so they scale with the text they sit in
|
|
// (the composer honours the prompt font-size preference).
|
|
"gap-[0.33em]",
|
|
"px-[0.5em]",
|
|
"rounded-[0.5em]",
|
|
"text-[0.86em]",
|
|
// Project icons render from 14px to 48px and keep one proportional corner.
|
|
"rounded-[25%]",
|
|
// An emoji project icon fills its container, whatever size the parent gives it.
|
|
"text-[length:80cqh]",
|
|
// The platform's own selection colour on a selected composer chip.
|
|
"bg-[Highlight]",
|
|
// Brand marks keep their brand colours (Cursor, Grok, Claude).
|
|
"fill-[#26251E]",
|
|
"fill-[#EDECEC]",
|
|
"fill-[#0F0F0F]",
|
|
"fill-[#F5F5F5]",
|
|
"fill-[#d97757]",
|
|
"text-[#d97757]",
|
|
],
|
|
},
|
|
],
|
|
"shadcn/no-restyle": [
|
|
"error",
|
|
{
|
|
allow: ["layout"],
|
|
contracts: [
|
|
{
|
|
// CollapsibleTrigger is a bare button with no styled counterpart
|
|
// (a disclosure row is not a Button), so its className is the API.
|
|
// Every other trigger has one: style them with render={<Button …/>}.
|
|
pattern: "^CollapsibleTrigger$",
|
|
allow: ["layout", "color", "typography", "spacing", "shape", "effects", "motion"],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
// The sign-in masthead is T3 brand artwork: fixed gradients, not theme surfaces.
|
|
files: ["apps/web/src/components/auth/AuthSurfaceShell.tsx"],
|
|
rules: { "shadcn/no-arbitrary-values": "off" },
|
|
},
|
|
{
|
|
// Shared client code must not call APIs missing from Hermes. Our ESNext
|
|
// TypeScript target accepts them even when they would crash mobile at launch.
|
|
// Tests run on Node and are exempt.
|
|
files: [
|
|
"apps/mobile/src/**",
|
|
"packages/client-runtime/src/**",
|
|
"packages/contracts/src/**",
|
|
"packages/shared/src/**",
|
|
],
|
|
excludeFiles: ["**/*.test.ts", "**/*.test.tsx"],
|
|
rules: { "t3code/no-hermes-unsupported-apis": "error" },
|
|
},
|
|
{
|
|
// Reviewed native and third-party interop boundaries that cannot consume a className.
|
|
files: [
|
|
"apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx",
|
|
"apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx",
|
|
"apps/mobile/src/features/files/FileMarkdownPreview.tsx",
|
|
"apps/mobile/src/features/files/SourceFileSurface.tsx",
|
|
"apps/mobile/src/features/files/AttachmentFileScreen.tsx",
|
|
"apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx",
|
|
"apps/mobile/src/features/files/thread-file-navigator-pane.tsx",
|
|
"apps/mobile/src/features/home/HomeHeader.tsx",
|
|
"apps/mobile/src/features/review/ReviewSheet.tsx",
|
|
"apps/mobile/src/features/review/useNativeReviewDiffBridge.ts",
|
|
"apps/mobile/src/features/settings/SettingsEnvironmentsRouteScreen.tsx",
|
|
"apps/mobile/src/features/threads/GitActionProgressOverlay.tsx",
|
|
"apps/mobile/src/features/threads/NewTaskDraftScreen.tsx",
|
|
"apps/mobile/src/features/threads/ThreadComposer.tsx",
|
|
"apps/mobile/src/features/threads/ThreadFeed.tsx",
|
|
"apps/mobile/src/features/settings/appearance/components/FontSizeSliderRow.tsx",
|
|
"apps/mobile/src/features/threads/NewTaskContextPickerScreens.tsx",
|
|
"apps/mobile/src/features/threads/ThreadQueueControl.tsx",
|
|
"apps/mobile/src/features/threads/ThreadAgentsSheet.tsx",
|
|
"apps/mobile/src/features/review/ReviewCommentCard.tsx",
|
|
"apps/mobile/src/features/threads/ThreadSettingsSheet.tsx",
|
|
"apps/mobile/src/features/threads/git/GitOverviewSheet.tsx",
|
|
"apps/mobile/src/features/threads/thread-list-items.tsx",
|
|
"apps/mobile/src/features/threads/thread-list-v2-items.tsx",
|
|
"apps/mobile/src/lib/useMobileNavigationTheme.ts",
|
|
"apps/mobile/src/native/T3ComposerEditor.ios.tsx",
|
|
"apps/mobile/src/native/T3ComposerEditor.native.tsx",
|
|
"apps/mobile/src/native/SelectableMarkdownText.android.tsx",
|
|
],
|
|
rules: {
|
|
"t3code/no-mobile-uniwind-theme-escape-hatches": ["error", { allowUniwindTheme: true }],
|
|
},
|
|
},
|
|
],
|
|
options: {
|
|
reportUnusedDisableDirectives: "error",
|
|
// Revisit once Oxlint's tsgolint path can integrate with @effect/tsgo diagnostics.
|
|
typeAware: false,
|
|
typeCheck: false,
|
|
},
|
|
},
|
|
});
|