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,441 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { ProjectNameResult } from './project-name';
|
|
3
|
+
import { formatProjectName } from './project-name';
|
|
4
|
+
|
|
5
|
+
const isWindows = process.platform === 'win32';
|
|
6
|
+
const isUnix = !isWindows;
|
|
7
|
+
|
|
8
|
+
describe('project-name utilities', () => {
|
|
9
|
+
describe('formatProjectName', () => {
|
|
10
|
+
it('should handle simple project name', () => {
|
|
11
|
+
const input = 'foo';
|
|
12
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
13
|
+
|
|
14
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
15
|
+
|
|
16
|
+
expect(result.name).toBe('foo');
|
|
17
|
+
if (isWindows) {
|
|
18
|
+
expect(result.root).toBe('C:\\workspace\\foo');
|
|
19
|
+
} else {
|
|
20
|
+
expect(result.root).toBe('/home/user/foo');
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should handle nested path project name', () => {
|
|
25
|
+
const input = isWindows ? 'foo\\bar' : 'foo/bar';
|
|
26
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
27
|
+
|
|
28
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
29
|
+
|
|
30
|
+
expect(result.name).toBe('bar');
|
|
31
|
+
if (isWindows) {
|
|
32
|
+
expect(result.root).toBe('C:\\workspace\\foo\\bar');
|
|
33
|
+
} else {
|
|
34
|
+
expect(result.root).toBe('/home/user/foo/bar');
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should handle scoped package name', () => {
|
|
39
|
+
const input = '@scope/foo';
|
|
40
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
41
|
+
|
|
42
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
43
|
+
|
|
44
|
+
expect(result.name).toBe('@scope/foo');
|
|
45
|
+
if (isWindows) {
|
|
46
|
+
expect(result.root).toBe('C:\\workspace\\@scope\\foo');
|
|
47
|
+
} else {
|
|
48
|
+
expect(result.root).toBe('/home/user/@scope/foo');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should handle relative path project name', () => {
|
|
53
|
+
const input = isWindows ? '.\\foo\\bar' : './foo/bar';
|
|
54
|
+
const cwd = isWindows
|
|
55
|
+
? 'C:\\workspace\\current'
|
|
56
|
+
: '/home/user/current';
|
|
57
|
+
|
|
58
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
59
|
+
|
|
60
|
+
expect(result.name).toBe('bar');
|
|
61
|
+
if (isWindows) {
|
|
62
|
+
expect(result.root).toBe('C:\\workspace\\current\\foo\\bar');
|
|
63
|
+
} else {
|
|
64
|
+
expect(result.root).toBe('/home/user/current/foo/bar');
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should handle absolute path project name', () => {
|
|
69
|
+
const input = isWindows
|
|
70
|
+
? 'C:\\projects\\my-app'
|
|
71
|
+
: '/root/path/to/foo';
|
|
72
|
+
const cwd = isWindows ? 'D:\\workspace' : '/home/user';
|
|
73
|
+
|
|
74
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
75
|
+
|
|
76
|
+
if (isWindows) {
|
|
77
|
+
expect(result.name).toBe('my-app');
|
|
78
|
+
expect(result.root).toBe('C:\\projects\\my-app');
|
|
79
|
+
} else {
|
|
80
|
+
expect(result.name).toBe('foo');
|
|
81
|
+
expect(result.root).toBe('/root/path/to/foo');
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should handle current directory', () => {
|
|
86
|
+
const input = '.';
|
|
87
|
+
const cwd = isWindows
|
|
88
|
+
? 'C:\\Users\\Developer\\Projects\\WindowsApp'
|
|
89
|
+
: '/home/user/projects/my-app';
|
|
90
|
+
|
|
91
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
92
|
+
|
|
93
|
+
if (isWindows) {
|
|
94
|
+
expect(result.name).toBe('WindowsApp');
|
|
95
|
+
expect(result.root).toBe(
|
|
96
|
+
'C:\\Users\\Developer\\Projects\\WindowsApp'
|
|
97
|
+
);
|
|
98
|
+
} else {
|
|
99
|
+
expect(result.name).toBe('my-app');
|
|
100
|
+
expect(result.root).toBe('/home/user/projects/my-app');
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should handle project name with trailing slashes', () => {
|
|
105
|
+
const input = isWindows ? 'foo\\' : 'foo/';
|
|
106
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
107
|
+
|
|
108
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
109
|
+
|
|
110
|
+
expect(result.name).toBe('foo');
|
|
111
|
+
if (isWindows) {
|
|
112
|
+
expect(result.root).toBe('C:\\workspace\\foo');
|
|
113
|
+
} else {
|
|
114
|
+
expect(result.root).toBe('/home/user/foo');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should handle project name with multiple trailing slashes', () => {
|
|
119
|
+
const input = isWindows ? 'foo\\\\\\' : 'foo///';
|
|
120
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
121
|
+
|
|
122
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
123
|
+
|
|
124
|
+
expect(result.name).toBe('foo');
|
|
125
|
+
if (isWindows) {
|
|
126
|
+
expect(result.root).toBe('C:\\workspace\\foo');
|
|
127
|
+
} else {
|
|
128
|
+
expect(result.root).toBe('/home/user/foo');
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should handle deep nested path', () => {
|
|
133
|
+
const input = isWindows
|
|
134
|
+
? 'path\\to\\nested\\project'
|
|
135
|
+
: 'path/to/nested/project';
|
|
136
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
137
|
+
|
|
138
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
139
|
+
|
|
140
|
+
expect(result.name).toBe('project');
|
|
141
|
+
if (isWindows) {
|
|
142
|
+
expect(result.root).toBe(
|
|
143
|
+
'C:\\workspace\\path\\to\\nested\\project'
|
|
144
|
+
);
|
|
145
|
+
} else {
|
|
146
|
+
expect(result.root).toBe('/home/user/path/to/nested/project');
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should handle scoped package with nested path', () => {
|
|
151
|
+
const input = isWindows
|
|
152
|
+
? '@company\\ui\\library'
|
|
153
|
+
: '@company/ui/library';
|
|
154
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
155
|
+
|
|
156
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
157
|
+
|
|
158
|
+
expect(result.name).toBe(input);
|
|
159
|
+
if (isWindows) {
|
|
160
|
+
expect(result.root).toBe(
|
|
161
|
+
'C:\\workspace\\@company\\ui\\library'
|
|
162
|
+
);
|
|
163
|
+
} else {
|
|
164
|
+
expect(result.root).toBe('/home/user/@company/ui/library');
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should handle scoped package starting with @', () => {
|
|
169
|
+
const input = '@my-org/my-package';
|
|
170
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
171
|
+
|
|
172
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
173
|
+
|
|
174
|
+
expect(result.name).toBe('@my-org/my-package');
|
|
175
|
+
if (isWindows) {
|
|
176
|
+
expect(result.root).toBe('C:\\workspace\\@my-org\\my-package');
|
|
177
|
+
} else {
|
|
178
|
+
expect(result.root).toBe('/home/user/@my-org/my-package');
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should handle empty path segment', () => {
|
|
183
|
+
const input = isWindows
|
|
184
|
+
? 'path\\\\with\\\\segments'
|
|
185
|
+
: 'path//with//segments';
|
|
186
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
187
|
+
|
|
188
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
189
|
+
|
|
190
|
+
expect(result.name).toBe('segments');
|
|
191
|
+
if (isWindows) {
|
|
192
|
+
expect(result.root).toBe('C:\\workspace\\path\\with\\segments');
|
|
193
|
+
} else {
|
|
194
|
+
expect(result.root).toBe('/home/user/path/with/segments');
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should handle single character project name', () => {
|
|
199
|
+
const input = 'a';
|
|
200
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
201
|
+
|
|
202
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
203
|
+
|
|
204
|
+
expect(result.name).toBe('a');
|
|
205
|
+
if (isWindows) {
|
|
206
|
+
expect(result.root).toBe('C:\\workspace\\a');
|
|
207
|
+
} else {
|
|
208
|
+
expect(result.root).toBe('/home/user/a');
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should handle project name with numbers and hyphens', () => {
|
|
213
|
+
const input = 'my-project-123';
|
|
214
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
215
|
+
|
|
216
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
217
|
+
|
|
218
|
+
expect(result.name).toBe('my-project-123');
|
|
219
|
+
if (isWindows) {
|
|
220
|
+
expect(result.root).toBe('C:\\workspace\\my-project-123');
|
|
221
|
+
} else {
|
|
222
|
+
expect(result.root).toBe('/home/user/my-project-123');
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should handle project name ending with slash', () => {
|
|
227
|
+
const input = isWindows ? 'my-project\\' : 'my-project/';
|
|
228
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
229
|
+
|
|
230
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
231
|
+
|
|
232
|
+
expect(result.name).toBe('my-project');
|
|
233
|
+
if (isWindows) {
|
|
234
|
+
expect(result.root).toBe('C:\\workspace\\my-project');
|
|
235
|
+
} else {
|
|
236
|
+
expect(result.root).toBe('/home/user/my-project');
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should fall back to default name when path ends with slash and no name', () => {
|
|
241
|
+
const input = '/';
|
|
242
|
+
const cwd = isWindows ? 'C:\\workspace' : '/home/user';
|
|
243
|
+
|
|
244
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
245
|
+
|
|
246
|
+
expect(result.root).toBe('/');
|
|
247
|
+
expect(result.name).toBe('esmx-project');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it.runIf(isWindows)(
|
|
251
|
+
'should handle Windows-style absolute paths',
|
|
252
|
+
() => {
|
|
253
|
+
const input = 'C:\\projects\\my-app';
|
|
254
|
+
const cwd = 'D:\\workspace';
|
|
255
|
+
|
|
256
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
257
|
+
|
|
258
|
+
expect(result.name).toBe('my-app');
|
|
259
|
+
expect(result.root).toMatch(/projects[\\/]my-app/);
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
it.runIf(isWindows)('should handle Windows UNC paths', () => {
|
|
264
|
+
const input = '\\\\server\\share\\project';
|
|
265
|
+
const cwd = 'C:\\workspace';
|
|
266
|
+
|
|
267
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
268
|
+
|
|
269
|
+
expect(result.name).toBe('project');
|
|
270
|
+
expect(result.root).toMatch(/project$/);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it.runIf(isUnix)('should handle mixed path separators on Unix', () => {
|
|
274
|
+
const input = 'path\\to/mixed/separators';
|
|
275
|
+
const cwd = '/home/user';
|
|
276
|
+
|
|
277
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
278
|
+
|
|
279
|
+
expect(result.name).toBe('separators');
|
|
280
|
+
expect(result.root).toBe('/home/user/path\\to/mixed/separators');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it.runIf(isWindows)(
|
|
284
|
+
'should handle Windows-style relative paths',
|
|
285
|
+
() => {
|
|
286
|
+
const input = 'foo\\bar';
|
|
287
|
+
const cwd = 'C:\\workspace';
|
|
288
|
+
|
|
289
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
290
|
+
|
|
291
|
+
expect(result.name).toBe('bar');
|
|
292
|
+
expect(result.root).toMatch(/workspace[\\/]foo[\\/]bar/);
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
it.runIf(isWindows)(
|
|
297
|
+
'should handle Windows-style trailing backslashes',
|
|
298
|
+
() => {
|
|
299
|
+
const input = 'foo\\bar\\\\\\';
|
|
300
|
+
const cwd = 'C:\\workspace';
|
|
301
|
+
|
|
302
|
+
const result: ProjectNameResult = formatProjectName(input, cwd);
|
|
303
|
+
|
|
304
|
+
expect(result.name).toBe('bar');
|
|
305
|
+
expect(result.root).toMatch(/workspace[\\/]foo[\\/]bar/);
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
describe('formatProjectName with cwd parameter', () => {
|
|
311
|
+
it('should use custom cwd when input is "."', () => {
|
|
312
|
+
const input = '.';
|
|
313
|
+
const customCwd = isWindows
|
|
314
|
+
? 'C:\\custom\\path\\to\\project'
|
|
315
|
+
: '/custom/path/to/project';
|
|
316
|
+
|
|
317
|
+
const result: ProjectNameResult = formatProjectName(
|
|
318
|
+
input,
|
|
319
|
+
customCwd
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
expect(result.name).toBe('project');
|
|
323
|
+
expect(result.root).toBe(customCwd);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should fallback to process.cwd() when cwd is not provided', () => {
|
|
327
|
+
const input = 'test-project';
|
|
328
|
+
|
|
329
|
+
const result: ProjectNameResult = formatProjectName(input);
|
|
330
|
+
|
|
331
|
+
expect(result.name).toBe('test-project');
|
|
332
|
+
expect(result.root).toMatch(/test-project$/);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should ignore cwd parameter when input is not "."', () => {
|
|
336
|
+
const input = 'my-project';
|
|
337
|
+
const customCwd = isWindows ? 'C:\\custom\\path' : '/custom/path';
|
|
338
|
+
|
|
339
|
+
const result: ProjectNameResult = formatProjectName(
|
|
340
|
+
input,
|
|
341
|
+
customCwd
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
expect(result.name).toBe('my-project');
|
|
345
|
+
if (isWindows) {
|
|
346
|
+
expect(result.root).toBe('C:\\custom\\path\\my-project');
|
|
347
|
+
} else {
|
|
348
|
+
expect(result.root).toBe('/custom/path/my-project');
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('should handle scoped packages with custom cwd', () => {
|
|
353
|
+
const input = '@scope/foo';
|
|
354
|
+
const customCwd = isWindows
|
|
355
|
+
? 'C:\\custom\\workspace'
|
|
356
|
+
: '/custom/workspace';
|
|
357
|
+
|
|
358
|
+
const result: ProjectNameResult = formatProjectName(
|
|
359
|
+
input,
|
|
360
|
+
customCwd
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
expect(result.name).toBe('@scope/foo');
|
|
364
|
+
if (isWindows) {
|
|
365
|
+
expect(result.root).toBe('C:\\custom\\workspace\\@scope\\foo');
|
|
366
|
+
} else {
|
|
367
|
+
expect(result.root).toBe('/custom/workspace/@scope/foo');
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should handle nested paths with custom cwd', () => {
|
|
372
|
+
const input = isWindows ? 'foo\\bar' : 'foo/bar';
|
|
373
|
+
const customCwd = isWindows
|
|
374
|
+
? 'C:\\custom\\workspace'
|
|
375
|
+
: '/custom/workspace';
|
|
376
|
+
|
|
377
|
+
const result: ProjectNameResult = formatProjectName(
|
|
378
|
+
input,
|
|
379
|
+
customCwd
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
expect(result.name).toBe('bar');
|
|
383
|
+
if (isWindows) {
|
|
384
|
+
expect(result.root).toBe('C:\\custom\\workspace\\foo\\bar');
|
|
385
|
+
} else {
|
|
386
|
+
expect(result.root).toBe('/custom/workspace/foo/bar');
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it.runIf(isUnix)(
|
|
391
|
+
'should handle Unix-style absolute paths in cwd',
|
|
392
|
+
() => {
|
|
393
|
+
const input = '/root/path/to/foo';
|
|
394
|
+
const customCwd = '/this/should/be/ignored';
|
|
395
|
+
|
|
396
|
+
const result: ProjectNameResult = formatProjectName(
|
|
397
|
+
input,
|
|
398
|
+
customCwd
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
expect(result.name).toBe('foo');
|
|
402
|
+
expect(result.root).toBe('/root/path/to/foo');
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
it.runIf(isWindows)(
|
|
407
|
+
'should handle Windows-style absolute paths in cwd',
|
|
408
|
+
() => {
|
|
409
|
+
const input = 'C:\\projects\\my-app';
|
|
410
|
+
const customCwd = 'D:\\this\\should\\be\\ignored';
|
|
411
|
+
|
|
412
|
+
const result: ProjectNameResult = formatProjectName(
|
|
413
|
+
input,
|
|
414
|
+
customCwd
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
expect(result.name).toBe('my-app');
|
|
418
|
+
expect(result.root).toBe('C:\\projects\\my-app');
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
it('should handle absolute input paths and ignore cwd', () => {
|
|
423
|
+
const input = isWindows
|
|
424
|
+
? 'C:\\projects\\absolute-project'
|
|
425
|
+
: '/projects/absolute-project';
|
|
426
|
+
const customCwd = isWindows ? 'D:\\ignored' : '/ignored';
|
|
427
|
+
|
|
428
|
+
const result: ProjectNameResult = formatProjectName(
|
|
429
|
+
input,
|
|
430
|
+
customCwd
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
expect(result.name).toBe('absolute-project');
|
|
434
|
+
if (isWindows) {
|
|
435
|
+
expect(result.root).toBe('C:\\projects\\absolute-project');
|
|
436
|
+
} else {
|
|
437
|
+
expect(result.root).toBe('/projects/absolute-project');
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project name utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { basename, isAbsolute, normalize, resolve } from 'node:path';
|
|
6
|
+
|
|
7
|
+
export interface ProjectNameResult {
|
|
8
|
+
name: string;
|
|
9
|
+
root: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Format project name and determine target directory
|
|
14
|
+
*
|
|
15
|
+
* Examples:
|
|
16
|
+
* 1. Input: 'foo', cwd: '/home/user' (Unix)
|
|
17
|
+
* Output: { name: 'foo', root: '/home/user/foo' }
|
|
18
|
+
* Input: 'foo', cwd: 'C:\\workspace' (Windows)
|
|
19
|
+
* Output: { name: 'foo', root: 'C:\\workspace\\foo' }
|
|
20
|
+
*
|
|
21
|
+
* 2. Input: 'foo/bar', cwd: '/home/user' (Unix)
|
|
22
|
+
* Output: { name: 'bar', root: '/home/user/foo/bar' }
|
|
23
|
+
* Input: 'foo\\bar', cwd: 'C:\\workspace' (Windows)
|
|
24
|
+
* Output: { name: 'bar', root: 'C:\\workspace\\foo\\bar' }
|
|
25
|
+
*
|
|
26
|
+
* 3. Input: '@scope/foo', cwd: '/home/user' (Unix)
|
|
27
|
+
* Output: { name: '@scope/foo', root: '/home/user/@scope/foo' }
|
|
28
|
+
* Input: '@scope/foo', cwd: 'C:\\workspace' (Windows)
|
|
29
|
+
* Output: { name: '@scope/foo', root: 'C:\\workspace\\@scope\\foo' }
|
|
30
|
+
*
|
|
31
|
+
* 4. Input: './foo/bar', cwd: '/home/user/current' (Unix)
|
|
32
|
+
* Output: { name: 'bar', root: '/home/user/current/foo/bar' }
|
|
33
|
+
* Input: '.\\foo\\bar', cwd: 'C:\\workspace\\current' (Windows)
|
|
34
|
+
* Output: { name: 'bar', root: 'C:\\workspace\\current\\foo\\bar' }
|
|
35
|
+
*
|
|
36
|
+
* 5. Input: '/root/path/to/foo', cwd: '/home/user' (Unix absolute)
|
|
37
|
+
* Output: { name: 'foo', root: '/root/path/to/foo' }
|
|
38
|
+
* Input: 'C:\\projects\\my-app', cwd: 'D:\\workspace' (Windows absolute)
|
|
39
|
+
* Output: { name: 'my-app', root: 'C:\\projects\\my-app' }
|
|
40
|
+
*
|
|
41
|
+
* 6. Input: '.', cwd: '/home/user/projects/my-app' (Unix current dir)
|
|
42
|
+
* Output: { name: 'my-app', root: '/home/user/projects/my-app' }
|
|
43
|
+
* Input: '.', cwd: 'C:\\Users\\Developer\\Projects\\WindowsApp' (Windows current dir)
|
|
44
|
+
* Output: { name: 'WindowsApp', root: 'C:\\Users\\Developer\\Projects\\WindowsApp' }
|
|
45
|
+
*
|
|
46
|
+
* 7. Input: '\\\\server\\share\\project', cwd: 'C:\\workspace' (Windows UNC)
|
|
47
|
+
* Output: { name: 'project', root: '\\\\server\\share\\project' }
|
|
48
|
+
*
|
|
49
|
+
* 8. Input: 'path\\to/project', cwd: '/home/user' (Mixed separators)
|
|
50
|
+
* Output: { name: 'project', root: '/home/user/path/to/project' }
|
|
51
|
+
*/
|
|
52
|
+
export function formatProjectName(
|
|
53
|
+
input: string,
|
|
54
|
+
cwd?: string
|
|
55
|
+
): ProjectNameResult {
|
|
56
|
+
const workingDir = cwd || process.cwd();
|
|
57
|
+
|
|
58
|
+
let cleanInput = input;
|
|
59
|
+
if (input !== '/' && input !== '\\' && input.length > 1) {
|
|
60
|
+
cleanInput = input.replace(/[\\/]+$/, '');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (cleanInput === '.') {
|
|
64
|
+
return {
|
|
65
|
+
name: basename(workingDir),
|
|
66
|
+
root: workingDir
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (cleanInput === '' || cleanInput === '/' || cleanInput === '\\') {
|
|
71
|
+
if (cleanInput === '/') {
|
|
72
|
+
return {
|
|
73
|
+
name: 'esmx-project',
|
|
74
|
+
root: '/'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
name: 'esmx-project',
|
|
79
|
+
root: resolve(workingDir, 'esmx-project')
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let root: string;
|
|
84
|
+
if (isAbsolute(cleanInput)) {
|
|
85
|
+
root = normalize(cleanInput);
|
|
86
|
+
} else {
|
|
87
|
+
root = resolve(workingDir, cleanInput);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let name: string;
|
|
91
|
+
if (cleanInput.startsWith('@')) {
|
|
92
|
+
name = cleanInput;
|
|
93
|
+
} else {
|
|
94
|
+
name = basename(normalize(cleanInput)) || 'esmx-project';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
name,
|
|
99
|
+
root
|
|
100
|
+
};
|
|
101
|
+
}
|