@514labs/moose-lsp 1.0.2 → 1.1.110051

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.
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var import_node_assert = __toESM(require("node:assert"));
25
+ var fs = __toESM(require("node:fs/promises"));
26
+ var os = __toESM(require("node:os"));
27
+ var path = __toESM(require("node:path"));
28
+ var import_node_test = require("node:test");
29
+ var import_pythonService = require("./pythonService");
30
+ (0, import_node_test.test)("Python Service Tests", async (t) => {
31
+ await t.test("initializes and finds Python files", async () => {
32
+ const tmpDir = await fs.mkdtemp(
33
+ path.join(os.tmpdir(), "python-service-test-")
34
+ );
35
+ try {
36
+ await fs.writeFile(
37
+ path.join(tmpDir, "main.py"),
38
+ `from moose_lib import sql
39
+ query = sql("SELECT * FROM users")
40
+ `
41
+ );
42
+ const service = (0, import_pythonService.createPythonService)();
43
+ await service.initialize(tmpDir);
44
+ import_node_assert.default.strictEqual(service.isHealthy(), true);
45
+ import_node_assert.default.strictEqual(service.getError(), null);
46
+ const files = service.getFiles();
47
+ import_node_assert.default.strictEqual(files.length, 1);
48
+ import_node_assert.default.ok(files[0].path.endsWith("main.py"));
49
+ } finally {
50
+ await fs.rm(tmpDir, { recursive: true, force: true });
51
+ }
52
+ });
53
+ await t.test("finds Python files in subdirectories", async () => {
54
+ const tmpDir = await fs.mkdtemp(
55
+ path.join(os.tmpdir(), "python-service-test-")
56
+ );
57
+ try {
58
+ await fs.mkdir(path.join(tmpDir, "queries"), { recursive: true });
59
+ await fs.writeFile(path.join(tmpDir, "main.py"), 'print("Hello")');
60
+ await fs.writeFile(
61
+ path.join(tmpDir, "queries", "users.py"),
62
+ `from moose_lib import sql
63
+ query = sql("SELECT * FROM users")
64
+ `
65
+ );
66
+ const service = (0, import_pythonService.createPythonService)();
67
+ await service.initialize(tmpDir);
68
+ const files = service.getFiles();
69
+ import_node_assert.default.strictEqual(files.length, 2);
70
+ } finally {
71
+ await fs.rm(tmpDir, { recursive: true, force: true });
72
+ }
73
+ });
74
+ await t.test("ignores venv and __pycache__ directories", async () => {
75
+ const tmpDir = await fs.mkdtemp(
76
+ path.join(os.tmpdir(), "python-service-test-")
77
+ );
78
+ try {
79
+ await fs.mkdir(path.join(tmpDir, "venv", "lib"), { recursive: true });
80
+ await fs.mkdir(path.join(tmpDir, "__pycache__"), { recursive: true });
81
+ await fs.writeFile(path.join(tmpDir, "main.py"), 'print("Hello")');
82
+ await fs.writeFile(
83
+ path.join(tmpDir, "venv", "lib", "something.py"),
84
+ 'print("In venv")'
85
+ );
86
+ await fs.writeFile(
87
+ path.join(tmpDir, "__pycache__", "cached.py"),
88
+ 'print("Cached")'
89
+ );
90
+ const service = (0, import_pythonService.createPythonService)();
91
+ await service.initialize(tmpDir);
92
+ const files = service.getFiles();
93
+ import_node_assert.default.strictEqual(files.length, 1);
94
+ import_node_assert.default.ok(files[0].path.endsWith("main.py"));
95
+ } finally {
96
+ await fs.rm(tmpDir, { recursive: true, force: true });
97
+ }
98
+ });
99
+ await t.test("updateFile updates cached content", async () => {
100
+ const tmpDir = await fs.mkdtemp(
101
+ path.join(os.tmpdir(), "python-service-test-")
102
+ );
103
+ try {
104
+ const filePath = path.join(tmpDir, "main.py");
105
+ await fs.writeFile(filePath, 'print("Hello")');
106
+ const service = (0, import_pythonService.createPythonService)();
107
+ await service.initialize(tmpDir);
108
+ service.updateFile(
109
+ filePath,
110
+ `from moose_lib import sql
111
+ query = sql("SELECT * FROM users")
112
+ `
113
+ );
114
+ const file = service.getFile(filePath);
115
+ import_node_assert.default.ok(file);
116
+ import_node_assert.default.ok(file.content.includes('sql("SELECT * FROM users")'));
117
+ } finally {
118
+ await fs.rm(tmpDir, { recursive: true, force: true });
119
+ }
120
+ });
121
+ await t.test(
122
+ "extractSqlLocations extracts SQL from single file",
123
+ async () => {
124
+ const tmpDir = await fs.mkdtemp(
125
+ path.join(os.tmpdir(), "python-service-test-")
126
+ );
127
+ try {
128
+ const filePath = path.join(tmpDir, "main.py");
129
+ await fs.writeFile(
130
+ filePath,
131
+ `from moose_lib import sql
132
+ query = sql("SELECT * FROM users")
133
+ `
134
+ );
135
+ const service = (0, import_pythonService.createPythonService)();
136
+ await service.initialize(tmpDir);
137
+ const locations = service.extractSqlLocations(filePath);
138
+ import_node_assert.default.strictEqual(locations.length, 1);
139
+ import_node_assert.default.strictEqual(locations[0].templateText, "SELECT * FROM users");
140
+ } finally {
141
+ await fs.rm(tmpDir, { recursive: true, force: true });
142
+ }
143
+ }
144
+ );
145
+ await t.test(
146
+ "extractAllSqlLocations extracts SQL from all files",
147
+ async () => {
148
+ const tmpDir = await fs.mkdtemp(
149
+ path.join(os.tmpdir(), "python-service-test-")
150
+ );
151
+ try {
152
+ await fs.writeFile(
153
+ path.join(tmpDir, "users.py"),
154
+ `from moose_lib import sql
155
+ query = sql("SELECT * FROM users")
156
+ `
157
+ );
158
+ await fs.writeFile(
159
+ path.join(tmpDir, "orders.py"),
160
+ `from moose_lib import sql
161
+ query = sql("SELECT * FROM orders")
162
+ `
163
+ );
164
+ const service = (0, import_pythonService.createPythonService)();
165
+ await service.initialize(tmpDir);
166
+ const locations = service.extractAllSqlLocations();
167
+ import_node_assert.default.strictEqual(locations.length, 2);
168
+ const templates = locations.map((l) => l.templateText);
169
+ import_node_assert.default.ok(templates.includes("SELECT * FROM users"));
170
+ import_node_assert.default.ok(templates.includes("SELECT * FROM orders"));
171
+ } finally {
172
+ await fs.rm(tmpDir, { recursive: true, force: true });
173
+ }
174
+ }
175
+ );
176
+ await t.test("getProjectRoot returns initialized root", async () => {
177
+ const tmpDir = await fs.mkdtemp(
178
+ path.join(os.tmpdir(), "python-service-test-")
179
+ );
180
+ try {
181
+ await fs.writeFile(path.join(tmpDir, "main.py"), 'print("Hello")');
182
+ const service = (0, import_pythonService.createPythonService)();
183
+ await service.initialize(tmpDir);
184
+ import_node_assert.default.strictEqual(service.getProjectRoot(), tmpDir);
185
+ } finally {
186
+ await fs.rm(tmpDir, { recursive: true, force: true });
187
+ }
188
+ });
189
+ await t.test("isHealthy returns false before initialization", () => {
190
+ const service = (0, import_pythonService.createPythonService)();
191
+ import_node_assert.default.strictEqual(service.isHealthy(), false);
192
+ });
193
+ await t.test("returns empty for non-existent file", async () => {
194
+ const tmpDir = await fs.mkdtemp(
195
+ path.join(os.tmpdir(), "python-service-test-")
196
+ );
197
+ try {
198
+ await fs.writeFile(path.join(tmpDir, "main.py"), 'print("Hello")');
199
+ const service = (0, import_pythonService.createPythonService)();
200
+ await service.initialize(tmpDir);
201
+ const locations = service.extractSqlLocations("/non/existent/file.py");
202
+ import_node_assert.default.strictEqual(locations.length, 0);
203
+ } finally {
204
+ await fs.rm(tmpDir, { recursive: true, force: true });
205
+ }
206
+ });
207
+ });
208
+ //# sourceMappingURL=pythonService.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pythonService.test.ts"],"sourcesContent":["import assert from 'node:assert';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { test } from 'node:test';\nimport { createPythonService } from './pythonService';\n\ntest('Python Service Tests', async (t) => {\n await t.test('initializes and finds Python files', async () => {\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'python-service-test-'),\n );\n\n try {\n // Create Python files\n await fs.writeFile(\n path.join(tmpDir, 'main.py'),\n `from moose_lib import sql\nquery = sql(\"SELECT * FROM users\")\n`,\n );\n\n const service = createPythonService();\n await service.initialize(tmpDir);\n\n assert.strictEqual(service.isHealthy(), true);\n assert.strictEqual(service.getError(), null);\n\n const files = service.getFiles();\n assert.strictEqual(files.length, 1);\n assert.ok(files[0].path.endsWith('main.py'));\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n });\n\n await t.test('finds Python files in subdirectories', async () => {\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'python-service-test-'),\n );\n\n try {\n // Create subdirectory structure\n await fs.mkdir(path.join(tmpDir, 'queries'), { recursive: true });\n\n await fs.writeFile(path.join(tmpDir, 'main.py'), 'print(\"Hello\")');\n\n await fs.writeFile(\n path.join(tmpDir, 'queries', 'users.py'),\n `from moose_lib import sql\nquery = sql(\"SELECT * FROM users\")\n`,\n );\n\n const service = createPythonService();\n await service.initialize(tmpDir);\n\n const files = service.getFiles();\n assert.strictEqual(files.length, 2);\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n });\n\n await t.test('ignores venv and __pycache__ directories', async () => {\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'python-service-test-'),\n );\n\n try {\n // Create directories that should be ignored\n await fs.mkdir(path.join(tmpDir, 'venv', 'lib'), { recursive: true });\n await fs.mkdir(path.join(tmpDir, '__pycache__'), { recursive: true });\n\n await fs.writeFile(path.join(tmpDir, 'main.py'), 'print(\"Hello\")');\n\n await fs.writeFile(\n path.join(tmpDir, 'venv', 'lib', 'something.py'),\n 'print(\"In venv\")',\n );\n\n await fs.writeFile(\n path.join(tmpDir, '__pycache__', 'cached.py'),\n 'print(\"Cached\")',\n );\n\n const service = createPythonService();\n await service.initialize(tmpDir);\n\n const files = service.getFiles();\n // Should only find main.py\n assert.strictEqual(files.length, 1);\n assert.ok(files[0].path.endsWith('main.py'));\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n });\n\n await t.test('updateFile updates cached content', async () => {\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'python-service-test-'),\n );\n\n try {\n const filePath = path.join(tmpDir, 'main.py');\n await fs.writeFile(filePath, 'print(\"Hello\")');\n\n const service = createPythonService();\n await service.initialize(tmpDir);\n\n // Update with new content\n service.updateFile(\n filePath,\n `from moose_lib import sql\nquery = sql(\"SELECT * FROM users\")\n`,\n );\n\n const file = service.getFile(filePath);\n assert.ok(file);\n assert.ok(file.content.includes('sql(\"SELECT * FROM users\")'));\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n });\n\n await t.test(\n 'extractSqlLocations extracts SQL from single file',\n async () => {\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'python-service-test-'),\n );\n\n try {\n const filePath = path.join(tmpDir, 'main.py');\n await fs.writeFile(\n filePath,\n `from moose_lib import sql\nquery = sql(\"SELECT * FROM users\")\n`,\n );\n\n const service = createPythonService();\n await service.initialize(tmpDir);\n\n const locations = service.extractSqlLocations(filePath);\n assert.strictEqual(locations.length, 1);\n assert.strictEqual(locations[0].templateText, 'SELECT * FROM users');\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n },\n );\n\n await t.test(\n 'extractAllSqlLocations extracts SQL from all files',\n async () => {\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'python-service-test-'),\n );\n\n try {\n await fs.writeFile(\n path.join(tmpDir, 'users.py'),\n `from moose_lib import sql\nquery = sql(\"SELECT * FROM users\")\n`,\n );\n\n await fs.writeFile(\n path.join(tmpDir, 'orders.py'),\n `from moose_lib import sql\nquery = sql(\"SELECT * FROM orders\")\n`,\n );\n\n const service = createPythonService();\n await service.initialize(tmpDir);\n\n const locations = service.extractAllSqlLocations();\n assert.strictEqual(locations.length, 2);\n\n const templates = locations.map((l) => l.templateText);\n assert.ok(templates.includes('SELECT * FROM users'));\n assert.ok(templates.includes('SELECT * FROM orders'));\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n },\n );\n\n await t.test('getProjectRoot returns initialized root', async () => {\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'python-service-test-'),\n );\n\n try {\n await fs.writeFile(path.join(tmpDir, 'main.py'), 'print(\"Hello\")');\n\n const service = createPythonService();\n await service.initialize(tmpDir);\n\n assert.strictEqual(service.getProjectRoot(), tmpDir);\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n });\n\n await t.test('isHealthy returns false before initialization', () => {\n const service = createPythonService();\n assert.strictEqual(service.isHealthy(), false);\n });\n\n await t.test('returns empty for non-existent file', async () => {\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'python-service-test-'),\n );\n\n try {\n await fs.writeFile(path.join(tmpDir, 'main.py'), 'print(\"Hello\")');\n\n const service = createPythonService();\n await service.initialize(tmpDir);\n\n const locations = service.extractSqlLocations('/non/existent/file.py');\n assert.strictEqual(locations.length, 0);\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n });\n});\n"],"mappings":"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","names":["assert"]}
@@ -0,0 +1,25 @@
1
+ import Parser from 'tree-sitter';
2
+ import { SqlLocation } from './sqlLocations.js';
3
+
4
+ /**
5
+ * Extract all SQL locations from a Python source file.
6
+ * Finds:
7
+ * 1. sql() function calls with string arguments
8
+ * 2. F-strings with :col format specifiers (moose-lib pattern)
9
+ */
10
+ declare function extractPythonSqlLocations(sourceCode: string, filePath: string): SqlLocation[];
11
+ /**
12
+ * Extract SQL locations from all provided Python files.
13
+ * Used for initial scan of the project.
14
+ */
15
+ declare function extractAllPythonSqlLocations(files: Array<{
16
+ path: string;
17
+ content: string;
18
+ }>): SqlLocation[];
19
+ /**
20
+ * Get the tree-sitter parser instance.
21
+ * Useful for tests or advanced usage.
22
+ */
23
+ declare function getParser(): Parser;
24
+
25
+ export { extractAllPythonSqlLocations, extractPythonSqlLocations, getParser };
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var pythonSqlExtractor_exports = {};
30
+ __export(pythonSqlExtractor_exports, {
31
+ extractAllPythonSqlLocations: () => extractAllPythonSqlLocations,
32
+ extractPythonSqlLocations: () => extractPythonSqlLocations,
33
+ getParser: () => getParser
34
+ });
35
+ module.exports = __toCommonJS(pythonSqlExtractor_exports);
36
+ var import_tree_sitter = __toESM(require("tree-sitter"));
37
+ var import_tree_sitter_python = __toESM(require("tree-sitter-python"));
38
+ const parser = new import_tree_sitter.default();
39
+ parser.setLanguage(import_tree_sitter_python.default);
40
+ function fileImportsMooseLib(rootNode) {
41
+ for (const child of rootNode.children) {
42
+ if (child.type === "import_statement") {
43
+ const moduleName = child.childForFieldName("name");
44
+ if (moduleName?.text === "moose_lib") {
45
+ return true;
46
+ }
47
+ if (moduleName?.type === "dotted_name" && moduleName.text.startsWith("moose_lib")) {
48
+ return true;
49
+ }
50
+ }
51
+ if (child.type === "import_from_statement") {
52
+ const moduleName = child.childForFieldName("module_name");
53
+ if (moduleName?.text === "moose_lib" || moduleName?.text?.startsWith("moose_lib.")) {
54
+ return true;
55
+ }
56
+ }
57
+ }
58
+ return false;
59
+ }
60
+ function isSqlFunctionCall(node) {
61
+ if (node.type !== "call") return false;
62
+ const functionNode = node.childForFieldName("function");
63
+ if (!functionNode) return false;
64
+ if (functionNode.type === "identifier" && functionNode.text === "sql") {
65
+ return true;
66
+ }
67
+ if (functionNode.type === "attribute") {
68
+ const attribute = functionNode.childForFieldName("attribute");
69
+ if (attribute?.text === "sql") {
70
+ return true;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+ function extractSqlFromCall(callNode) {
76
+ const arguments_ = callNode.childForFieldName("arguments");
77
+ if (!arguments_) return null;
78
+ for (const child of arguments_.children) {
79
+ if (child.type === "string" || child.type === "concatenated_string" || child.type === "formatted_string") {
80
+ const result = extractStringContent(child);
81
+ if (result) {
82
+ return {
83
+ text: result,
84
+ startPosition: child.startPosition,
85
+ endPosition: child.endPosition
86
+ };
87
+ }
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+ function extractStringContent(node) {
93
+ const text = node.text;
94
+ const isFString = /^f['"]/.test(text);
95
+ if (node.type === "string") {
96
+ if (isFString) {
97
+ return extractFStringContent(text);
98
+ }
99
+ return extractQuotedStringContent(text);
100
+ }
101
+ if (node.type === "formatted_string") {
102
+ return extractFStringContent(text);
103
+ }
104
+ if (node.type === "concatenated_string") {
105
+ let result = "";
106
+ for (const child of node.children) {
107
+ if (child.type === "string" || child.type === "formatted_string") {
108
+ const content = extractStringContent(child);
109
+ if (content !== null) {
110
+ result += content;
111
+ }
112
+ }
113
+ }
114
+ return result || null;
115
+ }
116
+ return null;
117
+ }
118
+ function extractQuotedStringContent(text) {
119
+ if (text.startsWith('"""') || text.startsWith("'''") || text.startsWith('f"""') || text.startsWith("f'''") || text.startsWith('r"""') || text.startsWith("r'''")) {
120
+ const prefixLen = text.startsWith("f") || text.startsWith("r") ? 4 : 3;
121
+ return text.slice(prefixLen, -3);
122
+ }
123
+ if (text.startsWith('"') || text.startsWith("'") || text.startsWith('f"') || text.startsWith("f'") || text.startsWith('r"') || text.startsWith("r'")) {
124
+ const prefixLen = text.startsWith("f") || text.startsWith("r") ? 2 : 1;
125
+ return text.slice(prefixLen, -1);
126
+ }
127
+ return text;
128
+ }
129
+ function extractFStringContent(text) {
130
+ let content;
131
+ if (text.startsWith('f"""') || text.startsWith("f'''")) {
132
+ content = text.slice(4, -3);
133
+ } else if (text.startsWith('f"') || text.startsWith("f'")) {
134
+ content = text.slice(2, -1);
135
+ } else {
136
+ content = text;
137
+ }
138
+ let result = "";
139
+ let i = 0;
140
+ while (i < content.length) {
141
+ if (content[i] === "{") {
142
+ if (content[i + 1] === "{") {
143
+ result += "{";
144
+ i += 2;
145
+ } else {
146
+ let depth = 1;
147
+ let j = i + 1;
148
+ while (j < content.length && depth > 0) {
149
+ if (content[j] === "{") depth++;
150
+ else if (content[j] === "}") depth--;
151
+ j++;
152
+ }
153
+ result += "${...}";
154
+ i = j;
155
+ }
156
+ } else if (content[i] === "}" && content[i + 1] === "}") {
157
+ result += "}";
158
+ i += 2;
159
+ } else {
160
+ result += content[i];
161
+ i++;
162
+ }
163
+ }
164
+ return result;
165
+ }
166
+ function looksLikeSql(text) {
167
+ const sqlKeywords = [
168
+ "SELECT",
169
+ "INSERT",
170
+ "UPDATE",
171
+ "DELETE",
172
+ "CREATE",
173
+ "DROP",
174
+ "ALTER",
175
+ "FROM",
176
+ "WHERE",
177
+ "JOIN",
178
+ "TABLE",
179
+ "INDEX",
180
+ "VIEW"
181
+ ];
182
+ const upperText = text.toUpperCase();
183
+ return sqlKeywords.some((keyword) => upperText.includes(keyword));
184
+ }
185
+ function hasColFormatSpecifier(node) {
186
+ const text = node.text;
187
+ return /:col\s*\}/.test(text);
188
+ }
189
+ function createSqlLocation(filePath, text, startPosition, endPosition) {
190
+ return {
191
+ id: `${filePath}:${startPosition.row + 1}:${startPosition.column + 1}`,
192
+ file: filePath,
193
+ line: startPosition.row + 1,
194
+ // 1-based
195
+ column: startPosition.column + 1,
196
+ // 1-based
197
+ endLine: endPosition.row + 1,
198
+ endColumn: endPosition.column + 1,
199
+ templateText: text
200
+ };
201
+ }
202
+ function extractPythonSqlLocations(sourceCode, filePath) {
203
+ const locations = [];
204
+ const tree = parser.parse(sourceCode);
205
+ const hasMooseImport = fileImportsMooseLib(tree.rootNode);
206
+ function visit(node) {
207
+ if (hasMooseImport && isSqlFunctionCall(node)) {
208
+ const sqlContent = extractSqlFromCall(node);
209
+ if (sqlContent) {
210
+ locations.push(
211
+ createSqlLocation(
212
+ filePath,
213
+ sqlContent.text,
214
+ sqlContent.startPosition,
215
+ sqlContent.endPosition
216
+ )
217
+ );
218
+ }
219
+ }
220
+ const isFString = node.type === "formatted_string" || node.type === "string" && /^f['"]/.test(node.text);
221
+ if (isFString && hasColFormatSpecifier(node)) {
222
+ const content = extractFStringContent(node.text);
223
+ if (content && looksLikeSql(content)) {
224
+ locations.push(
225
+ createSqlLocation(
226
+ filePath,
227
+ content,
228
+ node.startPosition,
229
+ node.endPosition
230
+ )
231
+ );
232
+ }
233
+ }
234
+ for (const child of node.children) {
235
+ visit(child);
236
+ }
237
+ }
238
+ visit(tree.rootNode);
239
+ return locations;
240
+ }
241
+ function extractAllPythonSqlLocations(files) {
242
+ const allLocations = [];
243
+ for (const file of files) {
244
+ const locations = extractPythonSqlLocations(file.content, file.path);
245
+ allLocations.push(...locations);
246
+ }
247
+ return allLocations;
248
+ }
249
+ function getParser() {
250
+ return parser;
251
+ }
252
+ // Annotate the CommonJS export names for ESM import in node:
253
+ 0 && (module.exports = {
254
+ extractAllPythonSqlLocations,
255
+ extractPythonSqlLocations,
256
+ getParser
257
+ });
258
+ //# sourceMappingURL=pythonSqlExtractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pythonSqlExtractor.ts"],"sourcesContent":["import Parser from 'tree-sitter';\nimport Python from 'tree-sitter-python';\nimport type { SqlLocation } from './sqlLocations';\n\n// Initialize tree-sitter parser with Python grammar\nconst parser = new Parser();\n// Cast to any to work around type compatibility issues between tree-sitter and tree-sitter-python\nparser.setLanguage(Python as unknown as Parser.Language);\n\n/**\n * Check if the file imports from moose_lib.\n * This is used to filter out sql() calls that aren't from moose-lib.\n */\nfunction fileImportsMooseLib(rootNode: Parser.SyntaxNode): boolean {\n for (const child of rootNode.children) {\n // Handle: import moose_lib\n if (child.type === 'import_statement') {\n const moduleName = child.childForFieldName('name');\n if (moduleName?.text === 'moose_lib') {\n return true;\n }\n // Handle: import moose_lib.sql\n if (\n moduleName?.type === 'dotted_name' &&\n moduleName.text.startsWith('moose_lib')\n ) {\n return true;\n }\n }\n\n // Handle: from moose_lib import sql\n // Handle: from moose_lib.sql import sql\n if (child.type === 'import_from_statement') {\n const moduleName = child.childForFieldName('module_name');\n if (\n moduleName?.text === 'moose_lib' ||\n moduleName?.text?.startsWith('moose_lib.')\n ) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Check if a function call is a `sql()` call from moose-lib.\n * We look for calls where the function name is 'sql'.\n */\nfunction isSqlFunctionCall(node: Parser.SyntaxNode): boolean {\n if (node.type !== 'call') return false;\n\n const functionNode = node.childForFieldName('function');\n if (!functionNode) return false;\n\n // Direct call: sql(\"...\")\n if (functionNode.type === 'identifier' && functionNode.text === 'sql') {\n return true;\n }\n\n // Attribute access: moose_lib.sql(\"...\") or similar\n if (functionNode.type === 'attribute') {\n const attribute = functionNode.childForFieldName('attribute');\n if (attribute?.text === 'sql') {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extract the SQL string from a sql() function call arguments.\n * Handles both regular strings and f-strings.\n */\nfunction extractSqlFromCall(callNode: Parser.SyntaxNode): {\n text: string;\n startPosition: Parser.Point;\n endPosition: Parser.Point;\n} | null {\n const arguments_ = callNode.childForFieldName('arguments');\n if (!arguments_) return null;\n\n // Get the first argument (the SQL string)\n for (const child of arguments_.children) {\n if (\n child.type === 'string' ||\n child.type === 'concatenated_string' ||\n child.type === 'formatted_string'\n ) {\n const result = extractStringContent(child);\n if (result) {\n return {\n text: result,\n startPosition: child.startPosition,\n endPosition: child.endPosition,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract text content from a string node (regular string or f-string).\n * Converts f-string interpolations to ${...} placeholders.\n */\nfunction extractStringContent(node: Parser.SyntaxNode): string | null {\n const text = node.text;\n const isFString = /^f['\"]/.test(text);\n\n if (node.type === 'string') {\n if (isFString) {\n // F-string: f\"SELECT {col} FROM users\"\n return extractFStringContent(text);\n }\n // Regular string: \"SELECT * FROM users\"\n // Remove quotes and extract content\n return extractQuotedStringContent(text);\n }\n\n if (node.type === 'formatted_string') {\n // F-string: f\"SELECT {col} FROM users\"\n return extractFStringContent(text);\n }\n\n if (node.type === 'concatenated_string') {\n // Handle concatenated strings: \"SELECT \" \"* FROM users\"\n let result = '';\n for (const child of node.children) {\n if (child.type === 'string' || child.type === 'formatted_string') {\n const content = extractStringContent(child);\n if (content !== null) {\n result += content;\n }\n }\n }\n return result || null;\n }\n\n return null;\n}\n\n/**\n * Extract content from a quoted string, removing the quotes.\n */\nfunction extractQuotedStringContent(text: string): string | null {\n // Handle triple-quoted strings\n if (\n text.startsWith('\"\"\"') ||\n text.startsWith(\"'''\") ||\n text.startsWith('f\"\"\"') ||\n text.startsWith(\"f'''\") ||\n text.startsWith('r\"\"\"') ||\n text.startsWith(\"r'''\")\n ) {\n const prefixLen = text.startsWith('f') || text.startsWith('r') ? 4 : 3;\n return text.slice(prefixLen, -3);\n }\n\n // Handle single/double quoted strings\n if (\n text.startsWith('\"') ||\n text.startsWith(\"'\") ||\n text.startsWith('f\"') ||\n text.startsWith(\"f'\") ||\n text.startsWith('r\"') ||\n text.startsWith(\"r'\")\n ) {\n const prefixLen = text.startsWith('f') || text.startsWith('r') ? 2 : 1;\n return text.slice(prefixLen, -1);\n }\n\n return text;\n}\n\n/**\n * Extract content from an f-string text, converting interpolations to ${...}.\n * Works with raw text (e.g., f\"SELECT {col} FROM users\").\n */\nfunction extractFStringContent(text: string): string {\n // Remove the f-string prefix (f\", f', f\"\"\", f''')\n let content: string;\n if (text.startsWith('f\"\"\"') || text.startsWith(\"f'''\")) {\n content = text.slice(4, -3);\n } else if (text.startsWith('f\"') || text.startsWith(\"f'\")) {\n content = text.slice(2, -1);\n } else {\n content = text;\n }\n\n // Replace balanced {expr} with ${...} using depth tracking\n // This handles nested braces like {func({1, 2})} correctly\n let result = '';\n let i = 0;\n while (i < content.length) {\n if (content[i] === '{') {\n if (content[i + 1] === '{') {\n // Escaped {{ → literal {\n result += '{';\n i += 2;\n } else {\n // Find matching closing brace with depth tracking\n let depth = 1;\n let j = i + 1;\n while (j < content.length && depth > 0) {\n if (content[j] === '{') depth++;\n else if (content[j] === '}') depth--;\n j++;\n }\n result += '${...}';\n i = j;\n }\n } else if (content[i] === '}' && content[i + 1] === '}') {\n // Escaped }} → literal }\n result += '}';\n i += 2;\n } else {\n result += content[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Check if a string node contains SQL-like content.\n * Used to identify f-strings that might contain SQL even outside sql() calls.\n */\nfunction looksLikeSql(text: string): boolean {\n const sqlKeywords = [\n 'SELECT',\n 'INSERT',\n 'UPDATE',\n 'DELETE',\n 'CREATE',\n 'DROP',\n 'ALTER',\n 'FROM',\n 'WHERE',\n 'JOIN',\n 'TABLE',\n 'INDEX',\n 'VIEW',\n ];\n\n const upperText = text.toUpperCase();\n return sqlKeywords.some((keyword) => upperText.includes(keyword));\n}\n\n/**\n * Check if a formatted string contains :col format specifier.\n * This is a moose-lib specific pattern for SQL columns.\n */\nfunction hasColFormatSpecifier(node: Parser.SyntaxNode): boolean {\n const text = node.text;\n // Look for patterns like {var:col} or {Model.field:col}\n return /:col\\s*\\}/.test(text);\n}\n\n/**\n * Extract SQL location from a node.\n */\nfunction createSqlLocation(\n filePath: string,\n text: string,\n startPosition: Parser.Point,\n endPosition: Parser.Point,\n): SqlLocation {\n return {\n id: `${filePath}:${startPosition.row + 1}:${startPosition.column + 1}`,\n file: filePath,\n line: startPosition.row + 1, // 1-based\n column: startPosition.column + 1, // 1-based\n endLine: endPosition.row + 1,\n endColumn: endPosition.column + 1,\n templateText: text,\n };\n}\n\n/**\n * Extract all SQL locations from a Python source file.\n * Finds:\n * 1. sql() function calls with string arguments\n * 2. F-strings with :col format specifiers (moose-lib pattern)\n */\nexport function extractPythonSqlLocations(\n sourceCode: string,\n filePath: string,\n): SqlLocation[] {\n const locations: SqlLocation[] = [];\n\n const tree = parser.parse(sourceCode);\n\n // Only process sql() calls if the file imports from moose_lib\n const hasMooseImport = fileImportsMooseLib(tree.rootNode);\n\n function visit(node: Parser.SyntaxNode): void {\n // Check for sql() function calls (only if file imports moose_lib)\n if (hasMooseImport && isSqlFunctionCall(node)) {\n const sqlContent = extractSqlFromCall(node);\n if (sqlContent) {\n locations.push(\n createSqlLocation(\n filePath,\n sqlContent.text,\n sqlContent.startPosition,\n sqlContent.endPosition,\n ),\n );\n }\n }\n\n // Check for f-strings with :col format specifier (moose-lib SQL pattern)\n // tree-sitter-python may use 'string' or 'formatted_string' for f-strings\n const isFString =\n node.type === 'formatted_string' ||\n (node.type === 'string' && /^f['\"]/.test(node.text));\n\n if (isFString && hasColFormatSpecifier(node)) {\n const content = extractFStringContent(node.text);\n if (content && looksLikeSql(content)) {\n locations.push(\n createSqlLocation(\n filePath,\n content,\n node.startPosition,\n node.endPosition,\n ),\n );\n }\n }\n\n // Recursively visit children\n for (const child of node.children) {\n visit(child);\n }\n }\n\n visit(tree.rootNode);\n return locations;\n}\n\n/**\n * Extract SQL locations from all provided Python files.\n * Used for initial scan of the project.\n */\nexport function extractAllPythonSqlLocations(\n files: Array<{ path: string; content: string }>,\n): SqlLocation[] {\n const allLocations: SqlLocation[] = [];\n\n for (const file of files) {\n const locations = extractPythonSqlLocations(file.content, file.path);\n allLocations.push(...locations);\n }\n\n return allLocations;\n}\n\n/**\n * Get the tree-sitter parser instance.\n * Useful for tests or advanced usage.\n */\nexport function getParser(): Parser {\n return parser;\n}\n"],"mappings":"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","names":["Parser","Python"]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }