@react-foundry/plop-pack 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +42 -0
- package/package.json +38 -0
- package/skel/app/.dockerignore +9 -0
- package/skel/app/Dockerfile +32 -0
- package/skel/app/Makefile.hbs +87 -0
- package/skel/app/README.md.hbs +91 -0
- package/skel/app/aws-lambda-entry.js +3 -0
- package/skel/app/cypress.config.mjs +6 -0
- package/skel/app/feat/home.spec.js +5 -0
- package/skel/app/gitignore +10 -0
- package/skel/app/jest.config.cjs +15 -0
- package/skel/app/lambda.Dockerfile +23 -0
- package/skel/app/package.json.hbs +61 -0
- package/skel/app/plopfile.mjs +5 -0
- package/skel/app/public/favicon.ico +0 -0
- package/skel/app/react-router.config.ts +13 -0
- package/skel/app/src/app/app.scss +0 -0
- package/skel/app/src/app/config.ts.hbs +1 -0
- package/skel/app/src/app/entry.client.tsx +17 -0
- package/skel/app/src/app/entry.server.tsx +85 -0
- package/skel/app/src/app/root.tsx +141 -0
- package/skel/app/src/app/routes/_index.tsx +23 -0
- package/skel/app/src/app/routes.ts +6 -0
- package/skel/app/src/server/config.ts +59 -0
- package/skel/app/src/server/dev.ts +13 -0
- package/skel/app/src/server/httpd.ts +49 -0
- package/skel/app/src/server/index.ts +33 -0
- package/skel/app/src/server/server-build.d.ts +14 -0
- package/skel/app/test.Dockerfile +18 -0
- package/skel/app/tsconfig.json +19 -0
- package/skel/app/vite.config.server.ts +24 -0
- package/skel/app/vite.config.ts +20 -0
- package/skel/component/README.md.hbs +66 -0
- package/skel/component/assets/Component.scss.hbs +9 -0
- package/skel/component/gitignore +5 -0
- package/skel/component/jest.config.js.hbs +15 -0
- package/skel/component/package.json.hbs +72 -0
- package/skel/component/spec/Component.mdx.hbs +19 -0
- package/skel/component/spec/Component.stories.tsx.hbs +22 -0
- package/skel/component/spec/Component.ts.hbs +28 -0
- package/skel/component/src/Component.tsx.hbs +32 -0
- package/skel/component/tsconfig.json +14 -0
- package/skel/lib/README.md.hbs +51 -0
- package/skel/lib/gitignore +5 -0
- package/skel/lib/jest.config.js.hbs +15 -0
- package/skel/lib/package.json.hbs +44 -0
- package/skel/lib/src/index.ts.hbs +6 -0
- package/skel/lib/tsconfig.json +14 -0
- package/src/action-paths.js +39 -0
- package/src/actions/copy.js +27 -0
- package/src/actions/merge.js +26 -0
- package/src/actions/message.js +3 -0
- package/src/actions/shell.js +21 -0
- package/src/actions/symlink.js +42 -0
- package/src/actions/write.js +20 -0
- package/src/extend-generator.js +38 -0
- package/src/generators/app.js +140 -0
- package/src/generators/component.js +73 -0
- package/src/generators/lib.js +54 -0
- package/src/helpers/eq.js +3 -0
- package/src/helpers/md-title.js +6 -0
- package/src/index.js +38 -0
- package/src/rel-to-skel.js +9 -0
- package/src/relative-path.js +5 -0
- package/src/run-plop.js +24 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2019-2025 Crown Copyright
|
|
4
|
+
Copyright (C) 2019-2026 Daniel A.C. Martin
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
7
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
8
|
+
the Software without restriction, including without limitation the rights to
|
|
9
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
10
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
11
|
+
so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
React Foundry - Plop Pack
|
|
2
|
+
=========================
|
|
3
|
+
|
|
4
|
+
Plop pack with misc tools.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Using this package
|
|
8
|
+
------------------
|
|
9
|
+
|
|
10
|
+
First install the package into your project:
|
|
11
|
+
|
|
12
|
+
```shell
|
|
13
|
+
npm install -D @react-foundry/plop-pack
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then use it in your `plopfile.js` as follows:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
module.exports = plop => {
|
|
20
|
+
plop.load('@react-foundry/plop-pack');
|
|
21
|
+
|
|
22
|
+
// ...
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Working on this package
|
|
28
|
+
-----------------------
|
|
29
|
+
|
|
30
|
+
Before working on this package you must install its dependencies using
|
|
31
|
+
the following command:
|
|
32
|
+
|
|
33
|
+
```shell
|
|
34
|
+
pnpm install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
### Testing
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
npm test
|
|
42
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@react-foundry/plop-pack",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Plop pack with misc tools.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"/src",
|
|
12
|
+
"/skel"
|
|
13
|
+
],
|
|
14
|
+
"author": "Daniel A.C. Martin <npm@daniel-martin.co.uk> (http://daniel-martin.co.uk/)",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=12.0.0"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"fs-extra": "^11.3.3",
|
|
21
|
+
"lodash": "^4.17.23",
|
|
22
|
+
"minimist": "^1.2.8",
|
|
23
|
+
"node-plop": "^0.32.3",
|
|
24
|
+
"plop": "^4.0.5",
|
|
25
|
+
"resolve": "^1.22.11",
|
|
26
|
+
"shelljs": "^0.10.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"handlebars": "4.7.8",
|
|
30
|
+
"jest": "30.2.0",
|
|
31
|
+
"jest-environment-jsdom": "30.2.0"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
35
|
+
"build": "true",
|
|
36
|
+
"clean": "true"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
FROM node:24.13.0-alpine
|
|
2
|
+
|
|
3
|
+
RUN apk add --no-cache ca-certificates \
|
|
4
|
+
&& apk upgrade --no-cache \
|
|
5
|
+
&& addgroup -S app \
|
|
6
|
+
&& adduser -S app -G app -u 31337 -h /app/ \
|
|
7
|
+
&& chown -R app:app /app/
|
|
8
|
+
|
|
9
|
+
USER app
|
|
10
|
+
WORKDIR /app
|
|
11
|
+
ENV NODE_ENV production
|
|
12
|
+
ENV MODE server
|
|
13
|
+
|
|
14
|
+
COPY package.json /app/
|
|
15
|
+
COPY dist/ /app/dist/
|
|
16
|
+
|
|
17
|
+
USER 31337
|
|
18
|
+
ENV LISTEN_HOST="::" \
|
|
19
|
+
LISTEN_PORT="8080" \
|
|
20
|
+
SSR_ONLY="false" \
|
|
21
|
+
SESSIONS_SECRET="changeme" \
|
|
22
|
+
AUTH_METHOD="none" \
|
|
23
|
+
OIDC_ISSUER="https://keycloak/realms/demo/" \
|
|
24
|
+
OIDC_CLIENT_ID="app" \
|
|
25
|
+
OIDC_CLIENT_SECRET="" \
|
|
26
|
+
OIDC_REDIRECT_URI="https://localhost" \
|
|
27
|
+
AUTH_HEADER_USERNAME="x-auth-username" \
|
|
28
|
+
AUTH_HEADER_GROUPS="x-auth-groups" \
|
|
29
|
+
AUTH_HEADER_ROLES="x-auth-roles"
|
|
30
|
+
EXPOSE ${LISTEN_PORT:-8080}
|
|
31
|
+
HEALTHCHECK CMD wget -q -O /dev/null http://localhost/healthz:${LISTEN_PORT} || exit 1
|
|
32
|
+
CMD ["node", "."]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
DOCKER_IMAGE ?= {{{pkg 'name'}}}-{{{name}}}
|
|
2
|
+
DOCKER_REGISTRY ?= hub.docker.com
|
|
3
|
+
DOCKER_REPO ?= $(DOCKER_REGISTRY)/$(DOCKER_IMAGE)
|
|
4
|
+
KUBE_CONTEXT ?= {{{pkg 'name'}}}-dev
|
|
5
|
+
KUBE_DEPLOYMENT ?= {{{pkg 'name'}}}-{{{name}}}
|
|
6
|
+
KUBE_CONTAINER ?= app
|
|
7
|
+
|
|
8
|
+
kubectl ?= kubectl --context $(KUBE_CONTEXT)
|
|
9
|
+
|
|
10
|
+
patch_version != jq -r '.version' 'package.json'
|
|
11
|
+
minor_version != echo "$(patch_version)" | awk -F '.' '{print $$1"."$$2}'
|
|
12
|
+
major_version != echo "$(patch_version)" | awk -F '.' '{print $$1}'
|
|
13
|
+
full_version != echo "$(patch_version)-$${BUILD_NUMBER/%/-}$$(git rev-parse --short HEAD)"
|
|
14
|
+
|
|
15
|
+
.PHONY: all aws-lambda build clean deploy deps distclean docker docker-clean docker-push docker-test netlify node-deps test unit-test
|
|
16
|
+
|
|
17
|
+
all: deps test docker
|
|
18
|
+
|
|
19
|
+
clean:
|
|
20
|
+
npm run clean
|
|
21
|
+
rm -rf node_modules/ pkg/
|
|
22
|
+
|
|
23
|
+
distclean: clean docker-clean
|
|
24
|
+
|
|
25
|
+
deps: node-deps
|
|
26
|
+
|
|
27
|
+
node-deps: node_modules/.bin/react-router
|
|
28
|
+
|
|
29
|
+
node_modules/%: package.json
|
|
30
|
+
pnpm install
|
|
31
|
+
|
|
32
|
+
build: dist/server/index.js
|
|
33
|
+
|
|
34
|
+
dist/%: node_modules/.bin/react-router
|
|
35
|
+
npm run build
|
|
36
|
+
|
|
37
|
+
docker: build
|
|
38
|
+
docker build -t '$(DOCKER_IMAGE)' .
|
|
39
|
+
|
|
40
|
+
docker-test:
|
|
41
|
+
docker build -t '$(DOCKER_IMAGE)-test' -f 'test.Dockerfile' .
|
|
42
|
+
|
|
43
|
+
docker-clean:
|
|
44
|
+
docker rmi -f '$(DOCKER_IMAGE)' || true
|
|
45
|
+
docker rmi -f '$(DOCKER_IMAGE)-test' || true
|
|
46
|
+
|
|
47
|
+
docker-push: docker
|
|
48
|
+
docker tag $(DOCKER_IMAGE) '$(DOCKER_REPO):$(full_version)' && docker push '$(DOCKER_REPO):$(full_version)'
|
|
49
|
+
docker tag $(DOCKER_IMAGE) '$(DOCKER_REPO):$(patch_version)' && docker push '$(DOCKER_REPO):$(patch_version)'
|
|
50
|
+
docker tag $(DOCKER_IMAGE) '$(DOCKER_REPO):$(minor_version)' && docker push '$(DOCKER_REPO):$(minor_version)'
|
|
51
|
+
docker tag $(DOCKER_IMAGE) '$(DOCKER_REPO):$(major_version)' && docker push '$(DOCKER_REPO):$(major_version)'
|
|
52
|
+
docker tag $(DOCKER_IMAGE) '$(DOCKER_REPO):latest' && docker push '$(DOCKER_REPO):latest'
|
|
53
|
+
|
|
54
|
+
docker-run:
|
|
55
|
+
docker run --name=docs -p '8080:8080' '$(DOCKER_IMAGE)'
|
|
56
|
+
|
|
57
|
+
aws-lambda: pkg/aws-lambda/{{{name}}}.zip
|
|
58
|
+
|
|
59
|
+
pkg/aws-lambda/%.zip: aws-lambda-entry.js package.json dist/server/index.js
|
|
60
|
+
mkdir -p '$(@D)'
|
|
61
|
+
zip -rv '$(@)' 'dist'
|
|
62
|
+
zip -v '$(@)' 'package.json'
|
|
63
|
+
cp '$(<)' '$(@D)/$(basename $(@F)).js'
|
|
64
|
+
zip -mvj '$(@)' '$(@D)/$(basename $(@F)).js'
|
|
65
|
+
|
|
66
|
+
netlify: pkg/netlify/functions/{{{name}}}.zip pkg/netlify/publish/_redirects
|
|
67
|
+
|
|
68
|
+
pkg/netlify/functions/%.zip: pkg/aws-lambda/%.zip
|
|
69
|
+
mkdir -p '$(@D)'
|
|
70
|
+
cp -a '$(<)' '$(@)'
|
|
71
|
+
|
|
72
|
+
pkg/netlify/publish/_redirects: dist/server/index.js
|
|
73
|
+
mkdir -p '$(@D)'
|
|
74
|
+
cp -a 'dist/app/client/'* '$(@D)'
|
|
75
|
+
echo '/* /.netlify/functions/{{{name}}}/:splat 200' > '$(@)'
|
|
76
|
+
|
|
77
|
+
deploy: docker-push
|
|
78
|
+
$(kubectl) set image 'deployment/$(KUBE_DEPLOYMENT)' '$(KUBE_CONTAINER)=$(DOCKER_REPO):$(full_version)'
|
|
79
|
+
$(kubectl) rollout status 'deployment/$(KUBE_DEPLOYMENT)'
|
|
80
|
+
|
|
81
|
+
functional-test: build
|
|
82
|
+
npm run 'test:functional:ci'
|
|
83
|
+
|
|
84
|
+
unit-test: node-deps
|
|
85
|
+
npm test
|
|
86
|
+
|
|
87
|
+
test: unit-test functional-test
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{{#mdTitle}}{{{titleCase (pkg 'name')}}} - {{{titleCase name}}}{{/mdTitle}}
|
|
2
|
+
|
|
3
|
+
{{{description}}}
|
|
4
|
+
|
|
5
|
+
## Welcome to React Router!
|
|
6
|
+
|
|
7
|
+
A modern, production-ready template for building full-stack React applications using React Router.
|
|
8
|
+
|
|
9
|
+
[](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default)
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 🚀 Server-side rendering
|
|
14
|
+
- ⚡️ Hot Module Replacement (HMR)
|
|
15
|
+
- 📦 Asset bundling and optimization
|
|
16
|
+
- 🔄 Data loading and mutations
|
|
17
|
+
- 🔒 TypeScript by default
|
|
18
|
+
- 🎉 TailwindCSS for styling
|
|
19
|
+
- 📖 [React Router docs](https://reactrouter.com/)
|
|
20
|
+
|
|
21
|
+
## Getting Started
|
|
22
|
+
|
|
23
|
+
### Installation
|
|
24
|
+
|
|
25
|
+
Install the dependencies:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Development
|
|
32
|
+
|
|
33
|
+
Start the development server with HMR:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run dev
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Your application will be available at `http://localhost:5173`.
|
|
40
|
+
|
|
41
|
+
## Building for Production
|
|
42
|
+
|
|
43
|
+
Create a production build:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm run build
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Deployment
|
|
50
|
+
|
|
51
|
+
### Docker Deployment
|
|
52
|
+
|
|
53
|
+
To build and run using Docker:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
docker build -t my-app .
|
|
57
|
+
|
|
58
|
+
# Run the container
|
|
59
|
+
docker run -p 3000:3000 my-app
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The containerized application can be deployed to any platform that supports Docker, including:
|
|
63
|
+
|
|
64
|
+
- AWS ECS
|
|
65
|
+
- Google Cloud Run
|
|
66
|
+
- Azure Container Apps
|
|
67
|
+
- Digital Ocean App Platform
|
|
68
|
+
- Fly.io
|
|
69
|
+
- Railway
|
|
70
|
+
|
|
71
|
+
### DIY Deployment
|
|
72
|
+
|
|
73
|
+
If you're familiar with deploying Node applications, the built-in app server is production-ready.
|
|
74
|
+
|
|
75
|
+
Make sure to deploy the output of `npm run build`
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
├── package.json
|
|
79
|
+
├── package-lock.json (or pnpm-lock.yaml, or bun.lockb)
|
|
80
|
+
├── build/
|
|
81
|
+
│ ├── client/ # Static assets
|
|
82
|
+
│ └── server/ # Server-side code
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Styling
|
|
86
|
+
|
|
87
|
+
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
Built with ❤️ using React Router.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const baseConfig = require('../../jest.config.base');
|
|
4
|
+
|
|
5
|
+
const config = {
|
|
6
|
+
...baseConfig,
|
|
7
|
+
collectCoverageFrom: [
|
|
8
|
+
'<rootDir>/src/**.{ts,tsx}',
|
|
9
|
+
],
|
|
10
|
+
testMatch: [
|
|
11
|
+
'<rootDir>/spec/**.{ts,tsx}'
|
|
12
|
+
]
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports = config;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
FROM public.ecr.aws/lambda/nodejs:24
|
|
2
|
+
|
|
3
|
+
ENV NODE_ENV production
|
|
4
|
+
ENV MODE serverless
|
|
5
|
+
|
|
6
|
+
COPY package.json ${LAMBDA_TASK_ROOT}/
|
|
7
|
+
COPY dist/ ${LAMBDA_TASK_ROOT}/dist/
|
|
8
|
+
COPY aws-lambda-entry.js ${LAMBDA_TASK_ROOT}/entry.js
|
|
9
|
+
|
|
10
|
+
USER 31337
|
|
11
|
+
ENV LISTEN_HOST="::" \
|
|
12
|
+
LISTEN_PORT="8080" \
|
|
13
|
+
SSR_ONLY="false" \
|
|
14
|
+
SESSIONS_SECRET="changeme" \
|
|
15
|
+
AUTH_METHOD="none" \
|
|
16
|
+
OIDC_ISSUER="https://keycloak/realms/demo/" \
|
|
17
|
+
OIDC_CLIENT_ID="app" \
|
|
18
|
+
OIDC_CLIENT_SECRET="" \
|
|
19
|
+
OIDC_REDIRECT_URI="https://localhost" \
|
|
20
|
+
AUTH_HEADER_USERNAME="x-auth-username" \
|
|
21
|
+
AUTH_HEADER_GROUPS="x-auth-groups" \
|
|
22
|
+
AUTH_HEADER_ROLES="x-auth-roles"
|
|
23
|
+
CMD ["entry.handler"]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{{pkg 'name'}}}/{{{name}}}",
|
|
3
|
+
"version": "{{{pkg 'version'}}}",
|
|
4
|
+
"description": "{{{description}}}",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/server/index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"start": "node .",
|
|
10
|
+
"start:test": "COOKIES_SECURE=no node .",
|
|
11
|
+
"test": "npm run test:unit",
|
|
12
|
+
"test:ci": "npm test && npm run test:functional:ci",
|
|
13
|
+
"test:functional": "cypress run ${CYPRESS_PROJECT_ID:+--record ${CYPRESS_FLAGS}}",
|
|
14
|
+
"test:functional:ci": "start-server-and-test 'start:test' 'http-get://localhost:8080/readiness' test:functional",
|
|
15
|
+
"test:unit": "jest --passWithNoTests",
|
|
16
|
+
"build": "npm run build:app && npm run build:server",
|
|
17
|
+
"build:app": "react-router build",
|
|
18
|
+
"build:server": "vite -c vite.config.server.ts build",
|
|
19
|
+
"heroku-postbuild": "npm run build",
|
|
20
|
+
"clean": "rm -rf dist/ pkg/ node_modules/.vite/ node_modules/.vite-temp/ .react-router/",
|
|
21
|
+
"dev": "NODE_ENV=development bun --watch --inspect ./src/server/dev.ts",
|
|
22
|
+
"cypress": "cypress open",
|
|
23
|
+
"create": "plop",
|
|
24
|
+
"create:deployment": "plop deployment",
|
|
25
|
+
"create:page": "plop page",
|
|
26
|
+
"typecheck": "react-router typegen && tsc"
|
|
27
|
+
},
|
|
28
|
+
"author": "{{{pkg 'author'}}}",
|
|
29
|
+
"license": "{{{pkg 'license'}}}",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@react-foundry/fastify": "{{#if (eq (pkg 'name')'react-foundry')}}workspace:*{{else}}0.1.0{{/if}}",
|
|
32
|
+
"@react-foundry/fastify-react-router": "{{#if (eq (pkg 'name')'react-foundry')}}workspace:*{{else}}0.1.0{{/if}}",
|
|
33
|
+
"@react-foundry/react-router-context": "{{#if (eq (pkg 'name')'react-foundry')}}workspace:*{{else}}0.1.0{{/if}}",
|
|
34
|
+
"@react-foundry/router": "{{#if (eq (pkg 'name')'react-foundry')}}workspace:*{{else}}0.1.0{{/if}}",
|
|
35
|
+
"@mdx-js/rollup": "3.1.1",
|
|
36
|
+
"@react-router/fs-routes": "7.12.0",
|
|
37
|
+
"@react-router/node": "7.12.0",
|
|
38
|
+
"isbot": "5.1.32",
|
|
39
|
+
"react": "19.2.3",
|
|
40
|
+
"react-dom": "19.2.3",
|
|
41
|
+
"react-router": "7.12.0",
|
|
42
|
+
"serverless-http": "4.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@react-foundry/app-plop-pack": "{{#if (eq (pkg 'name')'react-foundry')}}workspace:*{{else}}0.1.0{{/if}}",
|
|
46
|
+
"@react-foundry/vite-html-react": "{{#if (eq (pkg 'name')'react-foundry')}}workspace:*{{else}}0.1.0{{/if}}",
|
|
47
|
+
"@react-router/dev": "7.12.0",
|
|
48
|
+
"@types/mdx": "2.0.13",
|
|
49
|
+
"@types/node": "24.10.9",
|
|
50
|
+
"@types/react": "19.2.8",
|
|
51
|
+
"@types/react-dom": "19.2.3",
|
|
52
|
+
"bun": "1.3.6",
|
|
53
|
+
"cypress": "15.9.0",
|
|
54
|
+
"jest": "30.2.0",
|
|
55
|
+
"jest-environment-jsdom": "30.2.0",
|
|
56
|
+
"start-server-and-test": "2.1.3",
|
|
57
|
+
"ts-jest": "29.4.6",
|
|
58
|
+
"typescript": "5.9.3",
|
|
59
|
+
"vite": "7.3.1"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const siteTitle: string = '{{{ title }}}';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { startTransition, StrictMode } from 'react';
|
|
2
|
+
import { hydrateRoot } from 'react-dom/client';
|
|
3
|
+
import { HydratedRouter } from 'react-router/dom';
|
|
4
|
+
|
|
5
|
+
startTransition(() => {
|
|
6
|
+
hydrateRoot(
|
|
7
|
+
document,
|
|
8
|
+
<StrictMode>
|
|
9
|
+
<HydratedRouter />
|
|
10
|
+
</StrictMode>,
|
|
11
|
+
{
|
|
12
|
+
onRecoverableError: (error, errorInfo) => {
|
|
13
|
+
console.warn(error, 'Component Stack:', errorInfo.componentStack);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { PassThrough } from "node:stream";
|
|
2
|
+
|
|
3
|
+
import type { EntryContext } from "react-router";
|
|
4
|
+
import { createReadableStreamFromReadable } from "@react-router/node";
|
|
5
|
+
import { ServerRouter } from "react-router";
|
|
6
|
+
import { isbot } from "isbot";
|
|
7
|
+
import type { RenderToPipeableStreamOptions } from "react-dom/server";
|
|
8
|
+
import { renderToPipeableStream } from "react-dom/server";
|
|
9
|
+
import type { Request } from "@react-foundry/fastify-react-router";
|
|
10
|
+
import type { RouterContextProvider } from "@react-foundry/react-router-context";
|
|
11
|
+
import { cspNonceContext } from "@react-foundry/react-router-context";
|
|
12
|
+
|
|
13
|
+
export const streamTimeout = 5_000;
|
|
14
|
+
const isServerless = process.env['MODE'] === 'serverless';
|
|
15
|
+
|
|
16
|
+
export default function handleRequest(
|
|
17
|
+
request: Request,
|
|
18
|
+
responseStatusCode: number,
|
|
19
|
+
responseHeaders: Headers,
|
|
20
|
+
routerContext: EntryContext,
|
|
21
|
+
loadContext: RouterContextProvider
|
|
22
|
+
) {
|
|
23
|
+
const nonce = request.cspNonce || loadContext.cspNonce || loadContext.get(cspNonceContext);
|
|
24
|
+
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
let shellRendered = false;
|
|
27
|
+
let userAgent = request.headers.get("user-agent");
|
|
28
|
+
|
|
29
|
+
// Ensure requests from bots and SPA Mode renders wait for all content to load before responding
|
|
30
|
+
// https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
|
|
31
|
+
let readyOption: keyof RenderToPipeableStreamOptions =
|
|
32
|
+
(userAgent && isbot(userAgent)) || routerContext.isSpaMode || isServerless
|
|
33
|
+
? "onAllReady"
|
|
34
|
+
: "onShellReady";
|
|
35
|
+
|
|
36
|
+
// Abort the rendering stream after the `streamTimeout` so it has time to
|
|
37
|
+
// flush down the rejected boundaries
|
|
38
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined = setTimeout(
|
|
39
|
+
() => abort(),
|
|
40
|
+
streamTimeout + 1000,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const { pipe, abort } = renderToPipeableStream(
|
|
44
|
+
<ServerRouter context={routerContext} url={request.url} nonce={nonce} />,
|
|
45
|
+
{
|
|
46
|
+
[readyOption]() {
|
|
47
|
+
shellRendered = true;
|
|
48
|
+
const body = new PassThrough({
|
|
49
|
+
final(callback) {
|
|
50
|
+
// Clear the timeout to prevent retaining the closure and memory leak
|
|
51
|
+
clearTimeout(timeoutId);
|
|
52
|
+
timeoutId = undefined;
|
|
53
|
+
callback();
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
const stream = createReadableStreamFromReadable(body);
|
|
57
|
+
|
|
58
|
+
responseHeaders.set("Content-Type", "text/html");
|
|
59
|
+
|
|
60
|
+
pipe(body);
|
|
61
|
+
|
|
62
|
+
resolve(
|
|
63
|
+
new Response(stream, {
|
|
64
|
+
headers: responseHeaders,
|
|
65
|
+
status: responseStatusCode,
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
nonce,
|
|
70
|
+
onShellError(error: unknown) {
|
|
71
|
+
reject(error);
|
|
72
|
+
},
|
|
73
|
+
onError(error: unknown) {
|
|
74
|
+
responseStatusCode = 500;
|
|
75
|
+
// Log streaming rendering errors from inside the shell. Don't log
|
|
76
|
+
// errors encountered during initial shell rendering since they'll
|
|
77
|
+
// reject and get logged in handleDocumentRequest.
|
|
78
|
+
if (shellRendered) {
|
|
79
|
+
console.error(error);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
}
|