(root, source, options)
| 331 | * @returns {ComponentAnalysis} |
| 332 | */ |
| 333 | export function analyze_component(root, source, options) { |
| 334 | const scope_root = new ScopeRoot(); |
| 335 | |
| 336 | const module = js(root.module, scope_root, false, null); |
| 337 | const instance = js(root.instance, scope_root, true, module.scope); |
| 338 | |
| 339 | const { scope, scopes, has_await } = create_scopes( |
| 340 | root.fragment, |
| 341 | scope_root, |
| 342 | false, |
| 343 | instance.scope |
| 344 | ); |
| 345 | |
| 346 | /** @type {Template} */ |
| 347 | const template = { ast: root.fragment, scope, scopes }; |
| 348 | |
| 349 | let synthetic_stores_legacy_check = []; |
| 350 | |
| 351 | const runes_option = options.runes?.({ filename: options.filename }); |
| 352 | |
| 353 | // create synthetic bindings for store subscriptions |
| 354 | for (const [name, references] of module.scope.references) { |
| 355 | if (name[0] !== '$' || RESERVED.includes(name)) continue; |
| 356 | if (name === '$' || name[1] === '$') { |
| 357 | e.global_reference_invalid(references[0].node, name); |
| 358 | } |
| 359 | |
| 360 | const store_name = name.slice(1); |
| 361 | const declaration = instance.scope.get(store_name); |
| 362 | const init = /** @type {ESTree.Node | undefined} */ (declaration?.initial); |
| 363 | |
| 364 | // If we're not in legacy mode through the compiler option, assume the user |
| 365 | // is referencing a rune and not a global store. |
| 366 | if ( |
| 367 | runes_option === false || |
| 368 | !is_rune(name) || |
| 369 | (declaration !== null && |
| 370 | // const state = $state(0) is valid |
| 371 | (get_rune(init, instance.scope) === null || |
| 372 | // rune-line names received as props are valid too (but we have to protect against $props as store) |
| 373 | (store_name !== 'props' && get_rune(init, instance.scope) === '$props')) && |
| 374 | // allow `import { derived } from 'svelte/store'` in the same file as `const x = $derived(..)` because one is not a subscription to the other |
| 375 | !( |
| 376 | name === '$derived' && |
| 377 | declaration.initial?.type === 'ImportDeclaration' && |
| 378 | declaration.initial.source.value === 'svelte/store' |
| 379 | )) |
| 380 | ) { |
| 381 | let is_nested_store_subscription_node = undefined; |
| 382 | search: for (const reference of references) { |
| 383 | for (let i = reference.path.length - 1; i >= 0; i--) { |
| 384 | const scope = |
| 385 | scopes.get(reference.path[i]) || |
| 386 | module.scopes.get(reference.path[i]) || |
| 387 | instance.scopes.get(reference.path[i]); |
| 388 | if (scope) { |
| 389 | const owner = scope?.owner(store_name); |
| 390 | if (!!owner && owner !== module.scope && owner !== instance.scope) { |
no test coverage detected