@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
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Links,
|
|
4
|
+
Meta,
|
|
5
|
+
Outlet,
|
|
6
|
+
Scripts,
|
|
7
|
+
ScrollRestoration,
|
|
8
|
+
isRouteErrorResponse,
|
|
9
|
+
useRouteLoaderData
|
|
10
|
+
} from 'react-router';
|
|
11
|
+
import type { Route } from './+types/root';
|
|
12
|
+
import { cspNonceContext, sanitiseUserInfo, userInfoContext } from '@react-foundry/react-router-context';
|
|
13
|
+
import { UserInfoContext } from '@react-foundry/user-info';
|
|
14
|
+
import { siteTitle } from './config';
|
|
15
|
+
|
|
16
|
+
import './app.scss';
|
|
17
|
+
|
|
18
|
+
export const links: Route.LinksFunction = () => [
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export const loader = async ({ context }: Route.LoaderArgs) => {
|
|
22
|
+
const nonce = context.get(cspNonceContext);
|
|
23
|
+
const user = context.get(userInfoContext);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
nonce,
|
|
27
|
+
user: user && sanitiseUserInfo(user)
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function Layout({ children }: { children: React.ReactNode }) {
|
|
32
|
+
const data = useRouteLoaderData('root');
|
|
33
|
+
const nonce = data?.nonce;
|
|
34
|
+
const userInfo = data?.user;
|
|
35
|
+
const sign = (
|
|
36
|
+
userInfo && userInfo.username
|
|
37
|
+
? {
|
|
38
|
+
href: '/auth/sign-out',
|
|
39
|
+
text: 'Sign out'
|
|
40
|
+
}
|
|
41
|
+
: {
|
|
42
|
+
href: '/auth/sign-in',
|
|
43
|
+
text: 'Sign in'
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<html lang="en">
|
|
49
|
+
<head>
|
|
50
|
+
<meta charSet="utf-8" />
|
|
51
|
+
<Meta />
|
|
52
|
+
<Links />
|
|
53
|
+
</head>
|
|
54
|
+
<body>
|
|
55
|
+
<UserInfoContext.Provider value={userInfo}>
|
|
56
|
+
<h1>{siteTitle}</h1>
|
|
57
|
+
{children}
|
|
58
|
+
<ul>
|
|
59
|
+
<a href={sign.href}>{sign.text}</a>
|
|
60
|
+
</ul>
|
|
61
|
+
</UserInfoContext.Provider>
|
|
62
|
+
<ScrollRestoration nonce={nonce} />
|
|
63
|
+
<Scripts nonce={nonce} />
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default function App() {
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
document.body.classList.add('js-enabled');
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
return <Outlet />;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
|
|
78
|
+
const statusToMessage: Record<number, string> = {
|
|
79
|
+
400: 'Bad request',
|
|
80
|
+
401: 'Unauthorised',
|
|
81
|
+
402: 'Payment required',
|
|
82
|
+
403: 'Forbidden',
|
|
83
|
+
404: 'Page not found',
|
|
84
|
+
405: 'Method not allowed',
|
|
85
|
+
406: 'Not acceptable',
|
|
86
|
+
407: 'Proxy authentication required',
|
|
87
|
+
408: 'Request timeout',
|
|
88
|
+
409: 'Conflict',
|
|
89
|
+
410: 'Gone',
|
|
90
|
+
418: 'I am a teapot',
|
|
91
|
+
500: 'Internal server error',
|
|
92
|
+
501: 'Not implemented',
|
|
93
|
+
502: 'Bad gateway',
|
|
94
|
+
503: 'Service unavailable',
|
|
95
|
+
504: 'Gateway timeout',
|
|
96
|
+
505: 'HTTP version not supported',
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const defaultMessage = 'Something went wrong';
|
|
100
|
+
const defaultDetails = 'An unexpected error occurred.';
|
|
101
|
+
|
|
102
|
+
const { message, details, stack } = (
|
|
103
|
+
isRouteErrorResponse(error)
|
|
104
|
+
? {
|
|
105
|
+
message: statusToMessage[error.status] || error.statusText || defaultMessage,
|
|
106
|
+
details: (
|
|
107
|
+
error.status !== 404
|
|
108
|
+
? error.statusText
|
|
109
|
+
: (
|
|
110
|
+
'If you typed the web address, check it is correct.\n' +
|
|
111
|
+
'If you pasted the web address, check you copied the entire address.'
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
: (
|
|
116
|
+
import.meta.env.DEV && error && error instanceof Error
|
|
117
|
+
? {
|
|
118
|
+
message: defaultMessage,
|
|
119
|
+
details: error.message,
|
|
120
|
+
stack: error.stack
|
|
121
|
+
}
|
|
122
|
+
: {
|
|
123
|
+
message: defaultMessage,
|
|
124
|
+
details: defaultDetails
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<main>
|
|
131
|
+
<title>{message}</title>
|
|
132
|
+
<h1>{message}</h1>
|
|
133
|
+
<p>{details}</p>
|
|
134
|
+
{stack && (
|
|
135
|
+
<pre>
|
|
136
|
+
<code>{stack}</code>
|
|
137
|
+
</pre>
|
|
138
|
+
)}
|
|
139
|
+
</main>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Route } from "./+types/_index";
|
|
2
|
+
import { siteTitle } from '../config';
|
|
3
|
+
|
|
4
|
+
export const title = 'Home';
|
|
5
|
+
const description = 'Our homepage';
|
|
6
|
+
|
|
7
|
+
export function meta({}: Route.MetaArgs) {
|
|
8
|
+
return [
|
|
9
|
+
{ title: `${title} - ${siteTitle}` },
|
|
10
|
+
{ name: 'description', content: description },
|
|
11
|
+
{ name: 'og:title', content: title },
|
|
12
|
+
{ name: 'og:description', content: description },
|
|
13
|
+
];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function Home() {
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<h1>{title}</h1>
|
|
20
|
+
<p>This is the home page.</p>
|
|
21
|
+
</>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { AuthMethod, Mode, NodeEnv, SessionStore, defaultsTrue, defaultsFalse } from '@react-foundry/fastify';
|
|
2
|
+
|
|
3
|
+
const env = process.env.NODE_ENV as NodeEnv;
|
|
4
|
+
const devMode = env === NodeEnv.Development;
|
|
5
|
+
const standardRoles: string[] = [];
|
|
6
|
+
|
|
7
|
+
const serverConfig = {
|
|
8
|
+
auth: {
|
|
9
|
+
method: (process.env.AUTH_METHOD || ( devMode ? AuthMethod.Dummy : AuthMethod.Basic )) as AuthMethod,
|
|
10
|
+
dummy: {
|
|
11
|
+
username: 'TestUser',
|
|
12
|
+
groups: [],
|
|
13
|
+
roles: standardRoles
|
|
14
|
+
},
|
|
15
|
+
headers: {
|
|
16
|
+
usernameHeader: process.env.AUTH_HEADER_USERNAME || 'x-auth-username',
|
|
17
|
+
groupsHeader: process.env.AUTH_HEADER_GROUPS || 'x-auth-groups',
|
|
18
|
+
rolesHeader: process.env.AUTH_HEADER_ROLES || 'x-auth-roles'
|
|
19
|
+
},
|
|
20
|
+
basic: {
|
|
21
|
+
username: process.env.AUTH_USERNAME || 'guest',
|
|
22
|
+
password: process.env.AUTH_PASSWORD || 'password',
|
|
23
|
+
roles: standardRoles,
|
|
24
|
+
},
|
|
25
|
+
oidc: {
|
|
26
|
+
issuer: process.env.OIDC_ISSUER || 'http://localhost:8001/realms/demo/',
|
|
27
|
+
clientId: process.env.OIDC_CLIENT_ID || 'app',
|
|
28
|
+
clientSecret: process.env.OIDC_CLIENT_SECRET || 'app-secret',
|
|
29
|
+
redirectUri: process.env.OIDC_REDIRECT_URI || 'http://localhost:8080'
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
contentSecurityPolicy: {
|
|
33
|
+
formAction: process.env.FORM_ACTION?.split(','),
|
|
34
|
+
frameAncestors: process.env.FRAME_ANCESTORS?.split(',')
|
|
35
|
+
},
|
|
36
|
+
cookies: {
|
|
37
|
+
secret: process.env.COOKIES_SECRET || 'changeme',
|
|
38
|
+
secure: ( devMode ? defaultsFalse : defaultsTrue )(process.env.COOKIES_SECURE)
|
|
39
|
+
},
|
|
40
|
+
devMode,
|
|
41
|
+
env,
|
|
42
|
+
formAction: [],
|
|
43
|
+
frameAncestors: [],
|
|
44
|
+
logger: {
|
|
45
|
+
destination: process.env.LOG_DESTINATION,
|
|
46
|
+
level: process.env.LOG_LEVEL || ( devMode ? 'debug' : 'info' )
|
|
47
|
+
},
|
|
48
|
+
httpd: {
|
|
49
|
+
host: process.env.LISTEN_HOST || '::',
|
|
50
|
+
port: Number(process.env.PORT) || Number(process.env.LISTEN_PORT) || 8080
|
|
51
|
+
},
|
|
52
|
+
mode: (process.env.MODE || 'server') as Mode,
|
|
53
|
+
privacy: defaultsFalse(process.env.PRIVACY),
|
|
54
|
+
session: {
|
|
55
|
+
store: process.env.SESSION_STORE as SessionStore
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default serverConfig;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Mode } from '@react-foundry/fastify';
|
|
2
|
+
import fastifyReactRouterDev from '@react-foundry/fastify-react-router/dev';
|
|
3
|
+
import { createServer, reactRouterOptions } from './httpd';
|
|
4
|
+
import config from './config';
|
|
5
|
+
|
|
6
|
+
const httpd = createServer();
|
|
7
|
+
|
|
8
|
+
await httpd.register(fastifyReactRouterDev, reactRouterOptions);
|
|
9
|
+
|
|
10
|
+
await httpd.listen({
|
|
11
|
+
host: '::1',
|
|
12
|
+
port: config.httpd.port
|
|
13
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { FastifyInstance, IsFunction, OnClose } from '@react-foundry/fastify';
|
|
2
|
+
|
|
3
|
+
import { AuthMethod, Fastify, Mode, SessionStore } from '@react-foundry/fastify';
|
|
4
|
+
import config from './config';
|
|
5
|
+
|
|
6
|
+
type Server = FastifyInstance;
|
|
7
|
+
|
|
8
|
+
const isReady: IsFunction = async () => true;
|
|
9
|
+
const onClose: OnClose = async () => undefined;
|
|
10
|
+
|
|
11
|
+
export const reactRouterOptions = {
|
|
12
|
+
stream: config.mode === Mode.Server
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const createServer = (): Server => {
|
|
16
|
+
const httpd = Fastify({
|
|
17
|
+
auth: config.auth && {
|
|
18
|
+
...(
|
|
19
|
+
( config.auth.method === AuthMethod.None && { method: AuthMethod.None } )
|
|
20
|
+
|| ( config.auth.method === AuthMethod.Dummy && { method: AuthMethod.Dummy, ...config.auth.dummy } )
|
|
21
|
+
|| ( config.auth.method === AuthMethod.Headers && { method: AuthMethod.Headers, ...config.auth.headers } )
|
|
22
|
+
|| ( config.auth.method === AuthMethod.Basic && { method: AuthMethod.Basic, ...config.auth.basic } )
|
|
23
|
+
|| ( config.auth.method === AuthMethod.OIDC && { method: AuthMethod.OIDC, ...config.auth.oidc } )
|
|
24
|
+
),
|
|
25
|
+
privacy: config.privacy,
|
|
26
|
+
} || undefined,
|
|
27
|
+
contentSecurityPolicy: {
|
|
28
|
+
formAction: config.formAction,
|
|
29
|
+
frameAncestors: config.frameAncestors
|
|
30
|
+
},
|
|
31
|
+
cookies: {
|
|
32
|
+
secret: config.cookies.secret,
|
|
33
|
+
secure: config.cookies.secure
|
|
34
|
+
},
|
|
35
|
+
dev: config.devMode,
|
|
36
|
+
isReady,
|
|
37
|
+
logger: {
|
|
38
|
+
file: config.logger.destination,
|
|
39
|
+
level: config.logger.level
|
|
40
|
+
},
|
|
41
|
+
onClose,
|
|
42
|
+
session: config.session && (
|
|
43
|
+
( config.session.store === SessionStore.Cookie && { store: SessionStore.Cookie } )
|
|
44
|
+
|| ( config.session.store === SessionStore.Memory && { store: SessionStore.Memory } )
|
|
45
|
+
) || undefined
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return httpd;
|
|
49
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/// <reference path="./server-build.d.ts" />
|
|
2
|
+
|
|
3
|
+
import type { Application } from 'serverless-http';
|
|
4
|
+
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import serverless from 'serverless-http';
|
|
7
|
+
import { Mode } from '@react-foundry/fastify';
|
|
8
|
+
import fastifyReactRouter from '@react-foundry/fastify-react-router';
|
|
9
|
+
import { createServer, reactRouterOptions } from './httpd';
|
|
10
|
+
import config from './config';
|
|
11
|
+
import * as serverBuild from '../../dist/app/server/index.js';
|
|
12
|
+
|
|
13
|
+
const assets = join(import.meta.dirname, '..', '..', 'dist', 'app', 'client');
|
|
14
|
+
const httpd = createServer();
|
|
15
|
+
|
|
16
|
+
await httpd.register(fastifyReactRouter, {
|
|
17
|
+
...reactRouterOptions,
|
|
18
|
+
assets,
|
|
19
|
+
serverBuild
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const handler = (
|
|
23
|
+
config.mode !== Mode.Serverless
|
|
24
|
+
? undefined
|
|
25
|
+
: serverless(httpd as unknown as Application)
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (config.mode === Mode.Server) {
|
|
29
|
+
await httpd.listen({
|
|
30
|
+
host: config.httpd.host,
|
|
31
|
+
port: config.httpd.port
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare module '*/dist/app/server/index.js' {
|
|
2
|
+
import type { ServerBuild } from './httpd';
|
|
3
|
+
|
|
4
|
+
export declare const entry: ServerBuild['entry'];
|
|
5
|
+
export declare const routes: ServerBuild['routes'];
|
|
6
|
+
export declare const assets: ServerBuild['assets'];
|
|
7
|
+
export declare const publicPath: ServerBuild['publicPath'];
|
|
8
|
+
export declare const assetsBuildDirectory: ServerBuild['assetsBuildDirectory'];
|
|
9
|
+
export declare const future: ServerBuild['future'];
|
|
10
|
+
export declare const ssr: ServerBuild['ssr'];
|
|
11
|
+
export declare const isSpaMode: ServerBuild['isSpaMode'];
|
|
12
|
+
export declare const prerender: ServerBuild['prerender'];
|
|
13
|
+
export declare const routeDiscovery: ServerBuild['routeDiscovery'];
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
FROM cypress/included:15.9.0
|
|
2
|
+
|
|
3
|
+
RUN mv /root/.cache /home/node/.cache && \
|
|
4
|
+
mkdir -p /cypress && \
|
|
5
|
+
chown -R node:node /home/node/.cache && \
|
|
6
|
+
chown -R node:node /cypress/
|
|
7
|
+
ENV CYPRESS_CACHE_FOLDER=/home/node/.cache/Cypress
|
|
8
|
+
|
|
9
|
+
COPY package.json fixtures/* /cypress/fixtures/
|
|
10
|
+
RUN echo '{ "pluginsFile": false }' > '/cypress.json' && \
|
|
11
|
+
rm /cypress/fixtures/package.json
|
|
12
|
+
COPY feat/ /cypress/integration/
|
|
13
|
+
|
|
14
|
+
USER node
|
|
15
|
+
ENV CYPRESS_BASE_URL="http://localhost:8080" \
|
|
16
|
+
CYPRESS_PROJECT_ID="" \
|
|
17
|
+
CYPRESS_RECORD_KEY=""
|
|
18
|
+
HEALTHCHECK NONE
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.vite.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"types": ["node", "vite/client", "mdx", "@catalyse/vite-html-react"],
|
|
5
|
+
"rootDirs": [".", ".react-router/types"],
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"paths": {
|
|
8
|
+
"~/*": ["./src/app/*"]
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"include": [
|
|
12
|
+
"src",
|
|
13
|
+
".react-router/types"
|
|
14
|
+
],
|
|
15
|
+
"exclude": [
|
|
16
|
+
"dist",
|
|
17
|
+
"node_modules"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
build: {
|
|
5
|
+
commonjsOptions: {
|
|
6
|
+
defaultIsModuleExports: true // Mimics Node.js
|
|
7
|
+
},
|
|
8
|
+
copyPublicDir: false,
|
|
9
|
+
minify: false,
|
|
10
|
+
modulePreload: false,
|
|
11
|
+
rollupOptions: {
|
|
12
|
+
output: {
|
|
13
|
+
manualChunks: null,
|
|
14
|
+
inlineDynamicImports: true
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
ssr: 'src/server/index.ts',
|
|
18
|
+
outDir: 'dist/server',
|
|
19
|
+
target: 'node24'
|
|
20
|
+
},
|
|
21
|
+
ssr: {
|
|
22
|
+
noExternal: true
|
|
23
|
+
}
|
|
24
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import mdx from '@mdx-js/rollup'
|
|
3
|
+
import html from '@react-foundry/vite-html-react';
|
|
4
|
+
import { reactRouter } from '@react-router/dev/vite';
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
build: {
|
|
8
|
+
commonjsOptions: {
|
|
9
|
+
defaultIsModuleExports: true // Mimics Node.js, aligns dev and prod
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
plugins: [
|
|
13
|
+
html(),
|
|
14
|
+
mdx(),
|
|
15
|
+
reactRouter(),
|
|
16
|
+
],
|
|
17
|
+
ssr: {
|
|
18
|
+
noExternal: /\.mdx$/
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{{#mdTitle}}{{{titleCase (pkg 'name')}}} - {{{titleCase name}}}{{/mdTitle}}
|
|
2
|
+
|
|
3
|
+
{{{description}}}
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Using this package
|
|
7
|
+
------------------
|
|
8
|
+
|
|
9
|
+
First install the package into your project:
|
|
10
|
+
|
|
11
|
+
```shell
|
|
12
|
+
npm install -S @{{{pkg 'name'}}}/{{{dashCase name}}}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then use it in your code as follows:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import React, { createElement as h } from 'react';
|
|
19
|
+
import {{{properCase name}}} from '@{{{pkg 'name'}}}/{{{dashCase name}}}';
|
|
20
|
+
|
|
21
|
+
export const MyComponent = props => (
|
|
22
|
+
<{{{properCase name}}}
|
|
23
|
+
// WRITEME
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export default MyComponent;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Working on this package
|
|
32
|
+
-----------------------
|
|
33
|
+
|
|
34
|
+
Before working on this package you must install its dependencies using
|
|
35
|
+
the following command:
|
|
36
|
+
|
|
37
|
+
```shell
|
|
38
|
+
pnpm install
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Testing
|
|
43
|
+
|
|
44
|
+
Run the unit tests.
|
|
45
|
+
|
|
46
|
+
```shell
|
|
47
|
+
npm test
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
### Building
|
|
52
|
+
|
|
53
|
+
Build the package by compiling the TypeScript source code.
|
|
54
|
+
|
|
55
|
+
```shell
|
|
56
|
+
npm run build
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### Clean-up
|
|
61
|
+
|
|
62
|
+
Remove any previously built files.
|
|
63
|
+
|
|
64
|
+
```shell
|
|
65
|
+
npm run clean
|
|
66
|
+
```
|
|
@@ -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/**/{!(*.stories),}.{ts,tsx}'
|
|
12
|
+
]
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports = config;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{{pkg 'name'}}}/{{{dashCase name}}}",
|
|
3
|
+
"version": "{{{pkg 'version'}}}",
|
|
4
|
+
"description": "{{{description}}}",
|
|
5
|
+
"main": "src/{{{properCase name}}}.tsx",
|
|
6
|
+
"sass": "assets/{{{properCase name}}}.scss",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"sass": "./assets/{{{properCase name}}}.scss",
|
|
10
|
+
"default": "./src/{{{properCase name}}}.tsx"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"main": "dist/{{{properCase name}}}.js",
|
|
15
|
+
"module": "dist/{{{properCase name}}}.mjs",
|
|
16
|
+
"typings": "dist/{{{properCase name}}}.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"sass": "./assets/{{{properCase name}}}.scss",
|
|
20
|
+
"types": "./dist/{{{properCase name}}}.d.ts",
|
|
21
|
+
"import": "./dist/{{{properCase name}}}.mjs",
|
|
22
|
+
"require": "./dist/{{{properCase name}}}.js",
|
|
23
|
+
"default": "./dist/{{{properCase name}}}.mjs"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"main": "dist/{{{properCase name}}}.js",
|
|
29
|
+
"typings": "dist/{{{properCase name}}}.d.ts"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"/assets",
|
|
33
|
+
"/dist"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
37
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
38
|
+
"build": "npm run build:esm && npm run build:cjs",
|
|
39
|
+
"build:esm": "tsc -m es2022 && find dist -name '*.js' -exec sh -c 'mv \"$0\" \"${0%.js}.mjs\"' {} \\;",
|
|
40
|
+
"build:cjs": "tsc",
|
|
41
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
|
42
|
+
},
|
|
43
|
+
"author": "{{{pkg 'author'}}}",
|
|
44
|
+
"license": "{{{pkg 'license'}}}",
|
|
45
|
+
"keywords": [
|
|
46
|
+
"react-components"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@react-foundry/component-helpers": "{{#if (eq (pkg 'name')'react-foundry')}}workspace:^0.1.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@react-foundry/docs-components": "^0.1.0",
|
|
53
|
+
"@storybook/addon-docs": "^9.1.17",
|
|
54
|
+
"react": "^19.2.3"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"@react-foundry/docs-components": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"@storybook/addon-docs": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@react-foundry/component-test-helpers": "{{#if (eq (pkg 'name')'react-foundry')}}workspace:*{{else}}0.1.0{{/if}}",
|
|
66
|
+
"@types/react": "19.2.8",
|
|
67
|
+
"jest": "30.2.0",
|
|
68
|
+
"jest-environment-jsdom": "30.2.0",
|
|
69
|
+
"ts-jest": "29.4.6",
|
|
70
|
+
"typescript": "5.9.3"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Canvas, Controls, Meta, Primary } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as Stories from './{{{properCase name}}}.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# {{{name}}}
|
|
7
|
+
|
|
8
|
+
{{{description}}}
|
|
9
|
+
WRITE MORE DESCRIPTION HERE.
|
|
10
|
+
|
|
11
|
+
<Primary />
|
|
12
|
+
<Controls />
|
|
13
|
+
|
|
14
|
+
## Stories
|
|
15
|
+
### Standard
|
|
16
|
+
|
|
17
|
+
A standard {{{name}}}.
|
|
18
|
+
|
|
19
|
+
<Canvas of={Stories.Primary} />
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { {{{properCase name}}} } from '../src/{{{properCase name}}}';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: '{{{name}}}',
|
|
7
|
+
component: {{{properCase name}}},
|
|
8
|
+
parameters: {
|
|
9
|
+
chromatic: { viewports: [768, 360] },
|
|
10
|
+
description: '{{{description}}}'
|
|
11
|
+
},
|
|
12
|
+
args: {
|
|
13
|
+
}
|
|
14
|
+
} satisfies Meta<typeof {{{properCase name}}}>;
|
|
15
|
+
|
|
16
|
+
export default meta;
|
|
17
|
+
type Story = StoryObj<typeof meta>;
|
|
18
|
+
|
|
19
|
+
export const Primary: Story = {
|
|
20
|
+
args: {
|
|
21
|
+
}
|
|
22
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createElement as h } from 'react';
|
|
2
|
+
import { render, screen } from '@react-foundry/component-test-helpers';
|
|
3
|
+
import {{{properCase name}}} from '../src/{{{properCase name}}}';
|
|
4
|
+
|
|
5
|
+
describe('{{{properCase name}}}', () => {
|
|
6
|
+
const minimalProps = {
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
describe('when given minimal valid props', () => {
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
render(h({{{properCase name}}}, minimalProps));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('renders an element', async () => expect(screen.getByRole('generic')).toBeInTheDocument());
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('when given all valid props', () => {
|
|
18
|
+
const props = {
|
|
19
|
+
...minimalProps
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
render(h({{{properCase name}}}, props));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('renders an element', async () => expect(screen.getByRole('generic')).toBeInTheDocument());
|
|
27
|
+
});
|
|
28
|
+
});
|