- The os.c marketplace is the place to publish open source projects with one
- idea in mind: To reduce the incredible duplication of efforts that
- really slows down innovation in the AECO industry.
+ The os.c marketplace is the place to publish open source projects with
+ one idea in mind: To reduce the incredible duplication of efforts that
+ really slows down innovation in the AECO industry.
The idea of this space is twofolded:
- 1. it‘s a collaborative repository to collect any code that is open and helpful to others in the AECO sector.
- Projects range from small scripts, that you wish you would have at hand instead of building it yourself
- to larger projects, that help you move faster.
+ 1. it‘s a collaborative repository to collect any code that is
+ open and helpful to others in the AECO sector. Projects range from
+ small scripts, that you wish you would have at hand instead of
+ building it yourself to larger projects, that help you move faster.
-
2. get to know the people behind the projects. Learn about their motivation to publish code open source and about the models they have
- found to make the work on it sustainable. In addition to the publication on the website, we offer the possibility to set up a community call
- that is recorded and also shared on the website.
+
+ {" "}
+ 2. get to know the people behind the projects. Learn about their
+ motivation to publish code open source and about the models they have
+ found to make the work on it sustainable. In addition to the
+ publication on the website, we offer the possibility to set up a
+ community call that is recorded and also shared on the website.
-
Let‘s keep pushing the industry further. Step by step and never
+
+ {" "}
+ Let‘s keep pushing the industry further. Step by step and never
stopping.
diff --git a/components/src/partials/projects.tsx b/components/src/partials/projects.tsx
index 8e011f8..5680257 100644
--- a/components/src/partials/projects.tsx
+++ b/components/src/partials/projects.tsx
@@ -1,8 +1,6 @@
-import { getPosts } from "../utils";
import { Card } from "../card";
-import { parseProjects } from "../projectUtils";
+import { getProjects, parseProjects } from "../projectMdxParser";
import { Button } from "../button";
-import Link from "next/link";
function getRandomItems(array: T[], numItems: number): T[] {
const shuffled = array.sort(() => 0.5 - Math.random());
@@ -10,7 +8,7 @@ function getRandomItems(array: T[], numItems: number): T[] {
}
export function ProjectsPartial() {
- let projects = getPosts("projects");
+ let projects = getProjects("projects");
const parsedProjects = parseProjects(projects);
const numberOfProjects = 3;
@@ -18,7 +16,7 @@ export function ProjectsPartial() {
let showFeatured = false;
const filteredProjects = showFeatured
- ? parsedProjects.filter((project) => project.featured)
+ ? parsedProjects.filter((project) => project.metadata.featured)
: getRandomItems(parsedProjects, numberOfProjects);
return (
diff --git a/components/src/projectMdxParser.ts b/components/src/projectMdxParser.ts
new file mode 100644
index 0000000..b6d66ba
--- /dev/null
+++ b/components/src/projectMdxParser.ts
@@ -0,0 +1,119 @@
+import fs from "fs";
+import path from "path";
+import YAML from "yaml";
+import { parseSlug } from "./utils";
+
+// Types & Interfaces
+export type Maturity = typeof validMaturities[number];
+
+export interface MdxProject {
+ title: string;
+ slug: string;
+ description: string;
+ metadata: {
+ featured: boolean;
+ maturity: Maturity;
+ };
+ links: {
+ url: string;
+ label: string;
+ }[];
+ content?: string;
+}
+
+// Constants
+export const validMaturities = ["sandbox", "incubation", "graduated"] as const;
+const defaultMaturity: Maturity = "sandbox";
+
+// File System Utils
+function getMDXFiles(dir: string): string[] {
+ return fs
+ .readdirSync(dir)
+ .filter(file =>
+ path.extname(file) === ".mdx" && !path.basename(file).startsWith("_")
+ );
+}
+
+function readMDXFile(filePath: string): MdxProject {
+ const rawContent = fs.readFileSync(filePath, "utf-8");
+ const slug = parseSlug(path.basename(filePath, path.extname(filePath)));
+ return parseFrontmatter(rawContent, slug);
+}
+
+// Parsing Utils
+function parseMaturity(maturity: string): Maturity {
+ const cleanedMaturity = maturity?.trim()?.toLowerCase();
+ return validMaturities.includes(cleanedMaturity as Maturity)
+ ? (cleanedMaturity as Maturity)
+ : defaultMaturity;
+}
+
+function parseFrontmatter(fileContent: string, slug: string): MdxProject {
+ try {
+ const frontmatterRegex = /---\s*([\s\S]*?)\s*---/;
+ const match = frontmatterRegex.exec(fileContent);
+
+ if (!match) {
+ throw new Error('No frontmatter found');
+ }
+
+ const frontMatterBlock = match[1];
+ const content = fileContent.replace(frontmatterRegex, "").trim();
+ const parsedMetadata = YAML.parse(frontMatterBlock);
+
+ return validateMetadata({ ...parsedMetadata, content }, slug);
+ } catch (error) {
+ console.error('Error parsing frontmatter:', error);
+ return validateMetadata({ content: fileContent }, slug);
+ }
+}
+
+// Validation Utils
+function validateMetadata(input: any, slug: string = ""): MdxProject {
+ const defaultProject: MdxProject = {
+ title: "",
+ description: "",
+ slug,
+ metadata: {
+ featured: false,
+ maturity: defaultMaturity
+ },
+ links: []
+ };
+
+ return {
+ ...defaultProject,
+ title: input?.title || defaultProject.title,
+ description: input?.description || defaultProject.description,
+ slug: input?.slug || slug || defaultProject.slug,
+ metadata: {
+ featured: input?.metadata?.featured ?? defaultProject.metadata.featured,
+ maturity: parseMaturity(input?.metadata?.maturity)
+ },
+ links: Array.isArray(input?.links) ? input.links : defaultProject.links,
+ content: input?.content
+ };
+}
+
+// Public API
+export function getProjects(dir?: string): MdxProject[] {
+ const contentDir = path.join(process.cwd(), "content", dir || "");
+ try {
+ const files = getMDXFiles(contentDir);
+ return files.map(file => readMDXFile(path.join(contentDir, file)));
+ } catch (error) {
+ console.error('Error reading projects:', error);
+ return [];
+ }
+}
+
+export function parseProjects(projects: MdxProject[]): MdxProject[] {
+ return projects.map((project) => {
+ if (!project) {
+ console.warn('Invalid project data:', project);
+ return validateMetadata({});
+ }
+
+ return validateMetadata(project);
+ });
+}
\ No newline at end of file
diff --git a/components/src/projectUtils.ts b/components/src/projectUtils.ts
deleted file mode 100644
index 438ae54..0000000
--- a/components/src/projectUtils.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import projectMapJson from "../../content/project-map.json";
-
-const defaultMaturity: Maturity = "sandbox";
-
-//TODO: Put this file in the correct location
-//TODO: Improve import path for projectMapJson
-
-export interface ProjectMap {
- title: string;
- slug: string;
- featured: boolean;
- maturity: Maturity;
-}
-
-//TODO: Maybe rename this to something else than Project
-export interface Project {
- title: string;
- slug: string;
- featured: boolean;
- maturity: Maturity;
- description: string;
-}
-
-export const validMaturities = ["sandbox", "incubation", "graduated"] as const;
-export type Maturity = typeof validMaturities[number];
-
-
-const projectMapBySlug = new Map(
- projectMapJson.map((project) => [project.slug, project])
-);
-
-function parseMaturity(maturity: string): Maturity {
- const cleanedMaturity = maturity.trim().toLowerCase();
- return validMaturities.includes(cleanedMaturity as Maturity)
- ? (cleanedMaturity as Maturity)
- : defaultMaturity;
-}
-
-/**
- * Function to parse and merge project data with additional metadata from the json file
- * @param projects - Array of raw project data from getPosts function
- * @returns Array of parsed and merged project data
- *
- * Usage example:
- *
- * ```typescript
- * import { getPosts } from "../utils/projectUtils";
- * import { parseProjects } from "./projectUtils";
- *
- * const rawProjects = getPosts("projects");
- * const parsedProjects = parseProjects(rawProjects);
- * ```
- */
-export function parseProjects(projects: any[]): Project[] {
- return projects.map((e) => {
- const { title, description, project: projectMetadata } = e.metadata;
- const { slug } = e;
-
- // Base project object from metadata
- const baseProject: Project = {
- ...projectMetadata,
- title,
- description,
- slug,
- maturity: e.maturity as Maturity,
- };
-
- // Merge extra data from projectMapBySlug
- const extraData = projectMapBySlug.get(slug);
- if (extraData) {
- return {
- ...baseProject,
- ...extraData,
- maturity: parseMaturity(extraData.maturity),
- };
- }
- return baseProject;
- });
-}
\ No newline at end of file
diff --git a/components/src/utils.ts b/components/src/utils.ts
index 4ced7c1..5f4a665 100644
--- a/components/src/utils.ts
+++ b/components/src/utils.ts
@@ -2,12 +2,22 @@ import fs from "fs";
import path from "path";
import YAML from "yaml";
+//Generic utility function to parse frontmatter from a file
+
export type Post = {
metadata: any;
slug: string;
content: string;
};
+export function parseSlug(filename: string): string {
+ return filename
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, '-')
+ .replace(/(^-|-$)/g, '');
+}
+
+
function parseFrontmatter(fileContent: string) {
let frontmatterRegex = /---\s*([\s\S]*?)\s*---/;
let match = frontmatterRegex.exec(fileContent);
@@ -32,14 +42,6 @@ function readMDXFile(filePath: string) {
return parseFrontmatter(rawContent);
}
-export function parseSlug(fileBasename: string) {
- let prefix = fileBasename.indexOf("-");
- if (prefix && +fileBasename.slice(0, prefix)) {
- return fileBasename.slice(prefix + 1);
- }
- return fileBasename;
-}
-
function getMDXData(dir: string): Post[] {
let mdxFiles = getMDXFiles(dir);
return mdxFiles.map((file) => {
diff --git a/content/projects/_template.mdx b/content/projects/_template.mdx
index 8d28711..ea455a9 100644
--- a/content/projects/_template.mdx
+++ b/content/projects/_template.mdx
@@ -1,6 +1,9 @@
---
title: Example project
description: Example description (Max 1 sentence)
+metadata:
+ featured: false
+ maturity: sandbox # possible values: sandbox, incubation, graduated
links:
- url: https://example.com
label: Website
@@ -9,35 +12,43 @@ links:
---
### Problem
+
Please describe in a few sentences what problem your solution addresses.
### Solution
+
Please describe in a few sentences how you approach this problem with your solution.
### Why Open Source?
+
Please describe in a few sentences why your solution is open source.
### Technology
+
Please describe in a few bullet points how your tech stack looks like.
### License
+
What licence(s) do you use?
### Operating Model
+
How do you maintain and update your solution?
Who contributes to your solution (team members of your companies / users and other volunteers / …)
Who are your typical customers?
How do you earn money?
### About the team
+
Please describe in a few sentences who are the key people behind the project.
### Contact
+
For more info, please reach out to: person xyz
### Image / Video Footage
+
Please insert download link(s) here:
Link1
Link2
…
-
diff --git a/content/projects/bldrs.mdx b/content/projects/bldrs.mdx
index 8c69720..6b51da3 100644
--- a/content/projects/bldrs.mdx
+++ b/content/projects/bldrs.mdx
@@ -1,6 +1,9 @@
---
title: Share by bldrs.ai
description: Bldrs specializes in developing web-based CAD collaboration tools designed to meet the needs of modern engineering workflows providing real-time collaboration and visualization capabilities for architects, engineers, and designers.
+metadata:
+ featured: true
+ maturity: incubation
links:
- url: http://bldrs.ai
label: Website
@@ -11,6 +14,7 @@ links:
- url: mailto:info@bldrs.ai
label: info@bldrs.ai
---
+
>> Link to slides](https://drive.google.com/file/d/1-48Ry6jGv1O6riFH_OiEhsQOLuOc3Dax/view?usp=sharing)
-
+
diff --git a/content/projects/ifc-model-checker.mdx b/content/projects/ifc-model-checker.mdx
index 71beee5..bee424c 100644
--- a/content/projects/ifc-model-checker.mdx
+++ b/content/projects/ifc-model-checker.mdx
@@ -1,6 +1,9 @@
---
title: IFC Model Checker
description: automating basic model checks
+metadata:
+ featured: false
+ maturity: sandbox
links:
- url: https://modelcheck.opensource.construction/
label: find the tool here
diff --git a/content/projects/lcax-and-epdx.mdx b/content/projects/lcax-and-epdx.mdx
index c2e12c3..b13bede 100644
--- a/content/projects/lcax-and-epdx.mdx
+++ b/content/projects/lcax-and-epdx.mdx
@@ -1,6 +1,9 @@
---
title: LCAx and EPDx
description: Facilitates interoperability in sustainability assessments by developing an open data format for LCA results and EPD information, promoting transparent and collaborative environmental impact analysis.
+metadata:
+ featured: false
+ maturity: sandbox
links:
- url: https://lcax.kongsgaard.eu
label: Details about LCAx
diff --git a/content/projects/pyrevit.mdx b/content/projects/pyrevit.mdx
index e33cc71..170a2d8 100644
--- a/content/projects/pyrevit.mdx
+++ b/content/projects/pyrevit.mdx
@@ -1,6 +1,9 @@
---
title: pyRevit
description: Rapid Application Development (RAD) Environment for Revit
+metadata:
+ featured: false
+ maturity: graduated
links:
- url: https://www.pyrevitlabs.io
label: Website
diff --git a/content/projects/speckle.mdx b/content/projects/speckle.mdx
index fb711b3..cd4b1d5 100644
--- a/content/projects/speckle.mdx
+++ b/content/projects/speckle.mdx
@@ -1,6 +1,9 @@
---
title: Speckle
description: Revolutionizes AEC industry collaboration by providing an open data platform for real-time sharing and project visualization, fostering efficiency and innovation across disciplines.
+metadata:
+ featured: false
+ maturity: graduated
links:
- url: https://speckle.systems/
label: Website
diff --git a/content/projects/sprint.mdx b/content/projects/sprint.mdx
index ffa2b72..11da811 100644
--- a/content/projects/sprint.mdx
+++ b/content/projects/sprint.mdx
@@ -1,6 +1,9 @@
---
title: sPrint
description: Batch print your documents from the Autodesk Construction Cloud in no time!
+metadata:
+ featured: false
+ maturity: incubation
links:
- url: https://github.com/PerkinsAndWill-IO/sPrint
label: GitHub
diff --git a/content/projects/that-open-company.mdx b/content/projects/that-open-company.mdx
index c988d97..a0aba02 100644
--- a/content/projects/that-open-company.mdx
+++ b/content/projects/that-open-company.mdx
@@ -1,6 +1,9 @@
---
title: ThatOpenCompany
description: delivering infrastructure for your BIM app in the web
+metadata:
+ featured: false
+ maturity: graduated
links:
- url: https://thatopen.com/
label: Website
From e3f85b8649c3d67f77e7520ef9d99be27ef93f1e Mon Sep 17 00:00:00 2001
From: Felix Brunold <48569186+TheVessen@users.noreply.github.com>
Date: Thu, 12 Dec 2024 08:55:20 +0100
Subject: [PATCH 02/33] refactor: migrate utility functions to mdxParser and
update imports across components
---
app/(single-page)/[pageType]/[slug]/page.tsx | 11 +-
app/projects/page.tsx | 20 +--
app/trainings/page.tsx | 29 ++--
components/__tests__/utils.test.ts | 2 +-
components/index.ts | 4 +-
components/src/mdxParser/contentParser.ts | 146 ++++++++++++++++
components/src/mdxParser/mdxValidators.ts | 165 +++++++++++++++++++
components/src/mdxParser/projectMdxParser.ts | 84 ++++++++++
components/src/partials/events.tsx | 2 +-
components/src/partials/faq.tsx | 4 +-
components/src/partials/projects.tsx | 9 +-
components/src/projectMdxParser.ts | 119 -------------
components/src/types/parserTypes.ts | 138 ++++++++++++++++
components/src/utils.ts | 59 -------
14 files changed, 572 insertions(+), 220 deletions(-)
create mode 100644 components/src/mdxParser/contentParser.ts
create mode 100644 components/src/mdxParser/mdxValidators.ts
create mode 100644 components/src/mdxParser/projectMdxParser.ts
delete mode 100644 components/src/projectMdxParser.ts
create mode 100644 components/src/types/parserTypes.ts
diff --git a/app/(single-page)/[pageType]/[slug]/page.tsx b/app/(single-page)/[pageType]/[slug]/page.tsx
index 24b510c..0a63c27 100644
--- a/app/(single-page)/[pageType]/[slug]/page.tsx
+++ b/app/(single-page)/[pageType]/[slug]/page.tsx
@@ -1,3 +1,8 @@
+import {
+ loadPosts,
+ loadProjects,
+ loadTrainings,
+} from "@opensource-construction/components/src/mdxParser/contentParser";
import { Page, getPosts } from "@opensource-construction/components";
import { notFound } from "next/navigation";
@@ -12,13 +17,13 @@ export async function generateStaticParams() {
let posts: SinglePageType[] = [];
posts = [
...posts,
- ...getPosts("projects").map((p) => {
+ ...loadProjects().map((p) => {
return { slug: p.slug, pageType: "projects" as PageType };
}),
...getPosts("events").map((p) => {
return { slug: p.slug, pageType: "events" as PageType };
}),
- ...getPosts("trainings").map((p) => {
+ ...loadTrainings().map((p) => {
return { slug: p.slug, pageType: "trainings" as PageType };
}),
...getPosts("page").map((p) => {
@@ -29,7 +34,7 @@ export async function generateStaticParams() {
}
export default function SinglePage({ params }: { params: SinglePageType }) {
- let page = getPosts(params.pageType).find(
+ let page = loadPosts(params.pageType).find(
(page) => page.slug === params.slug,
);
diff --git a/app/projects/page.tsx b/app/projects/page.tsx
index c489080..3aea643 100644
--- a/app/projects/page.tsx
+++ b/app/projects/page.tsx
@@ -1,23 +1,19 @@
import { Button, Section } from "@/components";
+import { loadProjects } from "@opensource-construction/components/src/mdxParser/contentParser";
import {
- parseProjects,
- validMaturities,
- MdxProject,
Maturity,
- getProjects,
-} from "@opensource-construction/components/src/projectMdxParser";
+ Project,
+ validMaturities,
+} from "@/components/src/types/parserTypes";
function capitalizeFirstLetter(string: string): string {
return string.charAt(0).toUpperCase() + string.slice(1);
}
export default function Projects() {
- let projects = getProjects("projects");
- let parsedProjects = parseProjects(projects);
+ let projects = loadProjects();
- const projectsByMaturity = parsedProjects.reduce<
- Record
- >(
+ const projectsByMaturity = projects.reduce>(
(acc, project) => {
const maturity = project.metadata.maturity; // Access maturity through project.project
if (!acc[maturity]) {
@@ -26,7 +22,7 @@ export default function Projects() {
acc[maturity].push(project);
return acc;
},
- {} as Record,
+ {} as Record,
);
const sortedProjectsByMaturity = Object.entries(projectsByMaturity).sort(
@@ -71,7 +67,6 @@ export default function Projects() {
building it yourself to larger projects, that help you move faster.
- {" "}
2. get to know the people behind the projects. Learn about their
motivation to publish code open source and about the models they have
found to make the work on it sustainable. In addition to the
@@ -80,7 +75,6 @@ export default function Projects() {
- {" "}
Let‘s keep pushing the industry further. Step by step and never
stopping.
diff --git a/app/trainings/page.tsx b/app/trainings/page.tsx
index da1c809..296d813 100644
--- a/app/trainings/page.tsx
+++ b/app/trainings/page.tsx
@@ -1,30 +1,27 @@
import { getPosts, Section } from "@/components";
+import { loadTrainings } from "@opensource-construction/components/src/mdxParser/contentParser";
import { TrainingsPartial } from "@/components/src/partials/trainings";
import { TrainingCard } from "@/components/src/trainingCard";
+import { Training } from "@/components/src/types/parserTypes";
export default function Trainings() {
- let trainings = getPosts("trainings");
+ let trainings = loadTrainings();
return (