You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is your feature request related to a problem? Please describe.
A code block embedded in a template — the TS/JS analogue of a JSP scriptlet — is invisible to every analysis this repo performs. Measured at HEAD (-a 2) on a fixture with one of each shape:
MODULES: ['src/real.ts']
src/Widget.vue (a <script lang="ts"> block with an import and two functions), src/pages/index.astro (frontmatter declaring a function), views/page.ejs (<% %> statements) and public/index.html (an inline <script> with a function and a call to it) each produce an artifact record carrying the full source — and nothing else. No callables, no call edges, no CFG, no entrypoints. Code that a request actually executes is stored as opaque text.
Two structurally different things are conflated by the word "scriptlet", and they want different answers:
(a) An embedded code BLOCK — .astro frontmatter (--- … ---, statements run server-side per request), .vue/.svelte<script>/<script setup> (declarations), EJS <% … %> statement scriptlets, an inline <script> in .html. This is a module whose file happens to be a template. It needs no new node kind: the content is TS/JS, callables have real byte spans in the real file, assignIds already mints can://<app>/ts/<file>/…, and heritage/call graph/CFG work unchanged. Because span.bytes are UTF-8 byte offsets into the file rather than into a preprocessed string (#179), a block starting at byte 130 of the .vue has a truthful span with no source-map layer.
(b) A template EXPRESSION — {{ count }} (Vue), {expr} (Svelte/Astro/JSX-in-markup), <%= total %> (EJS), a directive value like @click="bump". One to three tokens, dozens per file, no signature and no body. Modeling each as a TSCallable is wrong. The only interesting fact about one is which declaration it reads, which is an edge, not a node — and the shape already exists in this repo as TS_USES_CONFIG (:TSBodyNode → target, prov: string[]).
Artifacts and modules are disjoint by construction, at two lines:
src/artifacts/index.ts:179 — if (SOURCE_EXTS.has(path.extname(e.name))) continue; // source lives in the symbol table
src/syntactic_analysis/discovery.ts:5 and its JS_EXTS sibling — only .ts/.tsx/.mts/.cts plus the JS extensions are admitted as modules.
So a .vue can only ever be an :Artifact. Relaxing this is what lets one file yield both an artifact record and a symbol_table entry — which is itself the decision that needs designing, because "one file, two sections" is a new relationship between two level-free layers, and is_entrypoint/entrypoints[] on a callable inside a template is a claim consumers will query.
Describe the solution you'd like
Spec, committed under docs/design/specs/, deciding: whether a template file appears in both artifacts{} and symbol_table{} or is moved wholesale; the id shape for a callable inside a template; whether (b) is a body-node role or an edge; and how roles[] on the artifact relates to the module.
Cross-language coining agreed before implementation. codeanalyzer-java is doing JSP/JSF/Thymeleaf now; whichever analyzer ships first coins the vocabulary for both, and this is exactly the case the parity clause exists for. Half (a) ideally coins nothing — a template file yielding a module needs no new vocabulary at all, which is the cheapest possible parity.
Only then: implementation, per whatever the spec decides.
Describe alternatives you've considered
Not stated in the original issue.
Additional context
Scope boundary
This is a contract change and belongs on the design rung, not here. It changes what a symbol_table key means (today: a TS/JS source file), and half (b) needs either a new body-node role or a new edge type. Filed as a work item so the thread is not dropped; entering it means entering designing-cldk-changes, not maintaining-cldk.
Parsing .astro's markup, Vue's <template>, or Pug is out of scope for (a): only the code block is.
Caveats and known risks
Which parser. ts-morph parses TS, not .vue/.astro. Extracting the block means either a small hand-written scanner for the delimiters (cheap, and the byte offsets stay honest) or a per-framework compiler dependency (@vue/compiler-sfc, @astrojs/compiler — accurate, but a new dependency per framework and a version-skew surface). The scanner is the lazy option and is probably right; the spec should say which and why.
Byte offsets are the whole reason this is tractable. Any approach that rewrites the block into a synthetic string before parsing needs a source map, and every span in the tree becomes a lie unless the map is applied everywhere. Parse in place, or accept that cost explicitly.
EJS <% %> blocks are not a module — they are statements in a per-render function whose parameters come from the render call. Treating them as file-scope declarations would produce plausible-looking nonsense; the spec should decide whether EJS is in the first cut at all.
Half (b) risks a large node-count increase: dozens of interpolations per template across a real application. Whatever shape is chosen has to be measured on a real project before it ships.
.html inline <script> has no module semantics at all (classic script scope, not module scope). Imports cannot appear; a <script type="module"> is different from a bare one. Do not collapse the two.
Definition of done
A committed spec that names, for each of (a) and (b), the exact section and id shape used; a recorded cross-analyzer agreement on any coined term (or an explicit statement that (a) coins nothing); and — for whichever half the spec puts in the first cut — a fixture where a .vue/.astro file's embedded function appears in symbol_table with a span whose bytes slice back to the original template text.
Is your feature request related to a problem? Please describe.
A code block embedded in a template — the TS/JS analogue of a JSP scriptlet — is invisible to every analysis this repo performs. Measured at HEAD (
-a 2) on a fixture with one of each shape:src/Widget.vue(a<script lang="ts">block with an import and two functions),src/pages/index.astro(frontmatter declaring a function),views/page.ejs(<% %>statements) andpublic/index.html(an inline<script>with a function and a call to it) each produce an artifact record carrying the fullsource— and nothing else. No callables, no call edges, no CFG, no entrypoints. Code that a request actually executes is stored as opaque text.Two structurally different things are conflated by the word "scriptlet", and they want different answers:
(a) An embedded code BLOCK —
.astrofrontmatter (--- … ---, statements run server-side per request),.vue/.svelte<script>/<script setup>(declarations), EJS<% … %>statement scriptlets, an inline<script>in.html. This is a module whose file happens to be a template. It needs no new node kind: the content is TS/JS, callables have real byte spans in the real file,assignIdsalready mintscan://<app>/ts/<file>/…, and heritage/call graph/CFG work unchanged. Becausespan.bytesare UTF-8 byte offsets into the file rather than into a preprocessed string (#179), a block starting at byte 130 of the.vuehas a truthful span with no source-map layer.(b) A template EXPRESSION —
{{ count }}(Vue),{expr}(Svelte/Astro/JSX-in-markup),<%= total %>(EJS), a directive value like@click="bump". One to three tokens, dozens per file, no signature and no body. Modeling each as aTSCallableis wrong. The only interesting fact about one is which declaration it reads, which is an edge, not a node — and the shape already exists in this repo asTS_USES_CONFIG(:TSBodyNode→ target,prov: string[]).Artifacts and modules are disjoint by construction, at two lines:
src/artifacts/index.ts:179—if (SOURCE_EXTS.has(path.extname(e.name))) continue; // source lives in the symbol tablesrc/syntactic_analysis/discovery.ts:5and itsJS_EXTSsibling — only.ts/.tsx/.mts/.ctsplus the JS extensions are admitted as modules.So a
.vuecan only ever be an:Artifact. Relaxing this is what lets one file yield both an artifact record and asymbol_tableentry — which is itself the decision that needs designing, because "one file, two sections" is a new relationship between two level-free layers, andis_entrypoint/entrypoints[]on a callable inside a template is a claim consumers will query.Describe the solution you'd like
docs/design/specs/, deciding: whether a template file appears in bothartifacts{}andsymbol_table{}or is moved wholesale; the id shape for a callable inside a template; whether (b) is a body-node role or an edge; and howroles[]on the artifact relates to the module.codeanalyzer-javais doing JSP/JSF/Thymeleaf now; whichever analyzer ships first coins the vocabulary for both, and this is exactly the case the parity clause exists for. Half (a) ideally coins nothing — a template file yielding a module needs no new vocabulary at all, which is the cheapest possible parity.Describe alternatives you've considered
Not stated in the original issue.
Additional context
Scope boundary
symbol_tablekey means (today: a TS/JS source file), and half (b) needs either a new body-node role or a new edge type. Filed as a work item so the thread is not dropped; entering it means enteringdesigning-cldk-changes, notmaintaining-cldk.roles: ["unknown"](.vue, .svelte, .astro, .ejs, .html) #208) — that one moves no contract and can land first..astro's markup, Vue's<template>, or Pug is out of scope for (a): only the code block is.Caveats and known risks
.vue/.astro. Extracting the block means either a small hand-written scanner for the delimiters (cheap, and the byte offsets stay honest) or a per-framework compiler dependency (@vue/compiler-sfc,@astrojs/compiler— accurate, but a new dependency per framework and a version-skew surface). The scanner is the lazy option and is probably right; the spec should say which and why.<% %>blocks are not a module — they are statements in a per-render function whose parameters come from the render call. Treating them as file-scope declarations would produce plausible-looking nonsense; the spec should decide whether EJS is in the first cut at all..htmlinline<script>has no module semantics at all (classic script scope, not module scope). Imports cannot appear; a<script type="module">is different from a bare one. Do not collapse the two.Definition of done
A committed spec that names, for each of (a) and (b), the exact section and id shape used; a recorded cross-analyzer agreement on any coined term (or an explicit statement that (a) coins nothing); and — for whichever half the spec puts in the first cut — a fixture where a
.vue/.astrofile's embedded function appears insymbol_tablewith a span whose bytes slice back to the original template text.