@blu1606/create-walrus-app 0.1.4 → 1.0.0
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/dist/index.js +0 -0
- package/package.json +68 -68
- package/dist/__tests__/helpers/adapter-compliance.d.ts +0 -2
- package/dist/__tests__/helpers/adapter-compliance.js +0 -47
- package/dist/__tests__/helpers/fixtures.d.ts +0 -21
- package/dist/__tests__/helpers/fixtures.js +0 -30
- package/dist/__tests__/helpers/fs-helpers.d.ts +0 -12
- package/dist/__tests__/helpers/fs-helpers.js +0 -35
- package/dist/__tests__/helpers/index.d.ts +0 -4
- package/dist/__tests__/helpers/index.js +0 -4
- package/dist/__tests__/helpers/test-hooks.d.ts +0 -3
- package/dist/__tests__/helpers/test-hooks.js +0 -18
- package/dist/context.test.d.ts +0 -1
- package/dist/context.test.js +0 -98
- package/dist/generator/index.test.d.ts +0 -1
- package/dist/generator/index.test.js +0 -143
- package/dist/generator/layers.test.d.ts +0 -1
- package/dist/generator/layers.test.js +0 -92
- package/dist/generator/merge.test.d.ts +0 -1
- package/dist/generator/merge.test.js +0 -79
- package/dist/generator/transform.test.d.ts +0 -1
- package/dist/generator/transform.test.js +0 -51
- package/dist/matrix.test.d.ts +0 -1
- package/dist/matrix.test.js +0 -70
- package/dist/types.test.d.ts +0 -1
- package/dist/types.test.js +0 -65
- package/dist/utils/detect-pm.test.d.ts +0 -1
- package/dist/utils/detect-pm.test.js +0 -52
- package/dist/validator.test.d.ts +0 -1
- package/dist/validator.test.js +0 -96
package/dist/index.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@blu1606/create-walrus-app",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Interactive CLI for scaffolding Walrus applications",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"create-walrus-app": "./dist/index.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"dist",
|
|
11
|
-
"templates",
|
|
12
|
-
"README.md"
|
|
13
|
-
],
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
},
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@blu1606/create-walrus-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Interactive CLI for scaffolding Walrus applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-walrus-app": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"templates",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"walrus",
|
|
16
|
+
"sui",
|
|
17
|
+
"scaffold",
|
|
18
|
+
"cli",
|
|
19
|
+
"template"
|
|
20
|
+
],
|
|
21
|
+
"author": "blu1606 dongthanhquandtq@gmail.com",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/blu1606/walrus-starter-kit.git",
|
|
26
|
+
"directory": "packages/cli"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"commander": "^11.1.0",
|
|
30
|
+
"cross-spawn": "^7.0.3",
|
|
31
|
+
"fs-extra": "^11.2.0",
|
|
32
|
+
"kleur": "^4.1.5",
|
|
33
|
+
"prompts": "^2.4.2",
|
|
34
|
+
"sort-package-json": "^2.10.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@semantic-release/exec": "^6.0.3",
|
|
38
|
+
"@types/cross-spawn": "^6.0.6",
|
|
39
|
+
"@types/fs-extra": "^11.0.4",
|
|
40
|
+
"@types/node": "^20.11.0",
|
|
41
|
+
"@types/prompts": "^2.4.9",
|
|
42
|
+
"@vitest/coverage-v8": "^4.0.17",
|
|
43
|
+
"@vitest/ui": "^4.0.17",
|
|
44
|
+
"execa": "^9.5.2",
|
|
45
|
+
"strip-ansi": "^7.1.0",
|
|
46
|
+
"typescript": "^5.3.0",
|
|
47
|
+
"vitest": "^4.0.17"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
|
51
|
+
"pnpm": ">=9.0.0"
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc",
|
|
58
|
+
"dev": "tsc --watch",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:watch": "vitest",
|
|
61
|
+
"test:ui": "vitest --ui",
|
|
62
|
+
"test:coverage": "vitest run --coverage",
|
|
63
|
+
"test:integration": "node tests/integration/integration.test.mjs",
|
|
64
|
+
"test:validation": "node tests/integration/validation.test.mjs",
|
|
65
|
+
"test:manual": "node tests/integration/manual.test.js",
|
|
66
|
+
"test:e2e": "node tests/integration/cli.e2e.test.mjs",
|
|
67
|
+
"test:all": "vitest run && pnpm test:e2e"
|
|
68
|
+
}
|
|
69
69
|
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
export function testStorageAdapterCompliance(adapterName, adapter) {
|
|
3
|
-
describe(`${adapterName} StorageAdapter Compliance`, () => {
|
|
4
|
-
describe('Interface Implementation', () => {
|
|
5
|
-
it('should implement upload method', () => {
|
|
6
|
-
expect(adapter.upload).toBeDefined();
|
|
7
|
-
expect(typeof adapter.upload).toBe('function');
|
|
8
|
-
});
|
|
9
|
-
it('should implement download method', () => {
|
|
10
|
-
expect(adapter.download).toBeDefined();
|
|
11
|
-
expect(typeof adapter.download).toBe('function');
|
|
12
|
-
});
|
|
13
|
-
it('should implement getMetadata method', () => {
|
|
14
|
-
expect(adapter.getMetadata).toBeDefined();
|
|
15
|
-
expect(typeof adapter.getMetadata).toBe('function');
|
|
16
|
-
});
|
|
17
|
-
it('should implement delete method', () => {
|
|
18
|
-
expect(adapter.delete).toBeDefined();
|
|
19
|
-
expect(typeof adapter.delete).toBe('function');
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
describe('Type Signatures', () => {
|
|
23
|
-
it('upload should accept File and UploadOptions', async () => {
|
|
24
|
-
const mockFile = new File(['test'], 'test.txt', { type: 'text/plain' });
|
|
25
|
-
const mockOptions = { epochs: 1 };
|
|
26
|
-
await expect(async () => {
|
|
27
|
-
await adapter.upload(mockFile, mockOptions);
|
|
28
|
-
}).rejects.toThrow();
|
|
29
|
-
});
|
|
30
|
-
it('download should accept string blobId', async () => {
|
|
31
|
-
await expect(async () => {
|
|
32
|
-
await adapter.download('test-blob-id');
|
|
33
|
-
}).rejects.toThrow();
|
|
34
|
-
});
|
|
35
|
-
it('getMetadata should accept string blobId', async () => {
|
|
36
|
-
await expect(async () => {
|
|
37
|
-
await adapter.getMetadata('test-blob-id');
|
|
38
|
-
}).rejects.toThrow();
|
|
39
|
-
});
|
|
40
|
-
it('delete should accept string blobId', async () => {
|
|
41
|
-
await expect(async () => {
|
|
42
|
-
await adapter.delete('test-blob-id');
|
|
43
|
-
}).rejects.toThrow();
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export declare const MOCK_PACKAGE_JSON: {
|
|
2
|
-
name: string;
|
|
3
|
-
version: string;
|
|
4
|
-
type: string;
|
|
5
|
-
scripts: {
|
|
6
|
-
dev: string;
|
|
7
|
-
build: string;
|
|
8
|
-
};
|
|
9
|
-
dependencies: {
|
|
10
|
-
react: string;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
|
-
export declare const MOCK_TSCONFIG: {
|
|
14
|
-
compilerOptions: {
|
|
15
|
-
target: string;
|
|
16
|
-
module: string;
|
|
17
|
-
strict: boolean;
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
export declare const MOCK_VITE_CONFIG = "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\nexport default defineConfig({\n plugins: [react()],\n});\n";
|
|
21
|
-
export declare const MOCK_README = "# Test Project\n\nThis is a test project.\n";
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export const MOCK_PACKAGE_JSON = {
|
|
2
|
-
name: 'test-app',
|
|
3
|
-
version: '0.1.0',
|
|
4
|
-
type: 'module',
|
|
5
|
-
scripts: {
|
|
6
|
-
dev: 'vite',
|
|
7
|
-
build: 'tsc && vite build',
|
|
8
|
-
},
|
|
9
|
-
dependencies: {
|
|
10
|
-
react: '^18.2.0',
|
|
11
|
-
},
|
|
12
|
-
};
|
|
13
|
-
export const MOCK_TSCONFIG = {
|
|
14
|
-
compilerOptions: {
|
|
15
|
-
target: 'ES2020',
|
|
16
|
-
module: 'ESNext',
|
|
17
|
-
strict: true,
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
export const MOCK_VITE_CONFIG = `import { defineConfig } from 'vite';
|
|
21
|
-
import react from '@vitejs/plugin-react';
|
|
22
|
-
|
|
23
|
-
export default defineConfig({
|
|
24
|
-
plugins: [react()],
|
|
25
|
-
});
|
|
26
|
-
`;
|
|
27
|
-
export const MOCK_README = `# Test Project
|
|
28
|
-
|
|
29
|
-
This is a test project.
|
|
30
|
-
`;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare function createTempDir(prefix?: string): Promise<string>;
|
|
2
|
-
export declare function cleanupTempDir(dir: string): Promise<void>;
|
|
3
|
-
export declare function createTestFile(dir: string, filename: string, content: string): Promise<string>;
|
|
4
|
-
export declare function readTestFile(filePath: string): Promise<string>;
|
|
5
|
-
export declare function createMockContext(overrides?: {}): {
|
|
6
|
-
projectName: string;
|
|
7
|
-
sdk: string;
|
|
8
|
-
framework: string;
|
|
9
|
-
useCase: string;
|
|
10
|
-
tailwind: boolean;
|
|
11
|
-
analytics: boolean;
|
|
12
|
-
};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = path.dirname(__filename);
|
|
6
|
-
export async function createTempDir(prefix = 'test-') {
|
|
7
|
-
const tempDir = path.join(__dirname, '../../../.tmp', `${prefix}${Date.now()}`);
|
|
8
|
-
await fs.ensureDir(tempDir);
|
|
9
|
-
return tempDir;
|
|
10
|
-
}
|
|
11
|
-
export async function cleanupTempDir(dir) {
|
|
12
|
-
if (await fs.pathExists(dir)) {
|
|
13
|
-
await fs.remove(dir);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
export async function createTestFile(dir, filename, content) {
|
|
17
|
-
const filePath = path.join(dir, filename);
|
|
18
|
-
await fs.ensureDir(path.dirname(filePath));
|
|
19
|
-
await fs.writeFile(filePath, content, 'utf-8');
|
|
20
|
-
return filePath;
|
|
21
|
-
}
|
|
22
|
-
export async function readTestFile(filePath) {
|
|
23
|
-
return await fs.readFile(filePath, 'utf-8');
|
|
24
|
-
}
|
|
25
|
-
export function createMockContext(overrides = {}) {
|
|
26
|
-
return {
|
|
27
|
-
projectName: 'test-project',
|
|
28
|
-
sdk: 'mysten',
|
|
29
|
-
framework: 'react',
|
|
30
|
-
useCase: 'simple-upload',
|
|
31
|
-
tailwind: false,
|
|
32
|
-
analytics: false,
|
|
33
|
-
...overrides,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { createTempDir, cleanupTempDir, createTestFile, readTestFile, createMockContext, } from './fs-helpers.js';
|
|
2
|
-
export { useTempDirectory } from './test-hooks.js';
|
|
3
|
-
export { MOCK_PACKAGE_JSON, MOCK_TSCONFIG, MOCK_VITE_CONFIG, MOCK_README, } from './fixtures.js';
|
|
4
|
-
export { testStorageAdapterCompliance } from './adapter-compliance.js';
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { createTempDir, cleanupTempDir, createTestFile, readTestFile, createMockContext, } from './fs-helpers.js';
|
|
2
|
-
export { useTempDirectory } from './test-hooks.js';
|
|
3
|
-
export { MOCK_PACKAGE_JSON, MOCK_TSCONFIG, MOCK_VITE_CONFIG, MOCK_README, } from './fixtures.js';
|
|
4
|
-
export { testStorageAdapterCompliance } from './adapter-compliance.js';
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach } from 'vitest';
|
|
2
|
-
import { createTempDir, cleanupTempDir } from './fs-helpers.js';
|
|
3
|
-
export function useTempDirectory() {
|
|
4
|
-
let tempDir;
|
|
5
|
-
beforeEach(async () => {
|
|
6
|
-
tempDir = await createTempDir();
|
|
7
|
-
});
|
|
8
|
-
afterEach(async () => {
|
|
9
|
-
if (tempDir) {
|
|
10
|
-
await cleanupTempDir(tempDir);
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
return {
|
|
14
|
-
get dir() {
|
|
15
|
-
return tempDir;
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
}
|
package/dist/context.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/context.test.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { buildContext } from './context.js';
|
|
3
|
-
import * as detectPmModule from './utils/detect-pm.js';
|
|
4
|
-
// Mock the detectPackageManager function
|
|
5
|
-
vi.mock('./utils/detect-pm.js', () => ({
|
|
6
|
-
detectPackageManager: vi.fn(() => 'pnpm'),
|
|
7
|
-
}));
|
|
8
|
-
describe('buildContext', () => {
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
vi.clearAllMocks();
|
|
11
|
-
});
|
|
12
|
-
it('should build context from args only', () => {
|
|
13
|
-
const args = {
|
|
14
|
-
projectName: 'test-app',
|
|
15
|
-
sdk: 'mysten',
|
|
16
|
-
framework: 'react',
|
|
17
|
-
useCase: 'simple-upload',
|
|
18
|
-
analytics: true,
|
|
19
|
-
tailwind: false,
|
|
20
|
-
};
|
|
21
|
-
const context = buildContext(args, {});
|
|
22
|
-
expect(context.projectName).toBe('test-app');
|
|
23
|
-
expect(context.sdk).toBe('mysten');
|
|
24
|
-
expect(context.framework).toBe('react');
|
|
25
|
-
expect(context.useCase).toBe('simple-upload');
|
|
26
|
-
expect(context.analytics).toBe(true);
|
|
27
|
-
expect(context.tailwind).toBe(false);
|
|
28
|
-
expect(context.packageManager).toBe('pnpm');
|
|
29
|
-
expect(context.projectPath).toMatch(/test-app$/);
|
|
30
|
-
});
|
|
31
|
-
it('should build context from prompt results only', () => {
|
|
32
|
-
const promptResults = {
|
|
33
|
-
projectName: 'prompt-app',
|
|
34
|
-
sdk: 'tusky',
|
|
35
|
-
framework: 'vue',
|
|
36
|
-
useCase: 'gallery',
|
|
37
|
-
analytics: false,
|
|
38
|
-
tailwind: true,
|
|
39
|
-
};
|
|
40
|
-
const context = buildContext({}, promptResults);
|
|
41
|
-
expect(context.projectName).toBe('prompt-app');
|
|
42
|
-
expect(context.sdk).toBe('tusky');
|
|
43
|
-
expect(context.framework).toBe('vue');
|
|
44
|
-
expect(context.useCase).toBe('gallery');
|
|
45
|
-
expect(context.analytics).toBe(false);
|
|
46
|
-
expect(context.tailwind).toBe(true);
|
|
47
|
-
});
|
|
48
|
-
it('should prioritize args over prompt results', () => {
|
|
49
|
-
const args = {
|
|
50
|
-
projectName: 'args-app',
|
|
51
|
-
sdk: 'mysten',
|
|
52
|
-
};
|
|
53
|
-
const promptResults = {
|
|
54
|
-
projectName: 'prompt-app',
|
|
55
|
-
sdk: 'tusky',
|
|
56
|
-
framework: 'react',
|
|
57
|
-
useCase: 'simple-upload',
|
|
58
|
-
analytics: true,
|
|
59
|
-
tailwind: true,
|
|
60
|
-
};
|
|
61
|
-
const context = buildContext(args, promptResults);
|
|
62
|
-
expect(context.projectName).toBe('args-app');
|
|
63
|
-
expect(context.sdk).toBe('mysten');
|
|
64
|
-
expect(context.framework).toBe('react');
|
|
65
|
-
expect(context.useCase).toBe('simple-upload');
|
|
66
|
-
});
|
|
67
|
-
it('should convert analytics to boolean correctly', () => {
|
|
68
|
-
const base = { sdk: 'mysten', framework: 'react', useCase: 'simple-upload' };
|
|
69
|
-
expect(buildContext({ projectName: 'test', analytics: true, ...base }, {}).analytics).toBe(true);
|
|
70
|
-
expect(buildContext({ projectName: 'test', analytics: false, ...base }, {}).analytics).toBe(false);
|
|
71
|
-
expect(buildContext({ projectName: 'test', analytics: 1, ...base }, {}).analytics).toBe(true);
|
|
72
|
-
expect(buildContext({ projectName: 'test', analytics: 0, ...base }, {}).analytics).toBe(false);
|
|
73
|
-
expect(buildContext({ projectName: 'test', analytics: 'yes', ...base }, {}).analytics).toBe(true);
|
|
74
|
-
expect(buildContext({ projectName: 'test', analytics: '', ...base }, {}).analytics).toBe(false);
|
|
75
|
-
});
|
|
76
|
-
it('should convert tailwind to boolean correctly', () => {
|
|
77
|
-
const base = { sdk: 'mysten', framework: 'react', useCase: 'simple-upload' };
|
|
78
|
-
expect(buildContext({ projectName: 'test', tailwind: true, ...base }, {}).tailwind).toBe(true);
|
|
79
|
-
expect(buildContext({ projectName: 'test', tailwind: false, ...base }, {}).tailwind).toBe(false);
|
|
80
|
-
expect(buildContext({ projectName: 'test', tailwind: 1, ...base }, {}).tailwind).toBe(true);
|
|
81
|
-
expect(buildContext({ projectName: 'test', tailwind: 0, ...base }, {}).tailwind).toBe(false);
|
|
82
|
-
});
|
|
83
|
-
it('should call detectPackageManager', () => {
|
|
84
|
-
buildContext({ projectName: 'test', sdk: 'mysten', framework: 'react', useCase: 'simple-upload' }, {});
|
|
85
|
-
expect(detectPmModule.detectPackageManager).toHaveBeenCalled();
|
|
86
|
-
});
|
|
87
|
-
it('should generate absolute projectPath', () => {
|
|
88
|
-
const context = buildContext({ projectName: 'test-app', sdk: 'mysten', framework: 'react', useCase: 'simple-upload' }, {});
|
|
89
|
-
expect(context.projectPath).toContain('test-app');
|
|
90
|
-
expect(context.projectPath.length).toBeGreaterThan('test-app'.length);
|
|
91
|
-
});
|
|
92
|
-
it('should handle partial args and prompts', () => {
|
|
93
|
-
const context = buildContext({ projectName: 'partial' }, { sdk: 'hibernuts', framework: 'react', useCase: 'simple-upload' });
|
|
94
|
-
expect(context.projectName).toBe('partial');
|
|
95
|
-
expect(context.sdk).toBe('hibernuts');
|
|
96
|
-
expect(context.framework).toBe('react');
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { generateProject } from './index.js';
|
|
3
|
-
import fs from 'fs-extra';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
describe('generateProject integration', () => {
|
|
9
|
-
const testOutputDir = path.join(__dirname, '../../test-output');
|
|
10
|
-
const templateDir = path.join(__dirname, '../../templates');
|
|
11
|
-
beforeEach(async () => {
|
|
12
|
-
// Clean up test output directory
|
|
13
|
-
await fs.remove(testOutputDir);
|
|
14
|
-
});
|
|
15
|
-
afterEach(async () => {
|
|
16
|
-
// Clean up after tests
|
|
17
|
-
await fs.remove(testOutputDir);
|
|
18
|
-
});
|
|
19
|
-
it('should generate project with all layers', async () => {
|
|
20
|
-
const context = {
|
|
21
|
-
projectName: 'test-walrus-app',
|
|
22
|
-
projectPath: path.join(testOutputDir, 'test-walrus-app'),
|
|
23
|
-
sdk: 'mysten',
|
|
24
|
-
framework: 'react',
|
|
25
|
-
useCase: 'simple-upload',
|
|
26
|
-
analytics: false,
|
|
27
|
-
tailwind: false,
|
|
28
|
-
packageManager: 'pnpm',
|
|
29
|
-
};
|
|
30
|
-
const result = await generateProject({
|
|
31
|
-
context,
|
|
32
|
-
templateDir,
|
|
33
|
-
targetDir: context.projectPath,
|
|
34
|
-
});
|
|
35
|
-
expect(result.success).toBe(true);
|
|
36
|
-
expect(result.filesCreated).toBeGreaterThan(0);
|
|
37
|
-
// Verify directory was created
|
|
38
|
-
const exists = await fs.pathExists(context.projectPath);
|
|
39
|
-
expect(exists).toBe(true);
|
|
40
|
-
// Verify package.json was merged
|
|
41
|
-
const pkgJsonPath = path.join(context.projectPath, 'package.json');
|
|
42
|
-
const pkgJsonExists = await fs.pathExists(pkgJsonPath);
|
|
43
|
-
expect(pkgJsonExists).toBe(true);
|
|
44
|
-
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
45
|
-
expect(pkgJson.name).toBe('test-walrus-app');
|
|
46
|
-
expect(pkgJson.dependencies['@mysten/walrus']).toBeDefined();
|
|
47
|
-
expect(pkgJson.dependencies['react']).toBeDefined();
|
|
48
|
-
expect(pkgJson.scripts.dev).toBeDefined();
|
|
49
|
-
expect(pkgJson.scripts.upload).toBeDefined();
|
|
50
|
-
});
|
|
51
|
-
it('should transform template variables', async () => {
|
|
52
|
-
const context = {
|
|
53
|
-
projectName: 'my-custom-app',
|
|
54
|
-
projectPath: path.join(testOutputDir, 'my-custom-app'),
|
|
55
|
-
sdk: 'mysten',
|
|
56
|
-
framework: 'react',
|
|
57
|
-
useCase: 'simple-upload',
|
|
58
|
-
analytics: false,
|
|
59
|
-
tailwind: false,
|
|
60
|
-
packageManager: 'npm',
|
|
61
|
-
};
|
|
62
|
-
const result = await generateProject({
|
|
63
|
-
context,
|
|
64
|
-
templateDir,
|
|
65
|
-
targetDir: context.projectPath,
|
|
66
|
-
});
|
|
67
|
-
expect(result.success).toBe(true);
|
|
68
|
-
// Check README transformation
|
|
69
|
-
const readmePath = path.join(context.projectPath, 'README.md');
|
|
70
|
-
const readmeExists = await fs.pathExists(readmePath);
|
|
71
|
-
expect(readmeExists).toBe(true);
|
|
72
|
-
const readmeContent = await fs.readFile(readmePath, 'utf-8');
|
|
73
|
-
expect(readmeContent).toContain('my-custom-app');
|
|
74
|
-
expect(readmeContent).toContain('react');
|
|
75
|
-
expect(readmeContent).not.toContain('{{projectName}}');
|
|
76
|
-
expect(readmeContent).not.toContain('{{framework}}');
|
|
77
|
-
});
|
|
78
|
-
it('should fail for non-empty directory', async () => {
|
|
79
|
-
const context = {
|
|
80
|
-
projectName: 'test-app',
|
|
81
|
-
projectPath: path.join(testOutputDir, 'non-empty'),
|
|
82
|
-
sdk: 'mysten',
|
|
83
|
-
framework: 'react',
|
|
84
|
-
useCase: 'simple-upload',
|
|
85
|
-
analytics: false,
|
|
86
|
-
tailwind: false,
|
|
87
|
-
packageManager: 'pnpm',
|
|
88
|
-
};
|
|
89
|
-
// Create non-empty directory
|
|
90
|
-
await fs.ensureDir(context.projectPath);
|
|
91
|
-
await fs.writeFile(path.join(context.projectPath, 'existing.txt'), 'content');
|
|
92
|
-
const result = await generateProject({
|
|
93
|
-
context,
|
|
94
|
-
templateDir,
|
|
95
|
-
targetDir: context.projectPath,
|
|
96
|
-
});
|
|
97
|
-
expect(result.success).toBe(false);
|
|
98
|
-
expect(result.error).toBeDefined();
|
|
99
|
-
expect(result.error?.message).toContain('not empty');
|
|
100
|
-
});
|
|
101
|
-
it('should rollback on error', async () => {
|
|
102
|
-
const context = {
|
|
103
|
-
projectName: 'test-app',
|
|
104
|
-
projectPath: path.join(testOutputDir, 'rollback-test'),
|
|
105
|
-
sdk: 'mysten',
|
|
106
|
-
framework: 'react',
|
|
107
|
-
useCase: 'simple-upload',
|
|
108
|
-
analytics: false,
|
|
109
|
-
tailwind: false,
|
|
110
|
-
packageManager: 'pnpm',
|
|
111
|
-
};
|
|
112
|
-
// This will succeed because templates exist
|
|
113
|
-
const result = await generateProject({
|
|
114
|
-
context,
|
|
115
|
-
templateDir,
|
|
116
|
-
targetDir: context.projectPath,
|
|
117
|
-
});
|
|
118
|
-
// Just verify it works for now - rollback is tested via non-empty dir test
|
|
119
|
-
expect(result.success).toBe(true);
|
|
120
|
-
});
|
|
121
|
-
it('should handle dry run mode', async () => {
|
|
122
|
-
const context = {
|
|
123
|
-
projectName: 'dry-run-test',
|
|
124
|
-
projectPath: path.join(testOutputDir, 'dry-run'),
|
|
125
|
-
sdk: 'mysten',
|
|
126
|
-
framework: 'react',
|
|
127
|
-
useCase: 'simple-upload',
|
|
128
|
-
analytics: false,
|
|
129
|
-
tailwind: false,
|
|
130
|
-
packageManager: 'pnpm',
|
|
131
|
-
};
|
|
132
|
-
const result = await generateProject({
|
|
133
|
-
context,
|
|
134
|
-
templateDir,
|
|
135
|
-
targetDir: context.projectPath,
|
|
136
|
-
dryRun: true,
|
|
137
|
-
});
|
|
138
|
-
expect(result.success).toBe(true);
|
|
139
|
-
// Verify no files were actually created
|
|
140
|
-
const exists = await fs.pathExists(context.projectPath);
|
|
141
|
-
expect(exists).toBe(false);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { resolveLayers } from './layers.js';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
describe('resolveLayers', () => {
|
|
5
|
-
it('should resolve base layers in correct priority order', () => {
|
|
6
|
-
const context = {
|
|
7
|
-
projectName: 'test-app',
|
|
8
|
-
projectPath: '/path/to/test-app',
|
|
9
|
-
sdk: 'mysten',
|
|
10
|
-
framework: 'react',
|
|
11
|
-
useCase: 'simple-upload',
|
|
12
|
-
analytics: false,
|
|
13
|
-
tailwind: false,
|
|
14
|
-
packageManager: 'pnpm',
|
|
15
|
-
};
|
|
16
|
-
const layers = resolveLayers(context);
|
|
17
|
-
expect(layers.length).toBe(4);
|
|
18
|
-
expect(layers[0].name).toBe('base');
|
|
19
|
-
expect(layers[0].priority).toBe(1);
|
|
20
|
-
expect(layers[1].name).toBe('sdk-mysten');
|
|
21
|
-
expect(layers[1].priority).toBe(2);
|
|
22
|
-
expect(layers[2].name).toBe('react');
|
|
23
|
-
expect(layers[2].priority).toBe(3);
|
|
24
|
-
expect(layers[3].name).toBe('simple-upload');
|
|
25
|
-
expect(layers[3].priority).toBe(4);
|
|
26
|
-
});
|
|
27
|
-
it('should include tailwind layer when enabled', () => {
|
|
28
|
-
const context = {
|
|
29
|
-
projectName: 'test-app',
|
|
30
|
-
projectPath: '/path/to/test-app',
|
|
31
|
-
sdk: 'mysten',
|
|
32
|
-
framework: 'react',
|
|
33
|
-
useCase: 'gallery',
|
|
34
|
-
analytics: false,
|
|
35
|
-
tailwind: true,
|
|
36
|
-
packageManager: 'pnpm',
|
|
37
|
-
};
|
|
38
|
-
const layers = resolveLayers(context);
|
|
39
|
-
expect(layers.length).toBe(5);
|
|
40
|
-
expect(layers[4].name).toBe('tailwind');
|
|
41
|
-
expect(layers[4].priority).toBe(5);
|
|
42
|
-
});
|
|
43
|
-
it('should include analytics layer when enabled', () => {
|
|
44
|
-
const context = {
|
|
45
|
-
projectName: 'test-app',
|
|
46
|
-
projectPath: '/path/to/test-app',
|
|
47
|
-
sdk: 'mysten',
|
|
48
|
-
framework: 'vue',
|
|
49
|
-
useCase: 'defi-nft',
|
|
50
|
-
analytics: true,
|
|
51
|
-
tailwind: false,
|
|
52
|
-
packageManager: 'npm',
|
|
53
|
-
};
|
|
54
|
-
const layers = resolveLayers(context);
|
|
55
|
-
expect(layers.length).toBe(5);
|
|
56
|
-
expect(layers[4].name).toBe('analytics');
|
|
57
|
-
expect(layers[4].priority).toBe(6);
|
|
58
|
-
});
|
|
59
|
-
it('should include both optional layers when enabled', () => {
|
|
60
|
-
const context = {
|
|
61
|
-
projectName: 'test-app',
|
|
62
|
-
projectPath: '/path/to/test-app',
|
|
63
|
-
sdk: 'mysten',
|
|
64
|
-
framework: 'plain-ts',
|
|
65
|
-
useCase: 'simple-upload',
|
|
66
|
-
analytics: true,
|
|
67
|
-
tailwind: true,
|
|
68
|
-
packageManager: 'yarn',
|
|
69
|
-
};
|
|
70
|
-
const layers = resolveLayers(context);
|
|
71
|
-
expect(layers.length).toBe(6);
|
|
72
|
-
expect(layers[4].name).toBe('tailwind');
|
|
73
|
-
expect(layers[5].name).toBe('analytics');
|
|
74
|
-
});
|
|
75
|
-
it('should use correct template paths', () => {
|
|
76
|
-
const context = {
|
|
77
|
-
projectName: 'test-app',
|
|
78
|
-
projectPath: '/path/to/test-app',
|
|
79
|
-
sdk: 'mysten',
|
|
80
|
-
framework: 'react',
|
|
81
|
-
useCase: 'simple-upload',
|
|
82
|
-
analytics: false,
|
|
83
|
-
tailwind: false,
|
|
84
|
-
packageManager: 'pnpm',
|
|
85
|
-
};
|
|
86
|
-
const layers = resolveLayers(context);
|
|
87
|
-
layers.forEach((layer) => {
|
|
88
|
-
expect(layer.path).toContain('templates');
|
|
89
|
-
expect(path.isAbsolute(layer.path)).toBe(true);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { deepMerge } from './merge.js';
|
|
3
|
-
describe('deepMerge', () => {
|
|
4
|
-
it('should merge objects recursively', () => {
|
|
5
|
-
const target = { a: 1, b: { c: 2 } };
|
|
6
|
-
const source = { b: { d: 3 }, e: 4 };
|
|
7
|
-
const result = deepMerge(target, source);
|
|
8
|
-
expect(result).toEqual({ a: 1, b: { c: 2, d: 3 }, e: 4 });
|
|
9
|
-
});
|
|
10
|
-
it('should replace arrays entirely', () => {
|
|
11
|
-
const target = { arr: [1, 2, 3] };
|
|
12
|
-
const source = { arr: [4, 5] };
|
|
13
|
-
const result = deepMerge(target, source);
|
|
14
|
-
expect(result).toEqual({ arr: [4, 5] });
|
|
15
|
-
});
|
|
16
|
-
it('should replace primitives', () => {
|
|
17
|
-
const target = { a: 1, b: 'old' };
|
|
18
|
-
const source = { a: 2, b: 'new' };
|
|
19
|
-
const result = deepMerge(target, source);
|
|
20
|
-
expect(result).toEqual({ a: 2, b: 'new' });
|
|
21
|
-
});
|
|
22
|
-
it('should handle null and undefined', () => {
|
|
23
|
-
// undefined: skip (return target)
|
|
24
|
-
const target1 = { a: 1 };
|
|
25
|
-
const source1 = undefined;
|
|
26
|
-
const result1 = deepMerge(target1, source1);
|
|
27
|
-
expect(result1).toEqual({ a: 1 });
|
|
28
|
-
// null: override (later layers win)
|
|
29
|
-
const target2 = { a: 1 };
|
|
30
|
-
const source2 = null;
|
|
31
|
-
const result2 = deepMerge(target2, source2);
|
|
32
|
-
expect(result2).toBeNull();
|
|
33
|
-
});
|
|
34
|
-
it('should merge nested objects deeply', () => {
|
|
35
|
-
const target = {
|
|
36
|
-
dependencies: { react: '^18.0.0' },
|
|
37
|
-
scripts: { build: 'tsc' },
|
|
38
|
-
};
|
|
39
|
-
const source = {
|
|
40
|
-
dependencies: { 'react-dom': '^18.0.0' },
|
|
41
|
-
scripts: { dev: 'vite' },
|
|
42
|
-
};
|
|
43
|
-
const result = deepMerge(target, source);
|
|
44
|
-
expect(result).toEqual({
|
|
45
|
-
dependencies: { react: '^18.0.0', 'react-dom': '^18.0.0' },
|
|
46
|
-
scripts: { build: 'tsc', dev: 'vite' },
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
it('should handle package.json-like merge', () => {
|
|
50
|
-
const base = {
|
|
51
|
-
name: 'base',
|
|
52
|
-
version: '1.0.0',
|
|
53
|
-
dependencies: {
|
|
54
|
-
commander: '^11.0.0',
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
const overlay = {
|
|
58
|
-
name: 'overlay',
|
|
59
|
-
dependencies: {
|
|
60
|
-
react: '^18.0.0',
|
|
61
|
-
},
|
|
62
|
-
devDependencies: {
|
|
63
|
-
typescript: '^5.0.0',
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
const result = deepMerge(base, overlay);
|
|
67
|
-
expect(result).toEqual({
|
|
68
|
-
name: 'overlay',
|
|
69
|
-
version: '1.0.0',
|
|
70
|
-
dependencies: {
|
|
71
|
-
commander: '^11.0.0',
|
|
72
|
-
react: '^18.0.0',
|
|
73
|
-
},
|
|
74
|
-
devDependencies: {
|
|
75
|
-
typescript: '^5.0.0',
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { transformString, buildVariables } from './transform.js';
|
|
3
|
-
describe('transformString', () => {
|
|
4
|
-
const vars = {
|
|
5
|
-
projectName: 'my-walrus-app',
|
|
6
|
-
sdkName: 'mysten',
|
|
7
|
-
framework: 'react',
|
|
8
|
-
useCase: 'simple-upload',
|
|
9
|
-
};
|
|
10
|
-
it('should replace projectName placeholder', () => {
|
|
11
|
-
const input = '# {{projectName}}';
|
|
12
|
-
const result = transformString(input, vars);
|
|
13
|
-
expect(result).toBe('# my-walrus-app');
|
|
14
|
-
});
|
|
15
|
-
it('should replace multiple placeholders', () => {
|
|
16
|
-
const input = 'Project: {{projectName}}, SDK: {{sdkName}}, Framework: {{framework}}';
|
|
17
|
-
const result = transformString(input, vars);
|
|
18
|
-
expect(result).toBe('Project: my-walrus-app, SDK: mysten, Framework: react');
|
|
19
|
-
});
|
|
20
|
-
it('should handle multiple occurrences of same placeholder', () => {
|
|
21
|
-
const input = '{{projectName}} is a {{projectName}} project';
|
|
22
|
-
const result = transformString(input, vars);
|
|
23
|
-
expect(result).toBe('my-walrus-app is a my-walrus-app project');
|
|
24
|
-
});
|
|
25
|
-
it('should not modify text without placeholders', () => {
|
|
26
|
-
const input = 'This is plain text';
|
|
27
|
-
const result = transformString(input, vars);
|
|
28
|
-
expect(result).toBe('This is plain text');
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe('buildVariables', () => {
|
|
32
|
-
it('should extract variables from context', () => {
|
|
33
|
-
const context = {
|
|
34
|
-
projectName: 'test-app',
|
|
35
|
-
projectPath: '/path/to/test-app',
|
|
36
|
-
sdk: 'mysten',
|
|
37
|
-
framework: 'react',
|
|
38
|
-
useCase: 'gallery',
|
|
39
|
-
analytics: false,
|
|
40
|
-
tailwind: true,
|
|
41
|
-
packageManager: 'pnpm',
|
|
42
|
-
};
|
|
43
|
-
const vars = buildVariables(context);
|
|
44
|
-
expect(vars).toEqual({
|
|
45
|
-
projectName: 'test-app',
|
|
46
|
-
sdkName: 'mysten',
|
|
47
|
-
framework: 'react',
|
|
48
|
-
useCase: 'gallery',
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
});
|
package/dist/matrix.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/matrix.test.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { COMPATIBILITY_MATRIX, SDK_METADATA } from './matrix.js';
|
|
3
|
-
describe('COMPATIBILITY_MATRIX', () => {
|
|
4
|
-
it('should have entries for all SDKs', () => {
|
|
5
|
-
expect(COMPATIBILITY_MATRIX).toHaveProperty('mysten');
|
|
6
|
-
expect(COMPATIBILITY_MATRIX).toHaveProperty('tusky');
|
|
7
|
-
expect(COMPATIBILITY_MATRIX).toHaveProperty('hibernuts');
|
|
8
|
-
});
|
|
9
|
-
it('should have frameworks and useCases for each SDK', () => {
|
|
10
|
-
Object.values(COMPATIBILITY_MATRIX).forEach((sdk) => {
|
|
11
|
-
expect(sdk).toHaveProperty('frameworks');
|
|
12
|
-
expect(sdk).toHaveProperty('useCases');
|
|
13
|
-
expect(Array.isArray(sdk.frameworks)).toBe(true);
|
|
14
|
-
expect(Array.isArray(sdk.useCases)).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
it('should have mysten support all frameworks', () => {
|
|
18
|
-
expect(COMPATIBILITY_MATRIX.mysten.frameworks).toEqual(['react', 'vue', 'plain-ts']);
|
|
19
|
-
});
|
|
20
|
-
it('should have mysten support all use cases', () => {
|
|
21
|
-
expect(COMPATIBILITY_MATRIX.mysten.useCases).toEqual(['simple-upload', 'gallery', 'defi-nft']);
|
|
22
|
-
});
|
|
23
|
-
it('should have tusky support limited use cases', () => {
|
|
24
|
-
expect(COMPATIBILITY_MATRIX.tusky.useCases).toEqual(['simple-upload', 'gallery']);
|
|
25
|
-
expect(COMPATIBILITY_MATRIX.tusky.useCases).not.toContain('defi-nft');
|
|
26
|
-
});
|
|
27
|
-
it('should have hibernuts with most restricted support', () => {
|
|
28
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.frameworks).toEqual(['react', 'plain-ts']);
|
|
29
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.useCases).toEqual(['simple-upload']);
|
|
30
|
-
});
|
|
31
|
-
it('should not have hibernuts support vue', () => {
|
|
32
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.frameworks).not.toContain('vue');
|
|
33
|
-
});
|
|
34
|
-
it('should not have hibernuts support gallery or defi-nft', () => {
|
|
35
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.useCases).not.toContain('gallery');
|
|
36
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.useCases).not.toContain('defi-nft');
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe('SDK_METADATA', () => {
|
|
40
|
-
it('should have metadata for all SDKs', () => {
|
|
41
|
-
expect(SDK_METADATA).toHaveProperty('mysten');
|
|
42
|
-
expect(SDK_METADATA).toHaveProperty('tusky');
|
|
43
|
-
expect(SDK_METADATA).toHaveProperty('hibernuts');
|
|
44
|
-
});
|
|
45
|
-
it('should have name, description, and docs for each SDK', () => {
|
|
46
|
-
Object.values(SDK_METADATA).forEach((metadata) => {
|
|
47
|
-
expect(metadata).toHaveProperty('name');
|
|
48
|
-
expect(metadata).toHaveProperty('description');
|
|
49
|
-
expect(metadata).toHaveProperty('docs');
|
|
50
|
-
expect(typeof metadata.name).toBe('string');
|
|
51
|
-
expect(typeof metadata.description).toBe('string');
|
|
52
|
-
expect(typeof metadata.docs).toBe('string');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
it('should have valid package names', () => {
|
|
56
|
-
expect(SDK_METADATA.mysten.name).toBe('@mysten/walrus');
|
|
57
|
-
expect(SDK_METADATA.tusky.name).toBe('@tusky-io/ts-sdk');
|
|
58
|
-
expect(SDK_METADATA.hibernuts.name).toBe('@hibernuts/walrus-sdk');
|
|
59
|
-
});
|
|
60
|
-
it('should have URLs in docs field', () => {
|
|
61
|
-
Object.values(SDK_METADATA).forEach((metadata) => {
|
|
62
|
-
expect(metadata.docs).toMatch(/^https?:\/\//);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
it('should have meaningful descriptions', () => {
|
|
66
|
-
expect(SDK_METADATA.mysten.description.length).toBeGreaterThan(10);
|
|
67
|
-
expect(SDK_METADATA.tusky.description.length).toBeGreaterThan(10);
|
|
68
|
-
expect(SDK_METADATA.hibernuts.description.length).toBeGreaterThan(10);
|
|
69
|
-
});
|
|
70
|
-
});
|
package/dist/types.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types.test.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
describe('Type definitions', () => {
|
|
3
|
-
it('should allow valid SDK values', () => {
|
|
4
|
-
const sdks = ['mysten', 'tusky', 'hibernuts'];
|
|
5
|
-
expect(sdks).toHaveLength(3);
|
|
6
|
-
});
|
|
7
|
-
it('should allow valid Framework values', () => {
|
|
8
|
-
const frameworks = ['react', 'vue', 'plain-ts'];
|
|
9
|
-
expect(frameworks).toHaveLength(3);
|
|
10
|
-
});
|
|
11
|
-
it('should allow valid UseCase values', () => {
|
|
12
|
-
const useCases = ['simple-upload', 'gallery', 'defi-nft'];
|
|
13
|
-
expect(useCases).toHaveLength(3);
|
|
14
|
-
});
|
|
15
|
-
it('should allow valid PackageManager values', () => {
|
|
16
|
-
const pms = ['npm', 'pnpm', 'yarn', 'bun'];
|
|
17
|
-
expect(pms).toHaveLength(4);
|
|
18
|
-
});
|
|
19
|
-
it('should create valid Context object', () => {
|
|
20
|
-
const context = {
|
|
21
|
-
projectName: 'test-app',
|
|
22
|
-
projectPath: '/path/to/test-app',
|
|
23
|
-
sdk: 'mysten',
|
|
24
|
-
framework: 'react',
|
|
25
|
-
useCase: 'simple-upload',
|
|
26
|
-
analytics: false,
|
|
27
|
-
tailwind: true,
|
|
28
|
-
packageManager: 'pnpm',
|
|
29
|
-
};
|
|
30
|
-
expect(context.projectName).toBe('test-app');
|
|
31
|
-
expect(context.sdk).toBe('mysten');
|
|
32
|
-
expect(context.framework).toBe('react');
|
|
33
|
-
expect(context.useCase).toBe('simple-upload');
|
|
34
|
-
expect(context.analytics).toBe(false);
|
|
35
|
-
expect(context.tailwind).toBe(true);
|
|
36
|
-
expect(context.packageManager).toBe('pnpm');
|
|
37
|
-
});
|
|
38
|
-
it('should create valid ValidationResult with success', () => {
|
|
39
|
-
const result = {
|
|
40
|
-
valid: true,
|
|
41
|
-
};
|
|
42
|
-
expect(result.valid).toBe(true);
|
|
43
|
-
expect(result.error).toBeUndefined();
|
|
44
|
-
expect(result.suggestion).toBeUndefined();
|
|
45
|
-
});
|
|
46
|
-
it('should create valid ValidationResult with error', () => {
|
|
47
|
-
const result = {
|
|
48
|
-
valid: false,
|
|
49
|
-
error: 'Something went wrong',
|
|
50
|
-
suggestion: 'Try this instead',
|
|
51
|
-
};
|
|
52
|
-
expect(result.valid).toBe(false);
|
|
53
|
-
expect(result.error).toBe('Something went wrong');
|
|
54
|
-
expect(result.suggestion).toBe('Try this instead');
|
|
55
|
-
});
|
|
56
|
-
it('should allow ValidationResult with error but no suggestion', () => {
|
|
57
|
-
const result = {
|
|
58
|
-
valid: false,
|
|
59
|
-
error: 'Error without suggestion',
|
|
60
|
-
};
|
|
61
|
-
expect(result.valid).toBe(false);
|
|
62
|
-
expect(result.error).toBe('Error without suggestion');
|
|
63
|
-
expect(result.suggestion).toBeUndefined();
|
|
64
|
-
});
|
|
65
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { detectPackageManager } from './detect-pm.js';
|
|
3
|
-
describe('detectPackageManager', () => {
|
|
4
|
-
let originalUserAgent;
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
originalUserAgent = process.env.npm_config_user_agent;
|
|
7
|
-
});
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
if (originalUserAgent === undefined) {
|
|
10
|
-
delete process.env.npm_config_user_agent;
|
|
11
|
-
}
|
|
12
|
-
else {
|
|
13
|
-
process.env.npm_config_user_agent = originalUserAgent;
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
it('should detect pnpm from user agent', () => {
|
|
17
|
-
process.env.npm_config_user_agent = 'pnpm/8.6.0 npm/? node/v18.16.0 linux x64';
|
|
18
|
-
expect(detectPackageManager()).toBe('pnpm');
|
|
19
|
-
});
|
|
20
|
-
it('should detect yarn from user agent', () => {
|
|
21
|
-
process.env.npm_config_user_agent = 'yarn/1.22.19 npm/? node/v18.16.0 linux x64';
|
|
22
|
-
expect(detectPackageManager()).toBe('yarn');
|
|
23
|
-
});
|
|
24
|
-
it('should detect bun from user agent', () => {
|
|
25
|
-
process.env.npm_config_user_agent = 'bun/1.0.0';
|
|
26
|
-
expect(detectPackageManager()).toBe('bun');
|
|
27
|
-
});
|
|
28
|
-
it('should default to npm when no user agent', () => {
|
|
29
|
-
delete process.env.npm_config_user_agent;
|
|
30
|
-
expect(detectPackageManager()).toBe('npm');
|
|
31
|
-
});
|
|
32
|
-
it('should default to npm for unknown user agent', () => {
|
|
33
|
-
process.env.npm_config_user_agent = 'unknown/1.0.0';
|
|
34
|
-
expect(detectPackageManager()).toBe('npm');
|
|
35
|
-
});
|
|
36
|
-
it('should handle user agent with pnpm in different positions', () => {
|
|
37
|
-
process.env.npm_config_user_agent = 'npm/? pnpm/8.0.0 node/v18.0.0';
|
|
38
|
-
expect(detectPackageManager()).toBe('pnpm');
|
|
39
|
-
});
|
|
40
|
-
it('should prioritize pnpm over yarn when both present', () => {
|
|
41
|
-
process.env.npm_config_user_agent = 'pnpm/8.0.0 yarn/1.22.0';
|
|
42
|
-
expect(detectPackageManager()).toBe('pnpm');
|
|
43
|
-
});
|
|
44
|
-
it('should prioritize yarn over bun when both present', () => {
|
|
45
|
-
process.env.npm_config_user_agent = 'yarn/1.22.0 bun/1.0.0';
|
|
46
|
-
expect(detectPackageManager()).toBe('yarn');
|
|
47
|
-
});
|
|
48
|
-
it('should handle empty string user agent', () => {
|
|
49
|
-
process.env.npm_config_user_agent = '';
|
|
50
|
-
expect(detectPackageManager()).toBe('npm');
|
|
51
|
-
});
|
|
52
|
-
});
|
package/dist/validator.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/validator.test.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { validateProjectName, validateContext } from './validator.js';
|
|
3
|
-
describe('validateProjectName', () => {
|
|
4
|
-
it('should accept valid project names', () => {
|
|
5
|
-
expect(validateProjectName('my-project')).toBe(true);
|
|
6
|
-
expect(validateProjectName('app123')).toBe(true);
|
|
7
|
-
expect(validateProjectName('walrus-app-v2')).toBe(true);
|
|
8
|
-
});
|
|
9
|
-
it('should reject names with path traversal', () => {
|
|
10
|
-
expect(validateProjectName('../my-project')).toContain('path separators');
|
|
11
|
-
expect(validateProjectName('../../escape')).toContain('path separators');
|
|
12
|
-
expect(validateProjectName('test/../bad')).toContain('path separators');
|
|
13
|
-
});
|
|
14
|
-
it('should reject names with forward slashes', () => {
|
|
15
|
-
expect(validateProjectName('my/project')).toContain('path separators');
|
|
16
|
-
expect(validateProjectName('/absolute/path')).toContain('path separators');
|
|
17
|
-
});
|
|
18
|
-
it('should reject names with backslashes', () => {
|
|
19
|
-
expect(validateProjectName('my\\project')).toContain('path separators');
|
|
20
|
-
expect(validateProjectName('C:\\Windows\\path')).toContain('path separators');
|
|
21
|
-
});
|
|
22
|
-
it('should reject absolute paths', () => {
|
|
23
|
-
expect(validateProjectName('/usr/local/bin')).toContain('path');
|
|
24
|
-
expect(validateProjectName('C:\\Program Files')).toContain('path');
|
|
25
|
-
});
|
|
26
|
-
it('should reject names with uppercase letters', () => {
|
|
27
|
-
expect(validateProjectName('MyProject')).toContain('lowercase');
|
|
28
|
-
expect(validateProjectName('TEST')).toContain('lowercase');
|
|
29
|
-
});
|
|
30
|
-
it('should reject names with special characters', () => {
|
|
31
|
-
expect(validateProjectName('my_project')).toContain('lowercase');
|
|
32
|
-
expect(validateProjectName('my.project')).toContain('lowercase');
|
|
33
|
-
expect(validateProjectName('my@project')).toContain('lowercase');
|
|
34
|
-
expect(validateProjectName('my project')).toContain('lowercase');
|
|
35
|
-
});
|
|
36
|
-
it('should reject names starting with hyphen', () => {
|
|
37
|
-
expect(validateProjectName('-myproject')).toContain('hyphen');
|
|
38
|
-
});
|
|
39
|
-
it('should reject names ending with hyphen', () => {
|
|
40
|
-
expect(validateProjectName('myproject-')).toContain('hyphen');
|
|
41
|
-
});
|
|
42
|
-
it('should accept names with hyphens in middle', () => {
|
|
43
|
-
expect(validateProjectName('my-awesome-project')).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
it('should accept numbers in names', () => {
|
|
46
|
-
expect(validateProjectName('app123')).toBe(true);
|
|
47
|
-
expect(validateProjectName('2024-project')).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
describe('validateContext', () => {
|
|
51
|
-
const createContext = (overrides = {}) => ({
|
|
52
|
-
projectName: 'test-app',
|
|
53
|
-
projectPath: '/path/to/test-app',
|
|
54
|
-
sdk: 'mysten',
|
|
55
|
-
framework: 'react',
|
|
56
|
-
useCase: 'simple-upload',
|
|
57
|
-
analytics: false,
|
|
58
|
-
tailwind: true,
|
|
59
|
-
packageManager: 'pnpm',
|
|
60
|
-
...overrides,
|
|
61
|
-
});
|
|
62
|
-
it('should validate compatible sdk and framework combinations', () => {
|
|
63
|
-
expect(validateContext(createContext({ sdk: 'mysten', framework: 'react' }))).toEqual({ valid: true });
|
|
64
|
-
expect(validateContext(createContext({ sdk: 'mysten', framework: 'vue' }))).toEqual({ valid: true });
|
|
65
|
-
expect(validateContext(createContext({ sdk: 'mysten', framework: 'plain-ts' }))).toEqual({ valid: true });
|
|
66
|
-
expect(validateContext(createContext({ sdk: 'tusky', framework: 'react' }))).toEqual({ valid: true });
|
|
67
|
-
expect(validateContext(createContext({ sdk: 'hibernuts', framework: 'react' }))).toEqual({ valid: true });
|
|
68
|
-
});
|
|
69
|
-
it('should reject incompatible sdk and framework combinations', () => {
|
|
70
|
-
const result = validateContext(createContext({ sdk: 'hibernuts', framework: 'vue' }));
|
|
71
|
-
expect(result.valid).toBe(false);
|
|
72
|
-
expect(result.error).toContain('incompatible');
|
|
73
|
-
expect(result.suggestion).toContain('Compatible frameworks');
|
|
74
|
-
});
|
|
75
|
-
it('should validate compatible sdk and useCase combinations', () => {
|
|
76
|
-
expect(validateContext(createContext({ sdk: 'mysten', useCase: 'simple-upload' }))).toEqual({ valid: true });
|
|
77
|
-
expect(validateContext(createContext({ sdk: 'mysten', useCase: 'gallery' }))).toEqual({ valid: true });
|
|
78
|
-
expect(validateContext(createContext({ sdk: 'mysten', useCase: 'defi-nft' }))).toEqual({ valid: true });
|
|
79
|
-
expect(validateContext(createContext({ sdk: 'tusky', useCase: 'simple-upload' }))).toEqual({ valid: true });
|
|
80
|
-
});
|
|
81
|
-
it('should reject incompatible sdk and useCase combinations', () => {
|
|
82
|
-
const result = validateContext(createContext({ sdk: 'tusky', useCase: 'defi-nft' }));
|
|
83
|
-
expect(result.valid).toBe(false);
|
|
84
|
-
expect(result.error).toContain('does not support');
|
|
85
|
-
expect(result.suggestion).toContain('Supported use cases');
|
|
86
|
-
});
|
|
87
|
-
it('should reject hibernuts with gallery use case', () => {
|
|
88
|
-
const result = validateContext(createContext({ sdk: 'hibernuts', useCase: 'gallery' }));
|
|
89
|
-
expect(result.valid).toBe(false);
|
|
90
|
-
expect(result.error).toBeDefined();
|
|
91
|
-
});
|
|
92
|
-
it('should provide helpful suggestions in error messages', () => {
|
|
93
|
-
const result = validateContext(createContext({ sdk: 'hibernuts', framework: 'vue' }));
|
|
94
|
-
expect(result.suggestion).toMatch(/react|plain-ts/);
|
|
95
|
-
});
|
|
96
|
-
});
|