-
Notifications
You must be signed in to change notification settings - Fork 0
Add configurable language extension mapping #147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
4db0f8f
65cf6ef
f56d931
4d8e010
5ca3a88
0877e91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,18 +36,27 @@ Commands that scan a project read `codegraph.config.json` from `--root` when it | |
| "includeGlobs": ["src/**/*.ts"], | ||
| "ignoreGlobs": ["tests/samples/**", "tests/languages/samples/**"], | ||
| "useGitignore": true | ||
| }, | ||
| "languages": { | ||
| "extensions": { | ||
| ".tpl": "php", | ||
| ".inc.php": "php", | ||
| ".build.ts": "ts" | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| - `discovery.includeGlobs` and `discovery.ignoreGlobs` are project-root-relative, even when a command scans child include roots. | ||
| - `discovery.ignoreGlobs` is for large fixture, generated, or vendored folders that should not be indexed. | ||
| - `languages.extensions` maps additional or built-in suffixes to supported language IDs; keys must start with `.`, values must name a supported language, and the longest suffix wins. | ||
| - Built-in suffixes remain active unless explicitly remapped by `languages.extensions`. | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 4d8e010. |
||
| - CLI `--include-glob` and `--ignore-glob` values are one-off additions relative to each scanned root. | ||
| - `inspect` follow-up commands preserve the selected `--root` and include roots. | ||
| - `--no-gitignore` overrides `useGitignore`. | ||
|
|
||
| Config globs and one-off CLI globs apply at different layers. `codegraph.config.json` globs are durable and project-root-relative. CLI scan-root globs are additive for a single command and are evaluated relative to each active scan root. `--no-gitignore` disables `.gitignore` filtering for that command only; it does not change config. | ||
| Cache and manifest reuse is rooted at `--root`. Reusing a project root lets commands share compatible index and graph entries when the file signatures, config, graph options, and relevant build options still match. Changing `--root`, changing discovery config, or changing graph options creates a different reuse boundary. Child include-root scans can reuse project-root cache entries, but command summaries and follow-up commands stay scoped to the selected include roots. | ||
| Configured language extensions automatically extend discovery for matching files and participate in cache compatibility checks. Reusing a project root lets commands share compatible index and graph entries when the file signatures, config, graph options, and relevant build options still match. Changing `--root`, changing discovery or language-extension config, or changing graph options creates a different reuse boundary. Child include-root scans can reuse project-root cache entries, but command summaries and follow-up commands stay scoped to the selected include roots. | ||
|
|
||
| ## Core commands | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,16 @@ | ||
| import fsp from "node:fs/promises"; | ||
| import path from "node:path"; | ||
| import { z } from "zod"; | ||
| import { type ProjectFileDiscoveryOptions } from "./util/projectFiles.js"; | ||
| import { supportById } from "./languages.js"; | ||
| import type { LanguageExtensionMap } from "./indexer/types.js"; | ||
| import type { ProjectFileDiscoveryOptions } from "./util/projectFiles.js"; | ||
|
|
||
| export const CODEGRAPH_CONFIG_FILE = "codegraph.config.json"; | ||
|
|
||
| const stringArraySchema = z.array(z.string().trim().min(1)); | ||
|
|
||
| const languageExtensionsSchema = z.record(z.string().trim().min(1), z.string().trim().min(1)); | ||
|
|
||
| const codegraphConfigSchema = z | ||
| .object({ | ||
| discovery: z | ||
|
|
@@ -17,13 +21,22 @@ const codegraphConfigSchema = z | |
| }) | ||
| .strict() | ||
| .optional(), | ||
| languages: z | ||
| .object({ | ||
| extensions: languageExtensionsSchema.optional(), | ||
| }) | ||
| .strict() | ||
| .optional(), | ||
| }) | ||
| .strict(); | ||
|
|
||
| type ParsedCodegraphConfig = z.infer<typeof codegraphConfigSchema>; | ||
|
|
||
| export type CodegraphConfig = { | ||
| discovery?: ProjectFileDiscoveryOptions; | ||
| languages?: { | ||
| extensions?: LanguageExtensionMap; | ||
| }; | ||
| }; | ||
|
|
||
| function uniq(values: readonly string[]): string[] { | ||
|
|
@@ -78,6 +91,33 @@ function normalizeDiscoveryConfig( | |
| return hasDiscoveryOptions(normalized) ? normalized : undefined; | ||
| } | ||
|
|
||
| function normalizeLanguageExtensions(extensions: Record<string, string> | undefined): LanguageExtensionMap | undefined { | ||
| if (!extensions) return undefined; | ||
| const normalized: LanguageExtensionMap = {}; | ||
| for (const [rawKey, rawLanguageId] of Object.entries(extensions)) { | ||
| const key = rawKey.trim().toLowerCase(); | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 4d8e010. Config validation now layers its actionable errors over the canonical |
||
| const languageId = rawLanguageId.trim(); | ||
| if (!key.startsWith(".")) { | ||
|
Copilot marked this conversation as resolved.
|
||
| throw new Error(`Invalid ${CODEGRAPH_CONFIG_FILE}: languages.extensions key "${rawKey}" must start with ".".`); | ||
| } | ||
| if (!supportById(languageId)) { | ||
| throw new Error( | ||
| `Invalid ${CODEGRAPH_CONFIG_FILE}: languages.extensions["${rawKey}"] references unknown language "${languageId}".`, | ||
| ); | ||
| } | ||
| normalized[key] = languageId; | ||
| } | ||
| return Object.keys(normalized).length ? normalized : undefined; | ||
| } | ||
|
|
||
| function languageExtensionIncludeGlobs(languageExtensions: LanguageExtensionMap | undefined): string[] { | ||
| return Object.keys(languageExtensions ?? {}) | ||
| .map((extension) => extension.trim().toLowerCase()) | ||
| .filter((extension) => extension.startsWith(".")) | ||
| .sort() | ||
| .map((extension) => `**/*${extension}`); | ||
| } | ||
|
|
||
| export async function loadCodegraphConfig(projectRoot: string): Promise<CodegraphConfig> { | ||
| const configPath = path.join(projectRoot, CODEGRAPH_CONFIG_FILE); | ||
| let raw: string; | ||
|
|
@@ -102,8 +142,13 @@ export async function loadCodegraphConfig(projectRoot: string): Promise<Codegrap | |
| if (!parsed.success) { | ||
| throw new Error(`Invalid ${CODEGRAPH_CONFIG_FILE}: ${z.prettifyError(parsed.error)}`); | ||
| } | ||
| const discovery = normalizeDiscoveryConfig(parsed.data.discovery); | ||
| const languageExtensions = normalizeLanguageExtensions(parsed.data.languages?.extensions); | ||
| const languageDiscovery = languageExtensions | ||
| ? { includeGlobs: languageExtensionIncludeGlobs(languageExtensions) } | ||
| : undefined; | ||
| const discovery = mergeDiscoveryOptions(normalizeDiscoveryConfig(parsed.data.discovery), languageDiscovery); | ||
| return { | ||
| ...(discovery ? { discovery } : {}), | ||
| ...(hasDiscoveryOptions(discovery) ? { discovery } : {}), | ||
| ...(languageExtensions ? { languages: { extensions: languageExtensions } } : {}), | ||
| }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 4d8e010. README now states that
.vueand.svelteare always handled as single-file components and cannot be remapped. Runtime behavior is aligned with that documentation too: programmatic SFC remaps normalize away, config remaps are rejected, and support/cache consumers retain the built-in Vue/Svelte identities.