Skip to content

feat: add CSS @import tracking and path alias resolution to dependency graph#13

Merged
giancarloerra merged 3 commits intogiancarloerra:mainfrom
cstuncsik:feat/alias-resolution-css-imports
Mar 19, 2026
Merged

feat: add CSS @import tracking and path alias resolution to dependency graph#13
giancarloerra merged 3 commits intogiancarloerra:mainfrom
cstuncsik:feat/alias-resolution-css-imports

Conversation

@cstuncsik
Copy link
Copy Markdown
Contributor

Summary

Follow-up to #9 (Svelte/Vue import parsing), addressing both known limitations listed there:

  1. Path aliases ($lib/, @/, ~/) were treated as external packages — now resolved via tsconfig.json / jsconfig.json compilerOptions.paths
  2. CSS @import in <style> blocks was not tracked — now extracted from Svelte and Vue <style> blocks (any combination of lang, scoped, module, global attributes), plus standalone .css/.scss/.sass/.less files

Both features add zero new dependencies, reusing existing ast-grep HTML parsing (same approach as #9) and the resolveRelativePath infrastructure.

How it works

CSS @import extraction:

  1. In Svelte/Vue files: after extracting <script> imports, also finds <style> blocks via style_element AST nodes and applies a regex to extract @import/@import url(...) statements (CSS/SCSS/LESS) and @require (Stylus)
  2. For standalone CSS/SCSS/SASS/LESS files: maps them to Lang.Css (lenient parser) and applies the same regex extractor
  3. CSS imports are tagged with isCssImport: true so the graph builder routes them to CSS resolution (with .css/.scss/.sass/.less/.styl extensions) instead of JS resolution — critical for Svelte/Vue files where script and style imports coexist
  4. SCSS partial resolution: @import "variables" correctly finds _variables.scss (underscore-prefix convention)

Path alias resolution:

  1. loadPathAliases() reads tsconfig.json (falling back to jsconfig.json) and parses compilerOptions.paths + baseUrl
  2. Follows extends chains (up to 10 levels, with circular reference protection) to find paths in parent configs — common in monorepo setups
  3. Converts wildcard patterns ("$lib/*": ["src/lib/*"]) to prefix→directory mappings
  4. Exact patterns ("~": ["./src"]) match only the exact specifier (not prefix-based)
  5. Aliases are loaded once per graph build and passed through to resolveImport()
  6. Works for both JS/TS/Svelte/Vue imports and CSS imports (e.g., $lib/styles/vars.css)

Changes

src/services/graph-aliases.ts (new):

  • loadPathAliases() — reads tsconfig/jsconfig, follows extends chains
  • parsePathAliases() — parses compilerOptions.paths with baseUrl support
  • parseTsconfigJson() — JSON parser with comment stripping that preserves string values
  • followExtendsChain() — walks extends references with circular reference and depth protection

src/services/graph-imports.ts:

  • Added extractCssImports() — regex-based extraction of @import, @import url(...), and Stylus @require
  • Added isCssImport flag to ImportInfo interface
  • Svelte/Vue handler: extracts CSS imports from <style> blocks after script extraction
  • Added "Css" case in AST switch for standalone CSS/SCSS/SASS/LESS files

src/services/graph-resolution.ts:

  • Added aliases parameter to resolveImport()
  • Added resolveAliasPath() helper with wildcard vs exact prefix matching
  • Added css/scss/sass/less language cases with CSS extension resolution
  • Added SCSS partial resolution (_ prefix) in resolveRelativePath()

src/services/code-graph.ts:

  • Loads path aliases once per graph build via loadPathAliases()
  • Routes isCssImport imports to CSS resolution language
  • Added .sass and .less to getAstGrepLang() mapping

Known limitations

  • Stylus bare-identifier @require (without quotes) is not supported — quoted form only
  • tsconfig.extends with package references (e.g., @tsconfig/node20) resolves via local node_modules/ — may not work with hoisted packages in some monorepo setups
  • CSS @import with bare identifiers (no quotes) is not supported — very rare in practice

Test plan

  • All 714 existing + new tests pass
  • TypeScript build passes with no errors
  • 21 tests for alias loading/parsing (tests/unit/graph-aliases.test.ts): tsconfig paths, baseUrl, jsconfig fallback, extends chains (single/multi-level), circular extends, missing files, comment stripping, malformed targets
  • 48 tests for import extraction (tests/unit/graph-imports.test.ts): CSS @import in Svelte/Vue style blocks (all attribute combinations), Stylus @require, standalone CSS, external URL filtering, isCssImport flag
  • 63 tests for resolution (tests/unit/graph-resolution.test.ts): alias resolution (wildcard/exact), SCSS partials (_prefix), CSS/SCSS/SASS/LESS resolution, alias + partial combined, backwards compatibility without aliases

…y graph

Add CSS @import extraction from Svelte/Vue <style> blocks and standalone
CSS/SCSS/SASS/LESS files. Add path alias resolution from tsconfig.json /
jsconfig.json compilerOptions.paths with extends chain support.
… aliases

- Move Svelte/Vue from "Indexing Only" to "Full Support" in README
- Move SASS/LESS to "Code Graph via Regex" in README
- Add graph-aliases.ts to DEVELOPER.md service file listing
- Update DEVELOPER.md data flow with CSS @import, path alias, and SCSS
  partial resolution steps
@cstuncsik cstuncsik force-pushed the feat/alias-resolution-css-imports branch from 6bd0ed3 to f4c5518 Compare March 18, 2026 19:18
@cstuncsik
Copy link
Copy Markdown
Contributor Author

@pineapplestrikesback, you might also want to have a look at this

@giancarloerra giancarloerra self-assigned this Mar 19, 2026
@giancarloerra giancarloerra merged commit 4590366 into giancarloerra:main Mar 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants