(raw: string)
| 43 | * preflight, so the two paths can never disagree on what is a property target. |
| 44 | */ |
| 45 | export function parsePropertyTarget(raw: string): PropertyTarget | null { |
| 46 | const trimmed = raw.trim(); |
| 47 | if (!PROPERTY_PREFIX.test(trimmed)) return null; |
| 48 | |
| 49 | const interior = trimmed.replace(PROPERTY_PREFIX, ""); |
| 50 | |
| 51 | // Reuse the shared FIELD pipe grammar: the parser splits on `|`, treats the |
| 52 | // first part as the "field name" (here our `field=value` core) and parses the |
| 53 | // rest as folder/tag/exclude-* filters — identical to {{FILE:}}/{{FIELD:}}. |
| 54 | // Only folder / tag / exclude-folder / exclude-tag / exclude-file are honored |
| 55 | // by the property query (see getMarkdownFilesWithProperty); other FIELD options |
| 56 | // the parser accepts (default:/inline:/case-sensitive:) are inert here — value |
| 57 | // matching is always case-insensitive by design. |
| 58 | const { fieldName: core, filters } = FieldSuggestionParser.parse(interior); |
| 59 | |
| 60 | const eq = core.indexOf("="); |
| 61 | const field = (eq >= 0 ? core.slice(0, eq) : core).trim(); |
| 62 | // Split on the FIRST `=`, so values may contain further `=` (e.g. `a=b`). |
| 63 | const rawValue = eq >= 0 ? core.slice(eq + 1).trim() : ""; |
| 64 | // Empty value (`property:type` or `property:type=`) → presence mode. |
| 65 | const value = rawValue.length > 0 ? rawValue : undefined; |
| 66 | |
| 67 | return { field, value, filter: filters }; |
| 68 | } |
no test coverage detected