* Extract {% schema %}...{% endschema %} blocks
(fileNodeId: string)
| 276 | * Extract {% schema %}...{% endschema %} blocks |
| 277 | */ |
| 278 | private extractSchema(fileNodeId: string): void { |
| 279 | // Match {% schema %}...{% endschema %} |
| 280 | const schemaRegex = /\{%[-]?\s*schema\s*[-]?%\}([\s\S]*?)\{%[-]?\s*endschema\s*[-]?%\}/g; |
| 281 | let match; |
| 282 | |
| 283 | while ((match = schemaRegex.exec(this.source)) !== null) { |
| 284 | const [fullMatch, schemaContent] = match; |
| 285 | const startLine = this.getLineNumber(match.index); |
| 286 | const endLine = this.getLineNumber(match.index + fullMatch.length); |
| 287 | |
| 288 | // Try to parse the schema JSON to get the name |
| 289 | let schemaName = 'schema'; |
| 290 | try { |
| 291 | const schemaJson = JSON.parse(schemaContent!); |
| 292 | if (schemaJson.name) { |
| 293 | // Shopify schema names can be translation objects like {"en": "...", "fr": "..."} |
| 294 | schemaName = typeof schemaJson.name === 'string' |
| 295 | ? schemaJson.name |
| 296 | : schemaJson.name.en || Object.values(schemaJson.name)[0] as string || 'schema'; |
| 297 | } |
| 298 | } catch { |
| 299 | // Schema isn't valid JSON, use default name |
| 300 | } |
| 301 | |
| 302 | // Create a node for the schema |
| 303 | const nodeId = generateNodeId(this.filePath, 'constant', `schema:${schemaName}`, startLine); |
| 304 | |
| 305 | const node: Node = { |
| 306 | id: nodeId, |
| 307 | kind: 'constant', |
| 308 | name: schemaName, |
| 309 | qualifiedName: `${this.filePath}::schema:${schemaName}`, |
| 310 | filePath: this.filePath, |
| 311 | language: 'liquid', |
| 312 | startLine, |
| 313 | endLine, |
| 314 | startColumn: match.index - this.getLineStart(startLine), |
| 315 | endColumn: 0, |
| 316 | // SECURITY (#383): don't dump the raw {% schema %} JSON (section settings |
| 317 | // + default values) into the docstring — the schema name is already in |
| 318 | // `name`, so the data block adds nothing but a potential leak of any |
| 319 | // IDs/endpoints/keys a developer placed in setting defaults. |
| 320 | updatedAt: Date.now(), |
| 321 | }; |
| 322 | |
| 323 | this.nodes.push(node); |
| 324 | |
| 325 | // Add containment edge from file |
| 326 | this.edges.push({ |
| 327 | source: fileNodeId, |
| 328 | target: nodeId, |
| 329 | kind: 'contains', |
| 330 | }); |
| 331 | } |
| 332 | } |
| 333 | |
| 334 | /** |
| 335 | * Extract {% assign var = value %} statements |
no test coverage detected