rehlma / flutter-genq-clir1
Use the owning genq command
PublicGenerate @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
namefixture
dependencies
collectionany
fixtures/flutter/docs/README.md
Fixture documentation
fixtures/flutter/pubspec.yaml
namefixture
dependencies
flutter
sdkflutter
fixtures/inline/pubspec.yaml
namefixture
dependencies
flutter sdkflutter
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"
]
}