@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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/generator.mjs +195 -0
  4. package/globals.d.ts +59 -0
  5. package/index.mjs +87 -0
  6. package/launch.mjs +136 -0
  7. package/lib/code-merge.mjs +246 -0
  8. package/lib/dynamic-checkbox.mjs +259 -0
  9. package/lib/files.mjs +6 -0
  10. package/lib/formatter.mjs +16 -0
  11. package/lib/generate-name.mjs +220 -0
  12. package/lib/theme.mjs +56 -0
  13. package/logo.mjs +3 -0
  14. package/package.json +26 -0
  15. package/steps/alias.mjs +50 -0
  16. package/steps/authentication.mjs +62 -0
  17. package/steps/database.mjs +56 -0
  18. package/steps/deploy.mjs +151 -0
  19. package/steps/features.mjs +415 -0
  20. package/steps/host.mjs +40 -0
  21. package/steps/index.mjs +33 -0
  22. package/steps/integrations.mjs +104 -0
  23. package/steps/name.mjs +69 -0
  24. package/steps/package.mjs +104 -0
  25. package/steps/port.mjs +42 -0
  26. package/steps/preset.mjs +188 -0
  27. package/steps/state-management.mjs +68 -0
  28. package/steps/third-party.mjs +19 -0
  29. package/steps/ui.mjs +87 -0
  30. package/templates/.dockerignore +5 -0
  31. package/templates/.gitignore.template +19 -0
  32. package/templates/.prettierignore +3 -0
  33. package/templates/.prettierrc +11 -0
  34. package/templates/Dockerfile.npm +49 -0
  35. package/templates/Dockerfile.pnpm +71 -0
  36. package/templates/Dockerfile.yarn +71 -0
  37. package/templates/README.docker.md +15 -0
  38. package/templates/README.md +35 -0
  39. package/templates/blank/package.json +6 -0
  40. package/templates/blank/src/App.jsx +5 -0
  41. package/templates/blank-ts/package.json +6 -0
  42. package/templates/blank-ts/src/App.tsx +5 -0
  43. package/templates/eslint.config.template.mjs +98 -0
  44. package/templates/get-started/package.json +6 -0
  45. package/templates/get-started/src/App.jsx +60 -0
  46. package/templates/get-started/src/Button.jsx +14 -0
  47. package/templates/get-started/src/Confetti.jsx +19 -0
  48. package/templates/get-started/src/global.css +3 -0
  49. package/templates/get-started-ts/globals.d.ts +3 -0
  50. package/templates/get-started-ts/package.json +6 -0
  51. package/templates/get-started-ts/src/App.tsx +66 -0
  52. package/templates/get-started-ts/src/Button.tsx +18 -0
  53. package/templates/get-started-ts/src/Confetti.tsx +19 -0
  54. package/templates/get-started-ts/src/global.css +3 -0
  55. package/templates/nextjs/globals.d.ts +3 -0
  56. package/templates/nextjs/react-server.config.json +9 -0
  57. package/templates/nextjs/src/app/layout.tsx +38 -0
  58. package/templates/nextjs/src/app/page.tsx +33 -0
  59. package/templates/nextjs/src/components/Button.tsx +18 -0
  60. package/templates/nextjs/src/components/Confetti.tsx +19 -0
  61. package/templates/nextjs/src/global.css +3 -0
  62. package/templates/package.css.json +5 -0
  63. package/templates/package.eslint.json +19 -0
  64. package/templates/package.eslint.ts.json +7 -0
  65. package/templates/package.json +12 -0
  66. package/templates/package.lightningcss.json +6 -0
  67. package/templates/package.prettier.json +8 -0
  68. package/templates/package.react-compiler.json +7 -0
  69. package/templates/package.swc.json +5 -0
  70. package/templates/package.tailwind.json +7 -0
  71. package/templates/package.ts.json +11 -0
  72. package/templates/postcss.config.mjs +6 -0
  73. package/templates/router/globals.d.ts +3 -0
  74. package/templates/router/react-server.config.json +19 -0
  75. package/templates/router/src/app/@content/about.tsx +98 -0
  76. package/templates/router/src/app/@content/index.tsx +33 -0
  77. package/templates/router/src/app/layout.tsx +44 -0
  78. package/templates/router/src/app/page.tsx +17 -0
  79. package/templates/router/src/components/Button.tsx +18 -0
  80. package/templates/router/src/components/Confetti.tsx +19 -0
  81. package/templates/router/src/components/Navigation.tsx +31 -0
  82. package/templates/router/src/global.css +3 -0
  83. package/templates/shared/public/github.svg +4 -0
  84. package/templates/shared/public/react-server.svg +51 -0
  85. package/templates/tailwind.config.mjs +6 -0
  86. package/templates/tsconfig.css.json +9 -0
  87. package/templates/tsconfig.template.json +15 -0
  88. package/templates/vite.config.lightningcss.mjs +15 -0
  89. package/templates/vite.config.lightningcss.ts +15 -0
  90. package/templates/vite.config.react-compiler.mjs +19 -0
  91. package/templates/vite.config.react-compiler.ts +19 -0
  92. package/templates/vite.config.swc.mjs +6 -0
  93. package/templates/vite.config.swc.ts +6 -0
  94. package/templates/vite.config.ts +3 -0
  95. package/wizard.mjs +122 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Viktor LƔzƔr
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @lazarv/create-react-server
2
+
3
+ To bootstrap your `@lazarv/react-server` project, you can also use `@lazarv/create-react-server`, the official CLI tool to create a new project and add initial tools, features and third-party integrations to your project with ease, just by answering a few questions. To use the tool, run:
4
+
5
+ ```sh
6
+ npx @lazarv/create-react-server
7
+ ```
8
+
9
+ Completing the wizard, follow the instructions in your terminal to explore your new project and have fun!
10
+
11
+ ```
12
+ Usage:
13
+ $ create-react-server [options]
14
+
15
+ Options:
16
+ --name <name> The name of the project
17
+ --preset <preset> The preset to use
18
+ --features <features> The features to use
19
+ --alias <alias> The TypeScript path alias to use
20
+ --host <host> The host to use
21
+ --port <port> The port to use
22
+ --deploy <deploy> The deployment adapter to use
23
+ --git Initialize a git repository
24
+ --dev Run in development mode
25
+ --open Open the project in the browser
26
+ --clean Clean the project directory before bootstrapping
27
+ --no-install Do not install dependencies (default: true)
28
+ --react-server <version> The version of @lazarv/react-server to use
29
+ -v, --version Display version number
30
+ -h, --help Display this message
31
+ ```
package/generator.mjs ADDED
@@ -0,0 +1,195 @@
1
+ import { execSync, spawn } from "node:child_process";
2
+ import { statSync } from "node:fs";
3
+ import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
+ import { extname, isAbsolute, join, relative } from "node:path";
5
+
6
+ import banner from "@lazarv/react-server/lib/utils/banner.mjs";
7
+ import merge from "@lazarv/react-server/lib/utils/merge.mjs";
8
+ import { filesize } from "filesize";
9
+ import colors from "picocolors";
10
+
11
+ import { mergeCodeFiles } from "./lib/code-merge.mjs";
12
+ import { format } from "./lib/formatter.mjs";
13
+
14
+ const extensionColor = {
15
+ ".json": "magenta",
16
+ ".css": "magenta",
17
+ ".scss": "magenta",
18
+ };
19
+
20
+ function size(bytes) {
21
+ const s = filesize(bytes);
22
+ return " ".repeat(Math.max(0, 8 - s.length)) + s;
23
+ }
24
+
25
+ function logFileEmit(name, file, bytes, maxLength) {
26
+ const padding = " ".repeat(maxLength - file.length + 1);
27
+ console.log(
28
+ `${colors.gray(`${name}/${colors[extensionColor[extname(file)] ?? "cyan"](file.replace(/\\/g, "/"))}${padding} ${colors.bold(size(bytes))}`).replace(/^\/+/, "")}`
29
+ );
30
+ }
31
+
32
+ const mergeFunctions = {
33
+ json: async (partials) => merge(...partials),
34
+ text: (partials) => partials.join("\n"),
35
+ code: (partials) => mergeCodeFiles(...partials),
36
+ };
37
+
38
+ function renderTemplate(template, context) {
39
+ return template.replace(/(?:\/\*)?<%=\s*([^\s]+)\s*%>(?:\*\/)?/g, (_, key) =>
40
+ key.split(".").reduce((acc, k) => acc?.[k] ?? "", context)
41
+ );
42
+ }
43
+
44
+ export async function generate(context) {
45
+ const {
46
+ env: { logger },
47
+ } = context;
48
+
49
+ console.log();
50
+ banner("bootstrapping project");
51
+
52
+ try {
53
+ if (statSync(context.env.projectDir).isDirectory()) {
54
+ if (context.env.options.clean) {
55
+ await rm(context.env.projectDir, { recursive: true });
56
+ } else {
57
+ logger.error(
58
+ `Directory ${colors.italic(relative(context.env.cwd, context.env.projectDir))} already exists!`
59
+ );
60
+ process.exit(1);
61
+ }
62
+ }
63
+ } catch {
64
+ // directory does not exist
65
+ }
66
+
67
+ await mkdir(context.env.projectDir, { recursive: true });
68
+
69
+ const maxLength = Math.max(
70
+ 24,
71
+ ...(context.files?.map((file) => file.length) ?? []),
72
+ ...Object.keys(context.partials)
73
+ );
74
+ if (context.files?.length > 0) {
75
+ const { files } = context;
76
+
77
+ for (const file of files) {
78
+ const filename = relative(
79
+ context.env.projectDir,
80
+ Array.isArray(file) ? file[1] : file
81
+ );
82
+ if (context.partials[filename]) {
83
+ const partial = context.partials[filename];
84
+ if (partial.merge) {
85
+ const content = await readFile(
86
+ Array.isArray(file) ? file[0] : file,
87
+ "utf8"
88
+ );
89
+ partial.merge = [
90
+ ...partial.merge,
91
+ partial.type === "json" ? JSON.parse(content) : content,
92
+ ];
93
+ }
94
+ continue;
95
+ }
96
+ const [src, dest] = Array.isArray(file)
97
+ ? file
98
+ : [
99
+ isAbsolute(file) ? file : join(context.env.templateAppDir, file),
100
+ join(
101
+ context.env.projectDir,
102
+ relative(context.env.templateDir, file)
103
+ ),
104
+ ];
105
+ logFileEmit(
106
+ context.props.projectName,
107
+ relative(context.env.projectDir, dest),
108
+ statSync(src).size,
109
+ maxLength
110
+ );
111
+ await cp(src, dest);
112
+ }
113
+ }
114
+
115
+ for (const [file, partial] of Object.entries(context.partials)) {
116
+ if (partial.merge) {
117
+ partial.content = await mergeFunctions[partial.type](partial.merge);
118
+ }
119
+
120
+ if (partial.template) {
121
+ partial.content = renderTemplate(partial.template, context);
122
+ }
123
+
124
+ if (partial.type === "json") {
125
+ partial.content = `${JSON.stringify(partial.content, null, 2)}\n`;
126
+ }
127
+
128
+ const content =
129
+ partial.type === "json"
130
+ ? await format(partial.content, "json")
131
+ : partial.format
132
+ ? await format(
133
+ partial.content,
134
+ typeof partial.format === "string" ? partial.format : "js"
135
+ )
136
+ : partial.content;
137
+ logFileEmit(context.props.projectName, file, content.length, maxLength);
138
+ await writeFile(join(context.env.projectDir, file), content, "utf8");
139
+ }
140
+
141
+ console.log();
142
+
143
+ if (
144
+ context.env.options.git ||
145
+ (context.features.includes("git") && context.env.options.git !== false)
146
+ ) {
147
+ logger.info("Initializing git repository šŸ—ƒļø");
148
+ execSync("git init", { cwd: context.env.projectDir, stdio: "ignore" });
149
+ }
150
+
151
+ if (
152
+ context.props.packageManager &&
153
+ context.env.options.install !== false &&
154
+ context.props.packageManager.install !== false &&
155
+ (!context.props.custom ||
156
+ (context.props.custom && context.props.packageManager.install !== false))
157
+ ) {
158
+ logger.info(
159
+ `Installing dependencies using ${context.props.packageManager.name} šŸ“¦`
160
+ );
161
+
162
+ await new Promise((resolve, reject) => {
163
+ const child = spawn(
164
+ context.props.packageManager.name,
165
+ ["install", ...context.props.packageManager.ciInstallArgs.split(" ")],
166
+ {
167
+ cwd: context.env.projectDir,
168
+ encoding: "utf8",
169
+ stdio: ["inherit", "pipe", "pipe"],
170
+ env: { ...process.env, NPM_CONFIG_COLOR: "always", CI: "1" },
171
+ }
172
+ );
173
+
174
+ child.stdout.on("data", (data) => {
175
+ process.stdout.write(colors.gray(data));
176
+ });
177
+ child.stderr.on("data", (data) => {
178
+ process.stdout.write(colors.gray(data));
179
+ });
180
+ child.on("close", (code) => {
181
+ if (code === 0) {
182
+ resolve(code);
183
+ } else {
184
+ reject(new Error(`Process exited with code ${code}`));
185
+ }
186
+ });
187
+ });
188
+
189
+ console.log();
190
+ logger.info("Ready to launch your project! šŸš€");
191
+ } else {
192
+ logger.warn("You need to install dependencies to start your project!");
193
+ context.props.installInstructions = true;
194
+ }
195
+ }
package/globals.d.ts ADDED
@@ -0,0 +1,59 @@
1
+ interface VitePlugin {
2
+ (): {
3
+ name: string;
4
+ };
5
+ }
6
+
7
+ declare module "vite" {
8
+ export interface UserConfig {
9
+ resolve?: {
10
+ alias?: Record<string, string>;
11
+ };
12
+ plugins?: VitePlugin[];
13
+ css?: {
14
+ transformer?: string;
15
+ lightningcss?: {
16
+ targets?: Record<string, string>;
17
+ };
18
+ };
19
+ build?: {
20
+ cssMinify?: string;
21
+ };
22
+ }
23
+ export function defineConfig(config: UserConfig): UserConfig;
24
+ }
25
+
26
+ declare namespace global {
27
+ interface ImportMeta {
28
+ readonly url: URL;
29
+ }
30
+ }
31
+
32
+ declare module "@vitejs/plugin-react" {
33
+ export default function reactPlugin(options: {
34
+ babel?: {
35
+ plugins: [
36
+ [
37
+ "babel-plugin-react-compiler",
38
+ {
39
+ compilationMode: "annotation";
40
+ },
41
+ ],
42
+ ];
43
+ };
44
+ }): VitePlugin;
45
+ }
46
+
47
+ declare module "@vitejs/plugin-react-swc" {
48
+ export default function reactSwcPlugin(): VitePlugin;
49
+ }
50
+
51
+ declare module "browserslist" {
52
+ export default function browserslist(query: string): string[];
53
+ }
54
+
55
+ declare module "lightningcss" {
56
+ export function browserslistToTargets(
57
+ browsers: string[]
58
+ ): Record<string, string>;
59
+ }
package/index.mjs ADDED
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ experimentalWarningSilence,
5
+ suppressReactWarnings,
6
+ } from "@lazarv/react-server/lib/sys.mjs";
7
+
8
+ experimentalWarningSilence();
9
+ suppressReactWarnings();
10
+
11
+ import { dirname, join } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ import createLogger from "@lazarv/react-server/lib/dev/create-logger.mjs";
15
+ import cac from "cac";
16
+
17
+ const cli = cac();
18
+
19
+ const { default: packageJson } = await import("./package.json", {
20
+ with: { type: "json" },
21
+ });
22
+
23
+ cli.usage("[options]");
24
+
25
+ cli
26
+ .option("--name <name>", "The name of the project")
27
+ .option("--preset <preset>", "The preset to use")
28
+ .option("--features <features>", "The features to use")
29
+ .option("--alias <alias>", "The TypeScript path alias to use")
30
+ .option("--host <host>", "The host to use")
31
+ .option("--port <port>", "The port to use")
32
+ .option("--deploy <deploy>", "The deployment adapter to use")
33
+ .option("--git", "Initialize a git repository")
34
+ .option("--dev", "Run in development mode")
35
+ .option("--open", "Open the project in the browser")
36
+ .option("--clean", "Clean the project directory before bootstrapping")
37
+ .option("--no-install", "Do not install dependencies")
38
+ .option(
39
+ "--react-server <version>",
40
+ "The version of @lazarv/react-server to use"
41
+ )
42
+ .version(packageJson.version);
43
+
44
+ cli.name = packageJson.name.split("/").pop();
45
+ cli.help();
46
+
47
+ const { options } = cli.parse();
48
+ const {
49
+ help,
50
+ v: version,
51
+ reactServer,
52
+ install,
53
+ // eslint-disable-next-line no-unused-vars
54
+ "--": _,
55
+ ...createOptions
56
+ } = options;
57
+ const hasOptions = Object.keys(createOptions).length > 1 || install === false;
58
+ createOptions.install = install;
59
+
60
+ if (help || version) {
61
+ process.exit(0);
62
+ }
63
+
64
+ await import("./logo.mjs");
65
+
66
+ const logger = createLogger();
67
+
68
+ const cwd = process.cwd();
69
+ const templateDir = join(dirname(fileURLToPath(import.meta.url)), "templates");
70
+
71
+ const [{ wizard }, { generate }, { launch }] = await Promise.all([
72
+ import("./wizard.mjs"),
73
+ import("./generator.mjs"),
74
+ import("./launch.mjs"),
75
+ ]);
76
+
77
+ const context = await wizard({
78
+ cwd,
79
+ logger,
80
+ templateDir,
81
+ hasOptions,
82
+ options: createOptions,
83
+ reactServer,
84
+ });
85
+
86
+ await generate(context);
87
+ await launch(context);
package/launch.mjs ADDED
@@ -0,0 +1,136 @@
1
+ import { spawn } from "node:child_process";
2
+ import { relative } from "node:path";
3
+
4
+ import { confirm } from "@inquirer/prompts";
5
+ import colors from "picocolors";
6
+
7
+ import { theme } from "./lib/theme.mjs";
8
+
9
+ export async function launch(context) {
10
+ const {
11
+ env: { cwd, logger, projectDir },
12
+ props: {
13
+ packageManager: { name: packageManager },
14
+ adapter,
15
+ projectName,
16
+ port,
17
+ },
18
+ } = context;
19
+
20
+ const launchInDev =
21
+ context.env.options.install !== false &&
22
+ context.props.packageManager.install !== false &&
23
+ (context.env.options.dev ??
24
+ (context.env.hasOptions
25
+ ? false
26
+ : await confirm({
27
+ message: "Launch in development mode?",
28
+ theme,
29
+ })));
30
+
31
+ const devCommand = {
32
+ npm: "npm run dev",
33
+ pnpm: "pnpm dev",
34
+ yarn: "yarn dev",
35
+ };
36
+
37
+ const buildCommand = {
38
+ npm: "npm run build",
39
+ pnpm: "pnpm build",
40
+ yarn: "yarn build",
41
+ };
42
+
43
+ const startCommand = {
44
+ npm: "npm start",
45
+ pnpm: "pnpm start",
46
+ yarn: "yarn start",
47
+ };
48
+
49
+ const instructions = () => {
50
+ console.log(`\nšŸ“‚ You can launch your project later by changing into the project directory by running:
51
+ ${colors.cyan(`cd ${relative(cwd, projectDir)}`)}
52
+
53
+ ${
54
+ context.props.installInstructions
55
+ ? `šŸ“¦ To install dependencies, run:
56
+ ${colors.cyan(`${context.props.packageManager.name} install`)}
57
+
58
+ `
59
+ : ""
60
+ }Then choose from the following commands.
61
+
62
+ 🚧 To launch the project in development mode, run:
63
+ ${colors.cyan(devCommand[packageManager])}
64
+
65
+ šŸ”§ To build the project for production, run:
66
+ ${colors.cyan(buildCommand[packageManager])}
67
+
68
+ šŸš€ To start the production server, run:
69
+ ${colors.cyan(startCommand[packageManager])}
70
+ ${
71
+ adapter === "docker"
72
+ ? `\n🐳 To build a Docker image and start your application in a Docker container, run:
73
+ ${colors.cyan(`docker build -t ${projectName} .`)}
74
+ ${colors.cyan(`docker run --rm -ti -p ${port}:${port} ${projectName}`)}\n`
75
+ : ""
76
+ }
77
+ šŸ’• Thanks for choosing ${colors.bold("@lazarv/react-server")}!
78
+ šŸ“š Check out the documentation at ${colors.underline("https://react-server.dev")}
79
+ šŸ’» Happy coding!
80
+ `);
81
+ };
82
+
83
+ if (launchInDev) {
84
+ process.chdir(projectDir);
85
+ const server = spawn(
86
+ packageManager,
87
+ [
88
+ ...(packageManager === "npm" ? ["run"] : []),
89
+ "dev",
90
+ ...(packageManager === "pnpm" ? [] : ["--"]),
91
+ "--host",
92
+ context.props.host,
93
+ "--port",
94
+ context.props.port,
95
+ ...(context.env.options.open || !context.props.custom
96
+ ? ["--open"]
97
+ : []),
98
+ ],
99
+ {
100
+ cwd: projectDir,
101
+ stdio: ["inherit", "pipe", "pipe"],
102
+ shell: true,
103
+ env: {
104
+ ...process.env,
105
+ NODE_ENV: "development",
106
+ NO_REACT_SERVER_LOGO: "true",
107
+ FORCE_COLOR: process.env.NO_COLOR ? undefined : "true",
108
+ },
109
+ }
110
+ );
111
+ server.stdout.on("data", (data) => {
112
+ let out = data;
113
+ const message = data.toString();
114
+ const lines = message.split("\n");
115
+ if (lines.length > 1) {
116
+ out = lines.filter((line) => !line.startsWith("> ")).join("\n");
117
+ out = out.replace(/\n\n/g, "\n");
118
+ }
119
+ process.stdout.write(out);
120
+ });
121
+ server.stderr.on("data", (data) => {
122
+ process.stderr.write(data);
123
+ });
124
+ server.on("error", (error) => {
125
+ logger.error(error);
126
+ process.exit(1);
127
+ });
128
+ server.on("close", () => {
129
+ process.chdir(cwd);
130
+ instructions();
131
+ process.exit(server.status);
132
+ });
133
+ } else {
134
+ instructions();
135
+ }
136
+ }