Skip to content
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

Clean up loader #424

Merged
merged 5 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/scripts/parse-template-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
getProject
} from "../src/parser";

loadPackage("Buildings");
loadPackage("Buildings.Templates");

const { options, scheduleOptions } = getOptions();

Expand Down
12 changes: 4 additions & 8 deletions server/src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,12 @@ export { SystemTypeN as SystemType, Template } from "./template";

/**
*
* @param packagePath Absolute path to package
* @param packageName Full class name of package (e.g. "Library.Package.SubPackage")
*
* @returns Templates
*/
export function loadPackage(packagePath: string): templates.SystemTemplateN[] {
//
const parsedPath = path.parse(packagePath);
parser.setPathPrefix(parsedPath.dir);
parser.loadPackage(parsedPath.name);

export function loadPackage(packageName: string): templates.SystemTemplateN[] {
parser.loadPackage(packageName);
return templates.getTemplates().map((t) => t.getSystemTemplate());
}

Expand All @@ -37,4 +33,4 @@ export function getAllTemplates(): templates.Template[] {

export function getProject(): templates.Project {
return templates.getProject();
}
}
77 changes: 48 additions & 29 deletions server/src/parser/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,36 @@ import { typeStore } from "./parser";
import config from "../../src/config";

export const TEMPLATE_IDENTIFIER = "__ctrlFlow_template";
export const MODELICAPATH = [
export let MODELICA_JSON_PATH = [
`${config.MODELICA_DEPENDENCIES}/template-json/json/`,
];

function _toModelicaPath(filePath: string) {
// Used for testing: registers additional search paths for Modelica JSON files
export function prependToModelicaJsonPath(paths: string[]) {
MODELICA_JSON_PATH = [...paths, ...MODELICA_JSON_PATH];
}

export function getClassNameFromRelativePath(filePath: string) {
filePath = filePath.endsWith(".json") ? filePath.slice(0, -5) : filePath;
return filePath.replace(/\//g, ".");
}

/**
* Finds all entry points that contain the template identifier for a given package.
* - LIMITATION: This function requires that the package uses
* [Directory Hierarchy Mapping](https://specification.modelica.org/maint/3.6/packages.html#directory-hierarchy-mapping)
* @param packageName - The Modelica class name of the package to search for entry points
* @returns An array of objects containing the path and parsed JSON for each entry point found
*/
export function findPackageEntryPoints(
prefix: string,
reference: string,
packageName: string,
): { path: string; json: Object | undefined }[] {
const entryPoints: { path: string; json: Object | undefined }[] = [];
[prefix, ...MODELICAPATH].forEach((dir) => {
const dirPath = path.resolve(dir, reference);
MODELICA_JSON_PATH.forEach((dir) => {
// We need a top directory to look up for entry points
// so we can simply convert the class name to a relative path
// without adding any file extension.
const dirPath = path.resolve(dir, packageName.replace(/\./g, "/"));
if (fs.existsSync(dirPath)) {
const cmd = `grep -rl ${dirPath} -e "${TEMPLATE_IDENTIFIER}"`;
const response = execSync(cmd).toString();
Expand All @@ -32,10 +46,10 @@ export function findPackageEntryPoints(
.sort((a, b) => (a.includes("package.json") ? -1 : 1))
.map((p) => path.relative(dir, p))
.map((p) => {
const path = _toModelicaPath(p);
const path = getClassNameFromRelativePath(p);
return {
path: path,
json: loader(dir, path),
json: loader(path),
};
}),
);
Expand All @@ -46,33 +60,36 @@ export function findPackageEntryPoints(
}

/**
* Searched the provided directory for a given
* @param prefix directory to search
* @param filePath path to try and find
*
* @returns the found file path or null if not found
* Gets the path to a Modelica JSON file based on the full class name.
* - LIMITATION: This function requires that the library packages use
* [Directory Hierarchy Mapping](https://specification.modelica.org/maint/3.6/packages.html#directory-hierarchy-mapping)
* @param className - The full Modelica class name (e.g. "Library.Package.Class")
* @param dirPath - The directory path to search in
* @returns The file path if found, null otherwise
*/
function _findPath(prefix: string, reference: string): string | null {
let filePath = path.parse(reference.replace(/\./g, "/"));
function getPathFromClassName(
className: string,
dirPath: string,
): string | null {
let filePath = path.parse(className.replace(/\./g, "/"));

let jsonFile = path.resolve(prefix, filePath.dir, `${filePath.name}.json`);
let jsonFile = path.resolve(dirPath, filePath.dir, `${filePath.name}.json`);

while (!fs.existsSync(jsonFile) && filePath.name) {
// check if definition already exists
// TODO - construct this path correctly...
const curPath = path.relative(filePath.dir, filePath.name);
const modelicaPath = _toModelicaPath(curPath);
const modelicaPath = getClassNameFromRelativePath(curPath);
if (typeStore.has(modelicaPath)) {
break;
}
// package definitions break the typical modelica path to file mapping that
// is used. A typical modelica path to file path look like:
// 'Template.AirHandlerFans.VAVMultizone' -> 'Template/AirhandlerFans/VAVMultizone
// 'Template.AirHandlerFans.VAVMultizone' -> 'Template/AirhandlerFans/VAVMultizone.json'
// We need to support mapping like this as well:
// 'Template.AirHandlerFans -> Template/AirhandlerFans/package'
// 'package' files behave kind of like 'index.html' files
// 'Template.AirHandlerFans -> Template/AirhandlerFans/package.json'
jsonFile = path.resolve(
prefix,
dirPath,
filePath.dir,
filePath.name,
"package.json",
Expand All @@ -81,20 +98,22 @@ function _findPath(prefix: string, reference: string): string | null {
break;
}
filePath = path.parse(filePath.dir);
jsonFile = path.resolve(prefix, filePath.dir, `${filePath.name}.json`);
jsonFile = path.resolve(dirPath, filePath.dir, `${filePath.name}.json`);
}

return fs.existsSync(jsonFile) ? jsonFile : null;
}

// When given a path, loads types. returns null if not found
export function loader(prefix: string, reference: string): Object | undefined {
const modelicaDirs = [prefix, ...MODELICAPATH];

/**
* Loads a Modelica JSON file given the full class name.
* @param className The full Modelica class name to load (e.g. "Library.Package.Class")
* @returns The loaded JSON object or undefined if not found
*/
export function loader(className: string): Object | undefined {
// TODO: allow modelica paths
if (!reference.startsWith("Modelica")) {
for (const dir of modelicaDirs) {
const jsonFile = _findPath(dir, reference);
if (!className.startsWith("Modelica")) {
for (const dir of MODELICA_JSON_PATH) {
const jsonFile = getPathFromClassName(className, dir);
if (jsonFile && fs.existsSync(jsonFile)) {
return require(jsonFile);
}
Expand Down
24 changes: 11 additions & 13 deletions server/src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1141,28 +1141,26 @@ export class File {
}
}

let pathPrefix = "";
export function setPathPrefix(prefix: string) {
pathPrefix = prefix;
}

/**
* Extracts the given file into the type store
* @param filePath - The ***relative*** path to the file to load (e.g. "Buildings/Templates/File")
*/
export const getFile = (filePath: string) => {
const jsonData = loader(pathPrefix, filePath);
export const getFile = (className: string) => {
const jsonData = loader(className);
if (jsonData) {
return new File(jsonData, filePath);
return new File(jsonData, className);
} else {
// console.log(`Not found: ${filePath}`);
}
};

// Searches a package for templates, then loads the file
// creating template instances
export const loadPackage = (filePath: string) => {
const paths = findPackageEntryPoints(pathPrefix, filePath);

/**
* Searches a package for templates, then loads the file
* creating template instances.
* @param packageName - The full class name of the package to load (e.g. "Library.Package.SubPackage")
*/
export const loadPackage = (packageName: string) => {
const paths = findPackageEntryPoints(packageName);
paths?.map(({ json, path }) => new File(json, path));

// Attempt to load project settings from a pre-defined path
Expand Down
7 changes: 3 additions & 4 deletions server/tests/integration/parser/expression.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { createTestModelicaJson, fullTempDirPath } from "./utils";
import { ModifiersN, getTemplates } from "../../../src/parser/template";
import { loadPackage, getSystemTypes, Template } from "../../../src/parser/";
import { loadPackage, Template } from "../../../src/parser/";
import { initializeTestModelicaJson } from "./utils";
import * as parser from "../../../src/parser/parser";
const testModelicaFile = "TestPackage.Template.TestTemplate";
Expand All @@ -10,8 +9,8 @@ let template: Template | undefined;

describe("Expression", () => {
beforeAll(() => {
createTestModelicaJson();
loadPackage(`${fullTempDirPath}/TestPackage`);
initializeTestModelicaJson();
loadPackage('TestPackage');
const templates = getTemplates();
template = templates.find(
(t) => t.modelicaPath === templatePath,
Expand Down
8 changes: 4 additions & 4 deletions server/tests/integration/parser/loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ describe("Parser file loading", () => {
});

it("Discovers template files and project options", () => {
const packagePath = "TestPackage";
const projectOptionsPath = "Buildings.Templates.Data.AllSystems";
parser.loadPackage(packagePath);
const projectOptionElement = parser.typeStore.find(projectOptionsPath);
const packageName = "TestPackage";
const projectOptionsClassName = "Buildings.Templates.Data.AllSystems";
parser.loadPackage(packageName);
const projectOptionElement = parser.typeStore.find(projectOptionsClassName);
expect(projectOptionElement).toBeDefined();
});
});
6 changes: 3 additions & 3 deletions server/tests/integration/parser/modifiers.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createTestModelicaJson, fullTempDirPath } from "./utils";
import { initializeTestModelicaJson } from "./utils";
import {
getTemplates,
Option,
Expand All @@ -21,8 +21,8 @@ const isExpression = (obj: any) =>

describe("Modifications", () => {
beforeAll(() => {
createTestModelicaJson();
loadPackage(`${fullTempDirPath}/TestPackage`);
initializeTestModelicaJson();
loadPackage('TestPackage');
const templates = getTemplates();
const template = templates.find(
(t) => t.modelicaPath === templatePath,
Expand Down
6 changes: 3 additions & 3 deletions server/tests/integration/parser/path-expansion.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createTestModelicaJson, fullTempDirPath } from "./utils";
import { initializeTestModelicaJson } from "./utils";
import { loadPackage, getOptions } from "../../../src/parser/";
import { evaluateExpression } from "../../../src/parser/expression";

Expand All @@ -9,8 +9,8 @@ import { evaluateExpression } from "../../../src/parser/expression";

describe("Path Expansion", () => {
beforeAll(() => {
createTestModelicaJson();
loadPackage(`${fullTempDirPath}/TestPackage`);
initializeTestModelicaJson();
loadPackage('TestPackage');
});

it("Parameter type paths are expanded", () => {
Expand Down
10 changes: 5 additions & 5 deletions server/tests/integration/parser/template.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createTestModelicaJson, fullTempDirPath } from "./utils";
import { initializeTestModelicaJson } from "./utils";
import {
loadPackage,
getSystemTypes,
Expand All @@ -13,8 +13,8 @@ const NESTED_TEMPLATE_PATH =

describe("Template wrapper class functionality", () => {
beforeAll(() => {
createTestModelicaJson();
loadPackage(`${fullTempDirPath}/TestPackage`);
initializeTestModelicaJson();
loadPackage('TestPackage');
});

it("Extracts two templates and three Template types to be in stores", () => {
Expand Down Expand Up @@ -170,8 +170,8 @@ const PROJECT_INSTANCE_NAME = "datAll";

describe("'Project' items are extracted", () => {
beforeAll(() => {
createTestModelicaJson();
loadPackage(`${fullTempDirPath}/TestPackage`);
initializeTestModelicaJson();
loadPackage('TestPackage');
});
it("Project is populated and all project options are included in options", () => {
const project = getProject();
Expand Down
3 changes: 2 additions & 1 deletion server/tests/integration/parser/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as parser from "../../../src/parser/parser";
import { prependToModelicaJsonPath } from '../../../src/parser/loader';
import { createModelicaJson } from "../../../scripts/generate-modelica-json";

// NOTE: if the test modelica package changes it will need to be
Expand All @@ -21,7 +22,7 @@ export function createTestModelicaJson() {
*/
export function initializeTestModelicaJson() {
createTestModelicaJson();
parser.setPathPrefix(fullTempDirPath);
prependToModelicaJsonPath([fullTempDirPath]);
}

type SimpleOption = {
Expand Down