@frame12/lint 0.0.0-stage → 0.2.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Laqwan Brodie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,226 @@
1
- # Temporary Holding Version
1
+ # `@frame12/lint`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Zero-dependency design-grammar checker for [FRAME/12](../../README.md) across TypeScript/JSX (`.tsx`, `.ts`, `.jsx`, `.js`), CSS (`.css`), and generated HTML (`lintHtml()`).
4
+
5
+ > **Distribution status:** `@frame12/lint` is currently a workspace package in the `lbrodie11/frame12` monorepo (`packages/lint`) and is not yet published to the public npm registry.
6
+
7
+ ---
8
+
9
+ ## Usage
10
+
11
+ ### Library API
12
+
13
+ ```ts
14
+ import {
15
+ lintSource,
16
+ lintFiles,
17
+ lintDirectory,
18
+ collectFiles,
19
+ lintHtml,
20
+ RULES,
21
+ type RuleId,
22
+ type Violation,
23
+ } from '@frame12/lint';
24
+
25
+ // 1. Lint TSX / JSX / TS / JS / CSS source text
26
+ const tsxViolations: Violation[] = lintSource(code, 'src/app/page.tsx');
27
+
28
+ // 2. Lint generated HTML documents or fragments
29
+ const htmlViolations: Violation[] = lintHtml(htmlString, {
30
+ file: 'report.html',
31
+ tokenStyleIds: ['f12-tokens'], // <style id="f12-tokens"> or <style data-f12-tokens> is exempt
32
+ });
33
+
34
+ // 3. Lint directories or file paths on disk (.tsx, .ts, .jsx, .js, .css, .html, .htm)
35
+ const { files, violations } = lintDirectory(['src/components', 'src/app', 'dist/report.html']);
36
+ ```
37
+
38
+ ### CLI (`@frame12/lint` / `frame12-lint`)
39
+
40
+ ```bash
41
+ npx @frame12/lint src/app src/components dist/report.html
42
+ # PASS check-frame12: 43 file(s) checked, 0 violations.
43
+ ```
44
+
45
+ The CLI accepts `.tsx`, `.ts`, `.jsx`, `.js`, `.css`, `.html`, and `.htm` files or directories (routing `.html` / `.htm` files to `lintHtml()`), and exits with code `1` if violations are found or if `0` lintable files match the target paths.
46
+
47
+ ### Optional `typescript` Peer Dependency
48
+
49
+ `typescript` (`>=5.0.0`) is an **optional** peer dependency:
50
+ - When `typescript` is installed, `lintSource()` parses JSX/TSX with the TypeScript compiler AST (`ts.createSourceFile`) to trace `cut-order` and `explicit-cut-in-autocut` through spread props, local variable identifiers, and `<PanelPair>` slots.
51
+ - When `typescript` is not installed (for example, in a standalone HTML generator that only calls `lintHtml()`), `lintHtml()` and all CSS/regex source rules work with zero dependencies, and `lintSource()` emits a one-time `WARN [@frame12/lint]` notice on `stderr` (suppressible via `{ quietFallbackWarning: true }`) before falling back to its built-in regex cut scanner.
52
+
53
+ ---
54
+
55
+ ## Rules
56
+
57
+ Every violation carries a stable `rule` ID (`RuleId`), `file`, 1-based `line` and `column`, human-readable `message`, and `snippet`.
58
+
59
+ | Rule ID | Summary |
60
+ | :--- | :--- |
61
+ | [`color-literal`](#color-literal) | Forbid hard-coded color literals (`#hex`, `rgb`/`hsl`/`oklch`, CSS gradients, font names) outside token definitions; use `var(--f-*)` tokens. |
62
+ | [`illegal-gutter`](#illegal-gutter) | Permit only `0`, `12px` (`--f-gutter-pair`), `20px` (`--f-gutter-scene`), or `32px` (`--f-gutter-change`) gutters on `gap`, `grid-gap`, `row-gap`, and `column-gap`. |
63
+ | [`marker-misuse`](#marker-misuse) | Permit `--f-marker` (`#ffd60a`) only as a surface background paired with `color: var(--f-marker-ink)` in the same rule; never on text, borders, outlines, shadows, or SVG strokes/fills. |
64
+ | [`cut-order`](#cut-order) | Require panel cut labels (`C-01`, `C-02`, …) to use two-digit formatting and appear in strict sequential reading order starting at `C-01` without gaps or duplicates. |
65
+ | [`explicit-cut-in-autocut`](#explicit-cut-in-autocut) | Forbid explicit `cut` props on `Panel`/`PanelPair` inside `<Sheet autoCut>` where cuts are numbered automatically. |
66
+ | [`raw-shadcn-import`](#raw-shadcn-import) | Forbid raw `@/components/ui/*`, `@radix-ui/*`, or third-party chart/motion imports where a FRAME/12 component or adapter exists. |
67
+ | [`unsafe-html`](#unsafe-html) | Forbid external resource loads (`<script src>`, `<link href>`, `<img src>`, CSS `@import`/`url()`), embedded frames (`<iframe>`, `<object>`, `<embed>`, `srcdoc`), `<meta http-equiv="refresh">`, `<base href>`, inline `on*=` handlers, and `javascript:` URLs in generated HTML. |
68
+
69
+ ---
70
+
71
+ ### `color-literal`
72
+
73
+ Forbid hard-coded color literals (`#hex`, `rgb()`, `hsl()`, `oklch()`, CSS gradients, and raw font-family names) outside token files (`frame12.css`, `frame12-tokens.css`, `<style data-f12-tokens>`, or `opts.tokenStyleIds`). Use `var(--f-*)` and `var(--font-f-*)` tokens instead.
74
+
75
+ **Bad:**
76
+ ```css
77
+ .card {
78
+ background: #ffffff;
79
+ color: rgb(18, 18, 18);
80
+ background-image: linear-gradient(180deg, #fff, #eee);
81
+ font-family: "Dela Gothic One", sans-serif;
82
+ }
83
+ ```
84
+
85
+ **Good:**
86
+ ```css
87
+ .card {
88
+ background: var(--f-panel);
89
+ color: var(--f-ink);
90
+ font-family: var(--font-f-display);
91
+ }
92
+ ```
93
+
94
+ ---
95
+
96
+ ### `illegal-gutter`
97
+
98
+ Permit only `0`, `12px` (`--f-gutter-pair`), `20px` (`--f-gutter-scene`), or `32px` (`--f-gutter-change`) gutters on `<Scene gutter="...">`, CSS `gap` / `grid-gap` / `row-gap` / `column-gap`, inline styles, and Tailwind `gap-*` classes.
99
+
100
+ **Bad:**
101
+ ```html
102
+ <div class="grid gap-4" style="gap: 14px"></div>
103
+ ```
104
+
105
+ **Good:**
106
+ ```html
107
+ <div class="grid gap-gutter-scene" style="gap: var(--f-gutter-pair)"></div>
108
+ ```
109
+
110
+ ---
111
+
112
+ ### `marker-misuse`
113
+
114
+ Permit `--f-marker` (`#ffd60a`) only as a surface background paired with `color: var(--f-marker-ink)` in the same CSS rule or inline style object. Never use `--f-marker` on `color`, `border`, `outline`, `box-shadow`, or SVG `stroke` / `fill`.
115
+
116
+ **Bad:**
117
+ ```css
118
+ .focus-ring:focus-visible {
119
+ outline: var(--f-stroke) solid var(--f-marker);
120
+ }
121
+ .badge {
122
+ background: var(--f-marker);
123
+ }
124
+ ```
125
+
126
+ **Good:**
127
+ ```css
128
+ .focus-ring:focus-visible {
129
+ outline: var(--f-stroke) solid var(--f-rule);
130
+ }
131
+ .badge {
132
+ background: var(--f-marker);
133
+ color: var(--f-marker-ink);
134
+ }
135
+ ```
136
+
137
+ ---
138
+
139
+ ### `cut-order`
140
+
141
+ Require panel cut labels (`C-01`, `C-02`, `C-03`, …) to use two-digit `C-NN` formatting, start at `C-01`, and increase sequentially by 1 in DOM/reading order without gaps or duplicates. Quiet panels (`variant="quiet"`) must not carry cut labels, and `data-cut="auto"` (or empty `data-cut=""`) is ignored.
142
+
143
+ **Bad:**
144
+ ```html
145
+ <section class="panel" data-cut="1">First</section>
146
+ <section class="panel" data-cut="C-03">Skipped C-02</section>
147
+ ```
148
+
149
+ **Good:**
150
+ ```html
151
+ <section class="panel" data-cut="C-01">First</section>
152
+ <section class="panel" data-cut="C-02">Second</section>
153
+ ```
154
+
155
+ ---
156
+
157
+ ### `explicit-cut-in-autocut`
158
+
159
+ Forbid explicit `cut`, `topCut`, or `bottomCut` props on `<Panel>` or `<PanelPair>` inside `<Sheet autoCut>` where cut numbers are assigned automatically.
160
+
161
+ **Bad:**
162
+ ```tsx
163
+ <Sheet autoCut>
164
+ <Panel cut="C-01" title="Overview">...</Panel>
165
+ </Sheet>
166
+ ```
167
+
168
+ **Good:**
169
+ ```tsx
170
+ <Sheet autoCut>
171
+ <Panel title="Overview">...</Panel>
172
+ </Sheet>
173
+ ```
174
+
175
+ ---
176
+
177
+ ### `raw-shadcn-import`
178
+
179
+ Forbid raw `@/components/ui/*` imports, `@radix-ui/*` primitives, or third-party chart/animation libraries (`recharts`, `chart.js`, `d3`, `framer-motion`, `gsap`) where a FRAME/12 component, adapter, or hook exists.
180
+
181
+ **Bad:**
182
+ ```tsx
183
+ import { Button } from '@/components/ui/button';
184
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
185
+ ```
186
+
187
+ **Good:**
188
+ ```tsx
189
+ import { Button, Dialog, DialogContent } from '@/components/frame12';
190
+ ```
191
+
192
+ ---
193
+
194
+ ### `unsafe-html`
195
+
196
+ Active in `lintHtml()` (and when linting `.html` / `.htm` files via `lintFiles()`, `lintDirectory()`, or the CLI). Forbid external `<script src="…">` tags, external resource loads (`<link rel="stylesheet" href="…">`, `<img src="https://…">`, `<source>`, `<video>`, `<audio>`, `<track>`, `<image>`, CSS `@import`, and external CSS `url(https://…)`), embedded frames (`<iframe>`, `<object>`, `<embed>`, `srcdoc`), `<meta http-equiv="refresh">`, `<base href="…">`, inline event handler attributes (`onclick=`, `onload=`, `onerror=`, …), and `javascript:` / `vbscript:` URLs in attribute values. Self-contained inline `<script>` elements without `src` (such as the on-twos pose clock), relative/anchor `<a href="…">` links, and `data:` URIs are permitted.
197
+
198
+ **Bad:**
199
+ ```html
200
+ <script src="https://cdn.example.com/tracker.js"></script>
201
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter" />
202
+ <iframe src="https://example.com/embed"></iframe>
203
+ <button onclick="doSomething()">Run</button>
204
+ <a href="javascript:alert(1)">Link</a>
205
+ ```
206
+
207
+ **Good:**
208
+ ```html
209
+ <script>
210
+ // Self-contained on-twos motion pose clock
211
+ </script>
212
+ <button type="button" id="run-btn">Run</button>
213
+ <a href="#details">Link</a>
214
+ ```
215
+
216
+ ---
217
+
218
+ ## `lintHtml()` Scanner Scope & Limits
219
+
220
+ `lintHtml(html, opts)` uses a fast, zero-dependency HTML tag/attribute/style scanner rather than a full WHATWG HTML5 parser:
221
+ - **Supported constructs:** `<!-- comments -->`, `<![CDATA[ ... ]]>`, `<!DOCTYPE ...>`, `<style>` and `<script>` raw-text blocks, single-quoted / double-quoted / unquoted attributes, boolean attributes, HTML void elements (`<img>`, `<meta>`, `<br>`, etc.), and self-closing SVG/HTML tags (`<line />`).
222
+ - **Token `<style>` exemption:** `<style data-f12-tokens>` and any `<style id="...">` whose `id` is listed in `opts.tokenStyleIds` (for example `{ tokenStyleIds: ['f12-tokens'] }`) are skipped by the CSS literal rules.
223
+ - **Known limits:**
224
+ - CSS rules inside `<style>` inspect single-level `{ ... }` declaration blocks; deeply nested CSS rules (`@layer` / native CSS nesting with `--f-marker` split across parent and nested blocks) are evaluated per declaration block.
225
+ - Dynamic DOM mutations performed at runtime by inline `<script>` code are not executed or inspected.
226
+ - Malformed HTML with unescaped `<` followed immediately by an ASCII letter in raw text (e.g. `a <b` instead of `a &lt; b`) is tokenized as an opening tag.
package/dist/cli.mjs ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from './index.js';
3
+
4
+ process.exit(runCli());
@@ -0,0 +1,68 @@
1
+ export type RuleId =
2
+ | 'color-literal'
3
+ | 'illegal-gutter'
4
+ | 'marker-misuse'
5
+ | 'cut-order'
6
+ | 'explicit-cut-in-autocut'
7
+ | 'raw-shadcn-import'
8
+ | 'unsafe-html';
9
+
10
+ export interface Violation {
11
+ rule: RuleId;
12
+ message: string;
13
+ file: string;
14
+ line: number;
15
+ column: number;
16
+ snippet?: string;
17
+ }
18
+
19
+ export interface LintHtmlOptions {
20
+ /**
21
+ * File name reported in returned `Violation` entries.
22
+ * @default "input.html"
23
+ */
24
+ file?: string;
25
+ /**
26
+ * `<style id="...">` element IDs whose contents define design tokens and
27
+ * should be exempt from CSS literal rules (in addition to `<style data-f12-tokens>`).
28
+ */
29
+ tokenStyleIds?: string[];
30
+ }
31
+
32
+ export interface LintSourceOptions {
33
+ /**
34
+ * Force fallback behavior as if the optional `typescript` peer dependency is not installed.
35
+ */
36
+ disableTypeScript?: boolean;
37
+ /**
38
+ * Suppress the one-time console warning when falling back to regex cut-order checks.
39
+ */
40
+ quietFallbackWarning?: boolean;
41
+ }
42
+
43
+ export declare const RULES: Readonly<Record<RuleId, string>>;
44
+ export declare const FORBIDDEN_SHADCN_MODULES: ReadonlySet<string>;
45
+ export declare const FORBIDDEN_EXTERNAL_PACKAGES: ReadonlySet<string>;
46
+
47
+ export declare function lintSource(
48
+ content: string,
49
+ filePath?: string,
50
+ opts?: LintSourceOptions
51
+ ): Violation[];
52
+
53
+ export declare function collectFiles(targetPaths: string[]): string[];
54
+
55
+ export declare function lintFiles(targetPaths: string[]): {
56
+ files: string[];
57
+ violations: Violation[];
58
+ };
59
+
60
+ export declare const lintDirectory: typeof lintFiles;
61
+
62
+ export declare function lintHtml(
63
+ /** HTML text, or an object whose string form is HTML (e.g. an `@frame12/static` `Html`). */
64
+ html: string | { toString(): string },
65
+ opts?: LintHtmlOptions
66
+ ): Violation[];
67
+
68
+ export declare function runCli(argv?: string[], cwd?: string): number;