irongit

A simpler way to view django projects

DjangoTree/src/project.ts
127 lines3.3 KBJavaScript
1import * as childProcess from "child_process";
2import { getDjangoProjectDir } from "./context";
3import * as fs from "fs";
4import { promisify } from "util";
5import regex from "./regex";
6import path from "path";
7import * as vscode from "vscode";
8
9const readFile = promisify(fs.readFile);
10
11export type Field = {
12 name: string;
13 type: string;
14 index: number;
15 raw: string;
16};
17
18export type Model = {
19 name: string;
20 index: number;
21 filename: string;
22 fields: Map<string, Field>;
23};
24
25export class App {
26 name: string;
27 module: string;
28 path: string;
29 models: Map<string, Model>;
30
31 constructor(module: string) {
32 this.module = module;
33 this.models = new Map();
34 let name = module.split(".").pop();
35 const pythonPath =
36 vscode.workspace.getConfiguration("python").get("pythonPath") ||
37 "python3";
38 if (name) {
39 this.name = name;
40 } else {
41 throw new Error("Invalid module name");
42 }
43 const pythonScript = `
44import importlib.util
45import os
46module = '${this.module}'
47spec = importlib.util.find_spec(module)
48if spec is not None and spec.origin is not None:
49 print(os.path.dirname(spec.origin))
50 `;
51 try {
52 const result = childProcess
53 .execSync(`${pythonPath} -c "${pythonScript}" 2>/dev/null`)
54 .toString();
55 this.path = result.trim();
56 } catch (error) {
57 throw new Error(`Error getting path for app ${this.name}`);
58 }
59 try {
60 const data = fs.readFileSync(`${this.path}/models.py`, "utf-8");
61 const models = data.matchAll(regex.model);
62 for (const modelMatch of models) {
63 const model: Model = {
64 name: modelMatch[1],
65 index: modelMatch.index || 0,
66 filename: `${this.path}/models.py`,
67 fields: new Map(),
68 };
69 const fields = modelMatch[2].matchAll(regex.modelField);
70 for (const fieldMatch of fields) {
71 const field: Field = {
72 name: fieldMatch[1],
73 type: fieldMatch[2],
74 index: fieldMatch.index || 0,
75 raw: fieldMatch[0],
76 };
77 model.fields.set(field.name, field);
78 }
79 this.models.set(model.name, model);
80 }
81 } catch (err) {
82 // throw new Error(`Error reading models.py for app ${this.name}`);
83 }
84 }
85}
86
87export default class DjangoProject {
88 projectDir: string;
89 name?: string;
90 apps: Map<string, App>;
91
92 constructor(rootDir: string) {
93 this.projectDir = rootDir;
94 this.apps = new Map();
95 }
96
97 async read() {
98 const originalDir = process.cwd();
99 process.chdir(this.projectDir);
100 try {
101 await this.parseManagePy();
102 await this.parseSettings();
103 } finally {
104 process.chdir(originalDir);
105 }
106 }
107
108 async parseManagePy() {
109 const managePyPath = `${this.projectDir}/manage.py`;
110 const text = await readFile(managePyPath, "utf-8");
111 const match = text.match(regex.projectName);
112 this.name = match ? match[1] : undefined;
113 }
114
115 async parseSettings() {
116 const settingsPath = `${this.projectDir}/${this.name}/settings.py`;
117 const text = await readFile(settingsPath, "utf-8");
118 const appContainerMatch = text.match(regex.appContainer);
119 const appContainer = appContainerMatch ? appContainerMatch[1] : "";
120 const apps = appContainer.matchAll(regex.module);
121 for (const app of apps ? apps : []) {
122 try {
123 this.apps.set(app[1], new App(app[1]));
124 } catch {}
125 }
126 }
127}