From 6f835183b1a8e5719744e5b663ef742eaa954568 Mon Sep 17 00:00:00 2001 From: Horus Lugo Date: Thu, 21 Mar 2024 00:05:10 +0100 Subject: [PATCH] Prepare demo --- .github/workflows/ci.yml | 2 +- apps/react-strict-dom-demo/.gitignore | 24 ++++ apps/react-strict-dom-demo/README.md | 30 +++++ apps/react-strict-dom-demo/index.html | 13 +++ apps/react-strict-dom-demo/package.json | 28 +++++ apps/react-strict-dom-demo/public/vite.svg | 1 + apps/react-strict-dom-demo/src/App.tsx | 11 ++ apps/react-strict-dom-demo/src/Card.tsx | 24 ++++ .../src/aliased-import-theme.stylex.ts | 5 + apps/react-strict-dom-demo/src/main.tsx | 9 ++ .../react-strict-dom-demo/src/theme.stylex.ts | 5 + apps/react-strict-dom-demo/src/vite-env.d.ts | 1 + apps/react-strict-dom-demo/test/build.test.ts | 105 ++++++++++++++++++ apps/react-strict-dom-demo/test/dev.test.ts | 85 ++++++++++++++ apps/react-strict-dom-demo/test/utils.ts | 5 + apps/react-strict-dom-demo/tsconfig.json | 31 ++++++ apps/react-strict-dom-demo/tsconfig.node.json | 10 ++ apps/react-strict-dom-demo/vite.config.ts | 14 +++ 18 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 apps/react-strict-dom-demo/.gitignore create mode 100644 apps/react-strict-dom-demo/README.md create mode 100644 apps/react-strict-dom-demo/index.html create mode 100644 apps/react-strict-dom-demo/package.json create mode 100644 apps/react-strict-dom-demo/public/vite.svg create mode 100644 apps/react-strict-dom-demo/src/App.tsx create mode 100644 apps/react-strict-dom-demo/src/Card.tsx create mode 100644 apps/react-strict-dom-demo/src/aliased-import-theme.stylex.ts create mode 100644 apps/react-strict-dom-demo/src/main.tsx create mode 100644 apps/react-strict-dom-demo/src/theme.stylex.ts create mode 100644 apps/react-strict-dom-demo/src/vite-env.d.ts create mode 100644 apps/react-strict-dom-demo/test/build.test.ts create mode 100644 apps/react-strict-dom-demo/test/dev.test.ts create mode 100644 apps/react-strict-dom-demo/test/utils.ts create mode 100644 apps/react-strict-dom-demo/tsconfig.json create mode 100644 apps/react-strict-dom-demo/tsconfig.node.json create mode 100644 apps/react-strict-dom-demo/vite.config.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f348928..402d9b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: needs: [check] strategy: matrix: - demo: [vite, remix, sveltekit, storybook, vue, qwik] + demo: [vite, remix, sveltekit, storybook, vue, qwik, react-strict-dom] steps: - uses: actions/checkout@v3 - uses: pnpm/action-setup@v2 diff --git a/apps/react-strict-dom-demo/.gitignore b/apps/react-strict-dom-demo/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/apps/react-strict-dom-demo/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/apps/react-strict-dom-demo/README.md b/apps/react-strict-dom-demo/README.md new file mode 100644 index 0000000..0d6babe --- /dev/null +++ b/apps/react-strict-dom-demo/README.md @@ -0,0 +1,30 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default { + // other rules... + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +} +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/apps/react-strict-dom-demo/index.html b/apps/react-strict-dom-demo/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/apps/react-strict-dom-demo/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/apps/react-strict-dom-demo/package.json b/apps/react-strict-dom-demo/package.json new file mode 100644 index 0000000..a3eb461 --- /dev/null +++ b/apps/react-strict-dom-demo/package.json @@ -0,0 +1,28 @@ +{ + "name": "react-strict-dom-demo", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "test": "tsx --test test/*.test.ts" + }, + "dependencies": { + "@stylexjs/open-props": "^0.3.0", + "@stylexjs/stylex": "^0.5.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@internal/test-utils": "workspace:*", + "@types/react": "^18.2.37", + "@types/react-dom": "^18.2.15", + "@vitejs/plugin-react": "^4.2.0", + "tsx": "^4.6.2", + "typescript": "^5.2.2", + "vite": "^4.5.1", + "vite-plugin-stylex": "workspace:*" + } +} diff --git a/apps/react-strict-dom-demo/public/vite.svg b/apps/react-strict-dom-demo/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/apps/react-strict-dom-demo/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/react-strict-dom-demo/src/App.tsx b/apps/react-strict-dom-demo/src/App.tsx new file mode 100644 index 0000000..fdfb4ef --- /dev/null +++ b/apps/react-strict-dom-demo/src/App.tsx @@ -0,0 +1,11 @@ +import Card from "./Card"; + +function App() { + return ( + <> + Very cool card 👍 + + ); +} + +export default App; diff --git a/apps/react-strict-dom-demo/src/Card.tsx b/apps/react-strict-dom-demo/src/Card.tsx new file mode 100644 index 0000000..f15e47c --- /dev/null +++ b/apps/react-strict-dom-demo/src/Card.tsx @@ -0,0 +1,24 @@ +import * as stylex from "@stylexjs/stylex"; +import { tokens } from "./theme.stylex"; +import { aliasedTokens } from "@/aliased-import-theme.stylex"; +import { colors } from "@stylexjs/open-props/lib/colors.stylex"; + +export default function Card({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ); +} + +const styles = stylex.create({ + root: { + backgroundColor: "white", + borderRadius: 8, + padding: 16, + boxShadow: "0 0 16px rgba(0, 0, 0, 0.1)", + color: tokens.primaryTextColor, + border: `1px solid ${colors.blue1}`, + outlineColor: aliasedTokens.primaryOutlineColor, + }, +}); diff --git a/apps/react-strict-dom-demo/src/aliased-import-theme.stylex.ts b/apps/react-strict-dom-demo/src/aliased-import-theme.stylex.ts new file mode 100644 index 0000000..6115ec6 --- /dev/null +++ b/apps/react-strict-dom-demo/src/aliased-import-theme.stylex.ts @@ -0,0 +1,5 @@ +import * as stylex from "@stylexjs/stylex"; + +export const aliasedTokens = stylex.defineVars({ + primaryOutlineColor: "blue", +}); diff --git a/apps/react-strict-dom-demo/src/main.tsx b/apps/react-strict-dom-demo/src/main.tsx new file mode 100644 index 0000000..f46c379 --- /dev/null +++ b/apps/react-strict-dom-demo/src/main.tsx @@ -0,0 +1,9 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + +); diff --git a/apps/react-strict-dom-demo/src/theme.stylex.ts b/apps/react-strict-dom-demo/src/theme.stylex.ts new file mode 100644 index 0000000..0fc1af8 --- /dev/null +++ b/apps/react-strict-dom-demo/src/theme.stylex.ts @@ -0,0 +1,5 @@ +import * as stylex from "@stylexjs/stylex"; + +export const tokens = stylex.defineVars({ + primaryTextColor: "#000", +}); diff --git a/apps/react-strict-dom-demo/src/vite-env.d.ts b/apps/react-strict-dom-demo/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/apps/react-strict-dom-demo/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/apps/react-strict-dom-demo/test/build.test.ts b/apps/react-strict-dom-demo/test/build.test.ts new file mode 100644 index 0000000..454b956 --- /dev/null +++ b/apps/react-strict-dom-demo/test/build.test.ts @@ -0,0 +1,105 @@ +import { describe, test, before, after } from "node:test"; +import * as vite from "vite"; +import { VITE_ROOT } from "./utils"; +import * as assert from "node:assert"; +import { makeTempDir } from "@internal/test-utils"; +import * as fs from "node:fs/promises"; +import * as path from "node:path"; + +describe("build", () => { + let tempDir: string; + + before(async () => { + tempDir = await makeTempDir(); + }); + + after(async () => { + await fs.rm(tempDir, { recursive: true }); + }); + + test("builds without crashing", async () => { + const build = await vite.build({ + root: VITE_ROOT, + }); + + assert.ok(build, "build should be truthy"); + }); +}); + +describe("build output", () => { + let tempDir: string; + + before(async () => { + tempDir = await makeTempDir(); + + await vite.build({ + root: VITE_ROOT, + build: { + outDir: tempDir, + }, + }); + }); + + after(async () => { + await fs.rm(tempDir, { recursive: true }); + }); + + test("built assets should contain a stylex stylesheet", async () => { + const files = await fs.readdir(path.join(tempDir, "assets")); + const stylexFile = files.some( + (file) => file.includes("stylex") && file.endsWith(".css") + ); + + assert.ok(stylexFile, "an stylex file should exist in the build output"); + }); + + test("index.html contains a link to the stylex stylesheet", async () => { + const indexHtml = await fs.readFile( + path.join(tempDir, "index.html"), + "utf-8" + ); + const stylexLink = indexHtml.includes( + ` { + const files = await fs.readdir(path.join(tempDir, "assets")); + const stylexFile = files.find( + (file) => file.includes("stylex") && file.endsWith(".css") + ); + const stylexCss = await fs.readFile( + path.join(tempDir, "assets", stylexFile), + "utf-8" + ); + const expectedCss = `background-color:white`; + + assert.ok( + stylexCss.includes(expectedCss), + `stylex stylesheet should contain the expected styles: ${expectedCss}` + ); + }); + + test("stylex stylesheet contains styles from @stylexjs/open-props package", async () => { + const files = await fs.readdir(path.join(tempDir, "assets")); + const stylexFile = files.find( + (file) => file.includes("stylex") && file.endsWith(".css") + ); + const stylexCss = await fs.readFile( + path.join(tempDir, "assets", stylexFile), + "utf-8" + ); + + // Corresponds with color.blue1 from @stylexjs/open-props + const expectedCss = `#d0ebff`; + assert.ok( + stylexCss.includes(expectedCss), + `stylex stylesheet should contain the expected styles: ${expectedCss}` + ); + }); +}); diff --git a/apps/react-strict-dom-demo/test/dev.test.ts b/apps/react-strict-dom-demo/test/dev.test.ts new file mode 100644 index 0000000..1f099a3 --- /dev/null +++ b/apps/react-strict-dom-demo/test/dev.test.ts @@ -0,0 +1,85 @@ +import { describe, test, beforeEach, afterEach } from "node:test"; +import * as vite from "vite"; +import path from "path"; +import { + Browser, + openBrowser, + closeBrowser, + runtimeInjectionTest, + hmrTest, + cleanHmrTest, + friendlyClassNameTest, + externalStyleXTest, +} from "@internal/test-utils"; +import { CARD_COMPONENT_PATH, VITE_ROOT } from "./utils"; + +describe("dev", () => { + let devServer: vite.ViteDevServer; + let serverUrl: string; + let browser: Browser; + + beforeEach(async () => { + devServer = await vite.createServer({ + root: VITE_ROOT, + server: { + port: 0, + host: "127.0.0.1", + }, + }); + + devServer = await devServer.listen(); + + const address = devServer.httpServer.address(); + + if (typeof address === "string") { + serverUrl = address; + } else { + serverUrl = `http://${address.address}:${address.port}`; + } + + browser = await openBrowser(); + }); + + afterEach(async () => { + await devServer.close(); + await closeBrowser(browser); + }); + + test("runtime injection works", async () => { + const page = await browser.newPage(); + await page.goto(serverUrl); + + await runtimeInjectionTest(page); + }); + + test("friendly classnames work", async () => { + const page = await browser.newPage(); + await page.goto(serverUrl); + + await friendlyClassNameTest(page); + }); + + test("external stylesheets like @stylexjs/open-props work", async () => { + const page = await browser.newPage(); + await page.goto(serverUrl); + + await externalStyleXTest(page); + }); + + describe("hmr", () => { + beforeEach(async () => { + await cleanHmrTest(CARD_COMPONENT_PATH); + }); + + afterEach(async () => { + await cleanHmrTest(CARD_COMPONENT_PATH); + }); + + test("updating a style works", async () => { + const page = await browser.newPage(); + await page.goto(serverUrl); + + await hmrTest(page, CARD_COMPONENT_PATH); + }); + }); +}); diff --git a/apps/react-strict-dom-demo/test/utils.ts b/apps/react-strict-dom-demo/test/utils.ts new file mode 100644 index 0000000..48cc857 --- /dev/null +++ b/apps/react-strict-dom-demo/test/utils.ts @@ -0,0 +1,5 @@ +import path from "path"; + +export const __dirname = path.dirname(new URL(import.meta.url).pathname); +export const CARD_COMPONENT_PATH = path.resolve(__dirname, "../src/Card.tsx"); +export const VITE_ROOT = path.resolve(__dirname, "../"); diff --git a/apps/react-strict-dom-demo/tsconfig.json b/apps/react-strict-dom-demo/tsconfig.json new file mode 100644 index 0000000..1631cfa --- /dev/null +++ b/apps/react-strict-dom-demo/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + "baseUrl": ".", + "rootDir": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/apps/react-strict-dom-demo/tsconfig.node.json b/apps/react-strict-dom-demo/tsconfig.node.json new file mode 100644 index 0000000..f63dadc --- /dev/null +++ b/apps/react-strict-dom-demo/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts", "test"] +} diff --git a/apps/react-strict-dom-demo/vite.config.ts b/apps/react-strict-dom-demo/vite.config.ts new file mode 100644 index 0000000..95a1173 --- /dev/null +++ b/apps/react-strict-dom-demo/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import styleX from "vite-plugin-stylex"; +import { resolve } from "node:path"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react(), styleX()], + resolve: { + alias: { + "@": resolve(__dirname, "src"), + }, + }, +});