create-esmx 3.0.0-rc.33

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 (40) hide show
  1. package/dist/index.d.ts +8 -0
  2. package/dist/index.mjs +282 -0
  3. package/dist/index.test.d.ts +1 -0
  4. package/dist/index.test.mjs +123 -0
  5. package/dist/integration.test.d.ts +1 -0
  6. package/dist/integration.test.mjs +165 -0
  7. package/dist/utils/index.d.ts +3 -0
  8. package/dist/utils/index.mjs +7 -0
  9. package/dist/utils/package-manager.d.ts +10 -0
  10. package/dist/utils/package-manager.mjs +49 -0
  11. package/dist/utils/package-manager.test.d.ts +4 -0
  12. package/dist/utils/package-manager.test.mjs +275 -0
  13. package/dist/utils/project-name.d.ts +30 -0
  14. package/dist/utils/project-name.mjs +17 -0
  15. package/dist/utils/project-name.test.d.ts +4 -0
  16. package/dist/utils/project-name.test.mjs +186 -0
  17. package/dist/utils/template.d.ts +19 -0
  18. package/dist/utils/template.mjs +8 -0
  19. package/dist/utils/template.test.d.ts +4 -0
  20. package/dist/utils/template.test.mjs +150 -0
  21. package/package.json +71 -0
  22. package/src/index.test.ts +159 -0
  23. package/src/index.ts +391 -0
  24. package/src/integration.test.ts +226 -0
  25. package/src/utils/index.ts +11 -0
  26. package/src/utils/package-manager.test.ts +540 -0
  27. package/src/utils/package-manager.ts +92 -0
  28. package/src/utils/project-name.test.ts +345 -0
  29. package/src/utils/project-name.ts +55 -0
  30. package/src/utils/template.test.ts +234 -0
  31. package/src/utils/template.ts +34 -0
  32. package/template/vue2/README.md +80 -0
  33. package/template/vue2/package.json +27 -0
  34. package/template/vue2/src/app.vue +127 -0
  35. package/template/vue2/src/components/hello-world.vue +79 -0
  36. package/template/vue2/src/create-app.ts +11 -0
  37. package/template/vue2/src/entry.client.ts +5 -0
  38. package/template/vue2/src/entry.node.ts +29 -0
  39. package/template/vue2/src/entry.server.ts +37 -0
  40. package/template/vue2/tsconfig.json +26 -0
