diff --git a/.gitignore b/.gitignore index efeb213abe..f2719989ee 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ jest.debug.config.js /graph/client/src/assets/generated-task-inputs /graph/client/src/assets/generated-source-maps /nx-dev/nx-dev/public/documentation +/nx-dev/nx-dev/public/tutorials /nx-dev/nx-dev/public/images/open-graph **/tests/temp-db @@ -38,6 +39,9 @@ out # Angular Cache .angular +# Astro Cache +.astro + # Local dev files .env.local .bashrc diff --git a/.nxignore b/.nxignore index 6672afdcd4..91b1c579c4 100644 --- a/.nxignore +++ b/.nxignore @@ -1,2 +1,5 @@ nx-dev/**/jest.config.js -.next \ No newline at end of file +.next +_files +_solution +nx-dev/tutorial/**/templates diff --git a/.prettierignore b/.prettierignore index 1016408800..ddb7730ed7 100644 --- a/.prettierignore +++ b/.prettierignore @@ -47,4 +47,6 @@ CODEOWNERS .pnpm-store /.nx/workspace-data -/.nx/workflows/dynamic-changesets.yaml \ No newline at end of file +/.nx/workflows/dynamic-changesets.yaml +_files +_solution diff --git a/docs/shared/tutorials/react-monorepo.md b/docs/shared/tutorials/react-monorepo.md index 4f894ecccf..10b4eefd00 100644 --- a/docs/shared/tutorials/react-monorepo.md +++ b/docs/shared/tutorials/react-monorepo.md @@ -927,7 +927,7 @@ Nx comes with a generic mechanism that allows you to assign "tags" to projects. } ``` -Then go to the `project.json` of your `products` library and assign the tags `type:feature` and `scope:products` to it. +Then go to the `package.json` of your `products` library and assign the tags `type:feature` and `scope:products` to it. ```json {% fileName="libs/products/package.json" %} { @@ -938,7 +938,7 @@ Then go to the `project.json` of your `products` library and assign the tags `ty } ``` -Finally, go to the `project.json` of the `shared-ui` library and assign the tags `type:ui` and `scope:shared` to it. +Finally, go to the `package.json` of the `shared-ui` library and assign the tags `type:ui` and `scope:shared` to it. ```json {% fileName="libs/shared/ui/package.json" %} { diff --git a/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx b/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx index 02af3c85b2..7356642944 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx @@ -1,13 +1,14 @@ /* eslint-disable @nx/enforce-module-boundaries */ // nx-ignore-next-line import type { TargetConfiguration } from '@nx/devkit'; +import { CopyToClipboardButton } from '@nx/graph/legacy/components'; import { ChevronDownIcon, ChevronUpIcon, EyeIcon, PlayIcon, } from '@heroicons/react/24/outline'; -import { CopyToClipboardButton } from '@nx/graph/legacy/components'; + import { Tooltip } from '@nx/graph/legacy/tooltips'; import { twMerge } from 'tailwind-merge'; import { Pill } from '../pill'; diff --git a/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-details.tsx b/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-details.tsx index 4724a67db3..43a0910f99 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-details.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-details.tsx @@ -19,6 +19,7 @@ import { FadingCollapsible } from './fading-collapsible'; import { TargetConfigurationProperty } from './target-configuration-property'; import { TooltipTriggerText } from './tooltip-trigger-text'; import { PropertyInfoTooltip } from '../tooltips/property-info-tooltip'; + interface TargetConfigurationDetailsProps { projectName: string; targetName: string; diff --git a/graph/ui-project-details/src/lib/target-executor/target-executor-title.tsx b/graph/ui-project-details/src/lib/target-executor/target-executor-title.tsx index 6121606371..8f20135c28 100644 --- a/graph/ui-project-details/src/lib/target-executor/target-executor-title.tsx +++ b/graph/ui-project-details/src/lib/target-executor/target-executor-title.tsx @@ -1,5 +1,5 @@ -import { CopyToClipboardButton } from '@nx/graph/legacy/components'; import { Tooltip } from '@nx/graph/legacy/tooltips'; +import { CopyToClipboardButton } from '@nx/graph/legacy/components'; import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text'; import { PropertyInfoTooltip } from '../tooltips/property-info-tooltip'; diff --git a/nx-dev/feature-search/src/lib/algolia-search.tsx b/nx-dev/feature-search/src/lib/algolia-search.tsx index 00c20abcbf..f5192e06d7 100644 --- a/nx-dev/feature-search/src/lib/algolia-search.tsx +++ b/nx-dev/feature-search/src/lib/algolia-search.tsx @@ -1,4 +1,4 @@ -import { DocSearchModal, useDocSearchKeyboardEvents } from '@docsearch/react'; +import * as docsearchReact from '@docsearch/react'; import { InternalDocSearchHit, StoredDocSearchHit, @@ -10,6 +10,8 @@ import { useRouter } from 'next/navigation'; import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; +const { DocSearchModal, useDocSearchKeyboardEvents } = docsearchReact; + const ACTION_KEY_DEFAULT = ['Ctrl ', 'Control']; const ACTION_KEY_APPLE = ['⌘', 'Command']; diff --git a/nx-dev/nx-dev/copy-tutorial.js b/nx-dev/nx-dev/copy-tutorial.js new file mode 100644 index 0000000000..6aca8df47f --- /dev/null +++ b/nx-dev/nx-dev/copy-tutorial.js @@ -0,0 +1,15 @@ +const { copySync, rmSync } = require('fs-extra'); +const path = require('path'); + +/** + * Copies the tutorial kit build artifacts + */ +rmSync(path.resolve(path.join(__dirname, 'public/tutorials')), { + recursive: true, + force: true, +}); +copySync( + path.resolve(path.join(__dirname, '../tutorial/dist')), + path.resolve(path.join(__dirname, 'public/tutorials')), + { overwrite: true } +); diff --git a/nx-dev/nx-dev/next.config.js b/nx-dev/nx-dev/next.config.js index 47cafa26f6..d1f67e9da1 100644 --- a/nx-dev/nx-dev/next.config.js +++ b/nx-dev/nx-dev/next.config.js @@ -13,6 +13,13 @@ module.exports = withNx({ }, async headers() { return [ + { + source: '/tutorials/:path*', + headers: [ + { key: 'Cross-Origin-Embedder-Policy', value: 'require-corp' }, + { key: 'Cross-Origin-Opener-Policy', value: 'same-origin' }, + ], + }, { source: '/:path*', headers: [ @@ -24,11 +31,23 @@ module.exports = withNx({ { key: 'X-XSS-Protection', value: '1; mode=block' }, { key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'X-Frame-Options', value: 'DENY' }, - { key: 'Referrer-Policy', value: 'no-referrer' }, + { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, ], }, ]; }, + async rewrites() { + return [ + { + source: '/tutorials/:path*', + destination: '/tutorials/:path*/index.html', + }, + { + source: '/tutorials', + destination: '/tutorials/index.html', + }, + ]; + }, async redirects() { const rules = []; diff --git a/nx-dev/nx-dev/project.json b/nx-dev/nx-dev/project.json index 9dc970352a..f7a14e07fd 100644 --- a/nx-dev/nx-dev/project.json +++ b/nx-dev/nx-dev/project.json @@ -64,11 +64,20 @@ "cwd": "nx-dev/nx-dev" } }, + "copy-tutorial": { + "inputs": ["{workspaceRoot}/nx-dev/tutorial/**/*"], + "outputs": ["{projectRoot}/public/tutorials"], + "command": "node ./copy-tutorial.js", + "dependsOn": ["tutorial:build"], + "options": { + "cwd": "nx-dev/nx-dev" + } + }, "serve-docs": { "executor": "nx:run-commands", "options": { "commands": [ - "nx watch --projects=docs -- nx run nx-dev:copy-docs", + "nx watch --projects=docs,tutorial -- nx run-many -t=copy-docs,copy-tutorial -p nx-dev", "nx run nx-dev:serve" ], "parallel": true @@ -80,7 +89,7 @@ }, "serve": { "executor": "@nx/next:server", - "dependsOn": ["copy-docs"], + "dependsOn": ["copy-docs", "copy-tutorial"], "options": { "buildTarget": "nx-dev:build-base", "dev": true @@ -111,6 +120,10 @@ "command": "nx copy-docs nx-dev", "description": "Copy generated docs to build output" }, + { + "command": "nx copy-tutorial nx-dev", + "description": "Copy tutorial to build output" + }, { "command": "npx ts-node -P scripts/tsconfig.scripts.json scripts/documentation/plugin-quality-indicators.ts", "description": "Fetch plugin data" diff --git a/nx-dev/tutorial/README.md b/nx-dev/tutorial/README.md new file mode 100644 index 0000000000..1476b457b8 --- /dev/null +++ b/nx-dev/tutorial/README.md @@ -0,0 +1,149 @@ +# TutorialKit Starter + +πŸ‘‹ Welcome to TutorialKit! + +This README includes everything you need to start writing your tutorial content quickly. + +## Project Structure + +```bash +. +β”œβ”€β”€ astro.config.mjs # TutorialKit uses Astro πŸš€ (https://astro.build) +β”œβ”€β”€ src +β”‚ β”œβ”€β”€ ... +β”‚ β”œβ”€β”€ content +β”‚ β”‚ └── tutorial # Your tutorial content lives here +β”‚ └── templates # Your templates (see below for more information) +β”œβ”€β”€ public +β”‚ β”œβ”€β”€ favicon.svg +β”‚ └── logo.svg # Default logo used in top left for your tutorial +β”œβ”€β”€ ... +β”œβ”€β”€ theme.ts # Customize the theme of the tutorial +└── uno.config.ts # UnoCSS config (https://unocss.dev/) +``` + +## Getting Started + +Make sure you have all dependencies installed and started the dev server: + +```bash +npm install +npm run dev +``` + +## UI Structure + +```markdown +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ ● ● ● β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ +β”‚ β”‚ Code Editor β”‚ +β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ +β”‚ Content β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ +β”‚ β”‚ Preview & Boot Screen β”‚ +β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ +β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ β”‚ +β”‚ β”‚ Terminal β”‚ +β”‚ β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Authoring Content + +A tutorial consists of parts, chapters, and lessons. For example: + +- Part 1: Basics of Vite + - Chapter 1: Introduction + - Lesson 1: Welcome! + - Lesson 2: Why Vite? + - … + - Chapter 2: Your first Vite project +- Part 2: CLI + - … + +Your content is organized into lessons, with chapters and parts providing a structure and defining common metadata for these lessons. + +Here’s an example of how it would look like in `src/content/tutorial`: + +```bash +tutorial +β”œβ”€β”€ 1-basics-of-vite +β”‚ β”œβ”€β”€ 1-introduction +β”‚ β”‚ β”œβ”€β”€ 1-welcome +β”‚ β”‚ β”‚ β”œβ”€β”€ content.md # The content of your lesson +β”‚ β”‚ β”‚ β”œβ”€β”€ _files # Initial set of files +β”‚ β”‚ β”‚ β”‚ └── ... +β”‚ β”‚ β”‚ └── _solution # Solution of the lesson +β”‚ β”‚ β”‚ └── ... +β”‚ β”‚ β”œβ”€β”€ 2-why-vite +β”‚ β”‚ β”‚ β”œβ”€β”€ content.md +β”‚ β”‚ β”‚ └── _files +β”‚ β”‚ β”‚ └── ... +β”‚ β”‚ └── meta.md # Metadata for the chapter +β”‚ └── meta.md # Metadata for the part +β”œβ”€β”€ 2-advanced +β”‚ β”œβ”€β”€ ... +β”‚ └── meta.md +└── meta.md # Metadata for the tutorial +``` + +### Supported Content Formats + +Content can be either written as Markdown (`.md`) files or using [MDX](https://mdxjs.com/) (`.mdx`). Files have a Front Matter at the top that contains the metadata and everything that comes after is the content of your lesson. + +**Example** + +```markdown +--- +type: lesson +title: Welcome! +--- + +# Welcome to TutorialKit! + +In this tutorial we'll walk you through how to setup your environment to +write your first tutorial 🀩 +``` + +The metadata file (`meta.md`) of parts, chapters, and lessons do not contain any content. It only contains the Front Matter for configuration. + +### Metadata + +Here is an overview of the properties that can be used as part of the Front Matter: + +| Property | Required | Type | Inherited | Description | +| --------------- | -------- | --------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| type | βœ… | `part \| chapter \| lesson` | ❌ | The type of the metadata. | +| title | βœ… | `string` | ❌ | The title of the part, chapter, or lesson. | +| slug | | `string` | ❌ | Let’s you customize the URL pathname which is `/:partSlug/:chapterSlug/:lessonSlug`. | +| previews | | `Preview[]` | βœ… | Configure which ports should be used for the previews. If not specified, the lowest port will be used. | +| autoReload | | `boolean` | βœ… | Navigating to a lesson that specifies `autoReload` will always reload the preview. This is typically only needed if your server does not support HMR. | +| prepareCommands | | `Command[]` | βœ… | List of commands to execute sequentially. They are typically used to install dependencies or to run scripts. | +| mainCommand | | `Command` | βœ… | The main command to be executed. This command will run after the `prepareCommands`. | + +A `Command` has the following shape: + +```ts +string | [command: string, title: string] | { command: string, title: string } +``` + +The `title` is used as part of the boot screen (see [UI Structure](#ui-structure)). + +A `Preview` has the following shape: + +```ts +string | [port: number, title: string] | { port: number, title: string } +``` + +In most cases, metadata is inherited. For example, if you specify a `mainCommand` on a chapter without specifying it on any of its lessons, each lesson will use the `mainCommand` from its respective chapter. This extends to chapter and parts as well. diff --git a/nx-dev/tutorial/astro.config.ts b/nx-dev/tutorial/astro.config.ts new file mode 100644 index 0000000000..b8d6cffe74 --- /dev/null +++ b/nx-dev/tutorial/astro.config.ts @@ -0,0 +1,52 @@ +import tutorialkit from '@tutorialkit/astro'; +import { defineConfig, envField } from 'astro/config'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; + +export const config = defineConfig({ + base: '/tutorials', + devToolbar: { + enabled: false, + }, + experimental: { + env: { + schema: { + NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA: envField.string({ + context: 'client', + access: 'public', + optional: true, + }), + NEXT_PUBLIC_FARO_URL: envField.string({ + context: 'client', + access: 'public', + optional: true, + }), + NEXT_PUBLIC_VERCEL_ENV: envField.string({ + context: 'client', + access: 'public', + optional: true, + }), + }, + }, + }, + vite: { + plugins: [nxViteTsPaths() as any], + ssr: { + noExternal: [ + '@tutorialkit/astro', + '@astrojs/mdx', + '@astrojs/react', + 'astro-expressive-code', + ], + }, + }, + integrations: [ + tutorialkit({ + components: { + HeadTags: './src/components/HeadTags.astro', + TopBar: './src/components/TopBar.astro', + }, + }), + ], +}); + +export default config; diff --git a/nx-dev/tutorial/package.json b/nx-dev/tutorial/package.json new file mode 100644 index 0000000000..0155dfb303 --- /dev/null +++ b/nx-dev/tutorial/package.json @@ -0,0 +1,15 @@ +{ + "name": "tutorial", + "version": "0.0.1", + "type": "module", + "private": true, + "scripts": { + "astro": "npx astro", + "build": "npx astro check && npx astro build", + "dev": "npx astro dev", + "preview": "npx astro preview", + "serve": "npx astro dev --port 4200" + }, + "dependencies": {}, + "devDependencies": {} +} diff --git a/nx-dev/tutorial/project.json b/nx-dev/tutorial/project.json new file mode 100644 index 0000000000..d40953bd56 --- /dev/null +++ b/nx-dev/tutorial/project.json @@ -0,0 +1,17 @@ +{ + "name": "tutorial", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "nx-dev/tutorial/src", + "projectType": "library", + "tags": [], + "// targets": "to see all targets run: nx show project tutorial --web", + "targets": { + "build": { + "inputs": ["{projectRoot}/src/**/**"] + }, + "lint": { + "command": "echo no linting", + "cache": true + } + } +} diff --git a/nx-dev/tutorial/public/favicon.svg b/nx-dev/tutorial/public/favicon.svg new file mode 100644 index 0000000000..c68d62fd45 --- /dev/null +++ b/nx-dev/tutorial/public/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/nx-dev/tutorial/public/images/github-cloud-pr-merged.avif b/nx-dev/tutorial/public/images/github-cloud-pr-merged.avif new file mode 100644 index 0000000000..21fd7bff77 Binary files /dev/null and b/nx-dev/tutorial/public/images/github-cloud-pr-merged.avif differ diff --git a/nx-dev/tutorial/public/images/github-pr-cloud-report.avif b/nx-dev/tutorial/public/images/github-pr-cloud-report.avif new file mode 100644 index 0000000000..a7d7989f51 Binary files /dev/null and b/nx-dev/tutorial/public/images/github-pr-cloud-report.avif differ diff --git a/nx-dev/tutorial/public/images/nx-cloud-github-connect.avif b/nx-dev/tutorial/public/images/nx-cloud-github-connect.avif new file mode 100644 index 0000000000..bb420026c2 Binary files /dev/null and b/nx-dev/tutorial/public/images/nx-cloud-github-connect.avif differ diff --git a/nx-dev/tutorial/public/images/nx-cloud-run-details.avif b/nx-dev/tutorial/public/images/nx-cloud-run-details.avif new file mode 100644 index 0000000000..4c9d8af75b Binary files /dev/null and b/nx-dev/tutorial/public/images/nx-cloud-run-details.avif differ diff --git a/nx-dev/tutorial/public/images/nx-media.png b/nx-dev/tutorial/public/images/nx-media.png new file mode 100644 index 0000000000..7c7300f294 Binary files /dev/null and b/nx-dev/tutorial/public/images/nx-media.png differ diff --git a/nx-dev/tutorial/public/logo-dark.svg b/nx-dev/tutorial/public/logo-dark.svg new file mode 100644 index 0000000000..5e42848b35 --- /dev/null +++ b/nx-dev/tutorial/public/logo-dark.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/nx-dev/tutorial/public/logo.svg b/nx-dev/tutorial/public/logo.svg new file mode 100644 index 0000000000..d4b4597a3b --- /dev/null +++ b/nx-dev/tutorial/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/nx-dev/tutorial/src/components/CommunityLinks.tsx b/nx-dev/tutorial/src/components/CommunityLinks.tsx new file mode 100644 index 0000000000..27be1775ff --- /dev/null +++ b/nx-dev/tutorial/src/components/CommunityLinks.tsx @@ -0,0 +1,125 @@ +export function CommunityLinks() { + return ( + <> + + Community channel + + + + Latest news + + + + Latest news on Bluesky + + + + Youtube channel + + + + + + + ); +} diff --git a/nx-dev/tutorial/src/components/DownloadButton.tsx b/nx-dev/tutorial/src/components/DownloadButton.tsx new file mode 100644 index 0000000000..de11e83ecb --- /dev/null +++ b/nx-dev/tutorial/src/components/DownloadButton.tsx @@ -0,0 +1,19 @@ +export function DownloadButton() { + return ( + + ); +} + +async function onClick() { + const button: any = document.querySelector('#download-button button'); + button.click(); +} diff --git a/nx-dev/tutorial/src/components/Editor.tsx b/nx-dev/tutorial/src/components/Editor.tsx new file mode 100644 index 0000000000..4ba0184170 --- /dev/null +++ b/nx-dev/tutorial/src/components/Editor.tsx @@ -0,0 +1,313 @@ +import type { + EditorDocument, + EditorUpdate, + ScrollPosition, +} from '@tutorialkit/react/core'; +import CodeMirrorEditor from '@tutorialkit/react/core/CodeMirrorEditor'; +import FileTree from '@tutorialkit/react/core/FileTree'; +import type { FileSystemTree, DirectoryNode } from '@webcontainer/api'; +import type { Terminal as XTerm } from '@xterm/xterm'; +import { Suspense, lazy, useEffect, useState } from 'react'; +import { useTheme } from './hooks/useTheme'; +import { useWebContainer } from './hooks/useWebcontainer'; + +const Terminal = lazy(() => import('@tutorialkit/react/core/Terminal')); + +export default function ExampleSimpleEditor() { + const [domLoaded, setDomLoaded] = useState(false); + + const theme = useTheme(); + const { + setTerminal, + previewSrc, + document, + files, + onChange, + onScroll, + selectedFile, + setSelectedFile, + } = useSimpleEditor(); + + useEffect(() => { + setDomLoaded(true); + }, []); + + return ( +
+
+ +
+
+ +
+
+
+
+
+
+ {domLoaded && ( + + + + )} +
+
+
+