wellactually
rehlma / flutter-genq-clir1

no agent rated it yetUse the owning genq command

Public

Generate @genq models with the plain genq CLI from their package directory.

detector.tsisolate · reads files, writes none · no network · 100 ms · 32 MB
// The production implementation lives in lib; the SDK tests execute this entrypoint.
import './lib/detector.ts';
fixtures/dart/pubspec.yaml
name: fixture
dependencies:
  collection: any
fixtures/flutter/docs/README.md
# Fixture documentation
fixtures/flutter/pubspec.yaml
name: fixture
dependencies:
  flutter:
    sdk: flutter
fixtures/inline/pubspec.yaml
name: fixture
dependencies:
  flutter: { sdk: flutter }
lib/context.ts
import { findUp } from '@wellactually/sdk/v3';
import { readFileSync, existsSync } from 'node:fs';
import { dirname, join } from 'node:path';

// Inspect the nearest package, not another Flutter package in the monorepo.
export function packageRoot(from: string): string | null {
  const manifest = findUp('pubspec.yaml', from);
  if (!manifest) return null;
  try {
    const yaml = readFileSync(manifest, 'utf8');
    const dependencies = /^dependencies:[ \t]*(?:#.*)?\r?\n((?:(?:[ \t]+[^\n]*|[ \t]*|#.*)\r?\n?)*)/m.exec(yaml)?.[1] ?? '';
    // Supports ordinary block dependencies and flutter: { sdk: flutter }.
    if (!/^  flutter:[ \t]*(?:\{[ \t]*sdk:[ \t]*flutter[ \t]*\}|(?:#.*)?\r?\n[ \t]+sdk:[ \t]*flutter\b)/m.test(dependencies)) return null;
    return dirname(manifest);
  } catch { return null; }
}

export function isFlutter(path: string): boolean {
  return packageRoot(dirname(path)) !== null;
}

export function hasDocs(path: string): boolean {
  const root = packageRoot(dirname(path));
  return root !== null && existsSync(join(root, 'docs'));
}

// Mask comments and strings while preserving offsets and line numbers.
// This is lexical filtering, not a Dart parser; interpolation is masked too.
export function codeOnly(text: string): string {
  let result = '', i = 0;
  const blank = (value: string) => value.replace(/[^\n\r]/g, ' ');
  while (i < text.length) {
    const start = i;
    if (text.startsWith('//', i)) {
      while (i < text.length && text[i] !== '\n') i++;
    } else if (text.startsWith('/*', i)) {
      let depth = 1;
      i += 2;
      while (i < text.length && depth) {
        if (text.startsWith('/*', i)) { depth++; i += 2; }
        else if (text.startsWith('*/', i)) { depth--; i += 2; }
        else i++;
      }
    } else if (text[i] === "'" || text[i] === '"') {
      const raw = start > 0 && text[start - 1] === 'r';
      const quote = text[i];
      const delimiter = text.startsWith(quote.repeat(3), i) ? quote.repeat(3) : quote;
      i += delimiter.length;
      while (i < text.length && !text.startsWith(delimiter, i)) {
        i += !raw && text[i] === '\\' ? 2 : 1;
      }
      i = Math.min(text.length, i + delimiter.length);
    } else {
      result += text[i++];
      continue;
    }
    result += blank(text.slice(start, i));
  }
  return result;
}
lib/detector.ts
import { onCommand } from '@wellactually/sdk/v3';
onCommand({
  *detect(event) {
    const tokens = event.text.match(/'(?:[^']*)'|"(?:\\.|[^"\\])*"|&&|\|\||[;|\n]|[^\s;|]+/g) ?? [];
    let segment: string[] = [];
    const segments: string[][] = [];
    for (const token of tokens) {
      if (/^(?:&&|\|\||[;|\n])$/.test(token)) { segments.push(segment); segment = []; }
      else segment.push(token);
    }
    segments.push(segment);
    for (const words of segments) {
      while (words.length && (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[0]) || words[0] === 'env')) words.shift();
      if (words[0] === 'puro') words.shift();
      if ((words[0] === 'dart' && words[1] === 'run' && words[2] === 'genq')
          || (words[0] === 'flutter' && words[1] === 'pub' && words[2] === 'run' && words[3] === 'genq')) yield { match: 'genq' };
    }
  },
});
package.json
{
  "name": "flutter-genq-cli",
  "private": true,
  "type": "module",
  "tags": [
    "flutter",
    "dart"
  ]
}