diff --git a/.github/sync.yml b/.github/sync.yml index d3409bcba0..fe52dae2ab 100644 --- a/.github/sync.yml +++ b/.github/sync.yml @@ -39,3 +39,10 @@ nextauthjs/next-auth-pages-example: deleteOrphaned: true - .github/FUNDING.yml - LICENSE + +nextauthjs/qwik-auth-example: + - source: apps/examples/qwik + dest: . + deleteOrphaned: true + - .github/FUNDING.yml + - LICENSE diff --git a/apps/examples/qwik/.env.example b/apps/examples/qwik/.env.example new file mode 100644 index 0000000000..90d0fe8a71 --- /dev/null +++ b/apps/examples/qwik/.env.example @@ -0,0 +1,4 @@ +AUTH_SECRET= + +AUTH_GITHUB_ID= +AUTH_GITHUB_SECRET= \ No newline at end of file diff --git a/apps/examples/qwik/.eslintignore b/apps/examples/qwik/.eslintignore new file mode 100644 index 0000000000..1acecc10a0 --- /dev/null +++ b/apps/examples/qwik/.eslintignore @@ -0,0 +1,38 @@ +**/*.log +**/.DS_Store +*. +.vscode/settings.json +.history +.yarn +bazel-* +bazel-bin +bazel-out +bazel-qwik +bazel-testlogs +dist +dist-dev +lib +lib-types +etc +external +node_modules +temp +tsc-out +tsdoc-metadata.json +target +output +rollup.config.js +build +.cache +.vscode +.rollup.cache +dist +tsconfig.tsbuildinfo +vite.config.ts +*.spec.tsx +*.spec.ts +.netlify +pnpm-lock.yaml +package-lock.json +yarn.lock +server diff --git a/apps/examples/qwik/.eslintrc.cjs b/apps/examples/qwik/.eslintrc.cjs new file mode 100644 index 0000000000..70dc5d0392 --- /dev/null +++ b/apps/examples/qwik/.eslintrc.cjs @@ -0,0 +1,42 @@ +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + node: true, + }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:qwik/recommended", + ], + parser: "@typescript-eslint/parser", + parserOptions: { + tsconfigRootDir: __dirname, + project: ["./tsconfig.json"], + ecmaVersion: 2021, + sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + }, + plugins: ["@typescript-eslint"], + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-this-alias": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/ban-ts-comment": "off", + "prefer-spread": "off", + "no-case-declarations": "off", + "no-console": "off", + "@typescript-eslint/no-unused-vars": ["error"], + "@typescript-eslint/consistent-type-imports": "warn", + "@typescript-eslint/no-unnecessary-condition": "warn", + }, +}; diff --git a/apps/examples/qwik/.gitignore b/apps/examples/qwik/.gitignore new file mode 100644 index 0000000000..48dce735b5 --- /dev/null +++ b/apps/examples/qwik/.gitignore @@ -0,0 +1,41 @@ +# Build +/dist +/lib +/lib-types +/server + +# Development +node_modules +*.local + +# Cache +.cache +.mf +.rollup.cache +tsconfig.tsbuildinfo + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Editor +.vscode/* +!.vscode/launch.json +!.vscode/*.code-snippets + +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Yarn +.yarn/* +!.yarn/releases diff --git a/apps/examples/qwik/.prettierignore b/apps/examples/qwik/.prettierignore new file mode 100644 index 0000000000..b62a96816e --- /dev/null +++ b/apps/examples/qwik/.prettierignore @@ -0,0 +1,37 @@ +**/*.log +**/.DS_Store +*. +.vscode/settings.json +.history +.yarn +bazel-* +bazel-bin +bazel-out +bazel-qwik +bazel-testlogs +dist +dist-dev +lib +lib-types +etc +external +node_modules +temp +tsc-out +tsdoc-metadata.json +target +output +rollup.config.js +build +.cache +.vscode +.rollup.cache +tsconfig.tsbuildinfo +vite.config.ts +*.spec.tsx +*.spec.ts +.netlify +pnpm-lock.yaml +package-lock.json +yarn.lock +server diff --git a/apps/examples/qwik/.prettierrc.js b/apps/examples/qwik/.prettierrc.js new file mode 100644 index 0000000000..7b5b93ec70 --- /dev/null +++ b/apps/examples/qwik/.prettierrc.js @@ -0,0 +1,3 @@ +export default { + plugins: ["prettier-plugin-tailwindcss"], +}; diff --git a/apps/examples/qwik/.vscode/launch.json b/apps/examples/qwik/.vscode/launch.json new file mode 100644 index 0000000000..e684cc8442 --- /dev/null +++ b/apps/examples/qwik/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Chrome", + "request": "launch", + "type": "chrome", + "url": "http://localhost:5173", + "webRoot": "${workspaceFolder}" + }, + { + "type": "node", + "name": "dev.debug", + "request": "launch", + "skipFiles": ["/**"], + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/node_modules/vite/bin/vite.js", + "args": ["--mode", "ssr", "--force"] + } + ] +} diff --git a/apps/examples/qwik/.vscode/qwik-city.code-snippets b/apps/examples/qwik/.vscode/qwik-city.code-snippets new file mode 100644 index 0000000000..a6cc6ce981 --- /dev/null +++ b/apps/examples/qwik/.vscode/qwik-city.code-snippets @@ -0,0 +1,36 @@ +{ + "onRequest": { + "scope": "javascriptreact,typescriptreact", + "prefix": "qonRequest", + "description": "onRequest function for a route index", + "body": [ + "export const onRequest: RequestHandler = (request) => {", + " $0", + "};" + ] + }, + "loader$": { + "scope": "javascriptreact,typescriptreact", + "prefix": "qloader$", + "description": "loader$()", + "body": ["export const $1 = routeLoader$(() => {", " $0", "});"] + }, + "action$": { + "scope": "javascriptreact,typescriptreact", + "prefix": "qaction$", + "description": "action$()", + "body": ["export const $1 = routeAction$((data) => {", " $0", "});"] + }, + "Full Page": { + "scope": "javascriptreact,typescriptreact", + "prefix": "qpage", + "description": "Simple page component", + "body": [ + "import { component$ } from '@builder.io/qwik';", + "", + "export default component$(() => {", + " $0", + "});" + ] + } +} diff --git a/apps/examples/qwik/.vscode/qwik.code-snippets b/apps/examples/qwik/.vscode/qwik.code-snippets new file mode 100644 index 0000000000..d4efe6b01e --- /dev/null +++ b/apps/examples/qwik/.vscode/qwik.code-snippets @@ -0,0 +1,78 @@ +{ + "Qwik component (simple)": { + "scope": "javascriptreact,typescriptreact", + "prefix": "qcomponent$", + "description": "Simple Qwik component", + "body": [ + "export const ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}} = component$(() => {", + " return <${2:div}>$4", + "});" + ] + }, + "Qwik component (props)": { + "scope": "typescriptreact", + "prefix": "qcomponent$ + props", + "description": "Qwik component w/ props", + "body": [ + "export interface ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}}Props {", + " $2", + "}", + "", + "export const $1 = component$<$1Props>((props) => {", + " const ${2:count} = useSignal(0);", + " return (", + " <${3:div} on${4:Click}$={(ev) => {$5}}>", + " $6", + " ", + " );", + "});" + ] + }, + "Qwik signal": { + "scope": "javascriptreact,typescriptreact", + "prefix": "quseSignal", + "description": "useSignal() declaration", + "body": ["const ${1:foo} = useSignal($2);", "$0"] + }, + "Qwik store": { + "scope": "javascriptreact,typescriptreact", + "prefix": "quseStore", + "description": "useStore() declaration", + "body": ["const ${1:state} = useStore({", " $2", "});", "$0"] + }, + "$ hook": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q$", + "description": "$() function hook", + "body": ["$(() => {", " $0", "});", ""] + }, + "useVisibleTask": { + "scope": "javascriptreact,typescriptreact", + "prefix": "quseVisibleTask", + "description": "useVisibleTask$() function hook", + "body": ["useVisibleTask$(({ track }) => {", " $0", "});", ""] + }, + "useTask": { + "scope": "javascriptreact,typescriptreact", + "prefix": "quseTask$", + "description": "useTask$() function hook", + "body": [ + "useTask$(({ track }) => {", + " track(() => $1);", + " $0", + "});", + "" + ] + }, + "useResource": { + "scope": "javascriptreact,typescriptreact", + "prefix": "quseResource$", + "description": "useResource$() declaration", + "body": [ + "const $1 = useResource$(({ track, cleanup }) => {", + " $0", + "});", + "" + ] + } +} diff --git a/apps/examples/qwik/README.md b/apps/examples/qwik/README.md new file mode 100644 index 0000000000..73a6dcc252 --- /dev/null +++ b/apps/examples/qwik/README.md @@ -0,0 +1,65 @@ +# Qwik City App ⚡️ + +- [Qwik Docs](https://qwik.dev/) +- [Discord](https://qwik.dev/chat) +- [Qwik GitHub](https://github.com/QwikDev/qwik) +- [@QwikDev](https://twitter.com/QwikDev) +- [Vite](https://vitejs.dev/) + +--- + +## Project Structure + +This project is using Qwik with [QwikCity](https://qwik.dev/qwikcity/overview/). QwikCity is just an extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more. + +Inside your project, you'll see the following directory structure: + +``` +├── public/ +│ └── ... +└── src/ + ├── components/ + │ └── ... + └── routes/ + └── ... +``` + +- `src/routes`: Provides the directory-based routing, which can include a hierarchy of `layout.tsx` layout files, and an `index.tsx` file as the page. Additionally, `index.ts` files are endpoints. Please see the [routing docs](https://qwik.dev/qwikcity/routing/overview/) for more info. + +- `src/components`: Recommended directory for components. + +- `public`: Any static assets, like images, can be placed in the public directory. Please see the [Vite public directory](https://vitejs.dev/guide/assets.html#the-public-directory) for more info. + +## Add Integrations and deployment + +Use the `pnpm qwik add` command to add additional integrations. Some examples of integrations includes: Cloudflare, Netlify or Express Server, and the [Static Site Generator (SSG)](https://qwik.dev/qwikcity/guides/static-site-generation/). + +```shell +pnpm qwik add # or `pnpm qwik add` +``` + +## Development + +Development mode uses [Vite's development server](https://vitejs.dev/). The `dev` command will server-side render (SSR) the output during development. + +```shell +npm start # or `pnpm start` +``` + +> Note: during dev mode, Vite may request a significant number of `.js` files. This does not represent a Qwik production build. + +## Preview + +The preview command will create a production build of the client modules, a production build of `src/entry.preview.tsx`, and run a local server. The preview server is only for convenience to preview a production build locally and should not be used as a production server. + +```shell +pnpm preview # or `pnpm preview` +``` + +## Production + +The production build will generate client and server modules by running both client and server build commands. The build command will use Typescript to run a type check on the source code. + +```shell +pnpm build # or `pnpm build` +``` diff --git a/apps/examples/qwik/package.json b/apps/examples/qwik/package.json new file mode 100644 index 0000000000..736314c1c7 --- /dev/null +++ b/apps/examples/qwik/package.json @@ -0,0 +1,49 @@ +{ + "name": "qwik-example-app", + "description": "An example project for Auth.js with Qwik", + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "engines-annotation": "Mostly required by sharp which needs a Node-API v9 compatible runtime", + "private": true, + "trustedDependencies": [ + "sharp" + ], + "trustedDependencies-annotation": "Needed for bun to allow running install scripts", + "type": "module", + "scripts": { + "build": "qwik build", + "build.client": "vite build", + "build.preview": "vite build --ssr src/entry.preview.tsx", + "build.types": "tsc --incremental --noEmit", + "deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'", + "dev": "vite --mode ssr", + "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force", + "fmt": "prettier --write .", + "fmt.check": "prettier --check .", + "lint": "eslint \"src/**/*.ts*\"", + "preview": "qwik build preview && vite preview --open", + "start": "vite --open --mode ssr", + "qwik": "qwik" + }, + "devDependencies": { + "@auth/qwik": "latest", + "@builder.io/qwik": "^1.5.7", + "@builder.io/qwik-city": "^1.5.7", + "@types/eslint": "^8.56.10", + "@types/node": "^20.12.7", + "@typescript-eslint/eslint-plugin": "^7.7.1", + "@typescript-eslint/parser": "^7.7.1", + "autoprefixer": "^10.4.14", + "eslint": "^8.57.0", + "eslint-plugin-qwik": "^1.5.7", + "postcss": "^8.4.31", + "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.4", + "tailwindcss": "3.3.3", + "typescript": "5.4.5", + "undici": "*", + "vite": "^5.2.10", + "vite-tsconfig-paths": "^4.2.1" + } +} diff --git a/apps/examples/qwik/postcss.config.cjs b/apps/examples/qwik/postcss.config.cjs new file mode 100644 index 0000000000..12a703d900 --- /dev/null +++ b/apps/examples/qwik/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/apps/examples/qwik/public/favicon.svg b/apps/examples/qwik/public/favicon.svg new file mode 100644 index 0000000000..0ded7c138b --- /dev/null +++ b/apps/examples/qwik/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/examples/qwik/public/manifest.json b/apps/examples/qwik/public/manifest.json new file mode 100644 index 0000000000..c18e75f72a --- /dev/null +++ b/apps/examples/qwik/public/manifest.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/web-manifest-combined.json", + "name": "qwik-project-name", + "short_name": "Welcome to Qwik", + "start_url": ".", + "display": "standalone", + "background_color": "#fff", + "description": "A Qwik project app." +} diff --git a/apps/examples/qwik/public/robots.txt b/apps/examples/qwik/public/robots.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/examples/qwik/qwik.env.d.ts b/apps/examples/qwik/qwik.env.d.ts new file mode 100644 index 0000000000..25af92b9b3 --- /dev/null +++ b/apps/examples/qwik/qwik.env.d.ts @@ -0,0 +1,4 @@ +// This file can be used to add references for global types like `vite/client`. + +// Add global `vite/client` types. For more info, see: https://vitejs.dev/guide/features#client-types +/// diff --git a/apps/examples/qwik/src/components/avatar/avatar.tsx b/apps/examples/qwik/src/components/avatar/avatar.tsx new file mode 100644 index 0000000000..aec7dff96c --- /dev/null +++ b/apps/examples/qwik/src/components/avatar/avatar.tsx @@ -0,0 +1,18 @@ +import { component$ } from "@builder.io/qwik"; + +type Props = { + src: string; + alt: string; +}; + +export const Avatar = component$((props) => { + return ( + {props.alt} + ); +}); diff --git a/apps/examples/qwik/src/components/header/header.tsx b/apps/examples/qwik/src/components/header/header.tsx new file mode 100644 index 0000000000..acf33c7f67 --- /dev/null +++ b/apps/examples/qwik/src/components/header/header.tsx @@ -0,0 +1,44 @@ +import { component$ } from "@builder.io/qwik"; +import { Link } from "@builder.io/qwik-city"; +import { useSession, useSignIn, useSignOut } from "~/routes/plugin@auth.js"; +import { QwikIcon } from "../icones/qwik"; +import { Avatar } from "../avatar/avatar"; + +export const Header = component$(() => { + const session = useSession(); + const signInSig = useSignIn(); + const signOutSig = useSignOut(); + + return ( +
+ + + + Auth.js with Qwik + + {session.value?.user ? ( +
+ + signOutSig.submit({ redirectTo: "/" })} + > + Logout + +
+ ) : ( +
+ signInSig.submit({ redirectTo: "/" })} + > + SignIn + +
+ )} +
+ ); +}); diff --git a/apps/examples/qwik/src/components/icones/qwik.tsx b/apps/examples/qwik/src/components/icones/qwik.tsx new file mode 100644 index 0000000000..1bdec25bd9 --- /dev/null +++ b/apps/examples/qwik/src/components/icones/qwik.tsx @@ -0,0 +1,33 @@ +import { type QwikIntrinsicElements } from "@builder.io/qwik"; + +export const QwikIcon = function FluentAnimalTurtle24Regular( + props: QwikIntrinsicElements["svg"], + key: string, +) { + return ( + + + + + + ); +}; diff --git a/apps/examples/qwik/src/components/router-head/router-head.tsx b/apps/examples/qwik/src/components/router-head/router-head.tsx new file mode 100644 index 0000000000..da5b68fa7c --- /dev/null +++ b/apps/examples/qwik/src/components/router-head/router-head.tsx @@ -0,0 +1,48 @@ +import { component$ } from "@builder.io/qwik"; +import { useDocumentHead, useLocation } from "@builder.io/qwik-city"; + +/** + * The RouterHead component is placed inside of the document `` element. + */ +export const RouterHead = component$(() => { + const head = useDocumentHead(); + const loc = useLocation(); + + return ( + <> + {head.title} + + + + + + {head.meta.map((m) => ( + + ))} + + {head.links.map((l) => ( + + ))} + + {head.styles.map((s) => ( +