create-esmx 3.0.0-rc.45 → 3.0.0-rc.47

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 (38) hide show
  1. package/dist/cli.integration.test.mjs +11 -7
  2. package/dist/cli.mjs +19 -18
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.mjs +1 -0
  5. package/dist/project.mjs +2 -2
  6. package/dist/project.test.mjs +21 -43
  7. package/dist/types.d.ts +1 -0
  8. package/dist/utils/project-name.d.ts +32 -14
  9. package/dist/utils/project-name.mjs +36 -11
  10. package/dist/utils/project-name.test.d.ts +0 -3
  11. package/dist/utils/project-name.test.mjs +251 -105
  12. package/package.json +5 -4
  13. package/src/cli.integration.test.ts +11 -7
  14. package/src/cli.ts +23 -19
  15. package/src/index.ts +2 -0
  16. package/src/project.test.ts +21 -46
  17. package/src/project.ts +6 -3
  18. package/src/types.ts +1 -0
  19. package/src/utils/project-name.test.ts +299 -203
  20. package/src/utils/project-name.ts +73 -27
  21. package/template/vue2-csr/README.md +80 -0
  22. package/template/vue2-csr/package.json +26 -0
  23. package/template/vue2-csr/src/components/hello-world.vue +77 -0
  24. package/template/vue2-csr/src/entry.node.ts +32 -0
  25. package/template/vue2-csr/src/entry.server.ts +26 -0
  26. package/template/{vue2 → vue2-ssr}/package.json +1 -1
  27. package/template/vue2-ssr/src/app.vue +127 -0
  28. package/template/vue2-ssr/src/create-app.ts +11 -0
  29. package/template/vue2-ssr/src/entry.client.ts +5 -0
  30. package/template/{vue2 → vue2-ssr}/src/entry.node.ts +1 -1
  31. package/template/vue2-ssr/tsconfig.json +26 -0
  32. /package/template/{vue2 → vue2-csr}/src/app.vue +0 -0
  33. /package/template/{vue2 → vue2-csr}/src/create-app.ts +0 -0
  34. /package/template/{vue2 → vue2-csr}/src/entry.client.ts +0 -0
  35. /package/template/{vue2 → vue2-csr}/tsconfig.json +0 -0
  36. /package/template/{vue2 → vue2-ssr}/README.md +0 -0
  37. /package/template/{vue2 → vue2-ssr}/src/components/hello-world.vue +0 -0
  38. /package/template/{vue2 → vue2-ssr}/src/entry.server.ts +0 -0