@@ -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,71 @@
1
+ {
2
+ "name": "create-esmx",
3
+ "version": "3.0.0-rc.33",
4
+ "description": "A scaffold tool for creating Esmx projects",
5
+ "type": "module",
6
+ "private": false,
7
+ "bin": {
8
+ "create-esmx": "./dist/index.mjs"
9
+ },
10
+ "template": "library-node",
11
+ "scripts": {
12
+ "lint:css": "stylelint '**/*.{css,vue}' --fix --aei",
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
+ },
19
+ "dependencies": {
20
+ "@clack/prompts": "^0.7.0",
21
+ "picocolors": "^1.0.0",
22
+ "minimist": "^1.2.8"
23
+ },
24
+ "devDependencies": {
25
+ "@esmx/lint": "3.0.0-rc.33",
26
+ "@types/node": "^24.0.0",
27
+ "@types/minimist": "^1.2.5",
28
+ "stylelint": "16.21.0",
29
+ "typescript": "5.8.3",
30
+ "vitest": "3.2.4",
31
+ "@vitest/coverage-v8": "3.2.4",
32
+ "@biomejs/biome": "1.9.4",
33
+ "unbuild": "3.5.0"
34
+ },
35
+ "exports": {
36
+ ".": {
37
+ "import": "./dist/index.mjs",
38
+ "types": "./dist/index.d.ts"
39
+ }
40
+ },
41
+ "module": "dist/index.mjs",
42
+ "types": "./dist/index.d.ts",
43
+ "files": [
44
+ "lib",
45
+ "src",
46
+ "dist",
47
+ "*.mjs",
48
+ "template",
49
+ "public",
50
+ "bin"
51
+ ],
52
+ "keywords": [
53
+ "esmx",
54
+ "scaffold",
55
+ "template",
56
+ "vue",
57
+ "ssr",
58
+ "esm"
59
+ ],
60
+ "author": "Esmx Team",
61
+ "license": "MIT",
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "git+https://github.com/esmnext/esmx.git",
65
+ "directory": "packages/create-esmx"
66
+ },
67
+ "bugs": {
68
+ "url": "https://github.com/esmnext/esmx/issues"
69
+ },
70
+ "homepage": "https://github.com/esmnext/esmx#readme"
71
+ }
@@ -0,0 +1,159 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
+ import { createProject } from './index';
7
+
8
+ // Test utilities
9
+ async function createTempDir(prefix = 'esmx-unit-test-'): Promise<string> {
10
+ return mkdtemp(join(tmpdir(), prefix));
11
+ }
12
+
13
+ async function cleanupTempDir(tempDir: string): Promise<void> {
14
+ try {
15
+ await rm(tempDir, { recursive: true, force: true });
16
+ } catch (error) {
17
+ console.warn(`Failed to cleanup temp directory: ${tempDir}`, error);
18
+ }
19
+ }
20
+
21
+ describe('createProject unit tests', () => {
22
+ let tmpDir: string;
23
+
24
+ beforeEach(async () => {
25
+ tmpDir = await createTempDir();
26
+ });
27
+
28
+ afterEach(async () => {
29
+ await cleanupTempDir(tmpDir);
30
+ });
31
+
32
+ it('should handle isDirectoryEmpty edge cases', async () => {
33
+ // Test with directory containing only hidden files
34
+ const hiddenFilesDir = join(tmpDir, 'hidden-files-dir');
35
+ await mkdir(hiddenFilesDir, { recursive: true });
36
+ await writeFile(join(hiddenFilesDir, '.hidden-file'), 'hidden content');
37
+ await writeFile(join(hiddenFilesDir, '.gitignore'), 'node_modules/');
38
+
39
+ await createProject({
40
+ argv: ['hidden-files-dir', '--template', 'vue2'],
41
+ cwd: tmpDir,
42
+ userAgent: 'npm/test'
43
+ });
44
+
45
+ // Should succeed because hidden files are ignored
46
+ expect(existsSync(join(hiddenFilesDir, 'package.json'))).toBe(true);
47
+ });
48
+
49
+ it('should handle directory creation for nested paths', async () => {
50
+ const deepPath = join(
51
+ tmpDir,
52
+ 'very',
53
+ 'deep',
54
+ 'nested',
55
+ 'path',
56
+ 'project'
57
+ );
58
+
59
+ await createProject({
60
+ argv: ['very/deep/nested/path/project', '--template', 'vue2'],
61
+ cwd: tmpDir,
62
+ userAgent: 'npm/test'
63
+ });
64
+
65
+ expect(existsSync(deepPath)).toBe(true);
66
+ expect(existsSync(join(deepPath, 'package.json'))).toBe(true);
67
+ });
68
+
69
+ it('should handle file copy with template variable replacement', async () => {
70
+ const projectPath = join(tmpDir, 'variable-test');
71
+
72
+ await createProject({
73
+ argv: ['variable-test', '--template', 'vue2'],
74
+ cwd: tmpDir,
75
+ userAgent: 'npm/test'
76
+ });
77
+
78
+ // Verify that package.json contains replaced variables
79
+ const packageJsonPath = join(projectPath, 'package.json');
80
+ expect(existsSync(packageJsonPath)).toBe(true);
81
+
82
+ const packageContent = require('node:fs').readFileSync(
83
+ packageJsonPath,
84
+ 'utf-8'
85
+ );
86
+ const packageJson = JSON.parse(packageContent);
87
+ expect(packageJson.name).toBe('variable-test');
88
+ });
89
+
90
+ it('should handle empty directory detection correctly', async () => {
91
+ // Test completely empty directory
92
+ const emptyDir = join(tmpDir, 'empty-dir');
93
+ await mkdir(emptyDir, { recursive: true });
94
+
95
+ await createProject({
96
+ argv: ['empty-dir', '--template', 'vue2'],
97
+ cwd: tmpDir,
98
+ userAgent: 'npm/test'
99
+ });
100
+
101
+ expect(existsSync(join(emptyDir, 'package.json'))).toBe(true);
102
+ });
103
+
104
+ it('should handle mixed file types in directory', async () => {
105
+ // Test directory with mix of hidden and non-hidden files
106
+ const mixedDir = join(tmpDir, 'mixed-dir');
107
+ await mkdir(mixedDir, { recursive: true });
108
+ await writeFile(join(mixedDir, '.dotfile'), 'hidden');
109
+ await writeFile(join(mixedDir, 'regular-file.txt'), 'visible');
110
+
111
+ // This should require force flag since directory is not empty
112
+ await createProject({
113
+ argv: ['mixed-dir', '--template', 'vue2', '--force'],
114
+ cwd: tmpDir,
115
+ userAgent: 'npm/test'
116
+ });
117
+
118
+ expect(existsSync(join(mixedDir, 'package.json'))).toBe(true);
119
+ });
120
+
121
+ it('should handle various package manager user agents', async () => {
122
+ const testCases = ['npm', 'yarn', 'pnpm', 'bun'];
123
+
124
+ for (const userAgent of testCases) {
125
+ const projectName = `test-${userAgent}`;
126
+ const projectPath = join(tmpDir, projectName);
127
+
128
+ await createProject({
129
+ argv: [projectName, '--template', 'vue2'],
130
+ cwd: tmpDir,
131
+ userAgent: `${userAgent}/test-version`
132
+ });
133
+
134
+ expect(existsSync(projectPath)).toBe(true);
135
+ expect(existsSync(join(projectPath, 'package.json'))).toBe(true);
136
+ }
137
+ });
138
+
139
+ it('should handle special characters in project names', async () => {
140
+ const specialNames = [
141
+ 'project-with-dashes',
142
+ 'project_with_underscores',
143
+ 'project.with.dots'
144
+ ];
145
+
146
+ for (const projectName of specialNames) {
147
+ const projectPath = join(tmpDir, projectName);
148
+
149
+ await createProject({
150
+ argv: [projectName, '--template', 'vue2'],
151
+ cwd: tmpDir,
152
+ userAgent: 'npm/test'
153
+ });
154
+
155
+ expect(existsSync(projectPath)).toBe(true);
156
+ expect(existsSync(join(projectPath, 'package.json'))).toBe(true);
157
+ }
158
+ });
159
+ });