Files
gstack/lib/frontend-scope.ts
T
Garry Tanandcapy-ai[bot] 5862765a20 feat(diff-scope): template UI in .ts/.js counts as frontend; receipt records the static design pass (GST-1718) (#4112)
* feat(diff-scope): template UI in .ts/.js counts as frontend; receipt records the static design pass (GST-1718)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test: regenerate ship goldens for the static design-review line (GST-1718)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: pre-landing review fixes

Review-Finding: p1/specialist-batched#1 reader=specialist
Review-Finding: p1/red-team#1 reader=red-team
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: count edits inside tagged templates and spaced template tags as frontend

Review-Finding: v1/outside-voice#1 reader=outside-voice
Review-Finding: v1/adversarial-native#1 reader=adversarial-native
Review-Finding: v1/outside-voice#2 reader=outside-voice
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: one set comparison per template file instead of a grep per changed line

Review-Finding: p2/red-team#2 reader=red-team
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs(changelog): GST-1718 fragment covers edits inside tagged templates

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs(ship-policy): design record wording matches the in-template rule

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-11-07 06:39:20 +00:00

40 lines
2.0 KiB
TypeScript

// lib/frontend-scope.ts — which repo paths count as frontend.
//
// Pure module: no I/O, no imports from scripts/. The patterns mirror the
// `m_frontend` arm of bin/gstack-diff-scope (the bash source of truth for
// SCOPE_FRONTEND); test/frontend-scope.test.ts pins the two against the same
// sample paths so they cannot drift. bin/gstack-design-detect.ts uses this to
// derive `scan --changed <base>` targets without consuming a shell-split list.
// The bash script's template-UI content arm (GST-1718: Lit or htm templates in
// .ts/.js) reads file contents and is not mirrored here; the detector scans
// paths, so its targets stay path-only.
const EXTENSIONS = new Set([
'.css', '.scss', '.less', '.sass', '.pcss',
'.tsx', '.jsx', '.vue', '.svelte', '.astro',
'.erb', '.haml', '.slim', '.hbs', '.ejs',
'.html',
// Dart: a Flutter widget and a repository share the extension, so the bash
// script counts .dart as frontend AND backend; only the frontend half lives here.
'.dart',
]);
// Root-level only: the bash arm's glob (`tailwind.config.*`) is matched against the
// whole repo-relative path, so a nested `apps/web/tailwind.config.js` is not frontend there.
const ROOT_CONFIG_PREFIXES = ['tailwind.config.', 'postcss.config.'];
/** Repo-relative path (forward slashes) → is it a frontend file per gstack-diff-scope? */
export function isFrontendPath(relPath: string): boolean {
const rel = relPath.replace(/\\/g, '/').replace(/^\.\//, '');
const base = rel.slice(rel.lastIndexOf('/') + 1);
const dot = base.lastIndexOf('.');
const ext = dot >= 0 ? base.slice(dot) : ''; // case-sensitive, exactly like gstack-diff-scope's globs
if (EXTENSIONS.has(ext)) return true;
if (!rel.includes('/') && ROOT_CONFIG_PREFIXES.some(p => base.startsWith(p))) return true;
if (rel.startsWith('app/views/')) return true;
if (rel.includes('/components/')) return true;
if (rel.startsWith('styles/') || rel.startsWith('css/')) return true;
if (rel.startsWith('app/assets/stylesheets/')) return true;
return false;
}