feat: add empty qwik-city frontend

This commit is contained in:
nobody 2025-10-30 17:55:43 -07:00
commit 9e383ee26e
Signed by: GrocerPublishAgent
GPG key ID: D460CD54A9E3AB86
22 changed files with 9661 additions and 0 deletions

42
frontend/.gitignore vendored Normal file
View file

@ -0,0 +1,42 @@
# Build
/dist
/lib
/lib-types
/server
# Development
node_modules
.env
*.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

37
frontend/.prettierignore Normal file
View file

@ -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

24
frontend/.vscode/launch.json vendored Normal file
View file

@ -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": ["<node_internals>/**"],
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/node_modules/vite/bin/vite.js",
"args": ["--mode", "ssr", "--force"]
}
]
}

View file

@ -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",
"});",
],
},
}

78
frontend/.vscode/qwik.code-snippets vendored Normal file
View file

@ -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</$2>",
"});",
],
},
"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",
" </${3}>",
" );",
"});",
],
},
"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",
"});",
"",
],
},
}

65
frontend/README.md Normal file
View file

@ -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 `npm run 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
npm run qwik add # or `yarn 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 `yarn 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
npm run preview # or `yarn 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
npm run build # or `yarn build`
```

73
frontend/eslint.config.js Normal file
View file

@ -0,0 +1,73 @@
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import { globalIgnores } from "eslint/config";
import { qwikEslint9Plugin } from "eslint-plugin-qwik";
const ignores = [
"**/*.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",
"eslint.config.js",
];
export default tseslint.config(
globalIgnores(ignores),
js.configs.recommended,
tseslint.configs.recommended,
qwikEslint9Plugin.configs.recommended,
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
...globals.es2021,
...globals.serviceworker,
},
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
{
rules: {
"@typescript-eslint/no-explicit-any": "off",
},
},
);

8936
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

50
frontend/package.json Normal file
View file

