[STU-147] Single-file generation: fix project list 403 + widget commit failures - #60
Conversation
|
@greptile review |
1 similar comment
|
@greptile review |
|
@greptile review |
1 similar comment
|
@greptile review |
|
@greptile review |
1 similar comment
|
@greptile review |
| if (!code.trim()) return ""; | ||
|
|
||
| const lines = code.split("\n"); | ||
| const { commentAndStringRanges } = scanDartSource(code); |
There was a problem hiding this comment.
When prose before a later fenced Dart block contains an unmatched token such as /*, scanning the raw response extends a comment range to EOF and classifies the later fence markers as comment content. The sanitizer then silently omits that Dart block, allowing incomplete widget code to be committed.
Knowledge Base Used: FlutterFlow artifact pipeline
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/flutterFlowCodeSanitizer.js
Line: 48
Comment:
**Prose hides later code fences**
When prose before a later fenced Dart block contains an unmatched token such as `/*`, scanning the raw response extends a comment range to EOF and classifies the later fence markers as comment content. The sanitizer then silently omits that Dart block, allowing incomplete widget code to be committed.
**Knowledge Base Used:** [FlutterFlow artifact pipeline](https://app.greptile.com/connect-i-o/-/custom-context/knowledge-base/sgardoll/customcodeconnectforflutterflow/-/docs/flutterflow-artifact-pipeline.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| if (ch === "'" || ch === '"') { | ||
| const triple = src.slice(i, i + 3) === ch.repeat(3); | ||
| // r'...' / R'''...''' are raw strings: the r must not be the tail of a | ||
| // longer identifier, or `var bar'` style code would misclassify. | ||
| const prev = i > 0 ? src[i - 1] : ""; | ||
| const beforePrev = i > 1 ? src[i - 2] : ""; | ||
| const raw = | ||
| (prev === "r" || prev === "R") && !IDENTIFIER_TAIL.test(beforePrev); |
There was a problem hiding this comment.
Interpolation breaks raw-string detection
When a raw string ending in a backslash occurs inside an interpolation, the $ before the inner expression matches IDENTIFIER_TAIL, so the r prefix is ignored. The scanner treats the backslash as escaping the closing quote, reports an unclosed string or interpolation, and blocks valid Dart at the pre-commit gate.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/flutterFlowCodeSanitizer.js
Line: 300-307
Comment:
**Interpolation breaks raw-string detection**
When a raw string ending in a backslash occurs inside an interpolation, the `$` before the inner expression matches `IDENTIFIER_TAIL`, so the `r` prefix is ignored. The scanner treats the backslash as escaping the closing quote, reports an unclosed string or interpolation, and blocks valid Dart at the pre-commit gate.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.|
@greptile review |
Issue
https://linear.app/stuart-gardoll/issue/STU-147/single-file-custom-code-generation-fails-with-project-list-and-code
Single-file custom code generation failed twice: listing FlutterFlow projects returned 403 Unauthorized, and after generation the commit was rejected by FlutterFlow with "Custom widget code is not formattable" and "No widget "LiquidGlassOrbs" found."
Root causes
1. Project list 403 — wrong endpoint path
listProjectswas the only FlutterFlow call inFlutterFlowApiClientthat did not use${this.baseUrl}<method>: it hardcodedhttps://api.flutterflow.io/v2/l/listProjects(note the stray/l/segment). Every working call (exportCode,syncCustomCodeChanges) targets${baseUrl}<method>. The gateway rejects unknown/unauthorized paths with 401/403 before the key is even evaluated, so a key that syncs fine could not list projects.Additionally, 401/403 surfaced as a raw status line. The app's re-auth mechanism for static FlutterFlow keys is re-entering the key in API Keys settings (the projects dropdown re-fetches on blur), but nothing told the user to do that.
2a. "not formattable" — markdown artifacts in generated Dart
LLM responses arrive wrapped in markdown fences, with a BOM, or padded with prose. The old fence stripping only handled exact
^```dart\n/\n```$shapes and missed everything else (prose around the pair, interior fences, trailing text after the closing fence), all of which dart_style rejects.2b. "No widget found" — file name vs declared class
FlutterFlow derives a custom widget's identity from the committed file name (its naive snake_case of the class:
LiquidGlassOrbs→liquid_glass_orbs.dart). The single-file flow named the file after the artifact's display name ("Liquid Glass Orbs") or artifactName instead of the class actually declared in the code, so FlutterFlow looked for a widget that didn't exist under that name.Fixes
listProjectsnow calls${this.baseUrl}listProjectsfirst; the legacy/v2/l/path is retried once on 404 only, so an auth rejection is never masked by a retry.src/flutterFlowCodeSanitizer.js—sanitizeGeneratedDart()strips BOM, all fence lines (wrapped and multi-block responses), outer prose when fenced, and blank padding before validation.findUnbalancedBracketError()runs pre-commit with a comment/string/interpolation-aware scanner (handles nested${user['name']}cases) and reports the exact line and bracket, replacing FlutterFlow's opaque post-push rejection.prepareCodeForCommitderives the commit file name from the widget class actually declared in the sanitized code (widgetFileNameForClass), logging any rename;fileNamefrom the validated artifact is threaded throughgetCurrentArtifactMetadata→ both commit paths. A defense-in-depth pre-commit check reports precise mismatch errors if anything slips through.deriveIdentifierName(·, "W"), exportedidentifierToFlutterFlowFileStem) rather than duplicating them.Tests
src/flutterFlowCodeSanitizer.test.js: 20 tests covering sanitization (fences/prose/BOM/truncated/multi-block), widget-class extraction (transitive superclasses, comment/string immunity), bracket scanning (interpolation nesting, triple quotes, line-numbered errors), naive snake_case round-trips incl. acronyms (QAReport↔q_a_report), and the exact STU-147 scenario.npm test: 226/226 pass (206 existing + 20 new)node --checkclean on app.js and all touched filesnpm run buildsucceedsCaveats
Fixes STU-147
Greptile Summary
This PR corrects FlutterFlow project listing, sanitizes generated Dart, validates source before single-file commits, and derives widget filenames from declared classes.
Confidence Score: 3/5
The PR is not yet safe to merge because valid generated code can be silently truncated or incorrectly rejected during sanitization.
Scanning unsanitized prose as Dart can hide later fenced code blocks, while raw-string detection inside interpolation can misparse a valid trailing backslash and block the commit.
Files Needing Attention: src/flutterFlowCodeSanitizer.js
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Generated response] --> B[Sanitize Markdown and BOM] B --> C[Detect widget class] C --> D[Derive FlutterFlow filename] D --> E[Run pre-commit checks] E --> F[Build sync metadata] F --> G[Commit to FlutterFlow]Prompt To Fix All With AI
Reviews (5): Last reviewed commit: "STU-147: nested-comment-aware ranges; un..." | Re-trigger Greptile
Context used (3)