From 999dcfbb0ff782318211805083ba8ccd924e98bb Mon Sep 17 00:00:00 2001 From: Juri Strumpflohner Date: Fri, 31 Jan 2025 15:03:08 +0100 Subject: [PATCH] feat(nx-dev): add epic nx release course (#29777) --- docs/courses/epic-nx-release/course.md | 7 ++++ docs/courses/explore-nx/course.md | 1 + docs/courses/pnpm-nx-next/course.md | 1 + .../src/lib/course.types.ts | 3 ++ .../src/lib/courses.api.ts | 42 +++++++++++++------ .../src/lib/course-overview.tsx | 18 +++++--- 6 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 docs/courses/epic-nx-release/course.md diff --git a/docs/courses/epic-nx-release/course.md b/docs/courses/epic-nx-release/course.md new file mode 100644 index 0000000000..21a4bf0d90 --- /dev/null +++ b/docs/courses/epic-nx-release/course.md @@ -0,0 +1,7 @@ +--- +title: 'Versioning and Releasing NPM packages with Nx' +description: 'Learn how Nx Release automates package versioning, changelog generation, and publishing workflows, making releases faster and more reliable.' +authors: [Juri Strumpflohner] +externalLink: 'https://www.epicweb.dev/tutorials/versioning-and-releasing-npm-packages-with-nx' +lessonCount: 20 +--- diff --git a/docs/courses/explore-nx/course.md b/docs/courses/explore-nx/course.md index 14443cc1fb..999841c71d 100644 --- a/docs/courses/explore-nx/course.md +++ b/docs/courses/explore-nx/course.md @@ -2,6 +2,7 @@ title: 'Introduction to Nx' description: 'New to Nx? Then this is where you should start.' authors: [Juri Strumpflohner] +order: 1 --- This course gives you a quick high-level overview of Nx, how running tasks works, task caching, how Nx provides code scaffolding functionality and how you can use `nx migrate` to automatically update your workspace dependencies and code across breaking changes. diff --git a/docs/courses/pnpm-nx-next/course.md b/docs/courses/pnpm-nx-next/course.md index 0676563ab0..3117512fa2 100644 --- a/docs/courses/pnpm-nx-next/course.md +++ b/docs/courses/pnpm-nx-next/course.md @@ -3,6 +3,7 @@ title: 'From PNPM Workspaces to Distributed CI' description: 'Learn how to transform a PNPM workspace monorepo into a high-performance distributed CI setup using Nx.' authors: [Juri Strumpflohner] repository: 'https://github.com/nrwl/nx-course-pnpm-nx' +order: 2 --- In this course, we'll walk through a step-by-step guide using the Tasker application as our example. Tasker is a task management app built with Next.js, structured as a PNPM workspace monorepo. The monorepo contains the Next.js application which is modularized into packages that handle data access via Prisma to a local DB, UI components, and more. diff --git a/nx-dev/data-access-courses/src/lib/course.types.ts b/nx-dev/data-access-courses/src/lib/course.types.ts index a060cee6bd..16f40afc03 100644 --- a/nx-dev/data-access-courses/src/lib/course.types.ts +++ b/nx-dev/data-access-courses/src/lib/course.types.ts @@ -8,8 +8,11 @@ export interface Course { authors: BlogAuthor[]; repository?: string; lessons: Lesson[]; + lessonCount?: number; filePath: string; + externalLink?: string; totalDuration: string; + order?: number; } export interface Lesson { diff --git a/nx-dev/data-access-courses/src/lib/courses.api.ts b/nx-dev/data-access-courses/src/lib/courses.api.ts index f05adc56a5..74c3f1582c 100644 --- a/nx-dev/data-access-courses/src/lib/courses.api.ts +++ b/nx-dev/data-access-courses/src/lib/courses.api.ts @@ -29,7 +29,17 @@ export class CoursesApi { }) .map((folder) => this.getCourse(folder)) ); - return courses; + return courses.sort((a, b) => { + // If both courses have order, sort by order + if (a.order !== undefined && b.order !== undefined) { + return a.order - b.order; + } + // If only one has order, prioritize the one with order + if (a.order !== undefined) return -1; + if (b.order !== undefined) return 1; + // If neither has order, sort by id (folder name) + return a.id.localeCompare(b.id); + }); } async getCourse(folderName: string): Promise { @@ -42,16 +52,19 @@ export class CoursesApi { const content = await readFile(courseFilePath, 'utf-8'); const frontmatter = extractFrontmatter(content); - const lessonFolders = await readdir(coursePath); - const lessons = await Promise.all( - lessonFolders - .filter((folder) => { - const stat = lstatSync(join(coursePath, folder)); - return stat.isDirectory(); - }) - .map((folder) => this.getLessons(folderName, folder)) - ); - const flattenedLessons = lessons.flat(); + let lessons: Lesson[] = []; + if (!frontmatter.externalLink) { + const lessonFolders = await readdir(coursePath); + const tmpLessons = await Promise.all( + lessonFolders + .filter((folder) => { + const stat = lstatSync(join(coursePath, folder)); + return stat.isDirectory(); + }) + .map((folder) => this.getLessons(folderName, folder)) + ); + lessons = tmpLessons.flat(); + } return { id: folderName, @@ -62,9 +75,12 @@ export class CoursesApi { frontmatter.authors.includes(author.name) ), repository: frontmatter.repository, - lessons: flattenedLessons, + lessons, filePath: courseFilePath, - totalDuration: calculateTotalDuration(flattenedLessons), + totalDuration: calculateTotalDuration(lessons), + lessonCount: frontmatter.lessonCount, + externalLink: frontmatter.externalLink, + order: frontmatter.order, }; } diff --git a/nx-dev/ui-video-courses/src/lib/course-overview.tsx b/nx-dev/ui-video-courses/src/lib/course-overview.tsx index 538a6060ed..a549934040 100644 --- a/nx-dev/ui-video-courses/src/lib/course-overview.tsx +++ b/nx-dev/ui-video-courses/src/lib/course-overview.tsx @@ -15,7 +15,7 @@ export function CourseOverview({ courses }: CourseOverviewProps): JSX.Element { {courses.map((course) => ( @@ -43,14 +43,20 @@ export function CourseOverview({ courses }: CourseOverviewProps): JSX.Element { )} - {course.lessons.length} lessons + + {course.lessons.length > 0 + ? `${course.lessons.length} lessons` + : `${course.lessonCount} lessons`} + - - - {course.totalDuration} - + {course.lessons.length > 0 && ( + + + {course.totalDuration} + + )}