@ -0,0 +1,50 @@
{
"name": "my-qwik-empty-starter",
"description": "Blank project with routing included",
"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,
"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": {
"@builder.io/qwik": "^1.17.1",
"@builder.io/qwik-city": "^1.17.1",
"@eslint/js": "latest",
"@types/node": "20.19.0",
"eslint": "9.32.0",
"eslint-plugin-qwik": "^1.17.1",
"globals": "16.4.0",
"prettier": "3.6.2",
"typescript": "5.4.5",
"typescript-eslint": "8.38.0",
"typescript-plugin-css-modules": "latest",
"undici": "*",
"vite": "7.1.11",
"vite-tsconfig-paths": "^4.2.1"
},
"dependencies": {
"mathjs": "^15.0.0",
"prosemirror-keymap": "^1.2.3",
"prosemirror-model": "^1.25.4",
"prosemirror-schema-basic": "^1.2.4",
"prosemirror-state": "^1.4.4",
"prosemirror-transform": "^1.10.4",
"prosemirror-view": "^1.41.3"
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 500 500"><g clip-path="url(#a)"><circle cx="250" cy="250" r="250" fill="#fff"/><path fill="#18B6F6" d="m367.87 418.45-61.17-61.18-.94.13v-.67L175.7 227.53l32.05-31.13L188.9 87.73 99.56 199.09c-15.22 15.42-18.03 40.51-7.08 59.03l55.83 93.11a46.82 46.82 0 0 0 40.73 22.81l27.65-.27 151.18 44.68Z"/><path fill="#AC7EF4" d="m401.25 196.94-12.29-22.81-6.41-11.67-2.54-4.56-.26.26-33.66-58.63a47.07 47.07 0 0 0-41.27-23.75l-29.51.8-88.01.28a47.07 47.07 0 0 0-40.33 23.34L93.4 207l95.76-119.54L314.7 226.19l-22.3 22.67 13.35 108.54.13-.26v.26h-.26l.26.27 10.42 10.2 50.62 49.78c2.13 2 5.6-.4 4.13-2.96l-31.25-61.85 54.5-101.3 1.73-2c.67-.81 1.33-1.62 1.87-2.42a46.8 46.8 0 0 0 3.34-50.18Z"/><path fill="#fff" d="M315.1 225.65 189.18 87.6l17.9 108.14L175 227l130.5 130.27-11.75-108.14 21.37-23.48Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h500v500H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 947 B

View file

@ -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."
}

View file

4
frontend/qwik.env.d.ts vendored Normal file
View file

@ -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
/// <reference types="vite/client" />

View file

@ -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 `<head>` element.
*/
export const RouterHead = component$(() => {
const head = useDocumentHead();
const loc = useLocation();
return (
<>
<title>{head.title}</title>
<link rel="canonical" href={loc.url.href} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
{head.meta.map((m) => (
<meta key={m.key} {...m} />
))}
{head.links.map((l) => (
<link key={l.key} {...l} />
))}
{head.styles.map((s) => (
<style
key={s.key}
{...s.props}
{...(s.props?.dangerouslySetInnerHTML
? {}
: { dangerouslySetInnerHTML: s.style })}
/>
))}
{head.scripts.map((s) => (
<script
key={s.key}
{...s.props}
{...(s.props?.dangerouslySetInnerHTML
? {}
: { dangerouslySetInnerHTML: s.script })}
/>
))}
</>
);
});

View file

@ -0,0 +1,17 @@
/*
* WHAT IS THIS FILE?
*
* Development entry point using only client-side modules:
* - Do not use this mode in production!
* - No SSR
* - No portion of the application is pre-rendered on the server.
* - All of the application is running eagerly in the browser.
* - More code is transferred to the browser than in SSR mode.
* - Optimizer/Serialization/Deserialization code is not exercised!
*/
import { render, type RenderOptions } from "@builder.io/qwik";
import Root from "./root";
export default function (opts: RenderOptions) {
return render(document, <Root />, opts);
}

View file

@ -0,0 +1,21 @@
/*
* WHAT IS THIS FILE?
*
* It's the bundle entry point for `npm run preview`.
* That is, serving your app built in production mode.
*
* Feel free to modify this file, but don't remove it!
*
* Learn more about Vite's preview command:
* - https://vitejs.dev/config/preview-options.html#preview-options
*
*/
import { createQwikCity } from "@builder.io/qwik-city/middleware/node";
import qwikCityPlan from "@qwik-city-plan";
// make sure qwikCityPlan is imported before entry
import render from "./entry.ssr";
/**
* The default export is the QwikCity adapter used by Vite preview.
*/
export default createQwikCity({ render, qwikCityPlan });

View file

@ -0,0 +1,31 @@
/**
* WHAT IS THIS FILE?
*
* SSR entry point, in all cases the application is rendered outside the browser, this
* entry point will be the common one.
*
* - Server (express, cloudflare...)
* - npm run start
* - npm run preview
* - npm run build
*
*/
import {
renderToStream,
type RenderToStreamOptions,
} from "@builder.io/qwik/server";
import Root from "./root";
export default function (opts: RenderToStreamOptions) {
return renderToStream(<Root />, {
...opts,
// Use container attributes to set attributes on the html tag.
containerAttributes: {
lang: "en-us",
...opts.containerAttributes,
},
serverData: {
...opts.serverData,
},
});
}

0
frontend/src/global.css Normal file
View file

32
frontend/src/root.tsx Normal file
View file

@ -0,0 +1,32 @@
import { component$, isDev } from "@builder.io/qwik";
import { QwikCityProvider, RouterOutlet } from "@builder.io/qwik-city";
import { RouterHead } from "./components/router-head/router-head";
import "./global.css";
export default component$(() => {
/**
* The root of a QwikCity site always start with the <QwikCityProvider> component,
* immediately followed by the document's <head> and <body>.
*
* Don't remove the `<head>` and `<body>` elements.
*/
return (
<QwikCityProvider>
<head>
<meta charset="utf-8" />
{!isDev && (
<link
rel="manifest"
href={`${import.meta.env.BASE_URL}manifest.json`}
/>
)}
<RouterHead />
</head>
<body lang="en">
<RouterOutlet />
</body>
</QwikCityProvider>
);
});

View file

@ -0,0 +1,25 @@
import { component$ } from "@builder.io/qwik";
import type { DocumentHead } from "@builder.io/qwik-city";
export default component$(() => {
return (
<>
<h1>Hi 👋</h1>
<div>
Can't wait to see what you build with qwik!
<br />
Happy coding.
</div>
</>
);
});
export const head: DocumentHead = {
title: "Welcome to Qwik",
meta: [
{
name: "description",
content: "Qwik site description",
},
],
};

26
frontend/tsconfig.json Normal file
View file

@ -0,0 +1,26 @@
{
"compilerOptions": {
"allowJs": true,
"target": "ES2020",
"module": "ES2022",
"lib": ["es2022", "DOM", "WebWorker", "DOM.Iterable"],
"jsx": "react-jsx",
"jsxImportSource": "@builder.io/qwik",
"strict": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "Bundler",
"esModuleInterop": true,
"skipLibCheck": true,
"incremental": true,
"isolatedModules": true,
"outDir": "tmp",
"noEmit": true,
"paths": {
"~/*": ["./src/*"]
},
/* if you do not use CSS modules, remove this line and delete the typescript-plugin-css-modules module from package.json */
"plugins": [{ "name": "typescript-plugin-css-modules" }]
},
"include": ["src", "./*.d.ts", "./*.config.ts"]
}

106
frontend/vite.config.ts Normal file
View file

@ -0,0 +1,106 @@
/**
* This is the base config for vite.
* When building, the adapter config is used which loads this file and extends it.
*/
import { defineConfig, type UserConfig } from "vite";
import { qwikVite } from "@builder.io/qwik/optimizer";
import { qwikCity } from "@builder.io/qwik-city/vite";
import tsconfigPaths from "vite-tsconfig-paths";
import pkg from "./package.json";
type PkgDep = Record<string, string>;
const { dependencies = {}, devDependencies = {} } = pkg as any as {
dependencies: PkgDep;
devDependencies: PkgDep;
[key: string]: unknown;
};
errorOnDuplicatesPkgDeps(devDependencies, dependencies);
/**
* Note that Vite normally starts from `index.html` but the qwikCity plugin makes start at `src/entry.ssr.tsx` instead.
*/
export default defineConfig(({ command, mode }): UserConfig => {
return {
plugins: [qwikCity(), qwikVite(), tsconfigPaths({ root: "." })],
// This tells Vite which dependencies to pre-build in dev mode.
optimizeDeps: {
// Put problematic deps that break bundling here, mostly those with binaries.
// For example ['better-sqlite3'] if you use that in server functions.
exclude: [],
},
/**
* This is an advanced setting. It improves the bundling of your server code. To use it, make sure you understand when your consumed packages are dependencies or dev dependencies. (otherwise things will break in production)
*/
// ssr:
// command === "build" && mode === "production"
// ? {
// // All dev dependencies should be bundled in the server build
// noExternal: Object.keys(devDependencies),
// // Anything marked as a dependency will not be bundled
// // These should only be production binary deps (including deps of deps), CLI deps, and their module graph
// // If a dep-of-dep needs to be external, add it here
// // For example, if something uses `bcrypt` but you don't have it as a dep, you can write
// // external: [...Object.keys(dependencies), 'bcrypt']
// external: Object.keys(dependencies),
// }
// : undefined,
server: {
headers: {
// Don't cache the server response in dev mode
"Cache-Control": "public, max-age=0",
},
},
preview: {
headers: {
// Do cache the server response in preview (non-adapter production build)
"Cache-Control": "public, max-age=600",
},
},
};
});
// *** utils ***
/**
* Function to identify duplicate dependencies and throw an error
* @param {Object} devDependencies - List of development dependencies
* @param {Object} dependencies - List of production dependencies
*/
function errorOnDuplicatesPkgDeps(
devDependencies: PkgDep,
dependencies: PkgDep,
) {
let msg = "";
// Create an array 'duplicateDeps' by filtering devDependencies.
// If a dependency also exists in dependencies, it is considered a duplicate.
const duplicateDeps = Object.keys(devDependencies).filter(
(dep) => dependencies[dep],
);
// include any known qwik packages
const qwikPkg = Object.keys(dependencies).filter((value) =>
/qwik/i.test(value),
);
// any errors for missing "qwik-city-plan"
// [PLUGIN_ERROR]: Invalid module "@qwik-city-plan" is not a valid package
msg = `Move qwik packages ${qwikPkg.join(", ")} to devDependencies`;
if (qwikPkg.length > 0) {
throw new Error(msg);
}
// Format the error message with the duplicates list.
// The `join` function is used to represent the elements of the 'duplicateDeps' array as a comma-separated string.
msg = `
Warning: The dependency "${duplicateDeps.join(", ")}" is listed in both "devDependencies" and "dependencies".
Please move the duplicated dependencies to "devDependencies" only and remove it from "dependencies"
`;
// Throw an error with the constructed message.
if (duplicateDeps.length > 0) {
throw new Error(msg);
}
}