@lazarv/create-react-server 0.0.0-experimental-d003259-20250211-2495688e
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 +21 -0
- package/README.md +31 -0
- package/generator.mjs +195 -0
- package/globals.d.ts +59 -0
- package/index.mjs +87 -0
- package/launch.mjs +136 -0
- package/lib/code-merge.mjs +246 -0
- package/lib/dynamic-checkbox.mjs +259 -0
- package/lib/files.mjs +6 -0
- package/lib/formatter.mjs +16 -0
- package/lib/generate-name.mjs +220 -0
- package/lib/theme.mjs +56 -0
- package/logo.mjs +3 -0
- package/package.json +26 -0
- package/steps/alias.mjs +50 -0
- package/steps/authentication.mjs +62 -0
- package/steps/database.mjs +56 -0
- package/steps/deploy.mjs +151 -0
- package/steps/features.mjs +415 -0
- package/steps/host.mjs +40 -0
- package/steps/index.mjs +33 -0
- package/steps/integrations.mjs +104 -0
- package/steps/name.mjs +69 -0
- package/steps/package.mjs +104 -0
- package/steps/port.mjs +42 -0
- package/steps/preset.mjs +188 -0
- package/steps/state-management.mjs +68 -0
- package/steps/third-party.mjs +19 -0
- package/steps/ui.mjs +87 -0
- package/templates/.dockerignore +5 -0
- package/templates/.gitignore.template +19 -0
- package/templates/.prettierignore +3 -0
- package/templates/.prettierrc +11 -0
- package/templates/Dockerfile.npm +49 -0
- package/templates/Dockerfile.pnpm +71 -0
- package/templates/Dockerfile.yarn +71 -0
- package/templates/README.docker.md +15 -0
- package/templates/README.md +35 -0
- package/templates/blank/package.json +6 -0
- package/templates/blank/src/App.jsx +5 -0
- package/templates/blank-ts/package.json +6 -0
- package/templates/blank-ts/src/App.tsx +5 -0
- package/templates/eslint.config.template.mjs +98 -0
- package/templates/get-started/package.json +6 -0
- package/templates/get-started/src/App.jsx +60 -0
- package/templates/get-started/src/Button.jsx +14 -0
- package/templates/get-started/src/Confetti.jsx +19 -0
- package/templates/get-started/src/global.css +3 -0
- package/templates/get-started-ts/globals.d.ts +3 -0
- package/templates/get-started-ts/package.json +6 -0
- package/templates/get-started-ts/src/App.tsx +66 -0
- package/templates/get-started-ts/src/Button.tsx +18 -0
- package/templates/get-started-ts/src/Confetti.tsx +19 -0
- package/templates/get-started-ts/src/global.css +3 -0
- package/templates/nextjs/globals.d.ts +3 -0
- package/templates/nextjs/react-server.config.json +9 -0
- package/templates/nextjs/src/app/layout.tsx +38 -0
- package/templates/nextjs/src/app/page.tsx +33 -0
- package/templates/nextjs/src/components/Button.tsx +18 -0
- package/templates/nextjs/src/components/Confetti.tsx +19 -0
- package/templates/nextjs/src/global.css +3 -0
- package/templates/package.css.json +5 -0
- package/templates/package.eslint.json +19 -0
- package/templates/package.eslint.ts.json +7 -0
- package/templates/package.json +12 -0
- package/templates/package.lightningcss.json +6 -0
- package/templates/package.prettier.json +8 -0
- package/templates/package.react-compiler.json +7 -0
- package/templates/package.swc.json +5 -0
- package/templates/package.tailwind.json +7 -0
- package/templates/package.ts.json +11 -0
- package/templates/postcss.config.mjs +6 -0
- package/templates/router/globals.d.ts +3 -0
- package/templates/router/react-server.config.json +19 -0
- package/templates/router/src/app/@content/about.tsx +98 -0
- package/templates/router/src/app/@content/index.tsx +33 -0
- package/templates/router/src/app/layout.tsx +44 -0
- package/templates/router/src/app/page.tsx +17 -0
- package/templates/router/src/components/Button.tsx +18 -0
- package/templates/router/src/components/Confetti.tsx +19 -0
- package/templates/router/src/components/Navigation.tsx +31 -0
- package/templates/router/src/global.css +3 -0
- package/templates/shared/public/github.svg +4 -0
- package/templates/shared/public/react-server.svg +51 -0
- package/templates/tailwind.config.mjs +6 -0
- package/templates/tsconfig.css.json +9 -0
- package/templates/tsconfig.template.json +15 -0
- package/templates/vite.config.lightningcss.mjs +15 -0
- package/templates/vite.config.lightningcss.ts +15 -0
- package/templates/vite.config.react-compiler.mjs +19 -0
- package/templates/vite.config.react-compiler.ts +19 -0
- package/templates/vite.config.swc.mjs +6 -0
- package/templates/vite.config.swc.ts +6 -0
- package/templates/vite.config.ts +3 -0
- package/wizard.mjs +122 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
import { confirm, select } from "@inquirer/prompts";
|
|
4
|
+
import colors from "picocolors";
|
|
5
|
+
|
|
6
|
+
import { theme } from "../lib/theme.mjs";
|
|
7
|
+
|
|
8
|
+
const isPnpmInstalled = async () => {
|
|
9
|
+
try {
|
|
10
|
+
execSync("pnpm --version", {
|
|
11
|
+
stdio: "ignore",
|
|
12
|
+
});
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const isYarnInstalled = async () => {
|
|
20
|
+
try {
|
|
21
|
+
execSync("yarn --version", {
|
|
22
|
+
stdio: "ignore",
|
|
23
|
+
});
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const lockFilename = {
|
|
31
|
+
npm: "package-lock.json",
|
|
32
|
+
pnpm: "pnpm-lock.yaml",
|
|
33
|
+
yarn: "yarn.lock",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const displayName = {
|
|
37
|
+
yarn: "Yarn",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const ciInstallArgs = {
|
|
41
|
+
npm: "--loglevel notice",
|
|
42
|
+
pnpm: "--reporter=append-only",
|
|
43
|
+
yarn: "--no-progress --inline-builds",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default async (context) => {
|
|
47
|
+
const npmUseragent = process.env.npm_config_user_agent;
|
|
48
|
+
const defaultPackageManager =
|
|
49
|
+
npmUseragent?.includes("pnpm") || !context.props.custom
|
|
50
|
+
? "pnpm"
|
|
51
|
+
: npmUseragent?.includes("yarn")
|
|
52
|
+
? "yarn"
|
|
53
|
+
: "npm";
|
|
54
|
+
const packageManager = !context.props.custom
|
|
55
|
+
? defaultPackageManager
|
|
56
|
+
: await select(
|
|
57
|
+
{
|
|
58
|
+
message: "Package manager",
|
|
59
|
+
default: defaultPackageManager,
|
|
60
|
+
choices: [
|
|
61
|
+
{ name: "npm", value: "npm" },
|
|
62
|
+
{
|
|
63
|
+
name: "pnpm",
|
|
64
|
+
value: "pnpm",
|
|
65
|
+
disabled: !(await isPnpmInstalled()) ? "(not installed)" : false,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "Yarn",
|
|
69
|
+
value: "yarn",
|
|
70
|
+
disabled: !(await isYarnInstalled()) ? "(not installed)" : false,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
theme,
|
|
74
|
+
},
|
|
75
|
+
context
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
let install = true;
|
|
79
|
+
if (context.props.custom) {
|
|
80
|
+
install = await confirm(
|
|
81
|
+
{
|
|
82
|
+
message: `Install dependencies using ${colors.cyan(displayName[packageManager] ?? packageManager)}?`,
|
|
83
|
+
default: true,
|
|
84
|
+
theme,
|
|
85
|
+
},
|
|
86
|
+
context
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
...context,
|
|
92
|
+
props: {
|
|
93
|
+
...context.props,
|
|
94
|
+
packageManager: {
|
|
95
|
+
name: packageManager,
|
|
96
|
+
lock: lockFilename[packageManager],
|
|
97
|
+
run: packageManager === "npm" ? "npm run" : packageManager,
|
|
98
|
+
start: packageManager === "npm" ? "npm start" : packageManager,
|
|
99
|
+
install,
|
|
100
|
+
ciInstallArgs: ciInstallArgs[packageManager],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
};
|
package/steps/port.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { number } from "@inquirer/prompts";
|
|
2
|
+
|
|
3
|
+
import { theme } from "../lib/theme.mjs";
|
|
4
|
+
|
|
5
|
+
export default async (context) => {
|
|
6
|
+
const port = context.env.options.port
|
|
7
|
+
? parseInt(context.env.options.port)
|
|
8
|
+
: !context.props.custom || context.env.hasOptions
|
|
9
|
+
? 3000
|
|
10
|
+
: await number(
|
|
11
|
+
{
|
|
12
|
+
message: "Port",
|
|
13
|
+
default: 3000,
|
|
14
|
+
description: "The port to use for the server",
|
|
15
|
+
min: 1,
|
|
16
|
+
max: 65535,
|
|
17
|
+
theme,
|
|
18
|
+
},
|
|
19
|
+
context
|
|
20
|
+
);
|
|
21
|
+
return {
|
|
22
|
+
...context,
|
|
23
|
+
props: {
|
|
24
|
+
...context.props,
|
|
25
|
+
port,
|
|
26
|
+
},
|
|
27
|
+
partials:
|
|
28
|
+
port !== 3000
|
|
29
|
+
? {
|
|
30
|
+
...context.partials,
|
|
31
|
+
"react-server.config.json": {
|
|
32
|
+
...context.partials["react-server.config.json"],
|
|
33
|
+
type: "json",
|
|
34
|
+
merge: [
|
|
35
|
+
...(context.partials["react-server.config.json"]?.merge ?? []),
|
|
36
|
+
{ server: { port } },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
: context.partials,
|
|
41
|
+
};
|
|
42
|
+
};
|
package/steps/preset.mjs
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { join, relative } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { confirm, select, Separator } from "@inquirer/prompts";
|
|
4
|
+
import glob from "fast-glob";
|
|
5
|
+
|
|
6
|
+
import { json } from "../lib/files.mjs";
|
|
7
|
+
import { theme } from "../lib/theme.mjs";
|
|
8
|
+
|
|
9
|
+
const defaultFeatures = ["git", "eslint", "prettier", "tailwind"];
|
|
10
|
+
const presets = {
|
|
11
|
+
blank: {
|
|
12
|
+
features: [],
|
|
13
|
+
},
|
|
14
|
+
"blank-ts": {
|
|
15
|
+
features: ["ts"],
|
|
16
|
+
},
|
|
17
|
+
"get-started": {
|
|
18
|
+
features: defaultFeatures,
|
|
19
|
+
},
|
|
20
|
+
"get-started-ts": {
|
|
21
|
+
features: ["ts", ...defaultFeatures],
|
|
22
|
+
alias: "~/*",
|
|
23
|
+
},
|
|
24
|
+
router: {
|
|
25
|
+
features: ["ts", ...defaultFeatures],
|
|
26
|
+
alias: "~/*",
|
|
27
|
+
},
|
|
28
|
+
nextjs: {
|
|
29
|
+
features: ["ts", ...defaultFeatures, "react-swc"],
|
|
30
|
+
alias: "~/*",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default async (context) => {
|
|
35
|
+
const choices = [
|
|
36
|
+
{
|
|
37
|
+
name: "Get started (TypeScript)",
|
|
38
|
+
value: "get-started-ts",
|
|
39
|
+
description: "A simple project to get you started using TypeScript",
|
|
40
|
+
shared: true,
|
|
41
|
+
},
|
|
42
|
+
new Separator(),
|
|
43
|
+
{
|
|
44
|
+
name: "Blank",
|
|
45
|
+
value: "blank",
|
|
46
|
+
description: "A blank project with no additional files",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Blank (TypeScript)",
|
|
50
|
+
value: "blank-ts",
|
|
51
|
+
description: "A blank TypeScript project with no additional files",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "Get started (JavaScript)",
|
|
55
|
+
value: "get-started",
|
|
56
|
+
description: "A simple project to get you started",
|
|
57
|
+
shared: true,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "File-system based routing",
|
|
61
|
+
value: "router",
|
|
62
|
+
description:
|
|
63
|
+
"A TypeScript project utilizing type-safe file-system based routing",
|
|
64
|
+
shared: true,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "Next.js App Router configuration",
|
|
68
|
+
value: "nextjs",
|
|
69
|
+
description:
|
|
70
|
+
"A TypeScript project utilizing a partially Next.js-compatible file-system based routing configuration",
|
|
71
|
+
shared: true,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "Blog",
|
|
75
|
+
value: "blog",
|
|
76
|
+
description: "A simple blog example project",
|
|
77
|
+
disabled: "(coming soon)",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "Portfolio",
|
|
81
|
+
value: "portfolio",
|
|
82
|
+
description: "A simple portfolio example project",
|
|
83
|
+
disabled: "(coming soon)",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "Documentation",
|
|
87
|
+
value: "docs",
|
|
88
|
+
description: "A simple documentation example project",
|
|
89
|
+
disabled: "(coming soon)",
|
|
90
|
+
},
|
|
91
|
+
new Separator(),
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const type =
|
|
95
|
+
context.env.options.preset ??
|
|
96
|
+
(await select(
|
|
97
|
+
{
|
|
98
|
+
message: "Preset",
|
|
99
|
+
choices,
|
|
100
|
+
theme,
|
|
101
|
+
},
|
|
102
|
+
context
|
|
103
|
+
));
|
|
104
|
+
|
|
105
|
+
let custom = false;
|
|
106
|
+
if (!context.env.hasOptions) {
|
|
107
|
+
custom = await confirm(
|
|
108
|
+
{
|
|
109
|
+
message: "Do you want to customize your project?",
|
|
110
|
+
default: false,
|
|
111
|
+
theme,
|
|
112
|
+
},
|
|
113
|
+
context
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const template = async (context) => {
|
|
118
|
+
const templateAppDir = join(context.env.templateDir, type);
|
|
119
|
+
const files = (
|
|
120
|
+
await glob(["**/*"], {
|
|
121
|
+
cwd: templateAppDir,
|
|
122
|
+
onlyFiles: true,
|
|
123
|
+
absolute: true,
|
|
124
|
+
})
|
|
125
|
+
).map((file) => [
|
|
126
|
+
file,
|
|
127
|
+
join(context.env.projectDir, relative(templateAppDir, file)),
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
let sharedFiles = [];
|
|
131
|
+
const choice = choices.find((choice) => choice.value === type);
|
|
132
|
+
if (choice.shared) {
|
|
133
|
+
const sharedTemplateAppDir = join(context.env.templateDir, "shared");
|
|
134
|
+
sharedFiles = (
|
|
135
|
+
await glob(["**/*"], {
|
|
136
|
+
cwd: sharedTemplateAppDir,
|
|
137
|
+
onlyFiles: true,
|
|
138
|
+
absolute: true,
|
|
139
|
+
})
|
|
140
|
+
).map((file) => [
|
|
141
|
+
file,
|
|
142
|
+
join(context.env.projectDir, relative(sharedTemplateAppDir, file)),
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
...context,
|
|
148
|
+
interactive: !context.env.options.preset,
|
|
149
|
+
env: {
|
|
150
|
+
...context.env,
|
|
151
|
+
templateAppDir,
|
|
152
|
+
},
|
|
153
|
+
files: [...(context.files ?? []), ...files, ...sharedFiles],
|
|
154
|
+
partials: {
|
|
155
|
+
...context.partials,
|
|
156
|
+
...(files.includes("package.json")
|
|
157
|
+
? {
|
|
158
|
+
"package.json": {
|
|
159
|
+
...context.partials["package.json"],
|
|
160
|
+
merge: [
|
|
161
|
+
...(context.partials["package.json"]?.merge ?? []),
|
|
162
|
+
await json(join(templateAppDir, "package.json")),
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
: {}),
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
...context,
|
|
173
|
+
props: {
|
|
174
|
+
...context.props,
|
|
175
|
+
template,
|
|
176
|
+
custom,
|
|
177
|
+
preset: {
|
|
178
|
+
type,
|
|
179
|
+
...presets[type],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
template,
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export async function prepare(context) {
|
|
187
|
+
return context.template(context);
|
|
188
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { select, Separator } from "@inquirer/prompts";
|
|
2
|
+
|
|
3
|
+
import { theme } from "../lib/theme.mjs";
|
|
4
|
+
|
|
5
|
+
export default [
|
|
6
|
+
(context) => context.props.thirdParty,
|
|
7
|
+
async (context) => {
|
|
8
|
+
const auth = !context.props.custom
|
|
9
|
+
? "none"
|
|
10
|
+
: await select(
|
|
11
|
+
{
|
|
12
|
+
message: "State management provider",
|
|
13
|
+
choices: [
|
|
14
|
+
{
|
|
15
|
+
name: "None",
|
|
16
|
+
value: "none",
|
|
17
|
+
description: "No state management",
|
|
18
|
+
},
|
|
19
|
+
new Separator(),
|
|
20
|
+
{
|
|
21
|
+
name: "TanStack Query",
|
|
22
|
+
value: "tanstack-query",
|
|
23
|
+
description: "Add TanStack Query integration",
|
|
24
|
+
disabled: "(coming soon)",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "Zustand",
|
|
28
|
+
value: "zustand",
|
|
29
|
+
description: "Add Zustand integration",
|
|
30
|
+
disabled: "(coming soon)",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "Jotai",
|
|
34
|
+
value: "jotai",
|
|
35
|
+
description: "Add Jotai integration",
|
|
36
|
+
disabled: "(coming soon)",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "Valtio",
|
|
40
|
+
value: "valtio",
|
|
41
|
+
description: "Add Valtio integration",
|
|
42
|
+
disabled: "(coming soon)",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "Redux",
|
|
46
|
+
value: "redux",
|
|
47
|
+
description: "Add Redux integration",
|
|
48
|
+
disabled: "(coming soon)",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "MobX",
|
|
52
|
+
value: "mobx",
|
|
53
|
+
description: "Add MobX integration",
|
|
54
|
+
disabled: "(coming soon)",
|
|
55
|
+
},
|
|
56
|
+
new Separator(),
|
|
57
|
+
],
|
|
58
|
+
theme,
|
|
59
|
+
},
|
|
60
|
+
context
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
...context,
|
|
65
|
+
props: { ...context.props, auth },
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { confirm } from "@inquirer/prompts";
|
|
2
|
+
|
|
3
|
+
import { theme } from "../lib/theme.mjs";
|
|
4
|
+
|
|
5
|
+
export default async (context) => {
|
|
6
|
+
const answer = await confirm({
|
|
7
|
+
message: "Use third-party integrations?",
|
|
8
|
+
default: false,
|
|
9
|
+
theme,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
...context,
|
|
14
|
+
props: {
|
|
15
|
+
...context.props,
|
|
16
|
+
thirdParty: answer,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
};
|
package/steps/ui.mjs
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { select, Separator } from "@inquirer/prompts";
|
|
2
|
+
import colors from "picocolors";
|
|
3
|
+
|
|
4
|
+
import { theme } from "../lib/theme.mjs";
|
|
5
|
+
|
|
6
|
+
export default [
|
|
7
|
+
(context) => context.props.thirdParty,
|
|
8
|
+
async (context) => {
|
|
9
|
+
const ui = !context.props.custom
|
|
10
|
+
? "none"
|
|
11
|
+
: await select(
|
|
12
|
+
{
|
|
13
|
+
message: "UI Framework",
|
|
14
|
+
choices: [
|
|
15
|
+
{
|
|
16
|
+
name: "None",
|
|
17
|
+
value: "none",
|
|
18
|
+
description: "No UI framework",
|
|
19
|
+
},
|
|
20
|
+
new Separator(),
|
|
21
|
+
{
|
|
22
|
+
name: `shadcn/ui ${colors.yellow("(recommended)")}`,
|
|
23
|
+
value: "shadcn",
|
|
24
|
+
description: "Shadcn/UI",
|
|
25
|
+
disabled: "(coming soon)",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "Radix UI",
|
|
29
|
+
value: "radix-ui",
|
|
30
|
+
description: "Radix UI",
|
|
31
|
+
disabled: "(coming soon)",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "Mantine",
|
|
35
|
+
value: "mantine",
|
|
36
|
+
description: "Mantine",
|
|
37
|
+
disabled: "(coming soon)",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "Material UI",
|
|
41
|
+
value: "mui",
|
|
42
|
+
description: "Material UI",
|
|
43
|
+
disabled: "(coming soon)",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "Chakra UI",
|
|
47
|
+
value: "chakra",
|
|
48
|
+
description: "Chakra UI",
|
|
49
|
+
disabled: "(coming soon)",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "Ant Design",
|
|
53
|
+
value: "ant",
|
|
54
|
+
description: "Ant Design",
|
|
55
|
+
disabled: "(coming soon)",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "Next UI",
|
|
59
|
+
value: "next-ui",
|
|
60
|
+
description: "Next UI",
|
|
61
|
+
disabled: "(coming soon)",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "Grommet",
|
|
65
|
+
value: "grommet",
|
|
66
|
+
description: "Grommet",
|
|
67
|
+
disabled: "(coming soon)",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "Flowbite",
|
|
71
|
+
value: "flowbite",
|
|
72
|
+
description: "Flowbite",
|
|
73
|
+
disabled: "(coming soon)",
|
|
74
|
+
},
|
|
75
|
+
new Separator(),
|
|
76
|
+
],
|
|
77
|
+
theme,
|
|
78
|
+
},
|
|
79
|
+
context
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
...context,
|
|
84
|
+
props: { ...context.props, ui },
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Define build argument for port with default value
|
|
2
|
+
ARG PORT=3000
|
|
3
|
+
|
|
4
|
+
# Stage 1: Build
|
|
5
|
+
FROM node:20-alpine AS builder
|
|
6
|
+
|
|
7
|
+
# Set working directory
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
|
|
10
|
+
# Copy source files
|
|
11
|
+
COPY . .
|
|
12
|
+
|
|
13
|
+
# Install dependencies
|
|
14
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
15
|
+
npm ci
|
|
16
|
+
|
|
17
|
+
# Build the application
|
|
18
|
+
RUN npm run build
|
|
19
|
+
|
|
20
|
+
# Stage 2: Production
|
|
21
|
+
FROM node:20-alpine AS runner
|
|
22
|
+
|
|
23
|
+
# Forward the build argument
|
|
24
|
+
ARG PORT
|
|
25
|
+
ENV PORT=$PORT
|
|
26
|
+
|
|
27
|
+
# Set working directory
|
|
28
|
+
WORKDIR /app
|
|
29
|
+
|
|
30
|
+
# Copy package.json, node_modules and .react-server from builder stage
|
|
31
|
+
COPY --from=builder /app/package.json ./
|
|
32
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
33
|
+
COPY --from=builder /app/.react-server ./.react-server
|
|
34
|
+
|
|
35
|
+
# Prune dev dependencies
|
|
36
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
37
|
+
npm prune --production
|
|
38
|
+
|
|
39
|
+
# Run as non-root user
|
|
40
|
+
RUN addgroup -g 1001 -S nodejs && \
|
|
41
|
+
adduser -S nodejs -u 1001 && \
|
|
42
|
+
chown -R nodejs:nodejs /app
|
|
43
|
+
USER nodejs
|
|
44
|
+
|
|
45
|
+
# Expose the port your app runs on
|
|
46
|
+
EXPOSE ${PORT}
|
|
47
|
+
|
|
48
|
+
# Start the application
|
|
49
|
+
CMD ["npm", "start", "--", "--host"]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Define build argument for port with default value
|
|
2
|
+
ARG PORT=3000
|
|
3
|
+
|
|
4
|
+
# Stage 1: Build
|
|
5
|
+
FROM node:20-alpine AS builder
|
|
6
|
+
|
|
7
|
+
# Set working directory
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
|
|
10
|
+
# Copy package files first to check for corepack and lock files
|
|
11
|
+
COPY package.json pnpm-lock.yaml ./
|
|
12
|
+
|
|
13
|
+
# Install pnpm using corepack or npm
|
|
14
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
15
|
+
if grep -q "\"packageManager\":" "package.json"; then \
|
|
16
|
+
corepack enable && corepack prepare; \
|
|
17
|
+
else \
|
|
18
|
+
npm install -g pnpm; \
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Install dependencies using pnpm
|
|
22
|
+
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
|
|
23
|
+
pnpm install --frozen-lockfile
|
|
24
|
+
|
|
25
|
+
# Copy source files and .react-server directory
|
|
26
|
+
COPY . .
|
|
27
|
+
|
|
28
|
+
# Build using pnpm
|
|
29
|
+
RUN pnpm run build
|
|
30
|
+
|
|
31
|
+
# Stage 2: Production
|
|
32
|
+
FROM node:20-alpine AS runner
|
|
33
|
+
|
|
34
|
+
# Forward the build argument
|
|
35
|
+
ARG PORT
|
|
36
|
+
ENV PORT=$PORT
|
|
37
|
+
|
|
38
|
+
# Set working directory
|
|
39
|
+
WORKDIR /app
|
|
40
|
+
|
|
41
|
+
# Copy package files and lock files
|
|
42
|
+
COPY --from=builder /app/package.json /app/pnpm-lock.yaml ./
|
|
43
|
+
|
|
44
|
+
# Install pnpm in production stage
|
|
45
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
46
|
+
if grep -q "\"packageManager\":" "package.json"; then \
|
|
47
|
+
corepack enable && corepack prepare; \
|
|
48
|
+
else \
|
|
49
|
+
npm install -g pnpm; \
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Copy package.json, pnpm-lock.yaml, node_modules and .react-server from builder stage
|
|
53
|
+
COPY --from=builder /app/package.json /app/pnpm-lock.yaml ./
|
|
54
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
55
|
+
COPY --from=builder /app/.react-server ./.react-server
|
|
56
|
+
|
|
57
|
+
# Prune dev dependencies using pnpm
|
|
58
|
+
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
|
|
59
|
+
pnpm prune --prod
|
|
60
|
+
|
|
61
|
+
# Run as non-root user
|
|
62
|
+
RUN addgroup -g 1001 -S nodejs && \
|
|
63
|
+
adduser -S nodejs -u 1001 && \
|
|
64
|
+
chown -R nodejs:nodejs /app
|
|
65
|
+
USER nodejs
|
|
66
|
+
|
|
67
|
+
# Expose the port your app runs on
|
|
68
|
+
EXPOSE ${PORT}
|
|
69
|
+
|
|
70
|
+
# Start the application using pnpm
|
|
71
|
+
CMD ["pnpm", "start", "--host"]
|