Implement ship target list#557
Conversation
Greptile SummaryThis PR replaces the stub
Confidence Score: 4/5Safe to merge after addressing the property-regex false-match issue, which can silently return a wrong enabled value when a use string contains a substring resembling enabled: false. The three previously reported parser bugs are properly fixed and covered by new regression tests. One remaining gap: readBooleanProperty and readStringProperty scan the raw per-entry body without neutralising adjacent string-literal contents, so a use value containing enabled: false would be matched before the real enabled field, returning a wrong boolean silently. packages/cli/src/commands/ship.ts — specifically the readStringProperty and readBooleanProperty functions and their interaction with adjacent string-valued properties in the same object body. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ship target list] --> B[readTargetSummary]
B --> C{File exists?}
C -- No --> D[throw Error]
C -- Yes --> E[readFileSync source]
E --> F[readObjectBody]
F --> G[stripComments]
G --> H{targets found?}
H -- No --> I[return empty array]
H -- Yes --> J[findMatchingBrace]
J --> K[readTopLevelObjectEntries]
K --> L[stripComments + keyRe loop]
L --> N[findMatchingBrace per entry]
N --> O[readStringProperty]
N --> P[readBooleanProperty]
O --> Q[TargetSummary array]
P --> Q
Q --> R{json flag?}
R -- Yes --> S[JSON output]
R -- No --> T[Colored terminal output]
Reviews (2): Last reviewed commit: "Add target parser regression tests" | Re-trigger Greptile |
Strip comments before locating targets and handle mixed quotes plus escaped backslashes.
Covers comments, mixed quote delimiters, and escaped backslash parsing.
|
Addressed the parser review findings in the latest commits:
Local verification:
|
| function readStringProperty(source: string, key: string): string | undefined { | ||
| const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(?:'([^']*)'|"([^"]*)")`).exec(source); | ||
| return match?.[1] ?? match?.[2]; | ||
| } | ||
|
|
||
| function readBooleanProperty(source: string, key: string): boolean | undefined { | ||
| const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(true|false)`).exec(source); | ||
| return match?.[1] === undefined ? undefined : match[1] === 'true'; | ||
| } |
There was a problem hiding this comment.
readBooleanProperty runs its regex against the raw per-entry body, which still contains string values from sibling properties. If a target's use value happens to embed a token matching enabled\s*:\s*(true|false) — e.g. use: "my enabled: false adapter" — the regex matches that substring first and returns the wrong boolean, silently overriding whatever the real enabled: field says. The same exposure exists for readStringProperty matching an enabled: pattern inside a longer use value. Stripping string-literal contents before running the property regexes would close this gap.
| function readStringProperty(source: string, key: string): string | undefined { | |
| const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(?:'([^']*)'|"([^"]*)")`).exec(source); | |
| return match?.[1] ?? match?.[2]; | |
| } | |
| function readBooleanProperty(source: string, key: string): boolean | undefined { | |
| const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(true|false)`).exec(source); | |
| return match?.[1] === undefined ? undefined : match[1] === 'true'; | |
| } | |
| function readStringProperty(source: string, key: string): string | undefined { | |
| const stripped = stripStringContents(source); | |
| const mStripped = new RegExp(`${escapeRegExp(key)}\\s*:`).exec(stripped); | |
| if (!mStripped) return undefined; | |
| const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(?:'([^']*)'|"([^"]*)")`).exec(source); | |
| return match?.[1] ?? match?.[2]; | |
| } | |
| function readBooleanProperty(source: string, key: string): boolean | undefined { | |
| const stripped = stripStringContents(source); | |
| const mStripped = new RegExp(`${escapeRegExp(key)}\\s*:`).exec(stripped); | |
| if (!mStripped) return undefined; | |
| const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(true|false)`).exec( | |
| source.slice(mStripped.index), | |
| ); | |
| return match?.[1] === undefined ? undefined : match[1] === 'true'; | |
| } | |
| function stripStringContents(source: string): string { | |
| let result = ''; | |
| let quote: '"' | "'" | undefined; | |
| for (let i = 0; i < source.length; i += 1) { | |
| const ch = source[i]; | |
| if (quote) { | |
| result += ch === quote && !isEscaped(source, i) ? ((quote = undefined), ch) : ' '; | |
| } else { | |
| if (ch === '"' || ch === "'") quote = ch as '"' | "'"; | |
| result += ch; | |
| } | |
| } | |
| return result; | |
| } |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
1 similar comment
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
No description provided.