@@ -109,13 +109,13 @@ describe("create-esmx CLI integration tests", () => {
109
109
  it("should handle --force parameter correctly", async () => {
110
110
  const projectPath = join(tmpDir, "test-project");
111
111
  await cli({
112
- argv: ["test-project", "--template", "vue2"],
112
+ argv: ["test-project", "--template", "vue2-csr"],
113
113
  cwd: tmpDir,
114
114
  userAgent: "npm/test"
115
115
  });
116
116
  expect(existsSync(join(projectPath, "package.json"))).toBe(true);
117
117
  await cli({
118
- argv: ["test-project", "--template", "vue2", "--force"],
118
+ argv: ["test-project", "--template", "vue2-csr", "--force"],
119
119
  cwd: tmpDir,
120
120
  userAgent: "npm/test"
121
121
  });
@@ -167,7 +167,11 @@ describe("create-esmx CLI integration tests", () => {
167
167
  it("should handle creating directory when target directory does not exist", async () => {
168
168
  const projectPath = join(tmpDir, "non-existent-parent", "test-project");
169
169
  await cli({
170
- argv: ["non-existent-parent/test-project", "--template", "vue2"],
170
+ argv: [
171
+ "non-existent-parent/test-project",
172
+ "--template",
173
+ "vue2-csr"
174
+ ],
171
175
  cwd: tmpDir,
172
176
  userAgent: "npm/test"
173
177
  });
@@ -183,7 +187,7 @@ describe("create-esmx CLI integration tests", () => {
183
187
  "existing content"
184
188
  );
185
189
  await cli({
186
- argv: ["test-project", "--template", "vue2", "--force"],
190
+ argv: ["test-project", "--template", "vue2-csr", "--force"],
187
191
  cwd: tmpDir,
188
192
  userAgent: "npm/test"
189
193
  });
@@ -194,7 +198,7 @@ describe("create-esmx CLI integration tests", () => {
194
198
  const testFile = join(tmpDir, "existing-file.txt");
195
199
  await writeFile(testFile, "existing content");
196
200
  await cli({
197
- argv: [".", "--template", "vue2", "--force"],
201
+ argv: [".", "--template", "vue2-csr", "--force"],
198
202
  cwd: tmpDir,
199
203
  userAgent: "npm/test"
200
204
  });
@@ -204,7 +208,7 @@ describe("create-esmx CLI integration tests", () => {
204
208
  });
205
209
  it('should create project in current directory when target is "."', async () => {
206
210
  await cli({
207
- argv: [".", "--template", "vue2"],
211
+ argv: [".", "--template", "vue2-csr"],
208
212
  cwd: tmpDir,
209
213
  userAgent: "npm/test"
210
214
  });
@@ -221,7 +225,7 @@ describe("create-esmx CLI integration tests", () => {
221
225
  for (const projectName of testCases) {
222
226
  const projectPath = join(tmpDir, projectName);
223
227
  await cli({
224
- argv: [projectName, "--template", "vue2"],
228
+ argv: [projectName, "--template", "vue2-csr"],
225
229
  cwd: tmpDir,
226
230
  userAgent: "npm/test"
227
231
  });
package/dist/cli.mjs CHANGED
@@ -22,7 +22,7 @@ ${color.bold("Usage:")}
22
22
  ${createCmd} [project-name] [options]
23
23
 
24
24
  ${color.bold("Options:")}
25
- -t, --template <template> Template to use (default: vue2)
25
+ -t, --template <template> Template to use (default: vue2-csr)
26
26
  -n, --name <name> Project name or path
27
27
  -f, --force Force overwrite existing directory
28
28
  -h, --help Show help information
@@ -30,9 +30,9 @@ ${color.bold("Options:")}
30
30
 
31
31
  ${color.bold("Examples:")}
32
32
  ${createCmd} my-project
33
- ${createCmd} my-project -t vue2
33
+ ${createCmd} my-project -t vue2-csr
34
34
  ${createCmd} my-project --force
35
- ${createCmd} . -f -t vue2
35
+ ${createCmd} . -f -t vue2-csr
36
36
 
37
37
  ${color.bold("Available Templates:")}
38
38
  ${getAvailableTemplates().map((t) => ` ${t.folder.padEnd(25)} ${t.description}`).join("\n")}
@@ -73,10 +73,10 @@ async function getTemplateType(argTemplate) {
73
73
  message: "Select a template:",
74
74
  options
75
75
  });
76
- return template;
76
+ return String(template);
77
77
  }
78
78
  export async function cli(options = {}) {
79
- const { argv, cwd, userAgent } = options;
79
+ const { argv, cwd, userAgent, version } = options;
80
80
  const commandLineArgs = argv || process.argv.slice(2);
81
81
  const workingDir = cwd || process.cwd();
82
82
  const parsedArgs = minimist(commandLineArgs, {
@@ -112,10 +112,7 @@ export async function cli(options = {}) {
112
112
  cancel("Operation cancelled");
113
113
  return;
114
114
  }
115
- const { packageName, targetDir } = formatProjectName(
116
- projectNameInput,
117
- workingDir
118
- );
115
+ const { name, root } = formatProjectName(projectNameInput, workingDir);
119
116
  const templateType = await getTemplateType(parsedArgs.template);
120
117
  if (isCancel(templateType)) {
121
118
  cancel("Operation cancelled");
@@ -128,13 +125,13 @@ export async function cli(options = {}) {
128
125
  const buildTypeCommand = getCommand("build:type", userAgent);
129
126
  const lintTypeCommand = getCommand("lint:type", userAgent);
130
127
  await createProjectFromTemplate(
131
- targetDir,
128
+ root,
132
129
  templateType,
133
130
  workingDir,
134
131
  parsedArgs.force,
135
132
  {
136
- projectName: packageName,
137
- esmxVersion: getEsmxVersion(),
133
+ projectName: name,
134
+ esmxVersion: version || getEsmxVersion(),
138
135
  installCommand,
139
136
  devCommand,
140
137
  buildCommand,
@@ -145,12 +142,16 @@ export async function cli(options = {}) {
145
142
  );
146
143
  const installCmd = installCommand;
147
144
  const devCmd = devCommand;
148
- const nextSteps = [
149
- color.reset(`1. ${color.cyan(`cd ${targetDir}`)}`),
150
- color.reset(`2. ${color.cyan(installCmd)}`),
151
- color.reset(`3. ${color.cyan("git init")} ${color.gray("(optional)")}`),
152
- color.reset(`4. ${color.cyan(devCmd)}`)
153
- ];
145
+ const targetDirForDisplay = projectNameInput === "." ? "." : projectNameInput;
146
+ const steps = [
147
+ projectNameInput !== "." ? `cd ${targetDirForDisplay}` : null,
148
+ installCmd,
149
+ `git init ${color.gray("(optional)")}`,
150
+ devCmd
151
+ ].filter(Boolean);
152
+ const nextSteps = steps.map((step, index) => {
153
+ return color.reset(`${index + 1}. ${color.cyan(step)}`);
154
+ });
154
155
  note(nextSteps.join("\n"), "Next steps");
155
156
  outro(color.reset(color.green("Happy coding! \u{1F389}")));
156
157
  }
package/dist/index.d.ts CHANGED
@@ -1 +1,3 @@
1
1
  export { cli } from './cli';
2
+ export { getAvailableTemplates } from './template';
3
+ export type { TemplateInfo } from './types';
package/dist/index.mjs CHANGED
@@ -1 +1,2 @@
1
1
  export { cli } from "./cli.mjs";
2
+ export { getAvailableTemplates } from "./template.mjs";
package/dist/project.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  import { existsSync, mkdirSync } from "node:fs";
2
- import { dirname, resolve } from "node:path";
2
+ import { dirname, isAbsolute, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { cancel, confirm, isCancel } from "@clack/prompts";
5
5
  import { copyTemplateFiles, isDirectoryEmpty } from "./template.mjs";
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
7
  export async function createProjectFromTemplate(targetDir, templateType, workingDir, force, variables) {
8
8
  const templatePath = resolve(__dirname, "../template", templateType);
9
- const targetPath = targetDir === "." ? workingDir : resolve(workingDir, targetDir);
9
+ const targetPath = isAbsolute(targetDir) ? targetDir : targetDir === "." ? workingDir : resolve(workingDir, targetDir);
10
10
  if (!existsSync(templatePath)) {
11
11
  throw new Error(`Template "${templateType}" not found`);
12
12
  }
@@ -9,12 +9,8 @@ import { formatProjectName } from "./utils/index.mjs";
9
9
  async function createTempDir(prefix = "esmx-unit-test-") {
10
10
  return mkdtemp(join(tmpdir(), prefix));
11
11
  }
12
- async function cleanupTempDir(tempDir) {
13
- try {
14
- await rm(tempDir, { recursive: true, force: true });
15
- } catch (error) {
16
- console.warn(`Failed to cleanup temp directory: ${tempDir}`, error);
17
- }
12
+ async function cleanupTempDir(tmpDir) {
13
+ await rm(tmpDir, { recursive: true, force: true });
18
14
  }
19
15
  describe("project unit tests", () => {
20
16
  let tmpDir;
@@ -30,12 +26,9 @@ describe("project unit tests", () => {
30
26
  await writeFile(join(hiddenFilesDir, ".hidden-file"), "hidden content");
31
27
  await writeFile(join(hiddenFilesDir, ".gitignore"), "node_modules/");
32
28
  const projectNameInput = "hidden-files-dir";
33
- const { packageName, targetDir } = formatProjectName(
34
- projectNameInput,
35
- tmpDir
36
- );
37
- await createProjectFromTemplate(targetDir, "vue2", tmpDir, false, {
38
- projectName: packageName,
29
+ const { name, root } = formatProjectName(projectNameInput, tmpDir);
30
+ await createProjectFromTemplate(root, "vue2-csr", tmpDir, false, {
31
+ projectName: name,
39
32
  esmxVersion: getEsmxVersion(),
40
33
  installCommand: "npm install",
41
34
  devCommand: "npm run dev",
@@ -56,12 +49,9 @@ describe("project unit tests", () => {
56
49
  "project"
57
50
  );
58
51
  const projectNameInput = "very/deep/nested/path/project";
59
- const { packageName, targetDir } = formatProjectName(
60
- projectNameInput,
61
- tmpDir
62
- );
63
- await createProjectFromTemplate(targetDir, "vue2", tmpDir, false, {
64
- projectName: packageName,
52
+ const { name, root } = formatProjectName(projectNameInput, tmpDir);
53
+ await createProjectFromTemplate(root, "vue2-csr", tmpDir, false, {
54
+ projectName: name,
65
55
  esmxVersion: getEsmxVersion(),
66
56
  installCommand: "npm install",
67
57
  devCommand: "npm run dev",
@@ -76,12 +66,9 @@ describe("project unit tests", () => {
76
66
  it("should handle file copy with template variable replacement", async () => {
77
67
  const projectPath = join(tmpDir, "variable-test");
78
68
  const projectNameInput = "variable-test";
79
- const { packageName, targetDir } = formatProjectName(
80
- projectNameInput,
81
- tmpDir
82
- );
83
- await createProjectFromTemplate(targetDir, "vue2", tmpDir, false, {
84
- projectName: packageName,
69
+ const { name, root } = formatProjectName(projectNameInput, tmpDir);
70
+ await createProjectFromTemplate(root, "vue2-csr", tmpDir, false, {
71
+ projectName: name,
85
72
  esmxVersion: getEsmxVersion(),
86
73
  installCommand: "npm install",
87
74
  devCommand: "npm run dev",
@@ -103,12 +90,9 @@ describe("project unit tests", () => {
103
90
  const emptyDir = join(tmpDir, "empty-dir");
104
91
  await mkdir(emptyDir, { recursive: true });
105
92
  const projectNameInput = "empty-dir";
106
- const { packageName, targetDir } = formatProjectName(
107
- projectNameInput,
108
- tmpDir
109
- );
110
- await createProjectFromTemplate(targetDir, "vue2", tmpDir, false, {
111
- projectName: packageName,
93
+ const { name, root } = formatProjectName(projectNameInput, tmpDir);
94
+ await createProjectFromTemplate(root, "vue2-csr", tmpDir, false, {
95
+ projectName: name,
112
96
  esmxVersion: getEsmxVersion(),
113
97
  installCommand: "npm install",
114
98
  devCommand: "npm run dev",
@@ -125,18 +109,15 @@ describe("project unit tests", () => {
125
109
  await writeFile(join(mixedDir, ".dotfile"), "hidden");
126
110
  await writeFile(join(mixedDir, "regular-file.txt"), "visible");
127
111
  const projectNameInput = "mixed-dir";
128
- const { packageName, targetDir } = formatProjectName(
129
- projectNameInput,
130
- tmpDir
131
- );
112
+ const { name, root } = formatProjectName(projectNameInput, tmpDir);
132
113
  await createProjectFromTemplate(
133
- targetDir,
134
- "vue2",
114
+ root,
115
+ "vue2-csr",
135
116
  tmpDir,
136
117
  true,
137
118
  // force flag
138
119
  {
139
- projectName: packageName,
120
+ projectName: name,
140
121
  esmxVersion: getEsmxVersion(),
141
122
  installCommand: "npm install",
142
123
  devCommand: "npm run dev",
@@ -156,12 +137,9 @@ describe("project unit tests", () => {
156
137
  ];
157
138
  for (const projectName of specialNames) {
158
139
  const projectPath = join(tmpDir, projectName);
159
- const { packageName, targetDir } = formatProjectName(
160
- projectName,
161
- tmpDir
162
- );
163
- await createProjectFromTemplate(targetDir, "vue2", tmpDir, false, {
164
- projectName: packageName,
140
+ const { name, root } = formatProjectName(projectName, tmpDir);
141
+ await createProjectFromTemplate(root, "vue2-csr", tmpDir, false, {
142
+ projectName: name,
165
143
  esmxVersion: getEsmxVersion(),
166
144
  installCommand: "npm install",
167
145
  devCommand: "npm run dev",
package/dist/types.d.ts CHANGED
@@ -5,6 +5,7 @@ export interface CliOptions {
5
5
  argv?: string[];
6
6
  cwd?: string;
7
7
  userAgent?: string;
8
+ version?: string;
8
9
  }
9
10
  /**
10
11
  * Template information structure
@@ -2,29 +2,47 @@
2
2
  * Project name utilities
3
3
  */
4
4
  export interface ProjectNameResult {
5
- packageName: string;
6
- targetDir: string;
5
+ name: string;
6
+ root: string;
7
7
  }
8
8
  /**
9
9
  * Format project name and determine target directory
10
10
  *
11
11
  * Examples:
12
- * 1. Input: 'foo'
13
- * Output: folder `<cwd>/foo`, `package.json#name` -> `foo`
12
+ * 1. Input: 'foo', cwd: '/home/user' (Unix)
13
+ * Output: { name: 'foo', root: '/home/user/foo' }
14
+ * Input: 'foo', cwd: 'C:\\workspace' (Windows)
15
+ * Output: { name: 'foo', root: 'C:\\workspace\\foo' }
14
16
  *
15
- * 2. Input: 'foo/bar'
16
- * Output: folder -> `<cwd>/foo/bar` folder, `package.json#name` -> `bar`
17
+ * 2. Input: 'foo/bar', cwd: '/home/user' (Unix)
18
+ * Output: { name: 'bar', root: '/home/user/foo/bar' }
19
+ * Input: 'foo\\bar', cwd: 'C:\\workspace' (Windows)
20
+ * Output: { name: 'bar', root: 'C:\\workspace\\foo\\bar' }
17
21
  *
18
- * 3. Input: '@scope/foo'
19
- * Output: folder -> `<cwd>/@scope/foo` folder, `package.json#name` -> `@scope/foo`
22
+ * 3. Input: '@scope/foo', cwd: '/home/user' (Unix)
23
+ * Output: { name: '@scope/foo', root: '/home/user/@scope/foo' }
24
+ * Input: '@scope/foo', cwd: 'C:\\workspace' (Windows)
25
+ * Output: { name: '@scope/foo', root: 'C:\\workspace\\@scope\\foo' }
20
26
  *
21
- * 4. Input: './foo/bar'
22
- * Output: folder -> `<cwd>/foo/bar` folder, `package.json#name` -> `bar`
27
+ * 4. Input: './foo/bar', cwd: '/home/user/current' (Unix)
28
+ * Output: { name: 'bar', root: '/home/user/current/foo/bar' }
29
+ * Input: '.\\foo\\bar', cwd: 'C:\\workspace\\current' (Windows)
30
+ * Output: { name: 'bar', root: 'C:\\workspace\\current\\foo\\bar' }
23
31
  *
24
- * 5. Input: '/root/path/to/foo'
25
- * Output: folder -> `'/root/path/to/foo'` folder, `package.json#name` -> `foo`
32
+ * 5. Input: '/root/path/to/foo', cwd: '/home/user' (Unix absolute)
33
+ * Output: { name: 'foo', root: '/root/path/to/foo' }
34
+ * Input: 'C:\\projects\\my-app', cwd: 'D:\\workspace' (Windows absolute)
35
+ * Output: { name: 'my-app', root: 'C:\\projects\\my-app' }
26
36
  *
27
- * 6. Input: '.'
28
- * Output: folder -> `<cwd>` folder, `package.json#name` -> `<current-dir-name>`
37
+ * 6. Input: '.', cwd: '/home/user/projects/my-app' (Unix current dir)
38
+ * Output: { name: 'my-app', root: '/home/user/projects/my-app' }
39
+ * Input: '.', cwd: 'C:\\Users\\Developer\\Projects\\WindowsApp' (Windows current dir)
40
+ * Output: { name: 'WindowsApp', root: 'C:\\Users\\Developer\\Projects\\WindowsApp' }
41
+ *
42
+ * 7. Input: '\\\\server\\share\\project', cwd: 'C:\\workspace' (Windows UNC)
43
+ * Output: { name: 'project', root: '\\\\server\\share\\project' }
44
+ *
45
+ * 8. Input: 'path\\to/project', cwd: '/home/user' (Mixed separators)
46
+ * Output: { name: 'project', root: '/home/user/path/to/project' }
29
47
  */
30
48
  export declare function formatProjectName(input: string, cwd?: string): ProjectNameResult;
@@ -1,17 +1,42 @@
1
- import { basename } from "node:path";
1
+ import { basename, isAbsolute, normalize, resolve } from "node:path";
2
2
  export function formatProjectName(input, cwd) {
3
- const targetDir = input.replace(/\/+$/g, "");
4
- let packageName;
5
- if (targetDir === ".") {
6
- const workingDir = cwd || process.cwd();
7
- packageName = basename(workingDir);
8
- } else if (targetDir.startsWith("@")) {
9
- packageName = targetDir;
3
+ const workingDir = cwd || process.cwd();
4
+ let cleanInput = input;
5
+ if (input !== "/" && input !== "\\" && input.length > 1) {
6
+ cleanInput = input.replace(/[\\/]+$/, "");
7
+ }
8
+ if (cleanInput === ".") {
9
+ return {
10
+ name: basename(workingDir),
11
+ root: workingDir
12
+ };
13
+ }
14
+ if (cleanInput === "" || cleanInput === "/" || cleanInput === "\\") {
15
+ if (cleanInput === "/") {
16
+ return {
17
+ name: "esmx-project",
18
+ root: "/"
19
+ };
20
+ }
21
+ return {
22
+ name: "esmx-project",
23
+ root: resolve(workingDir, "esmx-project")
24
+ };
25
+ }
26
+ let root;
27
+ if (isAbsolute(cleanInput)) {
28
+ root = normalize(cleanInput);
29
+ } else {
30
+ root = resolve(workingDir, cleanInput);
31
+ }
32
+ let name;
33
+ if (cleanInput.startsWith("@")) {
34
+ name = cleanInput;
10
35
  } else {
11
- packageName = targetDir.split("/").pop() || "esmx-project";
36
+ name = basename(normalize(cleanInput)) || "esmx-project";
12
37
  }
13
38
  return {
14
- packageName,
15
- targetDir
39
+ name,
40
+ root
16
41
  };
17
42
  }
@@ -1,4 +1 @@
1
- /**
2
- * Unit tests for project-name utilities
3
- */
4
1
  export {};