bsmnt 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/2026-02-11-test-patch-bump.md +5 -0
- package/.changeset/README.md +10 -0
- package/.changeset/config.json +16 -0
- package/.cursor/rules/README.md +184 -0
- package/.cursor/rules/architecture.mdc +437 -0
- package/.cursor/rules/components.mdc +436 -0
- package/.cursor/rules/integrations.mdc +447 -0
- package/.cursor/rules/main.mdc +278 -0
- package/.cursor/rules/styling.mdc +433 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/.github/workflows/.gitkeep +0 -0
- package/.github/workflows/ci.yml +37 -0
- package/.github/workflows/release.yml +54 -0
- package/.tldr/cache/call_graph.json +7 -0
- package/.tldr/languages.json +6 -0
- package/.tldr/status +1 -0
- package/.tldrignore +84 -0
- package/.vscode/extensions.json +20 -0
- package/.vscode/settings.json +98 -0
- package/CHANGELOG.md +13 -0
- package/CLAUDE.md +138 -0
- package/README.md +176 -0
- package/bin/index.js +262 -0
- package/biome.json +44 -0
- package/bun.lock +496 -0
- package/changelog/04-02-26.md +86 -0
- package/changelog/05-02-26.md +101 -0
- package/changelog/09-02-26.md +83 -0
- package/docs/fix-studio-hydration.md +46 -0
- package/docs/plans/2026-01-29-sanity-smart-merge-design.md +196 -0
- package/docs/plans/2026-01-29-sanity-smart-merge-implementation.md +695 -0
- package/docs/sanity-setup-steps.md +199 -0
- package/integrations/basehub/README.md +3 -0
- package/integrations/sanity/app/api/draft-mode/disable/route.ts +7 -0
- package/integrations/sanity/app/api/draft-mode/enable/route.ts +21 -0
- package/integrations/sanity/app/api/revalidate/route.ts +37 -0
- package/integrations/sanity/app/layout.tsx +111 -0
- package/integrations/sanity/app/sitemap.ts +80 -0
- package/integrations/sanity/app/studio/[[...tool]]/page.tsx +8 -0
- package/integrations/sanity/app/studio/layout.tsx +7 -0
- package/integrations/sanity/components/ui/sanity-image/index.tsx +37 -0
- package/integrations/sanity/lib/integrations/README.md +58 -0
- package/integrations/sanity/lib/integrations/check-integration.ts +62 -0
- package/integrations/sanity/lib/integrations/sanity/README.md +144 -0
- package/integrations/sanity/lib/integrations/sanity/client.ts +30 -0
- package/integrations/sanity/lib/integrations/sanity/components/disable-draft-mode.tsx +29 -0
- package/integrations/sanity/lib/integrations/sanity/components/rich-text.tsx +73 -0
- package/integrations/sanity/lib/integrations/sanity/env.ts +38 -0
- package/integrations/sanity/lib/integrations/sanity/live/index.tsx +34 -0
- package/integrations/sanity/lib/integrations/sanity/queries.ts +99 -0
- package/integrations/sanity/lib/integrations/sanity/sanity.cli.ts +20 -0
- package/integrations/sanity/lib/integrations/sanity/sanity.config.ts +94 -0
- package/integrations/sanity/lib/integrations/sanity/sanity.types.ts +337 -0
- package/integrations/sanity/lib/integrations/sanity/schema.json +1850 -0
- package/integrations/sanity/lib/integrations/sanity/schemas/article.ts +132 -0
- package/integrations/sanity/lib/integrations/sanity/schemas/example.ts +203 -0
- package/integrations/sanity/lib/integrations/sanity/schemas/index.ts +37 -0
- package/integrations/sanity/lib/integrations/sanity/schemas/link.ts +127 -0
- package/integrations/sanity/lib/integrations/sanity/schemas/metadata.ts +68 -0
- package/integrations/sanity/lib/integrations/sanity/schemas/navigation.ts +39 -0
- package/integrations/sanity/lib/integrations/sanity/schemas/page.ts +77 -0
- package/integrations/sanity/lib/integrations/sanity/schemas/richText.ts +59 -0
- package/integrations/sanity/lib/integrations/sanity/structure.ts +5 -0
- package/integrations/sanity/lib/integrations/sanity/utils/image.ts +11 -0
- package/integrations/sanity/lib/integrations/sanity/utils/link.ts +61 -0
- package/integrations/sanity/lib/scripts/copy-sanity-mcp.ts +23 -0
- package/integrations/sanity/lib/scripts/generate-page.ts +310 -0
- package/integrations/sanity/lib/utils/metadata.ts +190 -0
- package/layers/experiment/components/layout/header/index.tsx +58 -0
- package/layers/experiment/components/layout/navigation-menu.tsx +127 -0
- package/layers/experiment/lib/constants.ts +12 -0
- package/layers/webgl/app/page.tsx +10 -0
- package/layers/webgl/components/webgl/canvas/dynamic.tsx +34 -0
- package/layers/webgl/components/webgl/canvas/index.tsx +43 -0
- package/layers/webgl/components/webgl/components/scene/index.tsx +21 -0
- package/layers/webgpu/.gitkeep +0 -0
- package/package.json +44 -0
- package/plugins/README.md +21 -0
- package/plugins/no-anchor-element.grit +11 -0
- package/plugins/no-relative-parent-imports.grit +6 -0
- package/plugins/no-unnecessary-forwardref.grit +5 -0
- package/src/commands/add-integration.js +325 -0
- package/src/commands/create.js +415 -0
- package/src/commands/setup-sanity.js +426 -0
- package/src/commands/worktree.js +805 -0
- package/src/mergers/check-integration-merger.js +105 -0
- package/src/mergers/config.js +137 -0
- package/src/mergers/index.js +355 -0
- package/src/mergers/layout-merger.js +223 -0
- package/src/mergers/next-config-merger.js +63 -0
- package/src/mergers/sitemap-merger.js +121 -0
- package/tasks/prd-next-starter-dynamic-layers.md +184 -0
- package/tasks/prd.json +153 -0
- package/tasks/progress.txt +115 -0
- package/template-hooks/use-battery.ts +126 -0
- package/template-hooks/use-device-perf.ts +184 -0
- package/template-hooks/use-intersection-observer.ts +32 -0
- package/template-hooks/use-media.ts +33 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { usePathname } from "next/navigation";
|
|
4
|
+
import { Link } from "@/components/ui/link";
|
|
5
|
+
import EXPERIMENTS from "@/lib/constants";
|
|
6
|
+
import { cn } from "@/lib/styles/cn";
|
|
7
|
+
import {
|
|
8
|
+
NavigationMenu,
|
|
9
|
+
NavigationMenuContent,
|
|
10
|
+
NavigationMenuItem,
|
|
11
|
+
NavigationMenuLink,
|
|
12
|
+
NavigationMenuList,
|
|
13
|
+
NavigationMenuTrigger,
|
|
14
|
+
} from "../navigation-menu";
|
|
15
|
+
|
|
16
|
+
export const Header = () => {
|
|
17
|
+
const pathname = usePathname();
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<nav className="fixed top-0 z-50 w-full py-2">
|
|
21
|
+
<div className="mx-auto flex items-center justify-between px-4">
|
|
22
|
+
<Link href="/" className="flex items-center gap-2 text-xl font-bold">
|
|
23
|
+
Basement Experiments Starter
|
|
24
|
+
</Link>
|
|
25
|
+
<NavigationMenu>
|
|
26
|
+
<NavigationMenuList>
|
|
27
|
+
<NavigationMenuItem>
|
|
28
|
+
<NavigationMenuTrigger className="text-sm font-medium px-8 py-6">
|
|
29
|
+
Experiments
|
|
30
|
+
</NavigationMenuTrigger>
|
|
31
|
+
<NavigationMenuContent>
|
|
32
|
+
<ul className="grid w-48 p-2 gap-1 bg-indigo-950/70 backdrop-blur-3xl text-white">
|
|
33
|
+
{EXPERIMENTS.map((experiment) => (
|
|
34
|
+
<li key={experiment.path}>
|
|
35
|
+
<NavigationMenuLink asChild>
|
|
36
|
+
<Link
|
|
37
|
+
href={experiment.path}
|
|
38
|
+
className={cn(
|
|
39
|
+
"block px-2 py-2 text-sm transition-colors duration-200 rounded-sm font-medium",
|
|
40
|
+
pathname === experiment.path
|
|
41
|
+
? "bg-purple-600 text-white"
|
|
42
|
+
: "text-white hover:bg-purple-500/20",
|
|
43
|
+
)}
|
|
44
|
+
>
|
|
45
|
+
{experiment.name}
|
|
46
|
+
</Link>
|
|
47
|
+
</NavigationMenuLink>
|
|
48
|
+
</li>
|
|
49
|
+
))}
|
|
50
|
+
</ul>
|
|
51
|
+
</NavigationMenuContent>
|
|
52
|
+
</NavigationMenuItem>
|
|
53
|
+
</NavigationMenuList>
|
|
54
|
+
</NavigationMenu>
|
|
55
|
+
</div>
|
|
56
|
+
</nav>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { ChevronDown } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/styles/cn";
|
|
6
|
+
|
|
7
|
+
function NavigationMenu({
|
|
8
|
+
className,
|
|
9
|
+
children,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root>) {
|
|
12
|
+
return (
|
|
13
|
+
<NavigationMenuPrimitive.Root
|
|
14
|
+
className={cn(
|
|
15
|
+
"relative z-10 flex max-w-max flex-1 items-center justify-center",
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
{children}
|
|
21
|
+
<NavigationMenuViewport />
|
|
22
|
+
</NavigationMenuPrimitive.Root>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function NavigationMenuList({
|
|
27
|
+
className,
|
|
28
|
+
...props
|
|
29
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
|
|
30
|
+
return (
|
|
31
|
+
<NavigationMenuPrimitive.List
|
|
32
|
+
className={cn(
|
|
33
|
+
"group flex flex-1 list-none items-center justify-center space-x-1",
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const NavigationMenuItem = NavigationMenuPrimitive.Item;
|
|
42
|
+
|
|
43
|
+
const navigationMenuTriggerStyle = cva(
|
|
44
|
+
"bg-indigo-950/30 backdrop-blur-3xl border border-purple-400/50 text-white group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-purple-800/50 hover:text-white focus:bg-purple-800/50 focus:text-white focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:text-white data-[state=open]:bg-indigo-900/70 data-[state=open]:hover:bg-purple-800/50 data-[state=open]:focus:bg-purple-800/50 cursor-pointer",
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
function NavigationMenuTrigger({
|
|
48
|
+
className,
|
|
49
|
+
children,
|
|
50
|
+
...props
|
|
51
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
|
|
52
|
+
return (
|
|
53
|
+
<NavigationMenuPrimitive.Trigger
|
|
54
|
+
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
|
55
|
+
{...props}
|
|
56
|
+
>
|
|
57
|
+
{children}{" "}
|
|
58
|
+
<ChevronDown
|
|
59
|
+
className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
|
|
60
|
+
aria-hidden="true"
|
|
61
|
+
/>
|
|
62
|
+
</NavigationMenuPrimitive.Trigger>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function NavigationMenuContent({
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
|
|
70
|
+
return (
|
|
71
|
+
<NavigationMenuPrimitive.Content
|
|
72
|
+
className={cn(
|
|
73
|
+
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto",
|
|
74
|
+
className,
|
|
75
|
+
)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const NavigationMenuLink = NavigationMenuPrimitive.Link;
|
|
82
|
+
|
|
83
|
+
function NavigationMenuViewport({
|
|
84
|
+
className,
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
|
|
87
|
+
return (
|
|
88
|
+
<div className={cn("absolute left-0 top-full flex justify-center")}>
|
|
89
|
+
<NavigationMenuPrimitive.Viewport
|
|
90
|
+
className={cn(
|
|
91
|
+
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border border-purple-400/50 bg-indigo-950/70 text-white shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
|
|
92
|
+
className,
|
|
93
|
+
)}
|
|
94
|
+
{...props}
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function NavigationMenuIndicator({
|
|
101
|
+
className,
|
|
102
|
+
...props
|
|
103
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
|
|
104
|
+
return (
|
|
105
|
+
<NavigationMenuPrimitive.Indicator
|
|
106
|
+
className={cn(
|
|
107
|
+
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
|
|
108
|
+
className,
|
|
109
|
+
)}
|
|
110
|
+
{...props}
|
|
111
|
+
>
|
|
112
|
+
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-purple-400 shadow-md" />
|
|
113
|
+
</NavigationMenuPrimitive.Indicator>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export {
|
|
118
|
+
NavigationMenu,
|
|
119
|
+
NavigationMenuContent,
|
|
120
|
+
NavigationMenuIndicator,
|
|
121
|
+
NavigationMenuItem,
|
|
122
|
+
NavigationMenuLink,
|
|
123
|
+
NavigationMenuList,
|
|
124
|
+
NavigationMenuTrigger,
|
|
125
|
+
navigationMenuTriggerStyle,
|
|
126
|
+
NavigationMenuViewport,
|
|
127
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import dynamic from "next/dynamic";
|
|
4
|
+
import { Suspense, useRef } from "react";
|
|
5
|
+
import { cn } from "@/lib/styles/cn";
|
|
6
|
+
|
|
7
|
+
const WebGLCanvas = dynamic(() => import("./index"), {
|
|
8
|
+
ssr: false,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export default function DynamicCanvas() {
|
|
12
|
+
const eventSourceRef = useRef<HTMLDivElement>(null);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
ref={eventSourceRef}
|
|
17
|
+
className={cn(
|
|
18
|
+
"pointer-events-none fixed top-0 left-0 z-50 h-lvh w-full overflow-hidden",
|
|
19
|
+
)}
|
|
20
|
+
>
|
|
21
|
+
<Suspense
|
|
22
|
+
fallback={
|
|
23
|
+
<div className="flex h-full w-full items-center justify-center bg-black/10">
|
|
24
|
+
<span className="font-mono text-xs uppercase tracking-widest opacity-30">
|
|
25
|
+
Loading WebGL...
|
|
26
|
+
</span>
|
|
27
|
+
</div>
|
|
28
|
+
}
|
|
29
|
+
>
|
|
30
|
+
<WebGLCanvas eventSource={eventSourceRef} />
|
|
31
|
+
</Suspense>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Preload } from "@react-three/drei";
|
|
4
|
+
import { Canvas } from "@react-three/fiber";
|
|
5
|
+
import dynamic from "next/dynamic";
|
|
6
|
+
|
|
7
|
+
import { cn } from "@/lib/styles/cn";
|
|
8
|
+
|
|
9
|
+
const Scene = dynamic(
|
|
10
|
+
() =>
|
|
11
|
+
import("@/components/webgl/components/scene").then((mod) => mod.default),
|
|
12
|
+
{
|
|
13
|
+
ssr: false,
|
|
14
|
+
},
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export default function WebGLCanvas({ ...props }) {
|
|
18
|
+
return (
|
|
19
|
+
<Canvas
|
|
20
|
+
className={cn("pointer-events-none h-lvh! w-full")}
|
|
21
|
+
dpr={[1, 2]}
|
|
22
|
+
eventPrefix="client"
|
|
23
|
+
frameloop="always"
|
|
24
|
+
camera={{
|
|
25
|
+
far: 1000,
|
|
26
|
+
near: 0.01,
|
|
27
|
+
fov: 16,
|
|
28
|
+
position: [0, 17, 350],
|
|
29
|
+
}}
|
|
30
|
+
gl={{
|
|
31
|
+
alpha: true,
|
|
32
|
+
antialias: true,
|
|
33
|
+
logarithmicDepthBuffer: true,
|
|
34
|
+
powerPreference: "high-performance",
|
|
35
|
+
premultipliedAlpha: true,
|
|
36
|
+
}}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
<Scene />
|
|
40
|
+
<Preload all />
|
|
41
|
+
</Canvas>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useFrame } from "@react-three/fiber";
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import type { Mesh } from "three";
|
|
4
|
+
|
|
5
|
+
const Scene = () => {
|
|
6
|
+
const meshRef = useRef<Mesh>(null);
|
|
7
|
+
|
|
8
|
+
useFrame((_state, delta) => {
|
|
9
|
+
meshRef.current.rotation.x += delta;
|
|
10
|
+
meshRef.current.rotation.y += delta;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<mesh ref={meshRef} scale={10}>
|
|
15
|
+
<boxGeometry />
|
|
16
|
+
<meshBasicMaterial color="orange" />
|
|
17
|
+
</mesh>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default Scene;
|
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bsmnt",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "CLI to scaffold basement projects and add integrations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"basement": "./bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "bun pm pack --dry-run --ignore-scripts",
|
|
11
|
+
"check": "for f in $(rg --files bin src -g '*.js'); do node --check \"$f\"; done",
|
|
12
|
+
"lint": "biome check .",
|
|
13
|
+
"lint:fix": "biome check --write .",
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
15
|
+
"changeset": "changeset",
|
|
16
|
+
"version-packages": "changeset version",
|
|
17
|
+
"release": "changeset publish",
|
|
18
|
+
"prepublishOnly": "bun run build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"webgpu",
|
|
22
|
+
"r3f",
|
|
23
|
+
"agents",
|
|
24
|
+
"claude",
|
|
25
|
+
"opencode"
|
|
26
|
+
],
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"chalk": "^5.6.2",
|
|
31
|
+
"dotenv": "^17.2.3",
|
|
32
|
+
"fs-extra": "^11.3.3",
|
|
33
|
+
"inquirer": "^9.3.8",
|
|
34
|
+
"ora": "^9.1.0",
|
|
35
|
+
"picocolors": "^1.1.1",
|
|
36
|
+
"prompts": "^2.4.2",
|
|
37
|
+
"tiged": "^2.12.7"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@biomejs/biome": "2.3.14",
|
|
41
|
+
"@changesets/changelog-github": "^0.5.2",
|
|
42
|
+
"@changesets/cli": "^2.29.8"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## Plugins
|
|
2
|
+
|
|
3
|
+
### 1. `no-anchor-element.grit`
|
|
4
|
+
Enforces using Next.js `<Link>` component instead of HTML `<a>` elements.
|
|
5
|
+
|
|
6
|
+
### 2. `no-unnecessary-forwardref.grit`
|
|
7
|
+
Checks for unnecessary `forwardRef` usage in React 19 with the compiler.
|
|
8
|
+
|
|
9
|
+
### 3. `no-relative-parent-imports.grit`
|
|
10
|
+
Forbids relative parent imports (`../`) and encourages alias imports (`@/`).
|
|
11
|
+
|
|
12
|
+
## Plugin Configuration
|
|
13
|
+
|
|
14
|
+
The plugins are configured in `biome.json`:
|
|
15
|
+
```json
|
|
16
|
+
"plugins": [
|
|
17
|
+
"./biome-plugins/no-anchor-element.grit",
|
|
18
|
+
"./biome-plugins/no-unnecessary-forwardref.grit",
|
|
19
|
+
"./biome-plugins/no-relative-parent-imports.grit"
|
|
20
|
+
]
|
|
21
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
`<a $attrs>$content</a>` as $anchor where {
|
|
4
|
+
! $anchor <: within `if ($condition) { return ($jsx) }` where {
|
|
5
|
+
$condition <: contains or {
|
|
6
|
+
`isExternal`,
|
|
7
|
+
`isExternalSSR`
|
|
8
|
+
}
|
|
9
|
+
} ,
|
|
10
|
+
register_diagnostic(span=$anchor, message="Use custom Link component instead of <a> element. The Link component handles both internal and external links automatically.", severity="error")
|
|
11
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
`import $imports from $source` as $import where {
|
|
4
|
+
$source <: r"['\"]\.\.\/\.\.\/.*['\"]",
|
|
5
|
+
register_diagnostic(span=$import, message="Use alias imports (~/dir/) instead of deep relative imports (../../). Single level imports (../) are allowed for colocated files.", severity="error")
|
|
6
|
+
}
|