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,332 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { formatProjectName } from "./project-name.mjs";
|
|
3
|
+
const isWindows = process.platform === "win32";
|
|
4
|
+
const isUnix = !isWindows;
|
|
5
|
+
describe("project-name utilities", () => {
|
|
6
|
+
describe("formatProjectName", () => {
|
|
7
|
+
it("should handle simple project name", () => {
|
|
8
|
+
const input = "foo";
|
|
9
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
10
|
+
const result = formatProjectName(input, cwd);
|
|
11
|
+
expect(result.name).toBe("foo");
|
|
12
|
+
if (isWindows) {
|
|
13
|
+
expect(result.root).toBe("C:\\workspace\\foo");
|
|
14
|
+
} else {
|
|
15
|
+
expect(result.root).toBe("/home/user/foo");
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
it("should handle nested path project name", () => {
|
|
19
|
+
const input = isWindows ? "foo\\bar" : "foo/bar";
|
|
20
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
21
|
+
const result = formatProjectName(input, cwd);
|
|
22
|
+
expect(result.name).toBe("bar");
|
|
23
|
+
if (isWindows) {
|
|
24
|
+
expect(result.root).toBe("C:\\workspace\\foo\\bar");
|
|
25
|
+
} else {
|
|
26
|
+
expect(result.root).toBe("/home/user/foo/bar");
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
it("should handle scoped package name", () => {
|
|
30
|
+
const input = "@scope/foo";
|
|
31
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
32
|
+
const result = formatProjectName(input, cwd);
|
|
33
|
+
expect(result.name).toBe("@scope/foo");
|
|
34
|
+
if (isWindows) {
|
|
35
|
+
expect(result.root).toBe("C:\\workspace\\@scope\\foo");
|
|
36
|
+
} else {
|
|
37
|
+
expect(result.root).toBe("/home/user/@scope/foo");
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
it("should handle relative path project name", () => {
|
|
41
|
+
const input = isWindows ? ".\\foo\\bar" : "./foo/bar";
|
|
42
|
+
const cwd = isWindows ? "C:\\workspace\\current" : "/home/user/current";
|
|
43
|
+
const result = formatProjectName(input, cwd);
|
|
44
|
+
expect(result.name).toBe("bar");
|
|
45
|
+
if (isWindows) {
|
|
46
|
+
expect(result.root).toBe("C:\\workspace\\current\\foo\\bar");
|
|
47
|
+
} else {
|
|
48
|
+
expect(result.root).toBe("/home/user/current/foo/bar");
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
it("should handle absolute path project name", () => {
|
|
52
|
+
const input = isWindows ? "C:\\projects\\my-app" : "/root/path/to/foo";
|
|
53
|
+
const cwd = isWindows ? "D:\\workspace" : "/home/user";
|
|
54
|
+
const result = formatProjectName(input, cwd);
|
|
55
|
+
if (isWindows) {
|
|
56
|
+
expect(result.name).toBe("my-app");
|
|
57
|
+
expect(result.root).toBe("C:\\projects\\my-app");
|
|
58
|
+
} else {
|
|
59
|
+
expect(result.name).toBe("foo");
|
|
60
|
+
expect(result.root).toBe("/root/path/to/foo");
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
it("should handle current directory", () => {
|
|
64
|
+
const input = ".";
|
|
65
|
+
const cwd = isWindows ? "C:\\Users\\Developer\\Projects\\WindowsApp" : "/home/user/projects/my-app";
|
|
66
|
+
const result = formatProjectName(input, cwd);
|
|
67
|
+
if (isWindows) {
|
|
68
|
+
expect(result.name).toBe("WindowsApp");
|
|
69
|
+
expect(result.root).toBe(
|
|
70
|
+
"C:\\Users\\Developer\\Projects\\WindowsApp"
|
|
71
|
+
);
|
|
72
|
+
} else {
|
|
73
|
+
expect(result.name).toBe("my-app");
|
|
74
|
+
expect(result.root).toBe("/home/user/projects/my-app");
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
it("should handle project name with trailing slashes", () => {
|
|
78
|
+
const input = isWindows ? "foo\\" : "foo/";
|
|
79
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
80
|
+
const result = formatProjectName(input, cwd);
|
|
81
|
+
expect(result.name).toBe("foo");
|
|
82
|
+
if (isWindows) {
|
|
83
|
+
expect(result.root).toBe("C:\\workspace\\foo");
|
|
84
|
+
} else {
|
|
85
|
+
expect(result.root).toBe("/home/user/foo");
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
it("should handle project name with multiple trailing slashes", () => {
|
|
89
|
+
const input = isWindows ? "foo\\\\\\" : "foo///";
|
|
90
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
91
|
+
const result = formatProjectName(input, cwd);
|
|
92
|
+
expect(result.name).toBe("foo");
|
|
93
|
+
if (isWindows) {
|
|
94
|
+
expect(result.root).toBe("C:\\workspace\\foo");
|
|
95
|
+
} else {
|
|
96
|
+
expect(result.root).toBe("/home/user/foo");
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
it("should handle deep nested path", () => {
|
|
100
|
+
const input = isWindows ? "path\\to\\nested\\project" : "path/to/nested/project";
|
|
101
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
102
|
+
const result = formatProjectName(input, cwd);
|
|
103
|
+
expect(result.name).toBe("project");
|
|
104
|
+
if (isWindows) {
|
|
105
|
+
expect(result.root).toBe(
|
|
106
|
+
"C:\\workspace\\path\\to\\nested\\project"
|
|
107
|
+
);
|
|
108
|
+
} else {
|
|
109
|
+
expect(result.root).toBe("/home/user/path/to/nested/project");
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
it("should handle scoped package with nested path", () => {
|
|
113
|
+
const input = isWindows ? "@company\\ui\\library" : "@company/ui/library";
|
|
114
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
115
|
+
const result = formatProjectName(input, cwd);
|
|
116
|
+
expect(result.name).toBe(input);
|
|
117
|
+
if (isWindows) {
|
|
118
|
+
expect(result.root).toBe(
|
|
119
|
+
"C:\\workspace\\@company\\ui\\library"
|
|
120
|
+
);
|
|
121
|
+
} else {
|
|
122
|
+
expect(result.root).toBe("/home/user/@company/ui/library");
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
it("should handle scoped package starting with @", () => {
|
|
126
|
+
const input = "@my-org/my-package";
|
|
127
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
128
|
+
const result = formatProjectName(input, cwd);
|
|
129
|
+
expect(result.name).toBe("@my-org/my-package");
|
|
130
|
+
if (isWindows) {
|
|
131
|
+
expect(result.root).toBe("C:\\workspace\\@my-org\\my-package");
|
|
132
|
+
} else {
|
|
133
|
+
expect(result.root).toBe("/home/user/@my-org/my-package");
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
it("should handle empty path segment", () => {
|
|
137
|
+
const input = isWindows ? "path\\\\with\\\\segments" : "path//with//segments";
|
|
138
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
139
|
+
const result = formatProjectName(input, cwd);
|
|
140
|
+
expect(result.name).toBe("segments");
|
|
141
|
+
if (isWindows) {
|
|
142
|
+
expect(result.root).toBe("C:\\workspace\\path\\with\\segments");
|
|
143
|
+
} else {
|
|
144
|
+
expect(result.root).toBe("/home/user/path/with/segments");
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
it("should handle single character project name", () => {
|
|
148
|
+
const input = "a";
|
|
149
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
150
|
+
const result = formatProjectName(input, cwd);
|
|
151
|
+
expect(result.name).toBe("a");
|
|
152
|
+
if (isWindows) {
|
|
153
|
+
expect(result.root).toBe("C:\\workspace\\a");
|
|
154
|
+
} else {
|
|
155
|
+
expect(result.root).toBe("/home/user/a");
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
it("should handle project name with numbers and hyphens", () => {
|
|
159
|
+
const input = "my-project-123";
|
|
160
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
161
|
+
const result = formatProjectName(input, cwd);
|
|
162
|
+
expect(result.name).toBe("my-project-123");
|
|
163
|
+
if (isWindows) {
|
|
164
|
+
expect(result.root).toBe("C:\\workspace\\my-project-123");
|
|
165
|
+
} else {
|
|
166
|
+
expect(result.root).toBe("/home/user/my-project-123");
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
it("should handle project name ending with slash", () => {
|
|
170
|
+
const input = isWindows ? "my-project\\" : "my-project/";
|
|
171
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
172
|
+
const result = formatProjectName(input, cwd);
|
|
173
|
+
expect(result.name).toBe("my-project");
|
|
174
|
+
if (isWindows) {
|
|
175
|
+
expect(result.root).toBe("C:\\workspace\\my-project");
|
|
176
|
+
} else {
|
|
177
|
+
expect(result.root).toBe("/home/user/my-project");
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
it("should fall back to default name when path ends with slash and no name", () => {
|
|
181
|
+
const input = "/";
|
|
182
|
+
const cwd = isWindows ? "C:\\workspace" : "/home/user";
|
|
183
|
+
const result = formatProjectName(input, cwd);
|
|
184
|
+
expect(result.root).toBe("/");
|
|
185
|
+
expect(result.name).toBe("esmx-project");
|
|
186
|
+
});
|
|
187
|
+
it.runIf(isWindows)(
|
|
188
|
+
"should handle Windows-style absolute paths",
|
|
189
|
+
() => {
|
|
190
|
+
const input = "C:\\projects\\my-app";
|
|
191
|
+
const cwd = "D:\\workspace";
|
|
192
|
+
const result = formatProjectName(input, cwd);
|
|
193
|
+
expect(result.name).toBe("my-app");
|
|
194
|
+
expect(result.root).toMatch(/projects[\\/]my-app/);
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
it.runIf(isWindows)("should handle Windows UNC paths", () => {
|
|
198
|
+
const input = "\\\\server\\share\\project";
|
|
199
|
+
const cwd = "C:\\workspace";
|
|
200
|
+
const result = formatProjectName(input, cwd);
|
|
201
|
+
expect(result.name).toBe("project");
|
|
202
|
+
expect(result.root).toMatch(/project$/);
|
|
203
|
+
});
|
|
204
|
+
it.runIf(isUnix)("should handle mixed path separators on Unix", () => {
|
|
205
|
+
const input = "path\\to/mixed/separators";
|
|
206
|
+
const cwd = "/home/user";
|
|
207
|
+
const result = formatProjectName(input, cwd);
|
|
208
|
+
expect(result.name).toBe("separators");
|
|
209
|
+
expect(result.root).toBe("/home/user/path\\to/mixed/separators");
|
|
210
|
+
});
|
|
211
|
+
it.runIf(isWindows)(
|
|
212
|
+
"should handle Windows-style relative paths",
|
|
213
|
+
() => {
|
|
214
|
+
const input = "foo\\bar";
|
|
215
|
+
const cwd = "C:\\workspace";
|
|
216
|
+
const result = formatProjectName(input, cwd);
|
|
217
|
+
expect(result.name).toBe("bar");
|
|
218
|
+
expect(result.root).toMatch(/workspace[\\/]foo[\\/]bar/);
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
it.runIf(isWindows)(
|
|
222
|
+
"should handle Windows-style trailing backslashes",
|
|
223
|
+
() => {
|
|
224
|
+
const input = "foo\\bar\\\\\\";
|
|
225
|
+
const cwd = "C:\\workspace";
|
|
226
|
+
const result = formatProjectName(input, cwd);
|
|
227
|
+
expect(result.name).toBe("bar");
|
|
228
|
+
expect(result.root).toMatch(/workspace[\\/]foo[\\/]bar/);
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
describe("formatProjectName with cwd parameter", () => {
|
|
233
|
+
it('should use custom cwd when input is "."', () => {
|
|
234
|
+
const input = ".";
|
|
235
|
+
const customCwd = isWindows ? "C:\\custom\\path\\to\\project" : "/custom/path/to/project";
|
|
236
|
+
const result = formatProjectName(
|
|
237
|
+
input,
|
|
238
|
+
customCwd
|
|
239
|
+
);
|
|
240
|
+
expect(result.name).toBe("project");
|
|
241
|
+
expect(result.root).toBe(customCwd);
|
|
242
|
+
});
|
|
243
|
+
it("should fallback to process.cwd() when cwd is not provided", () => {
|
|
244
|
+
const input = "test-project";
|
|
245
|
+
const result = formatProjectName(input);
|
|
246
|
+
expect(result.name).toBe("test-project");
|
|
247
|
+
expect(result.root).toMatch(/test-project$/);
|
|
248
|
+
});
|
|
249
|
+
it('should ignore cwd parameter when input is not "."', () => {
|
|
250
|
+
const input = "my-project";
|
|
251
|
+
const customCwd = isWindows ? "C:\\custom\\path" : "/custom/path";
|
|
252
|
+
const result = formatProjectName(
|
|
253
|
+
input,
|
|
254
|
+
customCwd
|
|
255
|
+
);
|
|
256
|
+
expect(result.name).toBe("my-project");
|
|
257
|
+
if (isWindows) {
|
|
258
|
+
expect(result.root).toBe("C:\\custom\\path\\my-project");
|
|
259
|
+
} else {
|
|
260
|
+
expect(result.root).toBe("/custom/path/my-project");
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
it("should handle scoped packages with custom cwd", () => {
|
|
264
|
+
const input = "@scope/foo";
|
|
265
|
+
const customCwd = isWindows ? "C:\\custom\\workspace" : "/custom/workspace";
|
|
266
|
+
const result = formatProjectName(
|
|
267
|
+
input,
|
|
268
|
+
customCwd
|
|
269
|
+
);
|
|
270
|
+
expect(result.name).toBe("@scope/foo");
|
|
271
|
+
if (isWindows) {
|
|
272
|
+
expect(result.root).toBe("C:\\custom\\workspace\\@scope\\foo");
|
|
273
|
+
} else {
|
|
274
|
+
expect(result.root).toBe("/custom/workspace/@scope/foo");
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
it("should handle nested paths with custom cwd", () => {
|
|
278
|
+
const input = isWindows ? "foo\\bar" : "foo/bar";
|
|
279
|
+
const customCwd = isWindows ? "C:\\custom\\workspace" : "/custom/workspace";
|
|
280
|
+
const result = formatProjectName(
|
|
281
|
+
input,
|
|
282
|
+
customCwd
|
|
283
|
+
);
|
|
284
|
+
expect(result.name).toBe("bar");
|
|
285
|
+
if (isWindows) {
|
|
286
|
+
expect(result.root).toBe("C:\\custom\\workspace\\foo\\bar");
|
|
287
|
+
} else {
|
|
288
|
+
expect(result.root).toBe("/custom/workspace/foo/bar");
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
it.runIf(isUnix)(
|
|
292
|
+
"should handle Unix-style absolute paths in cwd",
|
|
293
|
+
() => {
|
|
294
|
+
const input = "/root/path/to/foo";
|
|
295
|
+
const customCwd = "/this/should/be/ignored";
|
|
296
|
+
const result = formatProjectName(
|
|
297
|
+
input,
|
|
298
|
+
customCwd
|
|
299
|
+
);
|
|
300
|
+
expect(result.name).toBe("foo");
|
|
301
|
+
expect(result.root).toBe("/root/path/to/foo");
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
it.runIf(isWindows)(
|
|
305
|
+
"should handle Windows-style absolute paths in cwd",
|
|
306
|
+
() => {
|
|
307
|
+
const input = "C:\\projects\\my-app";
|
|
308
|
+
const customCwd = "D:\\this\\should\\be\\ignored";
|
|
309
|
+
const result = formatProjectName(
|
|
310
|
+
input,
|
|
311
|
+
customCwd
|
|
312
|
+
);
|
|
313
|
+
expect(result.name).toBe("my-app");
|
|
314
|
+
expect(result.root).toBe("C:\\projects\\my-app");
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
it("should handle absolute input paths and ignore cwd", () => {
|
|
318
|
+
const input = isWindows ? "C:\\projects\\absolute-project" : "/projects/absolute-project";
|
|
319
|
+
const customCwd = isWindows ? "D:\\ignored" : "/ignored";
|
|
320
|
+
const result = formatProjectName(
|
|
321
|
+
input,
|
|
322
|
+
customCwd
|
|
323
|
+
);
|
|
324
|
+
expect(result.name).toBe("absolute-project");
|
|
325
|
+
if (isWindows) {
|
|
326
|
+
expect(result.root).toBe("C:\\projects\\absolute-project");
|
|
327
|
+
} else {
|
|
328
|
+
expect(result.root).toBe("/projects/absolute-project");
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template variable replacement utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Replace template variables in content using mustache-style syntax {{variableName}}
|
|
6
|
+
*
|
|
7
|
+
* @param content - The content string containing template variables
|
|
8
|
+
* @param variables - Object containing variable names and their replacement values
|
|
9
|
+
* @returns Content with all template variables replaced
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const content = "Hello {{name}}, version {{version}}!";
|
|
14
|
+
* const variables = { name: "World", version: "1.0.0" };
|
|
15
|
+
* const result = replaceTemplateVariables(content, variables);
|
|
16
|
+
* // Result: "Hello World, version 1.0.0!"
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function replaceTemplateVariables(content: string, variables: Record<string, string>): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function replaceTemplateVariables(content, variables) {
|
|
2
|
+
let result = content;
|
|
3
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
4
|
+
const pattern = new RegExp(`\\{\\{${key}\\}\\}`, "g");
|
|
5
|
+
result = result.replace(pattern, value);
|
|
6
|
+
}
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { replaceTemplateVariables } from "./template.mjs";
|
|
3
|
+
describe("replaceTemplateVariables", () => {
|
|
4
|
+
describe("basic functionality", () => {
|
|
5
|
+
it("should replace single variable", () => {
|
|
6
|
+
const content = "Hello {{name}}!";
|
|
7
|
+
const variables = { name: "World" };
|
|
8
|
+
const result = replaceTemplateVariables(content, variables);
|
|
9
|
+
expect(result).toBe("Hello World!");
|
|
10
|
+
});
|
|
11
|
+
it("should replace multiple different variables", () => {
|
|
12
|
+
const content = "Project {{projectName}} version {{version}}";
|
|
13
|
+
const variables = {
|
|
14
|
+
projectName: "my-app",
|
|
15
|
+
version: "1.0.0"
|
|
16
|
+
};
|
|
17
|
+
const result = replaceTemplateVariables(content, variables);
|
|
18
|
+
expect(result).toBe("Project my-app version 1.0.0");
|
|
19
|
+
});
|
|
20
|
+
it("should replace same variable multiple times", () => {
|
|
21
|
+
const content = "{{greeting}} {{name}}, {{greeting}} again!";
|
|
22
|
+
const variables = {
|
|
23
|
+
greeting: "Hello",
|
|
24
|
+
name: "World"
|
|
25
|
+
};
|
|
26
|
+
const result = replaceTemplateVariables(content, variables);
|
|
27
|
+
expect(result).toBe("Hello World, Hello again!");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe("edge cases", () => {
|
|
31
|
+
it("should handle content without variables", () => {
|
|
32
|
+
const content = "No variables here";
|
|
33
|
+
const variables = { name: "World" };
|
|
34
|
+
const result = replaceTemplateVariables(content, variables);
|
|
35
|
+
expect(result).toBe("No variables here");
|
|
36
|
+
});
|
|
37
|
+
it("should handle empty variables object", () => {
|
|
38
|
+
const content = "Hello {{name}}!";
|
|
39
|
+
const variables = {};
|
|
40
|
+
const result = replaceTemplateVariables(content, variables);
|
|
41
|
+
expect(result).toBe("Hello {{name}}!");
|
|
42
|
+
});
|
|
43
|
+
it("should handle empty content", () => {
|
|
44
|
+
const content = "";
|
|
45
|
+
const variables = { name: "World" };
|
|
46
|
+
const result = replaceTemplateVariables(content, variables);
|
|
47
|
+
expect(result).toBe("");
|
|
48
|
+
});
|
|
49
|
+
it("should handle variables with special characters", () => {
|
|
50
|
+
const content = "Command: {{installCommand}}";
|
|
51
|
+
const variables = { installCommand: "npm install --save-dev" };
|
|
52
|
+
const result = replaceTemplateVariables(content, variables);
|
|
53
|
+
expect(result).toBe("Command: npm install --save-dev");
|
|
54
|
+
});
|
|
55
|
+
it("should handle variables with regex special characters", () => {
|
|
56
|
+
const content = "Pattern: {{pattern}}";
|
|
57
|
+
const variables = { pattern: "[a-z]+.*$" };
|
|
58
|
+
const result = replaceTemplateVariables(content, variables);
|
|
59
|
+
expect(result).toBe("Pattern: [a-z]+.*$");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe("real-world scenarios", () => {
|
|
63
|
+
it("should handle all template variables from create-esmx", () => {
|
|
64
|
+
const content = `# {{projectName}}
|
|
65
|
+
|
|
66
|
+
Install dependencies:
|
|
67
|
+
\`\`\`bash
|
|
68
|
+
{{installCommand}}
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
Start development:
|
|
72
|
+
\`\`\`bash
|
|
73
|
+
{{devCommand}}
|
|
74
|
+
\`\`\`
|
|
75
|
+
|
|
76
|
+
Build for production:
|
|
77
|
+
\`\`\`bash
|
|
78
|
+
{{buildCommand}}
|
|
79
|
+
\`\`\`
|
|
80
|
+
|
|
81
|
+
Start production server:
|
|
82
|
+
\`\`\`bash
|
|
83
|
+
{{startCommand}}
|
|
84
|
+
\`\`\`
|
|
85
|
+
|
|
86
|
+
Esmx version: {{esmxVersion}}`;
|
|
87
|
+
const variables = {
|
|
88
|
+
projectName: "my-awesome-app",
|
|
89
|
+
installCommand: "pnpm install",
|
|
90
|
+
devCommand: "pnpm dev",
|
|
91
|
+
buildCommand: "pnpm build",
|
|
92
|
+
startCommand: "pnpm start",
|
|
93
|
+
esmxVersion: "3.0.0-rc.33"
|
|
94
|
+
};
|
|
95
|
+
const result = replaceTemplateVariables(content, variables);
|
|
96
|
+
expect(result).toContain("# my-awesome-app");
|
|
97
|
+
expect(result).toContain("pnpm install");
|
|
98
|
+
expect(result).toContain("pnpm dev");
|
|
99
|
+
expect(result).toContain("pnpm build");
|
|
100
|
+
expect(result).toContain("pnpm start");
|
|
101
|
+
expect(result).toContain("3.0.0-rc.33");
|
|
102
|
+
expect(result).not.toContain("{{");
|
|
103
|
+
expect(result).not.toContain("}}");
|
|
104
|
+
});
|
|
105
|
+
it("should handle package.json template", () => {
|
|
106
|
+
const content = `{
|
|
107
|
+
"name": "{{projectName}}",
|
|
108
|
+
"version": "1.0.0",
|
|
109
|
+
"scripts": {
|
|
110
|
+
"dev": "esmx dev",
|
|
111
|
+
"build": "esmx build",
|
|
112
|
+
"start": "esmx start"
|
|
113
|
+
},
|
|
114
|
+
"dependencies": {
|
|
115
|
+
"esmx": "{{esmxVersion}}"
|
|
116
|
+
}
|
|
117
|
+
}`;
|
|
118
|
+
const variables = {
|
|
119
|
+
projectName: "@scope/my-package",
|
|
120
|
+
esmxVersion: "^3.0.0"
|
|
121
|
+
};
|
|
122
|
+
const result = replaceTemplateVariables(content, variables);
|
|
123
|
+
expect(result).toContain('"name": "@scope/my-package"');
|
|
124
|
+
expect(result).toContain('"esmx": "^3.0.0"');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe("variable name validation", () => {
|
|
128
|
+
it("should handle variables with underscores", () => {
|
|
129
|
+
const content = "Command: {{install_command}}";
|
|
130
|
+
const variables = { install_command: "npm install" };
|
|
131
|
+
const result = replaceTemplateVariables(content, variables);
|
|
132
|
+
expect(result).toBe("Command: npm install");
|
|
133
|
+
});
|
|
134
|
+
it("should handle variables with numbers", () => {
|
|
135
|
+
const content = "Node version: {{node18}}";
|
|
136
|
+
const variables = { node18: "v18.12.0" };
|
|
137
|
+
const result = replaceTemplateVariables(content, variables);
|
|
138
|
+
expect(result).toBe("Node version: v18.12.0");
|
|
139
|
+
});
|
|
140
|
+
it("should be case sensitive", () => {
|
|
141
|
+
const content = "Hello {{Name}} and {{name}}!";
|
|
142
|
+
const variables = {
|
|
143
|
+
Name: "Alice",
|
|
144
|
+
name: "Bob"
|
|
145
|
+
};
|
|
146
|
+
const result = replaceTemplateVariables(content, variables);
|
|
147
|
+
expect(result).toBe("Hello Alice and Bob!");
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-esmx",
|
|
3
|
+
"version": "3.0.0-rc.104",
|
|
4
|
+
"description": "A scaffold tool for creating Esmx projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-esmx": "./dist/create.mjs"
|
|
9
|
+
},
|
|
10
|
+
"template": "library-node",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"lint:css": "pnpm run lint:js",
|
|
13
|
+
"lint:type": "tsc --noEmit",
|
|
14
|
+
"test": "vitest run --pass-with-no-tests",
|
|
15
|
+
"coverage": "vitest run --coverage --pass-with-no-tests",
|
|
16
|
+
"lint:js": "biome check --write --no-errors-on-unmatched",
|
|
17
|
+
"build": "unbuild",
|
|
18
|
+
"postbuild": "node build/postbuild.mjs"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@clack/prompts": "^0.7.0",
|
|
22
|
+
"minimist": "^1.2.8",
|
|
23
|
+
"picocolors": "^1.0.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@biomejs/biome": "2.3.7",
|
|
27
|
+
"@esmx/core": "3.0.0-rc.104",
|
|
28
|
+
"@esmx/rspack": "3.0.0-rc.104",
|
|
29
|
+
"@esmx/rspack-react": "3.0.0-rc.104",
|
|
30
|
+
"@esmx/rspack-vue": "3.0.0-rc.104",
|
|
31
|
+
"@types/minimist": "^1.2.5",
|
|
32
|
+
"@types/node": "^24.0.0",
|
|
33
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
34
|
+
"typescript": "5.9.3",
|
|
35
|
+
"unbuild": "3.6.1",
|
|
36
|
+
"vitest": "3.2.4",
|
|
37
|
+
"vue": "^3.5.23"
|
|
38
|
+
},
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"import": "./dist/index.mjs",
|
|
42
|
+
"types": "./dist/index.d.ts"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"module": "dist/index.mjs",
|
|
46
|
+
"types": "./dist/index.d.ts",
|
|
47
|
+
"files": [
|
|
48
|
+
"lib",
|
|
49
|
+
"src",
|
|
50
|
+
"dist",
|
|
51
|
+
"*.mjs",
|
|
52
|
+
"template",
|
|
53
|
+
"public"
|
|
54
|
+
],
|
|
55
|
+
"keywords": [
|
|
56
|
+
"esmx",
|
|
57
|
+
"scaffold",
|
|
58
|
+
"template",
|
|
59
|
+
"vue",
|
|
60
|
+
"ssr",
|
|
61
|
+
"esm"
|
|
62
|
+
],
|
|
63
|
+
"author": "Esmx Team",
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "git",
|
|
67
|
+
"url": "git+https://github.com/esmnext/esmx.git",
|
|
68
|
+
"directory": "packages/create-esmx"
|
|
69
|
+
},
|
|
70
|
+
"bugs": {
|
|
71
|
+
"url": "https://github.com/esmnext/esmx/issues"
|
|
72
|
+
},
|
|
73
|
+
"homepage": "https://github.com/esmnext/esmx#readme",
|
|
74
|
+
"gitHead": "329b299f906e7b59ba909a23b6c96509405ced51"
|
|
75
|
+
}
|