create-esmx 3.0.0-rc.104
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 +52 -0
- package/README.zh-CN.md +52 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.integration.test.d.ts +1 -0
- package/dist/cli.integration.test.mjs +238 -0
- package/dist/cli.mjs +166 -0
- package/dist/create.d.ts +2 -0
- package/dist/create.mjs +6 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +2 -0
- package/dist/project.d.ts +5 -0
- package/dist/project.mjs +46 -0
- package/dist/project.test.d.ts +1 -0
- package/dist/project.test.mjs +155 -0
- package/dist/template.d.ts +17 -0
- package/dist/template.mjs +76 -0
- package/dist/template.test.d.ts +1 -0
- package/dist/template.test.mjs +106 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.mjs +0 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.mjs +7 -0
- package/dist/utils/package-manager.d.ts +10 -0
- package/dist/utils/package-manager.mjs +49 -0
- package/dist/utils/package-manager.test.d.ts +4 -0
- package/dist/utils/package-manager.test.mjs +275 -0
- package/dist/utils/project-name.d.ts +48 -0
- package/dist/utils/project-name.mjs +42 -0
- package/dist/utils/project-name.test.d.ts +1 -0
- package/dist/utils/project-name.test.mjs +332 -0
- package/dist/utils/template.d.ts +19 -0
- package/dist/utils/template.mjs +8 -0
- package/dist/utils/template.test.d.ts +4 -0
- package/dist/utils/template.test.mjs +150 -0
- package/package.json +75 -0
- package/src/cli.integration.test.ts +289 -0
- package/src/cli.ts +214 -0
- package/src/create.ts +8 -0
- package/src/index.ts +3 -0
- package/src/project.test.ts +200 -0
- package/src/project.ts +75 -0
- package/src/template.test.ts +135 -0
- package/src/template.ts +117 -0
- package/src/types.ts +32 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/package-manager.test.ts +540 -0
- package/src/utils/package-manager.ts +92 -0
- package/src/utils/project-name.test.ts +441 -0
- package/src/utils/project-name.ts +101 -0
- package/src/utils/template.test.ts +234 -0
- package/src/utils/template.ts +34 -0
- package/template/react-csr/README.md +81 -0
- package/template/react-csr/package.json +29 -0
- package/template/react-csr/src/app.css +98 -0
- package/template/react-csr/src/app.tsx +26 -0
- package/template/react-csr/src/components/hello-world.css +48 -0
- package/template/react-csr/src/components/hello-world.tsx +29 -0
- package/template/react-csr/src/create-app.tsx +9 -0
- package/template/react-csr/src/entry.client.ts +13 -0
- package/template/react-csr/src/entry.node.ts +35 -0
- package/template/react-csr/src/entry.server.tsx +27 -0
- package/template/react-csr/tsconfig.json +27 -0
- package/template/react-ssr/README.md +81 -0
- package/template/react-ssr/package.json +29 -0
- package/template/react-ssr/src/app.css +98 -0
- package/template/react-ssr/src/app.tsx +26 -0
- package/template/react-ssr/src/components/hello-world.css +48 -0
- package/template/react-ssr/src/components/hello-world.tsx +29 -0
- package/template/react-ssr/src/create-app.tsx +9 -0
- package/template/react-ssr/src/entry.client.ts +13 -0
- package/template/react-ssr/src/entry.node.ts +32 -0
- package/template/react-ssr/src/entry.server.tsx +36 -0
- package/template/react-ssr/tsconfig.json +27 -0
- package/template/shared-modules/README.md +85 -0
- package/template/shared-modules/package.json +28 -0
- package/template/shared-modules/src/entry.client.ts +50 -0
- package/template/shared-modules/src/entry.node.ts +67 -0
- package/template/shared-modules/src/entry.server.ts +299 -0
- package/template/shared-modules/src/index.ts +3 -0
- package/template/shared-modules/src/vue/index.ts +1 -0
- package/template/shared-modules/src/vue2/index.ts +1 -0
- package/template/shared-modules/tsconfig.json +26 -0
- package/template/vue-csr/README.md +80 -0
- package/template/vue-csr/package.json +26 -0
- package/template/vue-csr/src/app.vue +127 -0
- package/template/vue-csr/src/components/hello-world.vue +77 -0
- package/template/vue-csr/src/create-app.ts +9 -0
- package/template/vue-csr/src/entry.client.ts +5 -0
- package/template/vue-csr/src/entry.node.ts +35 -0
- package/template/vue-csr/src/entry.server.ts +26 -0
- package/template/vue-csr/tsconfig.json +26 -0
- package/template/vue-ssr/README.md +80 -0
- package/template/vue-ssr/package.json +27 -0
- package/template/vue-ssr/src/app.vue +127 -0
- package/template/vue-ssr/src/components/hello-world.vue +77 -0
- package/template/vue-ssr/src/create-app.ts +9 -0
- package/template/vue-ssr/src/entry.client.ts +5 -0
- package/template/vue-ssr/src/entry.node.ts +37 -0
- package/template/vue-ssr/src/entry.server.ts +30 -0
- package/template/vue-ssr/tsconfig.json +26 -0
- package/template/vue2-csr/README.md +80 -0
- package/template/vue2-csr/package.json +26 -0
- package/template/vue2-csr/src/app.vue +127 -0
- package/template/vue2-csr/src/components/hello-world.vue +77 -0
- package/template/vue2-csr/src/create-app.ts +11 -0
- package/template/vue2-csr/src/entry.client.ts +5 -0
- package/template/vue2-csr/src/entry.node.ts +35 -0
- package/template/vue2-csr/src/entry.server.ts +26 -0
- package/template/vue2-csr/tsconfig.json +26 -0
- package/template/vue2-ssr/README.md +80 -0
- package/template/vue2-ssr/package.json +27 -0
- package/template/vue2-ssr/src/app.vue +127 -0
- package/template/vue2-ssr/src/components/hello-world.vue +77 -0
- package/template/vue2-ssr/src/create-app.ts +11 -0
- package/template/vue2-ssr/src/entry.client.ts +5 -0
- package/template/vue2-ssr/src/entry.node.ts +32 -0
- package/template/vue2-ssr/src/entry.server.ts +37 -0
- package/template/vue2-ssr/tsconfig.json +26 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for template variable replacement utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
import { replaceTemplateVariables } from './template';
|
|
7
|
+
|
|
8
|
+
describe('replaceTemplateVariables', () => {
|
|
9
|
+
describe('basic functionality', () => {
|
|
10
|
+
it('should replace single variable', () => {
|
|
11
|
+
// Arrange
|
|
12
|
+
const content = 'Hello {{name}}!';
|
|
13
|
+
const variables = { name: 'World' };
|
|
14
|
+
|
|
15
|
+
// Act
|
|
16
|
+
const result = replaceTemplateVariables(content, variables);
|
|
17
|
+
|
|
18
|
+
// Assert
|
|
19
|
+
expect(result).toBe('Hello World!');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should replace multiple different variables', () => {
|
|
23
|
+
// Arrange
|
|
24
|
+
const content = 'Project {{projectName}} version {{version}}';
|
|
25
|
+
const variables = {
|
|
26
|
+
projectName: 'my-app',
|
|
27
|
+
version: '1.0.0'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Act
|
|
31
|
+
const result = replaceTemplateVariables(content, variables);
|
|
32
|
+
|
|
33
|
+
// Assert
|
|
34
|
+
expect(result).toBe('Project my-app version 1.0.0');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should replace same variable multiple times', () => {
|
|
38
|
+
// Arrange
|
|
39
|
+
const content = '{{greeting}} {{name}}, {{greeting}} again!';
|
|
40
|
+
const variables = {
|
|
41
|
+
greeting: 'Hello',
|
|
42
|
+
name: 'World'
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Act
|
|
46
|
+
const result = replaceTemplateVariables(content, variables);
|
|
47
|
+
|
|
48
|
+
// Assert
|
|
49
|
+
expect(result).toBe('Hello World, Hello again!');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('edge cases', () => {
|
|
54
|
+
it('should handle content without variables', () => {
|
|
55
|
+
// Arrange
|
|
56
|
+
const content = 'No variables here';
|
|
57
|
+
const variables = { name: 'World' };
|
|
58
|
+
|
|
59
|
+
// Act
|
|
60
|
+
const result = replaceTemplateVariables(content, variables);
|
|
61
|
+
|
|
62
|
+
// Assert
|
|
63
|
+
expect(result).toBe('No variables here');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should handle empty variables object', () => {
|
|
67
|
+
// Arrange
|
|
68
|
+
const content = 'Hello {{name}}!';
|
|
69
|
+
const variables = {};
|
|
70
|
+
|
|
71
|
+
// Act
|
|
72
|
+
const result = replaceTemplateVariables(content, variables);
|
|
73
|
+
|
|
74
|
+
// Assert
|
|
75
|
+
expect(result).toBe('Hello {{name}}!');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should handle empty content', () => {
|
|
79
|
+
// Arrange
|
|
80
|
+
const content = '';
|
|
81
|
+
const variables = { name: 'World' };
|
|
82
|
+
|
|
83
|
+
// Act
|
|
84
|
+
const result = replaceTemplateVariables(content, variables);
|
|
85
|
+
|
|
86
|
+
// Assert
|
|
87
|
+
expect(result).toBe('');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should handle variables with special characters', () => {
|
|
91
|
+
// Arrange
|
|
92
|
+
const content = 'Command: {{installCommand}}';
|
|
93
|
+
const variables = { installCommand: 'npm install --save-dev' };
|
|
94
|
+
|
|
95
|
+
// Act
|
|
96
|
+
const result = replaceTemplateVariables(content, variables);
|
|
97
|
+
|
|
98
|
+
// Assert
|
|
99
|
+
expect(result).toBe('Command: npm install --save-dev');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should handle variables with regex special characters', () => {
|
|
103
|
+
// Arrange
|
|
104
|
+
const content = 'Pattern: {{pattern}}';
|
|
105
|
+
const variables = { pattern: '[a-z]+.*$' };
|
|
106
|
+
|
|
107
|
+
// Act
|
|
108
|
+
const result = replaceTemplateVariables(content, variables);
|
|
109
|
+
|
|
110
|
+
// Assert
|
|
111
|
+
expect(result).toBe('Pattern: [a-z]+.*$');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('real-world scenarios', () => {
|
|
116
|
+
it('should handle all template variables from create-esmx', () => {
|
|
117
|
+
// Arrange
|
|
118
|
+
const content = `# {{projectName}}
|
|
119
|
+
|
|
120
|
+
Install dependencies:
|
|
121
|
+
\`\`\`bash
|
|
122
|
+
{{installCommand}}
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
Start development:
|
|
126
|
+
\`\`\`bash
|
|
127
|
+
{{devCommand}}
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
Build for production:
|
|
131
|
+
\`\`\`bash
|
|
132
|
+
{{buildCommand}}
|
|
133
|
+
\`\`\`
|
|
134
|
+
|
|
135
|
+
Start production server:
|
|
136
|
+
\`\`\`bash
|
|
137
|
+
{{startCommand}}
|
|
138
|
+
\`\`\`
|
|
139
|
+
|
|
140
|
+
Esmx version: {{esmxVersion}}`;
|
|
141
|
+
|
|
142
|
+
const variables = {
|
|
143
|
+
projectName: 'my-awesome-app',
|
|
144
|
+
installCommand: 'pnpm install',
|
|
145
|
+
devCommand: 'pnpm dev',
|
|
146
|
+
buildCommand: 'pnpm build',
|
|
147
|
+
startCommand: 'pnpm start',
|
|
148
|
+
esmxVersion: '3.0.0-rc.33'
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// Act
|
|
152
|
+
const result = replaceTemplateVariables(content, variables);
|
|
153
|
+
|
|
154
|
+
// Assert
|
|
155
|
+
expect(result).toContain('# my-awesome-app');
|
|
156
|
+
expect(result).toContain('pnpm install');
|
|
157
|
+
expect(result).toContain('pnpm dev');
|
|
158
|
+
expect(result).toContain('pnpm build');
|
|
159
|
+
expect(result).toContain('pnpm start');
|
|
160
|
+
expect(result).toContain('3.0.0-rc.33');
|
|
161
|
+
expect(result).not.toContain('{{');
|
|
162
|
+
expect(result).not.toContain('}}');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should handle package.json template', () => {
|
|
166
|
+
// Arrange
|
|
167
|
+
const content = `{
|
|
168
|
+
"name": "{{projectName}}",
|
|
169
|
+
"version": "1.0.0",
|
|
170
|
+
"scripts": {
|
|
171
|
+
"dev": "esmx dev",
|
|
172
|
+
"build": "esmx build",
|
|
173
|
+
"start": "esmx start"
|
|
174
|
+
},
|
|
175
|
+
"dependencies": {
|
|
176
|
+
"esmx": "{{esmxVersion}}"
|
|
177
|
+
}
|
|
178
|
+
}`;
|
|
179
|
+
|
|
180
|
+
const variables = {
|
|
181
|
+
projectName: '@scope/my-package',
|
|
182
|
+
esmxVersion: '^3.0.0'
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Act
|
|
186
|
+
const result = replaceTemplateVariables(content, variables);
|
|
187
|
+
|
|
188
|
+
// Assert
|
|
189
|
+
expect(result).toContain('"name": "@scope/my-package"');
|
|
190
|
+
expect(result).toContain('"esmx": "^3.0.0"');
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('variable name validation', () => {
|
|
195
|
+
it('should handle variables with underscores', () => {
|
|
196
|
+
// Arrange
|
|
197
|
+
const content = 'Command: {{install_command}}';
|
|
198
|
+
const variables = { install_command: 'npm install' };
|
|
199
|
+
|
|
200
|
+
// Act
|
|
201
|
+
const result = replaceTemplateVariables(content, variables);
|
|
202
|
+
|
|
203
|
+
// Assert
|
|
204
|
+
expect(result).toBe('Command: npm install');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('should handle variables with numbers', () => {
|
|
208
|
+
// Arrange
|
|
209
|
+
const content = 'Node version: {{node18}}';
|
|
210
|
+
const variables = { node18: 'v18.12.0' };
|
|
211
|
+
|
|
212
|
+
// Act
|
|
213
|
+
const result = replaceTemplateVariables(content, variables);
|
|
214
|
+
|
|
215
|
+
// Assert
|
|
216
|
+
expect(result).toBe('Node version: v18.12.0');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should be case sensitive', () => {
|
|
220
|
+
// Arrange
|
|
221
|
+
const content = 'Hello {{Name}} and {{name}}!';
|
|
222
|
+
const variables = {
|
|
223
|
+
Name: 'Alice',
|
|
224
|
+
name: 'Bob'
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Act
|
|
228
|
+
const result = replaceTemplateVariables(content, variables);
|
|
229
|
+
|
|
230
|
+
// Assert
|
|
231
|
+
expect(result).toBe('Hello Alice and Bob!');
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template variable replacement utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Replace template variables in content using mustache-style syntax {{variableName}}
|
|
7
|
+
*
|
|
8
|
+
* @param content - The content string containing template variables
|
|
9
|
+
* @param variables - Object containing variable names and their replacement values
|
|
10
|
+
* @returns Content with all template variables replaced
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const content = "Hello {{name}}, version {{version}}!";
|
|
15
|
+
* const variables = { name: "World", version: "1.0.0" };
|
|
16
|
+
* const result = replaceTemplateVariables(content, variables);
|
|
17
|
+
* // Result: "Hello World, version 1.0.0!"
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function replaceTemplateVariables(
|
|
21
|
+
content: string,
|
|
22
|
+
variables: Record<string, string>
|
|
23
|
+
): string {
|
|
24
|
+
let result = content;
|
|
25
|
+
|
|
26
|
+
// Iterate through all variables and replace them
|
|
27
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
28
|
+
// Create regex pattern for {{variableName}} with global flag
|
|
29
|
+
const pattern = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
|
|
30
|
+
result = result.replace(pattern, value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
An Esmx project with React and Client-Side Rendering.
|
|
4
|
+
|
|
5
|
+
## 📦 Tech Stack
|
|
6
|
+
|
|
7
|
+
- **Framework**: [Esmx](https://esmx.dev) - Next generation micro-frontend framework based on native ESM
|
|
8
|
+
- **UI Framework**: React 18
|
|
9
|
+
- **Build Tool**: Rspack
|
|
10
|
+
- **Type Checking**: TypeScript
|
|
11
|
+
- **Rendering Mode**: Client-Side Rendering (CSR)
|
|
12
|
+
|
|
13
|
+
## 🚀 Quick Start
|
|
14
|
+
|
|
15
|
+
### Install Dependencies
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
{{installCommand}}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Development Environment
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
{{devCommand}}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Visit http://localhost:3000 to see the development environment.
|
|
28
|
+
|
|
29
|
+
### Production Build
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
{{buildCommand}}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Start Production Server
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
{{startCommand}}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Type Generation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
{{buildTypeCommand}}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Type Checking
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
{{lintTypeCommand}}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 📁 Project Structure
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
{{projectName}}/
|
|
57
|
+
├── src/
|
|
58
|
+
│ ├── app.tsx # Main application component with Esmx and React logos
|
|
59
|
+
│ ├── components/ # UI components
|
|
60
|
+
│ │ └── hello-world.tsx # Example component with counter functionality
|
|
61
|
+
│ ├── create-app.tsx # React app instance creation
|
|
62
|
+
│ ├── entry.client.ts # Client-side entry
|
|
63
|
+
│ ├── entry.node.ts # Node.js environment entry point
|
|
64
|
+
│ └── entry.server.tsx # CSR HTML shell (no SSR)
|
|
65
|
+
├── package.json
|
|
66
|
+
├── tsconfig.json
|
|
67
|
+
└── README.md
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 🔧 Configuration Details
|
|
71
|
+
|
|
72
|
+
- `entry.client.ts` - Responsible for client-side interaction and dynamic updates
|
|
73
|
+
- `entry.node.ts` - Handles development environment setup and tooling
|
|
74
|
+
- `entry.server.tsx` - Generates the HTML shell for CSR (no SSR)
|
|
75
|
+
|
|
76
|
+
## 📚 Additional Resources
|
|
77
|
+
|
|
78
|
+
- [Esmx Official Documentation](https://esmx.dev)
|
|
79
|
+
- [React Documentation](https://react.dev)
|
|
80
|
+
- [TypeScript Documentation](https://www.typescriptlang.org)
|
|
81
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React Client-Side Rendering framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": true,
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "esmx dev",
|
|
9
|
+
"build": "esmx build",
|
|
10
|
+
"preview": "esmx preview",
|
|
11
|
+
"start": "esmx start",
|
|
12
|
+
"lint:type": "tsc --noEmit",
|
|
13
|
+
"build:type": "tsc --declaration --emitDeclarationOnly --outDir dist/src && tsc-alias -p tsconfig.json --outDir dist/src"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@esmx/core": "{{esmxVersion}}",
|
|
17
|
+
"react": "^18.0.0",
|
|
18
|
+
"react-dom": "^18.0.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"tsc-alias": "^1.8.16",
|
|
22
|
+
"@esmx/rspack-react": "{{esmxVersion}}",
|
|
23
|
+
"@types/node": "^24.0.0",
|
|
24
|
+
"@types/react": "^18.0.0",
|
|
25
|
+
"@types/react-dom": "^18.0.0",
|
|
26
|
+
"typescript": "5.8.3"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--esmx-primary: #001137;
|
|
3
|
+
--esmx-secondary: #273498;
|
|
4
|
+
--esmx-accent: #0074c2;
|
|
5
|
+
--esmx-light: #00abe7;
|
|
6
|
+
--esmx-sun-core: #ffa000;
|
|
7
|
+
--esmx-sun-rays: #ffc107;
|
|
8
|
+
--react-color: #61dafb;
|
|
9
|
+
--react-dark: #20232a;
|
|
10
|
+
--border-color: rgba(0, 17, 55, 0.12);
|
|
11
|
+
--shadow-color: rgba(0, 17, 55, 0.05);
|
|
12
|
+
--text-primary: #213547;
|
|
13
|
+
--text-secondary: #666;
|
|
14
|
+
--bg-card: #fcfcfc;
|
|
15
|
+
--bg-hover: rgba(255, 250, 240, 0.8);
|
|
16
|
+
--font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
body {
|
|
20
|
+
margin: 0;
|
|
21
|
+
font-family: var(--font-family);
|
|
22
|
+
color: var(--text-primary);
|
|
23
|
+
background-color: white;
|
|
24
|
+
line-height: 1.6;
|
|
25
|
+
-webkit-font-smoothing: antialiased;
|
|
26
|
+
-moz-osx-font-smoothing: grayscale;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.container {
|
|
30
|
+
max-width: 1280px;
|
|
31
|
+
margin: 0 auto;
|
|
32
|
+
padding: 2rem;
|
|
33
|
+
text-align: center;
|
|
34
|
+
font-family: var(--font-family);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.logo-container {
|
|
38
|
+
display: flex;
|
|
39
|
+
justify-content: center;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: 3.5rem;
|
|
42
|
+
margin-bottom: 3rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.logo-link {
|
|
46
|
+
text-decoration: none;
|
|
47
|
+
position: relative;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.logo-wrapper {
|
|
51
|
+
display: flex;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
align-items: center;
|
|
54
|
+
width: 6.5em;
|
|
55
|
+
height: 6.5em;
|
|
56
|
+
border-radius: 12px;
|
|
57
|
+
background-color: var(--bg-card);
|
|
58
|
+
padding: 1em;
|
|
59
|
+
box-shadow: 0 2px 12px var(--shadow-color);
|
|
60
|
+
border: 1px solid var(--border-color);
|
|
61
|
+
transition: all 0.3s ease;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.logo-wrapper:hover {
|
|
65
|
+
transform: translateY(-5px);
|
|
66
|
+
box-shadow: 0 5px 15px var(--shadow-color);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.logo {
|
|
70
|
+
height: 100%;
|
|
71
|
+
width: auto;
|
|
72
|
+
transition: transform 0.3s ease;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.logo-wrapper:hover .logo {
|
|
76
|
+
transform: scale(1.1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.logo-wrapper.esmx:hover {
|
|
80
|
+
background-color: rgba(255, 192, 7, 0.1);
|
|
81
|
+
border-color: var(--esmx-sun-rays);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.logo-wrapper.react:hover {
|
|
85
|
+
background-color: rgba(97, 218, 251, 0.1);
|
|
86
|
+
border-color: var(--react-color);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@media (max-width: 768px) {
|
|
90
|
+
.logo-container {
|
|
91
|
+
gap: 2rem;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.logo-wrapper {
|
|
95
|
+
width: 5em;
|
|
96
|
+
height: 5em;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import HelloWorld from './components/hello-world';
|
|
3
|
+
import './app.css';
|
|
4
|
+
|
|
5
|
+
export default function App() {
|
|
6
|
+
const title = 'React CSR Demo';
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div id="app" className="container">
|
|
10
|
+
<div className="logo-container">
|
|
11
|
+
<a href="https://esmx.dev" target="_blank" rel="noopener noreferrer" className="logo-link">
|
|
12
|
+
<div className="logo-wrapper esmx">
|
|
13
|
+
<img src="https://esmx.dev/logo.svg" className="logo" alt="Esmx logo" />
|
|
14
|
+
</div>
|
|
15
|
+
</a>
|
|
16
|
+
<a href="https://react.dev/" target="_blank" rel="noopener noreferrer" className="logo-link">
|
|
17
|
+
<div className="logo-wrapper react">
|
|
18
|
+
<img src="https://react.dev/favicon-192x192.png" className="logo" alt="React logo" />
|
|
19
|
+
</div>
|
|
20
|
+
</a>
|
|
21
|
+
</div>
|
|
22
|
+
<HelloWorld msg={title} />
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
.card {
|
|
2
|
+
padding: 2em;
|
|
3
|
+
border-radius: 12px;
|
|
4
|
+
margin: 2.5em 0;
|
|
5
|
+
background-color: var(--bg-card);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
button {
|
|
9
|
+
border-radius: 8px;
|
|
10
|
+
border: 1px solid transparent;
|
|
11
|
+
padding: 0.6em 1.2em;
|
|
12
|
+
font-size: 1em;
|
|
13
|
+
font-weight: 600;
|
|
14
|
+
font-family: inherit;
|
|
15
|
+
background-color: var(--esmx-sun-rays);
|
|
16
|
+
color: #fff;
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
margin-bottom: 1em;
|
|
19
|
+
transition: all 0.25s ease;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
button:hover {
|
|
23
|
+
background-color: var(--esmx-sun-core);
|
|
24
|
+
transform: translateY(-1px);
|
|
25
|
+
box-shadow: 0 4px 8px rgba(255, 160, 0, 0.25);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
h1 {
|
|
29
|
+
font-size: 3.2em;
|
|
30
|
+
line-height: 1.1;
|
|
31
|
+
margin-bottom: 1em;
|
|
32
|
+
color: var(--esmx-primary);
|
|
33
|
+
font-weight: 700;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
p {
|
|
37
|
+
line-height: 1.6;
|
|
38
|
+
margin: 0.5em 0;
|
|
39
|
+
color: var(--text-primary);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
code {
|
|
43
|
+
background-color: rgba(0, 116, 194, 0.1);
|
|
44
|
+
padding: 0.2em 0.4em;
|
|
45
|
+
border-radius: 4px;
|
|
46
|
+
font-family: monospace;
|
|
47
|
+
font-size: 0.9em;
|
|
48
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import './hello-world.css';
|
|
3
|
+
|
|
4
|
+
interface HelloWorldProps {
|
|
5
|
+
msg: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default function HelloWorld({ msg }: HelloWorldProps) {
|
|
9
|
+
const [count, setCount] = useState<number>(0);
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div>
|
|
13
|
+
<h1>{msg}</h1>
|
|
14
|
+
|
|
15
|
+
<div className="card">
|
|
16
|
+
<button type="button" onClick={() => setCount(count + 1)}>
|
|
17
|
+
Counter: {count}
|
|
18
|
+
</button>
|
|
19
|
+
<p>
|
|
20
|
+
Edit
|
|
21
|
+
<code>components/HelloWorld.tsx</code> to test HMR
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<p>Experience React with client-side rendering powered by Esmx framework</p>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { createApp } from './create-app';
|
|
4
|
+
|
|
5
|
+
const { app } = createApp();
|
|
6
|
+
|
|
7
|
+
const container = document.getElementById('app');
|
|
8
|
+
if (container) {
|
|
9
|
+
const root = createRoot(container);
|
|
10
|
+
root.render(app);
|
|
11
|
+
} else {
|
|
12
|
+
console.error('Container element #app not found');
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import type { EsmxOptions } from '@esmx/core';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
modules: {
|
|
6
|
+
exports: ['pkg:react']
|
|
7
|
+
},
|
|
8
|
+
async devApp(esmx) {
|
|
9
|
+
return import('@esmx/rspack-react').then((m) =>
|
|
10
|
+
m.createRspackReactApp(esmx, {
|
|
11
|
+
config(context) {
|
|
12
|
+
// Custom Rspack configuration
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
);
|
|
16
|
+
},
|
|
17
|
+
async postBuild(esmx) {
|
|
18
|
+
const rc = await esmx.render();
|
|
19
|
+
esmx.writeSync(esmx.resolvePath('dist/client', 'index.html'), rc.html);
|
|
20
|
+
},
|
|
21
|
+
async server(esmx) {
|
|
22
|
+
const server = http.createServer((req, res) => {
|
|
23
|
+
esmx.middleware(req, res, async () => {
|
|
24
|
+
const rc = await esmx.render({
|
|
25
|
+
params: { url: req.url }
|
|
26
|
+
});
|
|
27
|
+
res.end(rc.html);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
server.listen(3000, () => {
|
|
32
|
+
console.log('Server started: http://localhost:3000');
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
} satisfies EsmxOptions;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RenderContext } from '@esmx/core';
|
|
2
|
+
|
|
3
|
+
export default async (rc: RenderContext) => {
|
|
4
|
+
await rc.commit();
|
|
5
|
+
|
|
6
|
+
rc.html = `<!DOCTYPE html>
|
|
7
|
+
<html lang="en">
|
|
8
|
+
<head>
|
|
9
|
+
<meta charset="UTF-8">
|
|
10
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
11
|
+
<meta name="description" content="React with Client-Side Rendering powered by Esmx framework">
|
|
12
|
+
<meta name="keywords" content="React, CSR, Client-Side Rendering, Esmx, React.js, JavaScript, TypeScript, Rspack">
|
|
13
|
+
<link rel="icon" href="https://esmx.dev/logo.svg" type="image/svg+xml">
|
|
14
|
+
${rc.preload()}
|
|
15
|
+
<title>React CSR Demo | Powered by Esmx</title>
|
|
16
|
+
${rc.css()}
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<div id="app"></div>
|
|
20
|
+
${rc.importmap()}
|
|
21
|
+
${rc.moduleEntry()}
|
|
22
|
+
${rc.modulePreload()}
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
|
25
|
+
`;
|
|
26
|
+
};
|
|
27
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "ESNext",
|
|
4
|
+
"moduleResolution": "node",
|
|
5
|
+
"isolatedModules": true,
|
|
6
|
+
"resolveJsonModule": true,
|
|
7
|
+
|
|
8
|
+
"target": "ESNext",
|
|
9
|
+
"lib": ["ESNext", "DOM"],
|
|
10
|
+
|
|
11
|
+
"strict": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"types": ["@types/node"],
|
|
14
|
+
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
"allowSyntheticDefaultImports": true,
|
|
17
|
+
|
|
18
|
+
"baseUrl": ".",
|
|
19
|
+
"paths": {
|
|
20
|
+
"{{projectName}}/src/*": ["./src/*"],
|
|
21
|
+
"{{projectName}}/*": ["./*"]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"include": ["src"],
|
|
25
|
+
"exclude": ["dist", "node_modules"]
|
|
26
|
+
}
|
|
27
|
+
|