diff --git a/CHANGELOG.md b/CHANGELOG.md index ded85aa2..6df18302 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixes +- In Vue, Svelte and Astro files, a component now owns its script, and what a ` +`); + const file = r.nodes.find((n) => n.kind === 'file')!; + const component = componentOf(r); + expect(file).toMatchObject({ id: 'file:pages/index.vue', startLine: 1, endLine: 9 }); + const p = parents(r); + expect(p.get(component.id)).toEqual([file.id]); + expect(p.get(file.id)).toBeUndefined(); + const load = r.nodes.find((n) => n.name === 'load')!; + expect(p.get(load.id)).toEqual([component.id]); + for (const [, list] of p) expect(list).toHaveLength(1); + }); + + it('gives a +`); + expect(from(r, 'useFetch').filter((id) => id.startsWith('component:'))).toHaveLength(1); + expect(r.unresolvedReferences.filter((ref) => ref.referenceKind === 'imports').every((ref) => ref.fromNodeId === 'file:pages/index.vue')).toBe(true); + }); + + it('leaves a plain +`); + expect(from(r, 'registerWidget')).toEqual(['file:src/Widget.vue']); + }); + + it('gives a Svelte instance script to the component, and a module script to the file', () => { + const r = extractFromSource('src/routes/+page.svelte', ` + + +

items

+`); + const component = componentOf(r).id; + expect(from(r, 'onMount')).toEqual([component]); + expect(from(r, 'preload')).toEqual(['file:src/routes/+page.svelte']); + expect(from(r, 'register')).toEqual(['file:src/routes/+page.svelte']); + }); + + it('gives Astro frontmatter to the component, and makes a file node for a file without one', () => { + const r = extractFromSource('src/pages/blog.astro', `--- +const posts = await getCollection('blog'); +--- + +`); + expect(from(r, 'getCollection')).toEqual([componentOf(r).id]); + const bare = extractFromSource('src/pages/about.astro', `

about

\n`); + expect(bare.nodes.map((n) => n.kind).sort()).toEqual(['component', 'file']); + }); +}); diff --git a/src/extraction/astro-extractor.ts b/src/extraction/astro-extractor.ts index e3898937..efdc33f7 100644 --- a/src/extraction/astro-extractor.ts +++ b/src/extraction/astro-extractor.ts @@ -2,6 +2,7 @@ import { Node, Edge, ExtractionResult, ExtractionError, UnresolvedReference } fr import { generateNodeId } from './tree-sitter-helpers'; import { TreeSitterExtractor } from './tree-sitter'; import { isLanguageSupported } from './grammars'; +import { foldScriptResult, sfcFileNode } from './sfc-script'; /** * Astro built-in components — compiler-provided (``) or shipped by @@ -45,8 +46,10 @@ export class AstroExtractor { const startTime = Date.now(); try { - // Create component node for the .astro file itself + // The file, holding the component the .astro file is + this.nodes.push(sfcFileNode(this.filePath, this.source, 'astro')); const componentNode = this.createComponentNode(); + this.edges.push({ source: `file:${this.filePath}`, target: componentNode.id, kind: 'contains' }); // Extract and process the frontmatter block (--- fenced, TypeScript) const frontmatter = this.extractFrontmatter(); @@ -199,45 +202,13 @@ export class AstroExtractor { const extractor = new TreeSitterExtractor(this.filePath, block.content, 'typescript'); const result = extractor.extract(); - // Offset line numbers from the block back to .astro file positions - for (const node of result.nodes) { - node.startLine += block.startLine; - node.endLine += block.startLine; - node.language = 'astro'; // Mark as astro, not TS - - this.nodes.push(node); - - // Add containment edge from component to this node - this.edges.push({ - source: componentNodeId, - target: node.id, - kind: 'contains', - }); - } - - // Offset edges (they reference line numbers) - for (const edge of result.edges) { - if (edge.line) { - edge.line += block.startLine; - } - this.edges.push(edge); - } - - // Offset unresolved references - for (const ref of result.unresolvedReferences) { - ref.line += block.startLine; - ref.filePath = this.filePath; - ref.language = 'astro'; - this.unresolvedReferences.push(ref); - } - - // Carry over errors - for (const error of result.errors) { - if (error.line) { - error.line += block.startLine; - } - this.errors.push(error); - } + // Frontmatter runs on every render of the component, and a