@tmhs/godot-correctness-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +34 -0
- package/README.md +118 -0
- package/data/godot-4.4.symbols.json.gz +0 -0
- package/data/godot-4.5.symbols.json.gz +0 -0
- package/data/tree-sitter-gdscript.wasm +0 -0
- package/dist/capabilities.d.ts +21 -0
- package/dist/capabilities.js +24 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +105 -0
- package/dist/cli.js.map +1 -0
- package/dist/gdscript/ast.d.ts +25 -0
- package/dist/gdscript/ast.js +82 -0
- package/dist/gdscript/ast.js.map +1 -0
- package/dist/gdscript/parser.d.ts +7 -0
- package/dist/gdscript/parser.js +44 -0
- package/dist/gdscript/parser.js.map +1 -0
- package/dist/godot/text-format.d.ts +35 -0
- package/dist/godot/text-format.js +105 -0
- package/dist/godot/text-format.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/lint/config.d.ts +83 -0
- package/dist/lint/config.js +55 -0
- package/dist/lint/config.js.map +1 -0
- package/dist/lint/engine.d.ts +7 -0
- package/dist/lint/engine.js +33 -0
- package/dist/lint/engine.js.map +1 -0
- package/dist/lint/project.d.ts +12 -0
- package/dist/lint/project.js +49 -0
- package/dist/lint/project.js.map +1 -0
- package/dist/lint/rules/await-misuse.d.ts +3 -0
- package/dist/lint/rules/await-misuse.js +34 -0
- package/dist/lint/rules/await-misuse.js.map +1 -0
- package/dist/lint/rules/connect-without-disconnect.d.ts +7 -0
- package/dist/lint/rules/connect-without-disconnect.js +38 -0
- package/dist/lint/rules/connect-without-disconnect.js.map +1 -0
- package/dist/lint/rules/delta-misuse.d.ts +7 -0
- package/dist/lint/rules/delta-misuse.js +78 -0
- package/dist/lint/rules/delta-misuse.js.map +1 -0
- package/dist/lint/rules/float-grid-equality.d.ts +6 -0
- package/dist/lint/rules/float-grid-equality.js +39 -0
- package/dist/lint/rules/float-grid-equality.js.map +1 -0
- package/dist/lint/rules/get-node-in-process.d.ts +3 -0
- package/dist/lint/rules/get-node-in-process.js +31 -0
- package/dist/lint/rules/get-node-in-process.js.map +1 -0
- package/dist/lint/rules/index.d.ts +4 -0
- package/dist/lint/rules/index.js +21 -0
- package/dist/lint/rules/index.js.map +1 -0
- package/dist/lint/rules/missing-onready.d.ts +3 -0
- package/dist/lint/rules/missing-onready.js +52 -0
- package/dist/lint/rules/missing-onready.js.map +1 -0
- package/dist/lint/rules/stringly-nodepath.d.ts +3 -0
- package/dist/lint/rules/stringly-nodepath.js +35 -0
- package/dist/lint/rules/stringly-nodepath.js.map +1 -0
- package/dist/lint/rules/untyped-declaration.d.ts +7 -0
- package/dist/lint/rules/untyped-declaration.js +25 -0
- package/dist/lint/rules/untyped-declaration.js.map +1 -0
- package/dist/lint/types.d.ts +36 -0
- package/dist/lint/types.js +2 -0
- package/dist/lint/types.js.map +1 -0
- package/dist/resources/skills.d.ts +12 -0
- package/dist/resources/skills.js +45 -0
- package/dist/resources/skills.js.map +1 -0
- package/dist/scenes/paths.d.ts +14 -0
- package/dist/scenes/paths.js +38 -0
- package/dist/scenes/paths.js.map +1 -0
- package/dist/scenes/report.d.ts +17 -0
- package/dist/scenes/report.js +25 -0
- package/dist/scenes/report.js.map +1 -0
- package/dist/scenes/validate.d.ts +16 -0
- package/dist/scenes/validate.js +185 -0
- package/dist/scenes/validate.js.map +1 -0
- package/dist/server-info.d.ts +13 -0
- package/dist/server-info.js +21 -0
- package/dist/server-info.js.map +1 -0
- package/dist/symbols/db.d.ts +11 -0
- package/dist/symbols/db.js +42 -0
- package/dist/symbols/db.js.map +1 -0
- package/dist/symbols/lookup.d.ts +89 -0
- package/dist/symbols/lookup.js +271 -0
- package/dist/symbols/lookup.js.map +1 -0
- package/dist/symbols/schema.d.ts +1419 -0
- package/dist/symbols/schema.js +109 -0
- package/dist/symbols/schema.js.map +1 -0
- package/dist/tools/api.d.ts +3 -0
- package/dist/tools/api.js +91 -0
- package/dist/tools/api.js.map +1 -0
- package/dist/tools/lint.d.ts +3 -0
- package/dist/tools/lint.js +45 -0
- package/dist/tools/lint.js.map +1 -0
- package/dist/tools/scenes.d.ts +3 -0
- package/dist/tools/scenes.js +43 -0
- package/dist/tools/scenes.js.map +1 -0
- package/package.json +57 -0
- package/skills/data-driven-resources.md +78 -0
- package/skills/resource-preload.md +43 -0
- package/skills/signal-hygiene.md +46 -0
- package/skills/typed-gdscript.md +47 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-format.js","sourceRoot":"","sources":["../../src/godot/text-format.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { registerSkillResources } from "./resources/skills.js";
|
|
6
|
+
import { getServerInfo, readVersion, SERVER_NAME } from "./server-info.js";
|
|
7
|
+
import { registerApiTools } from "./tools/api.js";
|
|
8
|
+
import { registerLintTools } from "./tools/lint.js";
|
|
9
|
+
import { registerSceneTools } from "./tools/scenes.js";
|
|
10
|
+
/** Build the MCP server with all tools registered. Exported for offline tests. */
|
|
11
|
+
export function createServer() {
|
|
12
|
+
const server = new McpServer({ name: SERVER_NAME, version: readVersion() });
|
|
13
|
+
server.tool("server_info", "Report this server's version, the pinned Godot versions it targets (4.4, 4.5), " +
|
|
14
|
+
"and which correctness pillars (GDScript lint, API symbols, scene/resource analysis) " +
|
|
15
|
+
"are currently available. Takes no arguments.", {}, async () => ({
|
|
16
|
+
content: [{ type: "text", text: JSON.stringify(getServerInfo(), null, 2) }],
|
|
17
|
+
}));
|
|
18
|
+
registerApiTools(server);
|
|
19
|
+
registerLintTools(server);
|
|
20
|
+
registerSceneTools(server);
|
|
21
|
+
registerSkillResources(server);
|
|
22
|
+
return server;
|
|
23
|
+
}
|
|
24
|
+
async function main() {
|
|
25
|
+
const server = createServer();
|
|
26
|
+
const transport = new StdioServerTransport();
|
|
27
|
+
await server.connect(transport);
|
|
28
|
+
}
|
|
29
|
+
// Run the stdio server only when executed directly, not when imported by tests.
|
|
30
|
+
const invokedDirectly = process.argv[1] !== undefined &&
|
|
31
|
+
import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
32
|
+
if (invokedDirectly) {
|
|
33
|
+
main().catch((err) => {
|
|
34
|
+
console.error(err);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,kFAAkF;AAClF,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IAE5E,MAAM,CAAC,IAAI,CACT,aAAa,EACb,iFAAiF;QAC/E,sFAAsF;QACtF,8CAA8C,EAChD,EAAE,EACF,KAAK,IAAI,EAAE,CAAC,CAAC;QACX,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KAC5E,CAAC,CACH,CAAC;IAEF,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAE/B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,gFAAgF;AAChF,MAAM,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS;IAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAE1D,IAAI,eAAe,EAAE,CAAC;IACpB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Severity } from "./types.js";
|
|
3
|
+
export declare const CONFIG_FILENAME = "godot-correctness.config.json";
|
|
4
|
+
declare const ruleConfigSchema: z.ZodObject<{
|
|
5
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
6
|
+
severity: z.ZodOptional<z.ZodEnum<["error", "warning", "info"]>>;
|
|
7
|
+
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
enabled?: boolean | undefined;
|
|
10
|
+
severity?: "error" | "warning" | "info" | undefined;
|
|
11
|
+
options?: Record<string, unknown> | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
enabled?: boolean | undefined;
|
|
14
|
+
severity?: "error" | "warning" | "info" | undefined;
|
|
15
|
+
options?: Record<string, unknown> | undefined;
|
|
16
|
+
}>;
|
|
17
|
+
export type RuleConfig = z.infer<typeof ruleConfigSchema>;
|
|
18
|
+
export declare const configSchema: z.ZodObject<{
|
|
19
|
+
/** Globs (relative to project root) of files to lint. */
|
|
20
|
+
include: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
21
|
+
/** Globs to exclude. */
|
|
22
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
23
|
+
/** Per-rule enable/disable, severity override, and options. */
|
|
24
|
+
rules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
25
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
26
|
+
severity: z.ZodOptional<z.ZodEnum<["error", "warning", "info"]>>;
|
|
27
|
+
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
enabled?: boolean | undefined;
|
|
30
|
+
severity?: "error" | "warning" | "info" | undefined;
|
|
31
|
+
options?: Record<string, unknown> | undefined;
|
|
32
|
+
}, {
|
|
33
|
+
enabled?: boolean | undefined;
|
|
34
|
+
severity?: "error" | "warning" | "info" | undefined;
|
|
35
|
+
options?: Record<string, unknown> | undefined;
|
|
36
|
+
}>>>;
|
|
37
|
+
/** Directories of data-driven .tres registries to deep-validate (Phase 4). */
|
|
38
|
+
registryDirs: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
39
|
+
/** Property-name globs to treat as resource paths (Phase 4). */
|
|
40
|
+
pathPropertyPatterns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
41
|
+
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
include?: string[] | undefined;
|
|
43
|
+
exclude?: string[] | undefined;
|
|
44
|
+
rules?: Record<string, {
|
|
45
|
+
enabled?: boolean | undefined;
|
|
46
|
+
severity?: "error" | "warning" | "info" | undefined;
|
|
47
|
+
options?: Record<string, unknown> | undefined;
|
|
48
|
+
}> | undefined;
|
|
49
|
+
registryDirs?: string[] | undefined;
|
|
50
|
+
pathPropertyPatterns?: string[] | undefined;
|
|
51
|
+
}, {
|
|
52
|
+
include?: string[] | undefined;
|
|
53
|
+
exclude?: string[] | undefined;
|
|
54
|
+
rules?: Record<string, {
|
|
55
|
+
enabled?: boolean | undefined;
|
|
56
|
+
severity?: "error" | "warning" | "info" | undefined;
|
|
57
|
+
options?: Record<string, unknown> | undefined;
|
|
58
|
+
}> | undefined;
|
|
59
|
+
registryDirs?: string[] | undefined;
|
|
60
|
+
pathPropertyPatterns?: string[] | undefined;
|
|
61
|
+
}>;
|
|
62
|
+
export type GodotCorrectnessConfig = z.infer<typeof configSchema>;
|
|
63
|
+
export declare const DEFAULT_INCLUDE: string[];
|
|
64
|
+
export declare const DEFAULT_EXCLUDE: string[];
|
|
65
|
+
export declare const DEFAULT_PATH_PROPERTY_PATTERNS: string[];
|
|
66
|
+
/** A fully-resolved config with defaults applied. */
|
|
67
|
+
export interface ResolvedConfig {
|
|
68
|
+
include: string[];
|
|
69
|
+
exclude: string[];
|
|
70
|
+
rules: Record<string, RuleConfig>;
|
|
71
|
+
registryDirs: string[];
|
|
72
|
+
pathPropertyPatterns: string[];
|
|
73
|
+
}
|
|
74
|
+
export declare function resolveConfig(raw?: GodotCorrectnessConfig): ResolvedConfig;
|
|
75
|
+
/** Load and validate the config from a project root, or defaults when absent. */
|
|
76
|
+
export declare function loadConfig(projectRoot: string): ResolvedConfig;
|
|
77
|
+
/** Whether a rule is enabled (default: enabled unless explicitly disabled). */
|
|
78
|
+
export declare function isRuleEnabled(config: ResolvedConfig, ruleId: string): boolean;
|
|
79
|
+
/** Config severity override for a rule, if any. */
|
|
80
|
+
export declare function severityOverride(config: ResolvedConfig, ruleId: string): Severity | undefined;
|
|
81
|
+
/** Per-rule options from config. */
|
|
82
|
+
export declare function ruleOptions(config: ResolvedConfig, ruleId: string): Record<string, unknown>;
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
export const CONFIG_FILENAME = "godot-correctness.config.json";
|
|
5
|
+
const severitySchema = z.enum(["error", "warning", "info"]);
|
|
6
|
+
const ruleConfigSchema = z.object({
|
|
7
|
+
enabled: z.boolean().optional(),
|
|
8
|
+
severity: severitySchema.optional(),
|
|
9
|
+
options: z.record(z.string(), z.unknown()).optional(),
|
|
10
|
+
});
|
|
11
|
+
export const configSchema = z.object({
|
|
12
|
+
/** Globs (relative to project root) of files to lint. */
|
|
13
|
+
include: z.array(z.string()).optional(),
|
|
14
|
+
/** Globs to exclude. */
|
|
15
|
+
exclude: z.array(z.string()).optional(),
|
|
16
|
+
/** Per-rule enable/disable, severity override, and options. */
|
|
17
|
+
rules: z.record(z.string(), ruleConfigSchema).optional(),
|
|
18
|
+
/** Directories of data-driven .tres registries to deep-validate (Phase 4). */
|
|
19
|
+
registryDirs: z.array(z.string()).optional(),
|
|
20
|
+
/** Property-name globs to treat as resource paths (Phase 4). */
|
|
21
|
+
pathPropertyPatterns: z.array(z.string()).optional(),
|
|
22
|
+
});
|
|
23
|
+
export const DEFAULT_INCLUDE = ["**/*.gd"];
|
|
24
|
+
export const DEFAULT_EXCLUDE = ["**/.godot/**"];
|
|
25
|
+
export const DEFAULT_PATH_PROPERTY_PATTERNS = ["*_path", "*_scene", "*_texture", "*_resource"];
|
|
26
|
+
export function resolveConfig(raw = {}) {
|
|
27
|
+
return {
|
|
28
|
+
include: raw.include ?? DEFAULT_INCLUDE,
|
|
29
|
+
exclude: raw.exclude ?? DEFAULT_EXCLUDE,
|
|
30
|
+
rules: raw.rules ?? {},
|
|
31
|
+
registryDirs: raw.registryDirs ?? [],
|
|
32
|
+
pathPropertyPatterns: raw.pathPropertyPatterns ?? DEFAULT_PATH_PROPERTY_PATTERNS,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Load and validate the config from a project root, or defaults when absent. */
|
|
36
|
+
export function loadConfig(projectRoot) {
|
|
37
|
+
const path = join(projectRoot, CONFIG_FILENAME);
|
|
38
|
+
if (!existsSync(path))
|
|
39
|
+
return resolveConfig();
|
|
40
|
+
const raw = JSON.parse(readFileSync(path, "utf-8"));
|
|
41
|
+
return resolveConfig(configSchema.parse(raw));
|
|
42
|
+
}
|
|
43
|
+
/** Whether a rule is enabled (default: enabled unless explicitly disabled). */
|
|
44
|
+
export function isRuleEnabled(config, ruleId) {
|
|
45
|
+
return config.rules[ruleId]?.enabled !== false;
|
|
46
|
+
}
|
|
47
|
+
/** Config severity override for a rule, if any. */
|
|
48
|
+
export function severityOverride(config, ruleId) {
|
|
49
|
+
return config.rules[ruleId]?.severity;
|
|
50
|
+
}
|
|
51
|
+
/** Per-rule options from config. */
|
|
52
|
+
export function ruleOptions(config, ruleId) {
|
|
53
|
+
return config.rules[ruleId]?.options ?? {};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lint/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ResolvedConfig } from "./config.js";
|
|
2
|
+
import type { Finding } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Lint one GDScript source string. `file` is used only for the reported path.
|
|
5
|
+
* Severity precedence: config override > per-finding severity > rule default.
|
|
6
|
+
*/
|
|
7
|
+
export declare function lintSource(file: string, source: string, config: ResolvedConfig): Promise<Finding[]>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { parseGDScript } from "../gdscript/parser.js";
|
|
2
|
+
import { isRuleEnabled, ruleOptions, severityOverride, } from "./config.js";
|
|
3
|
+
import { DEFAULT_RULES } from "./rules/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Lint one GDScript source string. `file` is used only for the reported path.
|
|
6
|
+
* Severity precedence: config override > per-finding severity > rule default.
|
|
7
|
+
*/
|
|
8
|
+
export async function lintSource(file, source, config) {
|
|
9
|
+
const tree = await parseGDScript(source);
|
|
10
|
+
const root = tree.rootNode;
|
|
11
|
+
const findings = [];
|
|
12
|
+
for (const rule of DEFAULT_RULES) {
|
|
13
|
+
if (!isRuleEnabled(config, rule.id))
|
|
14
|
+
continue;
|
|
15
|
+
const raws = rule.check({ root, source, options: ruleOptions(config, rule.id) });
|
|
16
|
+
for (const raw of raws) {
|
|
17
|
+
const severity = severityOverride(config, rule.id) ?? raw.severity ?? rule.defaultSeverity;
|
|
18
|
+
const pos = raw.node.startPosition;
|
|
19
|
+
findings.push({
|
|
20
|
+
file,
|
|
21
|
+
line: pos.row + 1,
|
|
22
|
+
col: pos.column + 1,
|
|
23
|
+
ruleId: rule.id,
|
|
24
|
+
severity,
|
|
25
|
+
message: raw.message,
|
|
26
|
+
suggestion: raw.suggestion,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
findings.sort((a, b) => a.line - b.line || a.col - b.col || a.ruleId.localeCompare(b.ruleId));
|
|
31
|
+
return findings;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/lint/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EACL,aAAa,EACb,WAAW,EACX,gBAAgB,GAEjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAY,EACZ,MAAc,EACd,MAAsB;IAEtB,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC3B,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;YAAE,SAAS;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACjF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,eAAe,CAAC;YAC3F,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC;YACnC,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI;gBACJ,IAAI,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;gBACjB,GAAG,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC;gBACnB,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ;gBACR,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,UAAU,EAAE,GAAG,CAAC,UAAU;aAC3B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9F,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ResolvedConfig } from "./config.js";
|
|
2
|
+
import type { Finding } from "./types.js";
|
|
3
|
+
export interface ProjectLintResult {
|
|
4
|
+
root: string;
|
|
5
|
+
fileCount: number;
|
|
6
|
+
findings: Finding[];
|
|
7
|
+
errorCount: number;
|
|
8
|
+
warningCount: number;
|
|
9
|
+
infoCount: number;
|
|
10
|
+
}
|
|
11
|
+
/** Walk a project root, lint every included .gd file, and aggregate findings. */
|
|
12
|
+
export declare function lintProject(root: string, config?: ResolvedConfig): Promise<ProjectLintResult>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
import picomatch from "picomatch";
|
|
4
|
+
import { loadConfig } from "./config.js";
|
|
5
|
+
import { lintSource } from "./engine.js";
|
|
6
|
+
function toPosix(p) {
|
|
7
|
+
return p.split(sep).join("/");
|
|
8
|
+
}
|
|
9
|
+
function collectFiles(root) {
|
|
10
|
+
const out = [];
|
|
11
|
+
const walkDir = (dir) => {
|
|
12
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
13
|
+
if (entry.isDirectory()) {
|
|
14
|
+
if (entry.name === ".git")
|
|
15
|
+
continue;
|
|
16
|
+
walkDir(join(dir, entry.name));
|
|
17
|
+
}
|
|
18
|
+
else if (entry.isFile()) {
|
|
19
|
+
out.push(join(dir, entry.name));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
walkDir(root);
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
/** Walk a project root, lint every included .gd file, and aggregate findings. */
|
|
27
|
+
export async function lintProject(root, config) {
|
|
28
|
+
const cfg = config ?? loadConfig(root);
|
|
29
|
+
const isIncluded = picomatch(cfg.include, { dot: true });
|
|
30
|
+
const isExcluded = picomatch(cfg.exclude, { dot: true });
|
|
31
|
+
const files = collectFiles(root)
|
|
32
|
+
.map((full) => ({ full, rel: toPosix(relative(root, full)) }))
|
|
33
|
+
.filter(({ rel }) => isIncluded(rel) && !isExcluded(rel));
|
|
34
|
+
const findings = [];
|
|
35
|
+
for (const { full, rel } of files) {
|
|
36
|
+
const source = readFileSync(full, "utf-8");
|
|
37
|
+
findings.push(...(await lintSource(rel, source, cfg)));
|
|
38
|
+
}
|
|
39
|
+
findings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.col - b.col);
|
|
40
|
+
return {
|
|
41
|
+
root,
|
|
42
|
+
fileCount: files.length,
|
|
43
|
+
findings,
|
|
44
|
+
errorCount: findings.filter((f) => f.severity === "error").length,
|
|
45
|
+
warningCount: findings.filter((f) => f.severity === "warning").length,
|
|
46
|
+
infoCount: findings.filter((f) => f.severity === "info").length,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/lint/project.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { findAll } from "../../gdscript/ast.js";
|
|
2
|
+
// Awaiting any of these is always wrong; awaiting an identifier/call is left
|
|
3
|
+
// alone because it may resolve to a Signal or coroutine (unknowable statically).
|
|
4
|
+
const LITERALS = new Set([
|
|
5
|
+
"integer",
|
|
6
|
+
"float",
|
|
7
|
+
"string",
|
|
8
|
+
"string_name",
|
|
9
|
+
"true",
|
|
10
|
+
"false",
|
|
11
|
+
"array",
|
|
12
|
+
"dictionary",
|
|
13
|
+
]);
|
|
14
|
+
/** Flags `await` applied to a literal value. Conservative: literals only. */
|
|
15
|
+
export const awaitMisuse = {
|
|
16
|
+
id: "await-misuse",
|
|
17
|
+
description: "await applied to a literal value (expects a signal or coroutine).",
|
|
18
|
+
defaultSeverity: "warning",
|
|
19
|
+
check(ctx) {
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const aw of findAll(ctx.root, "await_expression")) {
|
|
22
|
+
const operand = aw.namedChild(0);
|
|
23
|
+
if (operand && LITERALS.has(operand.type)) {
|
|
24
|
+
out.push({
|
|
25
|
+
node: aw,
|
|
26
|
+
message: "await on a literal value; await expects a signal or a coroutine.",
|
|
27
|
+
suggestion: "Await a signal (e.g. `await timer.timeout`) or a coroutine call.",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=await-misuse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"await-misuse.js","sourceRoot":"","sources":["../../../src/lint/rules/await-misuse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGhD,6EAA6E;AAC7E,iFAAiF;AACjF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACvB,SAAS;IACT,OAAO;IACP,QAAQ;IACR,aAAa;IACb,MAAM;IACN,OAAO;IACP,OAAO;IACP,YAAY;CACb,CAAC,CAAC;AAEH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,WAAW,GAAS;IAC/B,EAAE,EAAE,cAAc;IAClB,WAAW,EAAE,mEAAmE;IAChF,eAAe,EAAE,SAAS;IAC1B,KAAK,CAAC,GAAG;QACP,MAAM,GAAG,GAAG,EAAE,CAAC;QACf,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,EAAE,CAAC;YACvD,MAAM,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACjC,IAAI,OAAO,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1C,GAAG,CAAC,IAAI,CAAC;oBACP,IAAI,EAAE,EAAE;oBACR,OAAO,EAAE,kEAAkE;oBAC3E,UAAU,EAAE,kEAAkE;iBAC/E,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Rule } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Flags connect() in _ready() with no CONNECT_ONE_SHOT flag and no disconnect()
|
|
4
|
+
* anywhere in the file. Conservative: if the file disconnects at all, or uses a
|
|
5
|
+
* one-shot flag, it stays silent to avoid false positives.
|
|
6
|
+
*/
|
|
7
|
+
export declare const connectWithoutDisconnect: Rule;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { calleeName, fieldText, findAll, text, walk } from "../../gdscript/ast.js";
|
|
2
|
+
/**
|
|
3
|
+
* Flags connect() in _ready() with no CONNECT_ONE_SHOT flag and no disconnect()
|
|
4
|
+
* anywhere in the file. Conservative: if the file disconnects at all, or uses a
|
|
5
|
+
* one-shot flag, it stays silent to avoid false positives.
|
|
6
|
+
*/
|
|
7
|
+
export const connectWithoutDisconnect = {
|
|
8
|
+
id: "connect-without-disconnect",
|
|
9
|
+
description: "Signal connected in _ready() with no matching disconnect or one-shot flag.",
|
|
10
|
+
defaultSeverity: "info",
|
|
11
|
+
check(ctx) {
|
|
12
|
+
const hasDisconnect = [...walk(ctx.root)].some((n) => (n.type === "call" || n.type === "attribute_call") &&
|
|
13
|
+
calleeName(n, ctx.source) === "disconnect");
|
|
14
|
+
if (hasDisconnect)
|
|
15
|
+
return [];
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const fn of findAll(ctx.root, "function_definition")) {
|
|
18
|
+
if (fieldText(fn, "name", ctx.source) !== "_ready")
|
|
19
|
+
continue;
|
|
20
|
+
for (const n of walk(fn)) {
|
|
21
|
+
if (n.type !== "call" && n.type !== "attribute_call")
|
|
22
|
+
continue;
|
|
23
|
+
if (calleeName(n, ctx.source) !== "connect")
|
|
24
|
+
continue;
|
|
25
|
+
const args = n.childForFieldName("arguments");
|
|
26
|
+
if (args && text(args, ctx.source).includes("CONNECT_ONE_SHOT"))
|
|
27
|
+
continue;
|
|
28
|
+
out.push({
|
|
29
|
+
node: n,
|
|
30
|
+
message: "Signal connected in _ready() with no disconnect() or CONNECT_ONE_SHOT; risks double-connect or a dangling reference.",
|
|
31
|
+
suggestion: "Disconnect in _exit_tree(), pass CONNECT_ONE_SHOT, or guard with is_connected().",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=connect-without-disconnect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-without-disconnect.js","sourceRoot":"","sources":["../../../src/lint/rules/connect-without-disconnect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAGnF;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAS;IAC5C,EAAE,EAAE,4BAA4B;IAChC,WAAW,EAAE,4EAA4E;IACzF,eAAe,EAAE,MAAM;IACvB,KAAK,CAAC,GAAG;QACP,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAC5C,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC;YAClD,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,YAAY,CAC7C,CAAC;QACF,IAAI,aAAa;YAAE,OAAO,EAAE,CAAC;QAE7B,MAAM,GAAG,GAAG,EAAE,CAAC;QACf,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,qBAAqB,CAAC,EAAE,CAAC;YAC1D,IAAI,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ;gBAAE,SAAS;YAC7D,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,gBAAgB;oBAAE,SAAS;gBAC/D,IAAI,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,SAAS;oBAAE,SAAS;gBACtD,MAAM,IAAI,GAAG,CAAC,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;gBAC9C,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC;oBAAE,SAAS;gBAC1E,GAAG,CAAC,IAAI,CAAC;oBACP,IAAI,EAAE,CAAC;oBACP,OAAO,EAAE,sHAAsH;oBAC/H,UAAU,EAAE,kFAAkF;iBAC/F,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Rule } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* error: `velocity` scaled by delta in a function that calls move_and_slide()
|
|
4
|
+
* (double-scales, since move_and_slide already applies frame timing).
|
|
5
|
+
* info: position/rotation mutated in _process without any delta factor.
|
|
6
|
+
*/
|
|
7
|
+
export declare const deltaMisuse: Rule;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { containsIdentifier, fieldText, findAll, functionCalls, operatorOf, text, walk, } from "../../gdscript/ast.js";
|
|
2
|
+
const PROCESS_FNS = new Set(["_process", "_physics_process"]);
|
|
3
|
+
/** Base identifier of an assignment target: `velocity` or `x` in `self.x`/`x.y`. */
|
|
4
|
+
function targetName(left, source) {
|
|
5
|
+
if (left.type === "identifier")
|
|
6
|
+
return text(left, source);
|
|
7
|
+
if (left.type === "attribute") {
|
|
8
|
+
// `self.velocity` -> last identifier; `position.x` -> base identifier.
|
|
9
|
+
const first = left.namedChild(0);
|
|
10
|
+
if (first && first.type === "identifier" && text(first, source) === "self") {
|
|
11
|
+
const second = left.namedChild(1);
|
|
12
|
+
return second && second.type === "identifier" ? text(second, source) : null;
|
|
13
|
+
}
|
|
14
|
+
return first && first.type === "identifier" ? text(first, source) : null;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
/** RHS contains a `*` with `delta` as a direct operand (either order). */
|
|
19
|
+
function multipliesByDelta(node, source) {
|
|
20
|
+
for (const b of walk(node)) {
|
|
21
|
+
if (b.type !== "binary_operator" || operatorOf(b, source) !== "*")
|
|
22
|
+
continue;
|
|
23
|
+
const l = b.childForFieldName("left");
|
|
24
|
+
const r = b.childForFieldName("right");
|
|
25
|
+
const isDelta = (n) => !!n && n.type === "identifier" && text(n, source) === "delta";
|
|
26
|
+
if (isDelta(l) || isDelta(r))
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* error: `velocity` scaled by delta in a function that calls move_and_slide()
|
|
33
|
+
* (double-scales, since move_and_slide already applies frame timing).
|
|
34
|
+
* info: position/rotation mutated in _process without any delta factor.
|
|
35
|
+
*/
|
|
36
|
+
export const deltaMisuse = {
|
|
37
|
+
id: "delta-misuse",
|
|
38
|
+
description: "velocity * delta into move_and_slide(); or delta-less position/rotation change in _process.",
|
|
39
|
+
defaultSeverity: "error",
|
|
40
|
+
check(ctx) {
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const fn of findAll(ctx.root, "function_definition")) {
|
|
43
|
+
const fname = fieldText(fn, "name", ctx.source);
|
|
44
|
+
const callsMove = functionCalls(fn, "move_and_slide", ctx.source);
|
|
45
|
+
for (const stmt of walk(fn)) {
|
|
46
|
+
if (stmt.type !== "assignment" && stmt.type !== "augmented_assignment")
|
|
47
|
+
continue;
|
|
48
|
+
const left = stmt.childForFieldName("left");
|
|
49
|
+
const right = stmt.childForFieldName("right");
|
|
50
|
+
if (!left || !right)
|
|
51
|
+
continue;
|
|
52
|
+
const target = targetName(left, ctx.source);
|
|
53
|
+
if (callsMove && target === "velocity" && multipliesByDelta(right, ctx.source)) {
|
|
54
|
+
out.push({
|
|
55
|
+
node: stmt,
|
|
56
|
+
severity: "error",
|
|
57
|
+
message: "velocity multiplied by delta before move_and_slide(); move_and_slide() already applies frame timing, so this double-scales movement.",
|
|
58
|
+
suggestion: "Set velocity in units per second (no * delta); move_and_slide() handles delta.",
|
|
59
|
+
});
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (fname &&
|
|
63
|
+
PROCESS_FNS.has(fname) &&
|
|
64
|
+
(target === "position" || target === "rotation") &&
|
|
65
|
+
!containsIdentifier(right, "delta", ctx.source)) {
|
|
66
|
+
out.push({
|
|
67
|
+
node: stmt,
|
|
68
|
+
severity: "info",
|
|
69
|
+
message: `${fname}() changes ${target} without a delta factor; motion becomes frame-rate dependent.`,
|
|
70
|
+
suggestion: "Scale the change by delta for frame-rate independence.",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=delta-misuse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta-misuse.js","sourceRoot":"","sources":["../../../src/lint/rules/delta-misuse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { calleeName, findAll, operatorOf } from "../../gdscript/ast.js";
|
|
2
|
+
const VEC_CTORS = new Set(["Vector2", "Vector3", "Vector4"]);
|
|
3
|
+
function isFloatOrVector(node, source) {
|
|
4
|
+
if (!node)
|
|
5
|
+
return false;
|
|
6
|
+
if (node.type === "float")
|
|
7
|
+
return true;
|
|
8
|
+
if (node.type === "call" && VEC_CTORS.has(calleeName(node, source) ?? ""))
|
|
9
|
+
return true;
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Flags `==` / `!=` where one operand is a float literal or Vector constructor.
|
|
14
|
+
* Info-only and false-positive prone by nature; easy to disable per project.
|
|
15
|
+
*/
|
|
16
|
+
export const floatGridEquality = {
|
|
17
|
+
id: "float-grid-equality",
|
|
18
|
+
description: "Direct == / != on float or Vector values (floating-point precision).",
|
|
19
|
+
defaultSeverity: "info",
|
|
20
|
+
check(ctx) {
|
|
21
|
+
const out = [];
|
|
22
|
+
for (const b of findAll(ctx.root, "binary_operator")) {
|
|
23
|
+
const op = operatorOf(b, ctx.source);
|
|
24
|
+
if (op !== "==" && op !== "!=")
|
|
25
|
+
continue;
|
|
26
|
+
const l = b.childForFieldName("left");
|
|
27
|
+
const r = b.childForFieldName("right");
|
|
28
|
+
if (isFloatOrVector(l, ctx.source) || isFloatOrVector(r, ctx.source)) {
|
|
29
|
+
out.push({
|
|
30
|
+
node: b,
|
|
31
|
+
message: "Direct equality on float/Vector values is unreliable due to floating-point precision.",
|
|
32
|
+
suggestion: "Use is_equal_approx() / is_zero_approx() (or Vector2.is_equal_approx()).",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=float-grid-equality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"float-grid-equality.js","sourceRoot":"","sources":["../../../src/lint/rules/float-grid-equality.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;AAE7D,SAAS,eAAe,CAAC,IAAiB,EAAE,MAAc;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IACvF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAS;IACrC,EAAE,EAAE,qBAAqB;IACzB,WAAW,EAAE,sEAAsE;IACnF,eAAe,EAAE,MAAM;IACvB,KAAK,CAAC,GAAG;QACP,MAAM,GAAG,GAAG,EAAE,CAAC;QACf,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,CAAC;YACrD,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI;gBAAE,SAAS;YACzC,MAAM,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YACtC,MAAM,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YACvC,IAAI,eAAe,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,eAAe,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrE,GAAG,CAAC,IAAI,CAAC;oBACP,IAAI,EAAE,CAAC;oBACP,OAAO,EAAE,uFAAuF;oBAChG,UAAU,EAAE,0EAA0E;iBACvF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { calleeName, fieldText, findAll, walk } from "../../gdscript/ast.js";
|
|
2
|
+
const PROCESS_FNS = new Set(["_process", "_physics_process"]);
|
|
3
|
+
const NODE_GETTERS = new Set(["get_node", "get_node_or_null"]);
|
|
4
|
+
/** Flags $ / get_node() lookups inside _process / _physics_process. */
|
|
5
|
+
export const getNodeInProcess = {
|
|
6
|
+
id: "get-node-in-process",
|
|
7
|
+
description: "Node lookups ($ or get_node()) inside _process/_physics_process run every frame.",
|
|
8
|
+
defaultSeverity: "warning",
|
|
9
|
+
check(ctx) {
|
|
10
|
+
const out = [];
|
|
11
|
+
for (const fn of findAll(ctx.root, "function_definition")) {
|
|
12
|
+
const name = fieldText(fn, "name", ctx.source);
|
|
13
|
+
if (!name || !PROCESS_FNS.has(name))
|
|
14
|
+
continue;
|
|
15
|
+
for (const n of walk(fn)) {
|
|
16
|
+
const isDollar = n.type === "get_node";
|
|
17
|
+
const isGetterCall = (n.type === "call" || n.type === "attribute_call") &&
|
|
18
|
+
NODE_GETTERS.has(calleeName(n, ctx.source) ?? "");
|
|
19
|
+
if (isDollar || isGetterCall) {
|
|
20
|
+
out.push({
|
|
21
|
+
node: n,
|
|
22
|
+
message: `Node lookup inside ${name}() runs every frame.`,
|
|
23
|
+
suggestion: "Cache the node once in an @onready var and reference that instead.",
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=get-node-in-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-node-in-process.js","sourceRoot":"","sources":["../../../src/lint/rules/get-node-in-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAG7E,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAC9D,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAE/D,uEAAuE;AACvE,MAAM,CAAC,MAAM,gBAAgB,GAAS;IACpC,EAAE,EAAE,qBAAqB;IACzB,WAAW,EAAE,kFAAkF;IAC/F,eAAe,EAAE,SAAS;IAC1B,KAAK,CAAC,GAAG;QACP,MAAM,GAAG,GAAG,EAAE,CAAC;QACf,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,qBAAqB,CAAC,EAAE,CAAC;YAC1D,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YAC/C,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YAC9C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzB,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC;gBACvC,MAAM,YAAY,GAChB,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC;oBAClD,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBACpD,IAAI,QAAQ,IAAI,YAAY,EAAE,CAAC;oBAC7B,GAAG,CAAC,IAAI,CAAC;wBACP,IAAI,EAAE,CAAC;wBACP,OAAO,EAAE,sBAAsB,IAAI,sBAAsB;wBACzD,UAAU,EAAE,oEAAoE;qBACjF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAC"}
|