diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index c00ef21b..d40f7a80 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -35,6 +35,21 @@ jobs:
- name: Test
run: pnpm test
+ test-e2e:
+ name: Test E2E
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - uses: ./.github/actions/setup-and-build
+
+ - name: Install Playwright Dependencies
+ run: pnpm --filter=tutorialkit-e2e exec playwright install chromium --with-deps
+
+ - name: Test
+ run: pnpm test:e2e
+
docs:
name: Docs
runs-on: ubuntu-latest
diff --git a/.gitignore b/.gitignore
index 592c9c08..130572d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,5 @@ tsconfig.tsbuildinfo
tsconfig.build.tsbuildinfo
.tmp
.tmp-*
+/e2e/**/test-results
+/e2e/**/.astro
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 00000000..3bd3b7de
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+shell-emulator=true
diff --git a/e2e/README.md b/e2e/README.md
new file mode 100644
index 00000000..b103c7be
--- /dev/null
+++ b/e2e/README.md
@@ -0,0 +1,42 @@
+# UI Tests
+
+> Tests for verifying TutorialKit works as expected in the browser. Tests are run against locally linked `@tutorialkit` packages.
+
+## Running
+
+- `pnpm exec playwright install chromium --with-deps` - When running the tests first time
+- `pnpm test`
+
+## Development
+
+- `pnpm start` - Starts example/fixture project's development server
+- `pnpm test:ui` - Start Playwright in UI mode
+
+## Structure
+
+Test cases are located in `test` directory.
+Each test file has its own `chapter`, that contains `lesson`s for test cases:
+
+For example Navigation tests:
+
+```
+├── src/content/tutorial
+│ └── tests
+│ └──── navigation
+│ ├── page-one
+│ ├── page-three
+│ └── page-two
+└── test
+ └── navigation.test.ts
+```
+
+Or File Tree tests:
+
+```
+├── src/content/tutorial
+│ └── tests
+│ └── file-tree
+│ └── lesson-and-solution
+└── test
+ └── file-tree.test.ts
+```
diff --git a/e2e/astro.config.ts b/e2e/astro.config.ts
new file mode 100644
index 00000000..6814ced5
--- /dev/null
+++ b/e2e/astro.config.ts
@@ -0,0 +1,23 @@
+import { createRequire } from 'node:module';
+import { resolve } from 'node:path';
+import tutorialkit from '@tutorialkit/astro';
+import { defineConfig } from 'astro/config';
+
+const require = createRequire(import.meta.url);
+const astroDist = resolve(require.resolve('astro/package.json'), '..');
+const swapFunctionEntry = resolve(astroDist, 'dist/transitions/swap-functions.js');
+
+export default defineConfig({
+ devToolbar: { enabled: false },
+ server: { port: 4329 },
+ integrations: [tutorialkit()],
+
+ vite: {
+ resolve: {
+ alias: {
+ // work-around for https://github.com/stackblitz/tutorialkit/pull/238
+ 'node_modules/astro/dist/transitions/swap-functions': swapFunctionEntry,
+ },
+ },
+ },
+});
diff --git a/e2e/package.json b/e2e/package.json
new file mode 100644
index 00000000..7436ff52
--- /dev/null
+++ b/e2e/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "tutorialkit-e2e",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "astro dev",
+ "preview": "astro build && astro preview",
+ "test": "playwright test",
+ "test:ui": "pnpm run test --ui"
+ },
+ "devDependencies": {
+ "@astrojs/react": "^3.6.0",
+ "@iconify-json/ph": "^1.1.13",
+ "@iconify-json/svg-spinners": "^1.1.2",
+ "@playwright/test": "^1.46.0",
+ "@tutorialkit/astro": "workspace:*",
+ "@tutorialkit/components-react": "workspace:*",
+ "@tutorialkit/runtime": "workspace:*",
+ "@tutorialkit/theme": "workspace:*",
+ "@tutorialkit/types": "workspace:*",
+ "@types/node": "^22.2.0",
+ "@unocss/reset": "^0.59.4",
+ "@unocss/transformer-directives": "^0.62.0",
+ "astro": "^4.12.0",
+ "fast-glob": "^3.3.2",
+ "playwright": "^1.46.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "unocss": "^0.59.4"
+ }
+}
diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts
new file mode 100644
index 00000000..3c09769f
--- /dev/null
+++ b/e2e/playwright.config.ts
@@ -0,0 +1,17 @@
+import { defineConfig } from '@playwright/test';
+
+export default defineConfig({
+ expect: {
+ timeout: process.env.CI ? 30_000 : 10_000,
+ },
+ use: {
+ baseURL: 'http://localhost:4329',
+ },
+ webServer: {
+ command: 'pnpm preview',
+ url: 'http://localhost:4329',
+ reuseExistingServer: !process.env.CI,
+ stdout: 'ignore',
+ stderr: 'pipe',
+ },
+});
diff --git a/e2e/public/logo.svg b/e2e/public/logo.svg
new file mode 100644
index 00000000..57ad62a4
--- /dev/null
+++ b/e2e/public/logo.svg
@@ -0,0 +1 @@
+
diff --git a/e2e/src/content/config.ts b/e2e/src/content/config.ts
new file mode 100644
index 00000000..8e595c05
--- /dev/null
+++ b/e2e/src/content/config.ts
@@ -0,0 +1,9 @@
+import { contentSchema } from '@tutorialkit/types';
+import { defineCollection } from 'astro:content';
+
+const tutorial = defineCollection({
+ type: 'content',
+ schema: contentSchema,
+});
+
+export const collections = { tutorial };
diff --git a/e2e/src/content/tutorial/meta.md b/e2e/src/content/tutorial/meta.md
new file mode 100644
index 00000000..29eef72c
--- /dev/null
+++ b/e2e/src/content/tutorial/meta.md
@@ -0,0 +1,5 @@
+---
+type: tutorial
+mainCommand: ''
+prepareCommands: []
+---
diff --git a/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_files/example.html b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_files/example.html
new file mode 100644
index 00000000..011dc537
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_files/example.html
@@ -0,0 +1,8 @@
+
+
+ Lesson file example.html title
+
+
+ Lesson file example.html content
+
+
diff --git a/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_files/example.js b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_files/example.js
new file mode 100644
index 00000000..cd356077
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_files/example.js
@@ -0,0 +1 @@
+export default 'Lesson file example.js content';
diff --git a/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_solution/example.html b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_solution/example.html
new file mode 100644
index 00000000..14493277
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_solution/example.html
@@ -0,0 +1,8 @@
+
+
+ Solution file example.html title
+
+
+ Solution file example.html content
+
+
diff --git a/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_solution/example.js b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_solution/example.js
new file mode 100644
index 00000000..6cf75a45
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/_solution/example.js
@@ -0,0 +1 @@
+export default 'Solution file example.js content';
diff --git a/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/content.md b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/content.md
new file mode 100644
index 00000000..8a53098b
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/file-tree/lesson-and-solution/content.md
@@ -0,0 +1,6 @@
+---
+type: lesson
+title: Lesson and solution
+---
+
+# File Tree test - Lesson and solution
diff --git a/e2e/src/content/tutorial/tests/file-tree/meta.md b/e2e/src/content/tutorial/tests/file-tree/meta.md
new file mode 100644
index 00000000..0c250534
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/file-tree/meta.md
@@ -0,0 +1,4 @@
+---
+type: chapter
+title: File Tree
+---
diff --git a/e2e/src/content/tutorial/tests/file-tree/no-solution/_files/example.html b/e2e/src/content/tutorial/tests/file-tree/no-solution/_files/example.html
new file mode 100644
index 00000000..011dc537
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/file-tree/no-solution/_files/example.html
@@ -0,0 +1,8 @@
+
+
+ Lesson file example.html title
+
+
+ Lesson file example.html content
+
+
diff --git a/e2e/src/content/tutorial/tests/file-tree/no-solution/_files/example.js b/e2e/src/content/tutorial/tests/file-tree/no-solution/_files/example.js
new file mode 100644
index 00000000..cd356077
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/file-tree/no-solution/_files/example.js
@@ -0,0 +1 @@
+export default 'Lesson file example.js content';
diff --git a/e2e/src/content/tutorial/tests/file-tree/no-solution/content.md b/e2e/src/content/tutorial/tests/file-tree/no-solution/content.md
new file mode 100644
index 00000000..54342155
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/file-tree/no-solution/content.md
@@ -0,0 +1,6 @@
+---
+type: lesson
+title: No solution
+---
+
+# File Tree test - No solution
diff --git a/e2e/src/content/tutorial/tests/meta.md b/e2e/src/content/tutorial/tests/meta.md
new file mode 100644
index 00000000..a9cbeaef
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/meta.md
@@ -0,0 +1,4 @@
+---
+type: part
+title: Tests
+---
diff --git a/e2e/src/content/tutorial/tests/navigation/meta.md b/e2e/src/content/tutorial/tests/navigation/meta.md
new file mode 100644
index 00000000..4842a08d
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/navigation/meta.md
@@ -0,0 +1,10 @@
+---
+type: chapter
+title: Navigation
+lessons:
+ - page-one
+ - page-two
+ - page-three
+mainCommand: ''
+prepareCommands: []
+---
diff --git a/e2e/src/content/tutorial/tests/navigation/page-one/content.md b/e2e/src/content/tutorial/tests/navigation/page-one/content.md
new file mode 100644
index 00000000..36d93ec8
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/navigation/page-one/content.md
@@ -0,0 +1,6 @@
+---
+type: lesson
+title: Page one
+---
+
+# Navigation test - Page one
diff --git a/e2e/src/content/tutorial/tests/navigation/page-three/content.md b/e2e/src/content/tutorial/tests/navigation/page-three/content.md
new file mode 100644
index 00000000..81e0a02e
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/navigation/page-three/content.md
@@ -0,0 +1,6 @@
+---
+type: lesson
+title: Page three
+---
+
+# Navigation test - Page three
diff --git a/e2e/src/content/tutorial/tests/navigation/page-two/content.md b/e2e/src/content/tutorial/tests/navigation/page-two/content.md
new file mode 100644
index 00000000..d6d92bc5
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/navigation/page-two/content.md
@@ -0,0 +1,6 @@
+---
+type: lesson
+title: Page two
+---
+
+# Navigation test - Page two
diff --git a/e2e/src/content/tutorial/tests/preview/meta.md b/e2e/src/content/tutorial/tests/preview/meta.md
new file mode 100644
index 00000000..89205e01
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/preview/meta.md
@@ -0,0 +1,5 @@
+---
+type: chapter
+title: Preview
+mainCommand: 'node ./index.mjs'
+---
diff --git a/e2e/src/content/tutorial/tests/preview/multiple/content.md b/e2e/src/content/tutorial/tests/preview/multiple/content.md
new file mode 100644
index 00000000..c7509b1d
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/preview/multiple/content.md
@@ -0,0 +1,9 @@
+---
+type: lesson
+title: Multiple
+previews:
+ - [8000, "First Server"]
+ - [8000, "Second Server", "/about.html"]
+---
+
+# Preview test - Multiple
diff --git a/e2e/src/content/tutorial/tests/preview/single/content.md b/e2e/src/content/tutorial/tests/preview/single/content.md
new file mode 100644
index 00000000..387761f4
--- /dev/null
+++ b/e2e/src/content/tutorial/tests/preview/single/content.md
@@ -0,0 +1,8 @@
+---
+type: lesson
+title: Single
+previews:
+ - [8000, "Node Server"]
+---
+
+# Preview test - Single
diff --git a/test/ui/src/content/tutorial/tests/terminal/default/content.md b/e2e/src/content/tutorial/tests/terminal/default/content.md
similarity index 100%
rename from test/ui/src/content/tutorial/tests/terminal/default/content.md
rename to e2e/src/content/tutorial/tests/terminal/default/content.md
diff --git a/test/ui/src/content/tutorial/tests/terminal/meta.md b/e2e/src/content/tutorial/tests/terminal/meta.md
similarity index 100%
rename from test/ui/src/content/tutorial/tests/terminal/meta.md
rename to e2e/src/content/tutorial/tests/terminal/meta.md
diff --git a/test/ui/src/content/tutorial/tests/terminal/open-by-default/content.md b/e2e/src/content/tutorial/tests/terminal/open-by-default/content.md
similarity index 100%
rename from test/ui/src/content/tutorial/tests/terminal/open-by-default/content.md
rename to e2e/src/content/tutorial/tests/terminal/open-by-default/content.md
diff --git a/e2e/src/env.d.ts b/e2e/src/env.d.ts
new file mode 100644
index 00000000..9505823a
--- /dev/null
+++ b/e2e/src/env.d.ts
@@ -0,0 +1,3 @@
+///
+///
+///
diff --git a/e2e/src/templates/default/index.mjs b/e2e/src/templates/default/index.mjs
new file mode 100644
index 00000000..148d1ae2
--- /dev/null
+++ b/e2e/src/templates/default/index.mjs
@@ -0,0 +1,22 @@
+import http from 'node:http';
+
+const server = http.createServer((req, res) => {
+ if (req.url === '/' || req.url === '/index.html') {
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end('Index page');
+
+ return;
+ }
+
+ if (req.url === '/about.html') {
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end('About page');
+
+ return;
+ }
+
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end('Not found');
+});
+
+server.listen(8000);
diff --git a/e2e/test/file-tree.test.ts b/e2e/test/file-tree.test.ts
new file mode 100644
index 00000000..2f062049
--- /dev/null
+++ b/e2e/test/file-tree.test.ts
@@ -0,0 +1,59 @@
+import { test, expect } from '@playwright/test';
+import { readLessonFilesAndSolution } from './utils.js';
+
+const BASE_URL = '/tests/file-tree';
+
+const fixtures = readLessonFilesAndSolution('file-tree/lesson-and-solution', 'file-tree/no-solution');
+
+test('user can see lesson and solution files', async ({ page }) => {
+ const testCase = 'lesson-and-solution';
+ await page.goto(`${BASE_URL}/${testCase}`);
+
+ await expect(page.getByRole('heading', { level: 1, name: 'File Tree test - Lesson and solution' })).toBeVisible();
+
+ // lesson files
+ for (const file of ['example.html', 'example.js']) {
+ await page.getByRole('button', { name: file }).click();
+ await expect(page.getByRole('button', { name: file, pressed: true })).toBeVisible();
+
+ await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText(fixtures[testCase].files[file], {
+ useInnerText: true,
+ });
+ }
+
+ await page.getByRole('button', { name: 'Solve', disabled: false }).click();
+ await expect(page.getByRole('button', { name: 'Reset' })).toBeVisible();
+
+ // solution files
+ for (const file of ['example.html', 'example.js']) {
+ await page.getByRole('button', { name: file }).click();
+ await expect(page.getByRole('button', { name: file, pressed: true })).toBeVisible();
+
+ // TODO: Figure out why this is flaky
+ await page.waitForTimeout(1_000);
+
+ await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText(fixtures[testCase].solution[file], {
+ useInnerText: true,
+ });
+ }
+});
+
+test('user can see cannot click solve on lessons without solution files', async ({ page }) => {
+ const testCase = 'no-solution';
+ await page.goto(`${BASE_URL}/${testCase}`);
+
+ await expect(page.getByRole('heading', { level: 1, name: 'File Tree test - No solution' })).toBeVisible();
+
+ // lesson files
+ for (const file of ['example.html', 'example.js']) {
+ await page.getByRole('button', { name: file }).click();
+ await expect(page.getByRole('button', { name: file, pressed: true })).toBeVisible();
+
+ await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText(fixtures[testCase].files[file], {
+ useInnerText: true,
+ });
+ }
+
+ // reset-button should be immediately visible
+ await expect(page.getByRole('button', { name: 'Reset' })).toBeVisible();
+});
diff --git a/e2e/test/navigation.test.ts b/e2e/test/navigation.test.ts
new file mode 100644
index 00000000..bf56be55
--- /dev/null
+++ b/e2e/test/navigation.test.ts
@@ -0,0 +1,30 @@
+import { test, expect } from '@playwright/test';
+
+const BASE_URL = '/tests/navigation/page-one';
+
+test('user can navigate between lessons using nav bar links', async ({ page }) => {
+ await page.goto(BASE_URL);
+ await expect(page.getByRole('heading', { level: 1, name: 'Navigation test - Page one' })).toBeVisible();
+
+ // navigate forwards
+ await navigateToPage('Page two');
+ await navigateToPage('Page three');
+
+ // navigate backwards
+ await navigateToPage('Page two');
+ await navigateToPage('Page one');
+
+ async function navigateToPage(title: string) {
+ await page.getByRole('link', { name: title }).click();
+ await expect(page.getByRole('heading', { level: 1, name: `Navigation test - ${title}` })).toBeVisible();
+ }
+});
+
+test('user can navigate between lessons using breadcrumbs', async ({ page }) => {
+ await page.goto(BASE_URL);
+
+ await page.getByRole('button', { name: 'Tests / Navigation / Page one' }).click({ force: true });
+ await page.getByRole('region', { name: 'Navigation' }).getByRole('link', { name: 'Page three' }).click();
+
+ await expect(page.getByRole('heading', { level: 1, name: 'Navigation test - Page three' })).toBeVisible();
+});
diff --git a/e2e/test/preview.test.ts b/e2e/test/preview.test.ts
new file mode 100644
index 00000000..7ebe4b84
--- /dev/null
+++ b/e2e/test/preview.test.ts
@@ -0,0 +1,26 @@
+import { test, expect } from '@playwright/test';
+
+const BASE_URL = '/tests/preview';
+
+test('user can see single preview tab', async ({ page }) => {
+ await page.goto(`${BASE_URL}/single`);
+
+ await expect(page.getByRole('heading', { level: 1, name: 'Preview test - Single' })).toBeVisible();
+
+ await expect(page.getByText('Node Server')).toBeVisible();
+
+ const preview = page.frameLocator('[title="Node Server"]');
+ await expect(preview.getByText('Index page')).toBeVisible();
+});
+
+test('user can see multiple preview tabs', async ({ page }) => {
+ await page.goto(`${BASE_URL}/multiple`);
+
+ await expect(page.getByRole('heading', { level: 1, name: 'Preview test - Multiple' })).toBeVisible();
+
+ await expect(page.getByText('First Server')).toBeVisible();
+ await expect(page.getByText('Second Server')).toBeVisible();
+
+ await expect(page.frameLocator('[title="First Server"]').getByText('Index page')).toBeVisible({ timeout: 10_000 });
+ await expect(page.frameLocator('[title="Second Server"]').getByText('About page')).toBeVisible({ timeout: 10_000 });
+});
diff --git a/test/ui/test/terminal.test.ts b/e2e/test/terminal.test.ts
similarity index 100%
rename from test/ui/test/terminal.test.ts
rename to e2e/test/terminal.test.ts
diff --git a/e2e/test/utils.ts b/e2e/test/utils.ts
new file mode 100644
index 00000000..d874e093
--- /dev/null
+++ b/e2e/test/utils.ts
@@ -0,0 +1,33 @@
+import { readdirSync, readFileSync, existsSync } from 'node:fs';
+import { fileURLToPath } from 'node:url';
+
+const TESTS_DIR = fileURLToPath(new URL('../src/content/tutorial/tests', import.meta.url));
+
+export function readLessonFilesAndSolution(
+ ...lessons: string[]
+): Record; solution: Record }> {
+ return lessons.reduce(
+ (all, lesson) => ({
+ ...all,
+ [lesson.split('/')[1]]: {
+ files: readDirFiles(`${TESTS_DIR}/${lesson}/_files`),
+ solution: readDirFiles(`${TESTS_DIR}/${lesson}/_solution`),
+ },
+ }),
+ {},
+ );
+}
+
+function readDirFiles(dir: string): Record {
+ if (!existsSync(dir)) {
+ return {};
+ }
+
+ return readdirSync(dir).reduce(
+ (files, file) => ({
+ ...files,
+ [file]: readFileSync(`${dir}/${file}`, 'utf8'),
+ }),
+ {},
+ );
+}
diff --git a/e2e/uno.config.ts b/e2e/uno.config.ts
new file mode 100644
index 00000000..7018afe8
--- /dev/null
+++ b/e2e/uno.config.ts
@@ -0,0 +1,47 @@
+import fs from 'node:fs/promises';
+import { basename, dirname, join } from 'node:path';
+import { globSync, convertPathToPattern } from 'fast-glob';
+import { defineConfig, presetIcons, presetUno, transformerDirectives } from 'unocss';
+import { rules, shortcuts, theme } from '@tutorialkit/theme';
+
+const iconPaths = globSync('./icons/languages/*.svg');
+
+const customIconCollection = iconPaths.reduce(
+ (acc, iconPath) => {
+ const collectionName = basename(dirname(iconPath));
+ const [iconName] = basename(iconPath).split('.');
+
+ acc[collectionName] ??= {};
+ acc[collectionName][iconName] = async () => fs.readFile(iconPath, 'utf8');
+
+ return acc;
+ },
+ {} as Record Promise>>,
+);
+
+export default defineConfig({
+ rules,
+ shortcuts,
+ theme,
+ content: {
+ inline: globSync([
+ `${convertPathToPattern(join(require.resolve('@tutorialkit/components-react'), '..')).replace('\\@', '/@')}/**/*.js`,
+ `${convertPathToPattern(join(require.resolve('@tutorialkit/astro'), '..')).replace('\\@', '/@')}/default/**/*.astro`,
+ ]).map((filePath) => {
+ return () => fs.readFile(filePath, { encoding: 'utf8' });
+ }),
+ },
+ transformers: [transformerDirectives()],
+ presets: [
+ presetUno({
+ dark: {
+ dark: '[data-theme="dark"]',
+ },
+ }),
+ presetIcons({
+ collections: {
+ ...customIconCollection,
+ },
+ }),
+ ],
+});
diff --git a/package.json b/package.json
index 049bfb39..fe8e6a9b 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"scripts": {
- "build": "pnpm run --stream --filter=@tutorialkit/* --filter=create-tutorial build",
+ "build": "pnpm run --stream --filter='@tutorialkit/*' --filter=create-tutorial build",
"dev": "TUTORIALKIT_DEV=true pnpm -r --parallel --stream --filter='./packages/**' run dev",
"changelog": "./scripts/changelog.mjs",
"clean": "./scripts/clean.sh",
@@ -16,7 +16,8 @@
"demo": "pnpm run --filter=demo.tutorialkit.dev dev",
"demo:build": "pnpm run build && pnpm run --filter=demo.tutorialkit.dev build",
"lint": "eslint \"{packages,docs,extensions,integration}/**/*\"",
- "test": "pnpm run --stream --filter=@tutorialkit/* test --run"
+ "test": "pnpm run --stream --filter='@tutorialkit/*' test --run",
+ "test:e2e": "pnpm run --filter='./e2e' test"
},
"license": "MIT",
"packageManager": "pnpm@8.15.6",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4911772c..7282e560 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -83,7 +83,7 @@ importers:
version: 0.59.4
astro:
specifier: ^4.12.0
- version: 4.12.2(@types/node@20.14.11)(sass@1.77.6)(typescript@5.5.3)
+ version: 4.12.2(@types/node@20.14.11)(typescript@5.5.3)
fast-glob:
specifier: ^3.3.2
version: 3.3.2
@@ -164,6 +164,63 @@ importers:
specifier: ^0.59.4
version: 0.59.4(postcss@8.4.39)(vite@5.3.4)
+ e2e:
+ devDependencies:
+ '@astrojs/react':
+ specifier: ^3.6.0
+ version: 3.6.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)(vite@5.3.4)
+ '@iconify-json/ph':
+ specifier: ^1.1.13
+ version: 1.1.13
+ '@iconify-json/svg-spinners':
+ specifier: ^1.1.2
+ version: 1.1.2
+ '@playwright/test':
+ specifier: ^1.46.0
+ version: 1.46.1
+ '@tutorialkit/astro':
+ specifier: workspace:*
+ version: link:../packages/astro
+ '@tutorialkit/components-react':
+ specifier: workspace:*
+ version: link:../packages/components/react
+ '@tutorialkit/runtime':
+ specifier: workspace:*
+ version: link:../packages/runtime
+ '@tutorialkit/theme':
+ specifier: workspace:*
+ version: link:../packages/theme
+ '@tutorialkit/types':
+ specifier: workspace:*
+ version: link:../packages/types
+ '@types/node':
+ specifier: ^22.2.0
+ version: 22.4.2
+ '@unocss/reset':
+ specifier: ^0.59.4
+ version: 0.59.4
+ '@unocss/transformer-directives':
+ specifier: ^0.62.0
+ version: 0.62.2
+ astro:
+ specifier: ^4.12.0
+ version: 4.12.2(@types/node@22.4.2)(typescript@5.5.3)
+ fast-glob:
+ specifier: ^3.3.2
+ version: 3.3.2
+ playwright:
+ specifier: ^1.46.0
+ version: 1.46.1
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ unocss:
+ specifier: ^0.59.4
+ version: 0.59.4(postcss@8.4.39)(vite@5.3.4)
+
extensions/vscode:
dependencies:
'@volar/language-core':
@@ -274,7 +331,7 @@ importers:
version: 1.2.0
astro:
specifier: ^4.12.0
- version: 4.12.2(@types/node@20.14.11)(sass@1.77.6)(typescript@5.5.3)
+ version: 4.12.2(@types/node@20.14.11)(typescript@5.5.3)
astro-expressive-code:
specifier: ^0.35.3
version: 0.35.3(astro@4.12.2)
@@ -613,7 +670,7 @@ importers:
version: 0.59.4
astro:
specifier: ^4.12.0
- version: 4.12.2(@types/node@20.14.11)(sass@1.77.6)(typescript@5.5.3)
+ version: 4.12.2(@types/node@20.14.11)(typescript@5.5.3)
fast-glob:
specifier: ^3.3.2
version: 3.3.2
@@ -795,7 +852,7 @@ packages:
'@astrojs/markdown-remark': 5.1.0
'@mdx-js/mdx': 3.0.1
acorn: 8.12.0
- astro: 4.12.2(@types/node@20.14.11)(sass@1.77.6)(typescript@5.5.3)
+ astro: 4.12.2(@types/node@20.14.11)(typescript@5.5.3)
es-module-lexer: 1.5.3
estree-util-visit: 2.0.0
github-slugger: 2.0.0
@@ -2521,6 +2578,10 @@ packages:
/@jridgewell/sourcemap-codec@1.4.15:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
+ /@jridgewell/sourcemap-codec@1.5.0:
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+ dev: true
+
/@jridgewell/trace-mapping@0.3.25:
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
dependencies:
@@ -2697,6 +2758,14 @@ packages:
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
dev: true
+ /@playwright/test@1.46.1:
+ resolution: {integrity: sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==}
+ engines: {node: '>=18'}
+ hasBin: true
+ dependencies:
+ playwright: 1.46.1
+ dev: true
+
/@polka/url@1.0.0-next.25:
resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==}
@@ -3243,6 +3312,12 @@ packages:
dependencies:
undici-types: 5.26.5
+ /@types/node@22.4.2:
+ resolution: {integrity: sha512-nAvM3Ey230/XzxtyDcJ+VjvlzpzoHwLsF7JaDRfoI0ytO0mVheerNmM45CtA0yOILXwXXxOrcUWH3wltX+7PSw==}
+ dependencies:
+ undici-types: 6.19.8
+ dev: true
+
/@types/normalize-package-data@2.4.4:
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
dev: true
@@ -3468,7 +3543,7 @@ packages:
'@unocss/core': 0.59.4
'@unocss/reset': 0.59.4
'@unocss/vite': 0.59.4(vite@5.3.4)
- vite: 5.3.4(@types/node@20.14.11)(sass@1.77.6)
+ vite: 5.3.4(@types/node@20.14.11)
transitivePeerDependencies:
- rollup
@@ -3503,6 +3578,10 @@ packages:
/@unocss/core@0.59.4:
resolution: {integrity: sha512-bBZ1sgcAtezQVZ1BST9IS3jqcsTLyqKNjiIf7FTnX3DHpfpYuMDFzSOtmkZDzBleOLO/CtcRWjT0HwTSQAmV0A==}
+ /@unocss/core@0.62.2:
+ resolution: {integrity: sha512-86jEFUJ/PSwdb1qqiEi0lWlewfKLQwiH+JAfnh8c2hLjOPVmCkb0nnsYSMh8drmtN5kpk6E06mN0IrKMO7OnvQ==}
+ dev: true
+
/@unocss/extractor-arbitrary-variants@0.59.4:
resolution: {integrity: sha512-RDe4FgMGJQ+tp9GLvhPHni7Cc2O0lHBRMElVlN8LoXJAdODMICdbrEPGJlEfrc+7x/QgVFoR895KpYJh3hIgGA==}
dependencies:
@@ -3593,6 +3672,14 @@ packages:
'@unocss/core': 0.59.4
magic-string: 0.30.10
+ /@unocss/rule-utils@0.62.2:
+ resolution: {integrity: sha512-0za00pkDHsGZhiXBiZfOuUyT+GjCInPxMXj+QsybRU4UrjJS+d3gAteC34BqNFfDAoKQb9G5q9etXztcNHXQbg==}
+ engines: {node: '>=14'}
+ dependencies:
+ '@unocss/core': 0.62.2
+ magic-string: 0.30.11
+ dev: true
+
/@unocss/scope@0.59.4:
resolution: {integrity: sha512-wBQJ39kw4Tfj4km7AoGvSIobPKVnRZVsgc0bema5Y0PL3g1NeVQ/LopBI2zEJWdpxGXUWxSDsXm7BZo6qVlD/A==}
@@ -3623,6 +3710,14 @@ packages:
'@unocss/rule-utils': 0.59.4
css-tree: 2.3.1
+ /@unocss/transformer-directives@0.62.2:
+ resolution: {integrity: sha512-5ZGTmsXkAkFd7pHjHkGy6LGgxhh6bPbZ3jLltf98OhgBZH558y9iui6LKq3n2LpUsSZox6ey3yh1AibvakQeeg==}
+ dependencies:
+ '@unocss/core': 0.62.2
+ '@unocss/rule-utils': 0.62.2
+ css-tree: 2.3.1
+ dev: true
+
/@unocss/transformer-variant-group@0.59.4:
resolution: {integrity: sha512-9XLixxn1NRgP62Kj4R/NC/rpqhql5F2s6ulJ8CAMTEbd/NylVhEANluPGDVUGcLJ4cj6E02hFa8C1PLGSm7/xw==}
dependencies:
@@ -3643,7 +3738,7 @@ packages:
chokidar: 3.6.0
fast-glob: 3.3.2
magic-string: 0.30.10
- vite: 5.3.4(@types/node@20.14.11)(sass@1.77.6)
+ vite: 5.3.4(@types/node@20.14.11)
transitivePeerDependencies:
- rollup
@@ -3658,7 +3753,7 @@ packages:
'@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.7)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
- vite: 5.3.4(@types/node@20.14.11)(sass@1.77.6)
+ vite: 5.3.4(@types/node@20.14.11)
transitivePeerDependencies:
- supports-color
@@ -4009,7 +4104,7 @@ packages:
peerDependencies:
astro: ^4.0.0-beta || ^3.3.0
dependencies:
- astro: 4.12.2(@types/node@20.14.11)(sass@1.77.6)(typescript@5.5.3)
+ astro: 4.12.2(@types/node@20.14.11)(typescript@5.5.3)
rehype-expressive-code: 0.35.3
/astro@4.12.2(@types/node@20.14.11)(sass@1.77.6)(typescript@5.5.3):
@@ -4091,6 +4186,168 @@ packages:
- supports-color
- terser
- typescript
+ dev: true
+
+ /astro@4.12.2(@types/node@20.14.11)(typescript@5.5.3):
+ resolution: {integrity: sha512-l6OmqlL+FiuSi9x6F+EGZitteOznq1JffOil7st7cdqeMCTEIym4oagI1a6zp6QekliKWEEZWdplGhgh1k1f7Q==}
+ engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'}
+ hasBin: true
+ dependencies:
+ '@astrojs/compiler': 2.9.1
+ '@astrojs/internal-helpers': 0.4.1
+ '@astrojs/markdown-remark': 5.2.0
+ '@astrojs/telemetry': 3.1.0
+ '@babel/core': 7.24.9
+ '@babel/generator': 7.24.10
+ '@babel/parser': 7.24.8
+ '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.9)
+ '@babel/traverse': 7.24.8
+ '@babel/types': 7.24.9
+ '@types/babel__core': 7.20.5
+ '@types/cookie': 0.6.0
+ acorn: 8.12.1
+ aria-query: 5.3.0
+ axobject-query: 4.1.0
+ boxen: 7.1.1
+ chokidar: 3.6.0
+ ci-info: 4.0.0
+ clsx: 2.1.1
+ common-ancestor-path: 1.0.1
+ cookie: 0.6.0
+ cssesc: 3.0.0
+ debug: 4.3.5
+ deterministic-object-hash: 2.0.2
+ devalue: 5.0.0
+ diff: 5.2.0
+ dlv: 1.1.3
+ dset: 3.1.3
+ es-module-lexer: 1.5.4
+ esbuild: 0.21.5
+ estree-walker: 3.0.3
+ execa: 8.0.1
+ fast-glob: 3.3.2
+ flattie: 1.1.1
+ github-slugger: 2.0.0
+ gray-matter: 4.0.3
+ html-escaper: 3.0.3
+ http-cache-semantics: 4.1.1
+ js-yaml: 4.1.0
+ kleur: 4.1.5
+ magic-string: 0.30.10
+ mrmime: 2.0.0
+ ora: 8.0.1
+ p-limit: 6.1.0
+ p-queue: 8.0.1
+ path-to-regexp: 6.2.2
+ preferred-pm: 4.0.0
+ prompts: 2.4.2
+ rehype: 13.0.1
+ semver: 7.6.2
+ shiki: 1.11.0
+ string-width: 7.2.0
+ strip-ansi: 7.1.0
+ tsconfck: 3.1.1(typescript@5.5.3)
+ unist-util-visit: 5.0.0
+ vfile: 6.0.2
+ vite: 5.3.4(@types/node@20.14.11)
+ vitefu: 0.2.5(vite@5.3.4)
+ which-pm: 3.0.0
+ yargs-parser: 21.1.1
+ zod: 3.23.8
+ zod-to-json-schema: 3.23.1(zod@3.23.8)
+ optionalDependencies:
+ sharp: 0.33.4
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - lightningcss
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - typescript
+
+ /astro@4.12.2(@types/node@22.4.2)(typescript@5.5.3):
+ resolution: {integrity: sha512-l6OmqlL+FiuSi9x6F+EGZitteOznq1JffOil7st7cdqeMCTEIym4oagI1a6zp6QekliKWEEZWdplGhgh1k1f7Q==}
+ engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'}
+ hasBin: true
+ dependencies:
+ '@astrojs/compiler': 2.9.1
+ '@astrojs/internal-helpers': 0.4.1
+ '@astrojs/markdown-remark': 5.2.0
+ '@astrojs/telemetry': 3.1.0
+ '@babel/core': 7.24.9
+ '@babel/generator': 7.24.10
+ '@babel/parser': 7.24.8
+ '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.9)
+ '@babel/traverse': 7.24.8
+ '@babel/types': 7.24.9
+ '@types/babel__core': 7.20.5
+ '@types/cookie': 0.6.0
+ acorn: 8.12.1
+ aria-query: 5.3.0
+ axobject-query: 4.1.0
+ boxen: 7.1.1
+ chokidar: 3.6.0
+ ci-info: 4.0.0
+ clsx: 2.1.1
+ common-ancestor-path: 1.0.1
+ cookie: 0.6.0
+ cssesc: 3.0.0
+ debug: 4.3.5
+ deterministic-object-hash: 2.0.2
+ devalue: 5.0.0
+ diff: 5.2.0
+ dlv: 1.1.3
+ dset: 3.1.3
+ es-module-lexer: 1.5.4
+ esbuild: 0.21.5
+ estree-walker: 3.0.3
+ execa: 8.0.1
+ fast-glob: 3.3.2
+ flattie: 1.1.1
+ github-slugger: 2.0.0
+ gray-matter: 4.0.3
+ html-escaper: 3.0.3
+ http-cache-semantics: 4.1.1
+ js-yaml: 4.1.0
+ kleur: 4.1.5
+ magic-string: 0.30.10
+ mrmime: 2.0.0
+ ora: 8.0.1
+ p-limit: 6.1.0
+ p-queue: 8.0.1
+ path-to-regexp: 6.2.2
+ preferred-pm: 4.0.0
+ prompts: 2.4.2
+ rehype: 13.0.1
+ semver: 7.6.2
+ shiki: 1.11.0
+ string-width: 7.2.0
+ strip-ansi: 7.1.0
+ tsconfck: 3.1.1(typescript@5.5.3)
+ unist-util-visit: 5.0.0
+ vfile: 6.0.2
+ vite: 5.3.4(@types/node@22.4.2)
+ vitefu: 0.2.5(vite@5.3.4)
+ which-pm: 3.0.0
+ yargs-parser: 21.1.1
+ zod: 3.23.8
+ zod-to-json-schema: 3.23.1(zod@3.23.8)
+ optionalDependencies:
+ sharp: 0.33.4
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - lightningcss
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - typescript
+ dev: true
/astrojs-compiler-sync@1.0.0(@astrojs/compiler@2.9.1):
resolution: {integrity: sha512-IM6FxpMoBxkGGdKppkFHNQIC9Wge7jspG2MIJff8DOhG41USNJLxJfxRm7wnkTKWlYK5Y1YFFNYr2vUUKkI8sw==}
@@ -5386,6 +5643,14 @@ packages:
universalify: 2.0.1
dev: true
+ /fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -5821,6 +6086,7 @@ packages:
/immutable@4.3.6:
resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==}
+ dev: true
/import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
@@ -6239,6 +6505,12 @@ packages:
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
+ /magic-string@0.30.11:
+ resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+ dev: true
+
/markdown-extensions@2.0.0:
resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==}
engines: {node: '>=16'}
@@ -7201,6 +7473,22 @@ packages:
mlly: 1.7.1
pathe: 1.1.2
+ /playwright-core@1.46.1:
+ resolution: {integrity: sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==}
+ engines: {node: '>=18'}
+ hasBin: true
+ dev: true
+
+ /playwright@1.46.1:
+ resolution: {integrity: sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==}
+ engines: {node: '>=18'}
+ hasBin: true
+ dependencies:
+ playwright-core: 1.46.1
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
/postcss-nested@6.0.1(postcss@8.4.39):
resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
engines: {node: '>=12.0'}
@@ -7703,6 +7991,7 @@ packages:
chokidar: 3.6.0
immutable: 4.3.6
source-map-js: 1.2.0
+ dev: true
/sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
@@ -8309,6 +8598,10 @@ packages:
/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+ /undici-types@6.19.8:
+ resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
+ dev: true
+
/unherit@3.0.1:
resolution: {integrity: sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==}
@@ -8472,7 +8765,7 @@ packages:
'@unocss/transformer-directives': 0.59.4
'@unocss/transformer-variant-group': 0.59.4
'@unocss/vite': 0.59.4(vite@5.3.4)
- vite: 5.3.4(@types/node@20.14.11)(sass@1.77.6)
+ vite: 5.3.4(@types/node@20.14.11)
transitivePeerDependencies:
- postcss
- rollup
@@ -8552,7 +8845,7 @@ packages:
debug: 4.3.5
pathe: 1.1.2
picocolors: 1.0.1
- vite: 5.3.4(@types/node@20.14.11)(sass@1.77.6)
+ vite: 5.3.4(@types/node@20.14.11)
transitivePeerDependencies:
- '@types/node'
- less
@@ -8582,7 +8875,7 @@ packages:
perfect-debounce: 1.0.0
picocolors: 1.0.1
sirv: 2.0.4
- vite: 5.3.4(@types/node@20.14.11)(sass@1.77.6)
+ vite: 5.3.4(@types/node@20.14.11)
transitivePeerDependencies:
- rollup
- supports-color
@@ -8599,12 +8892,47 @@ packages:
debug: 4.3.5
globrex: 0.1.2
tsconfck: 3.1.0(typescript@5.5.3)
- vite: 5.3.4(@types/node@20.14.11)(sass@1.77.6)
+ vite: 5.3.4(@types/node@20.14.11)
transitivePeerDependencies:
- supports-color
- typescript
dev: true
+ /vite@5.3.4(@types/node@20.14.11):
+ resolution: {integrity: sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ dependencies:
+ '@types/node': 20.14.11
+ esbuild: 0.21.5
+ postcss: 8.4.39
+ rollup: 4.18.1
+ optionalDependencies:
+ fsevents: 2.3.3
+
/vite@5.3.4(@types/node@20.14.11)(sass@1.77.6):
resolution: {integrity: sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -8640,6 +8968,43 @@ packages:
sass: 1.77.6
optionalDependencies:
fsevents: 2.3.3
+ dev: true
+
+ /vite@5.3.4(@types/node@22.4.2):
+ resolution: {integrity: sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ dependencies:
+ '@types/node': 22.4.2
+ esbuild: 0.21.5
+ postcss: 8.4.39
+ rollup: 4.18.1
+ optionalDependencies:
+ fsevents: 2.3.3
+ dev: true
/vitefu@0.2.5(vite@5.3.4):
resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==}
@@ -8649,7 +9014,7 @@ packages:
vite:
optional: true
dependencies:
- vite: 5.3.4(@types/node@20.14.11)(sass@1.77.6)
+ vite: 5.3.4(@types/node@20.14.11)
/vitest@1.6.0(@types/node@20.14.11):
resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==}
@@ -8694,7 +9059,7 @@ packages:
strip-literal: 2.1.0
tinybench: 2.8.0
tinypool: 0.8.4
- vite: 5.3.4(@types/node@20.14.11)(sass@1.77.6)
+ vite: 5.3.4(@types/node@20.14.11)
vite-node: 1.6.0(@types/node@20.14.11)
why-is-node-running: 2.2.2
transitivePeerDependencies:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index de54ed4f..120900a4 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -4,3 +4,4 @@ packages:
- 'docs/*'
- 'extensions/*'
- 'integration'
+ - 'e2e'