@swell/cli 2.2.0 → 2.2.1
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/commands/app/dev.js +5 -3
- package/dist/commands/create/content.js +1 -1
- package/dist/commands/create/index.js +5 -1
- package/dist/commands/create/tests.d.ts +15 -0
- package/dist/commands/create/tests.js +79 -0
- package/dist/commands/inspect/content.js +1 -1
- package/dist/lib/apps/index.js +1 -0
- package/dist/lib/create/tests/templates/env-dts.d.ts +1 -0
- package/dist/lib/create/tests/templates/env-dts.js +14 -0
- package/dist/lib/create/tests/templates/index.d.ts +8 -0
- package/dist/lib/create/tests/templates/index.js +8 -0
- package/dist/lib/create/tests/templates/integration-test.d.ts +1 -0
- package/dist/lib/create/tests/templates/integration-test.js +19 -0
- package/dist/lib/create/tests/templates/mock-request.d.ts +2 -0
- package/dist/lib/create/tests/templates/mock-request.js +112 -0
- package/dist/lib/create/tests/templates/setup-globals.d.ts +1 -0
- package/dist/lib/create/tests/templates/setup-globals.js +23 -0
- package/dist/lib/create/tests/templates/swell-client.d.ts +1 -0
- package/dist/lib/create/tests/templates/swell-client.js +126 -0
- package/dist/lib/create/tests/templates/tsconfig.d.ts +1 -0
- package/dist/lib/create/tests/templates/tsconfig.js +15 -0
- package/dist/lib/create/tests/templates/unit-test.d.ts +1 -0
- package/dist/lib/create/tests/templates/unit-test.js +42 -0
- package/dist/lib/create/tests/templates/vitest-config.d.ts +2 -0
- package/dist/lib/create/tests/templates/vitest-config.js +121 -0
- package/dist/lib/create/tests/types.d.ts +21 -0
- package/dist/lib/create/tests/types.js +1 -0
- package/dist/lib/create/tests.d.ts +4 -0
- package/dist/lib/create/tests.js +113 -0
- package/oclif.manifest.json +2529 -0
- package/package.json +1 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export function vitestConfigTemplate(options) {
|
|
2
|
+
return `\
|
|
3
|
+
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
|
|
4
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
type SdkAuth = {
|
|
9
|
+
storeId: string;
|
|
10
|
+
sessionId: string;
|
|
11
|
+
apiBaseUrl: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const isCI = Boolean(process.env.CI || process.env.CONTINUOUS_INTEGRATION);
|
|
15
|
+
|
|
16
|
+
function loadSwellAuth(): SdkAuth {
|
|
17
|
+
const envStoreId = process.env.SWELL_STORE_ID;
|
|
18
|
+
const envSessionId = process.env.SWELL_SESSION_ID;
|
|
19
|
+
const envApiBaseUrl = process.env.SWELL_API_BASE_URL;
|
|
20
|
+
|
|
21
|
+
if (envStoreId && envSessionId) {
|
|
22
|
+
return {
|
|
23
|
+
storeId: envStoreId,
|
|
24
|
+
sessionId: envSessionId,
|
|
25
|
+
apiBaseUrl: envApiBaseUrl || \`https://\${envStoreId}.swell.store/admin/api\`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (isCI) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"CI environment detected but SWELL_STORE_ID and SWELL_SESSION_ID are not set. " +
|
|
32
|
+
"Add these as CI secrets/variables to run integration tests.",
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const home = homedir();
|
|
37
|
+
const configPath = path.resolve(home, ".swell", "config.json");
|
|
38
|
+
if (!existsSync(configPath)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
\`Swell CLI config not found at \${configPath}. Run \\\`swell login\\\` or set SWELL_STORE_ID and SWELL_SESSION_ID.\`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const rawConfig = readFileSync(configPath, "utf-8");
|
|
45
|
+
|
|
46
|
+
interface CliConfig {
|
|
47
|
+
defaultStore?: string;
|
|
48
|
+
stores?: { storeId: string; sessionId?: string }[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let configJson: CliConfig;
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
configJson = JSON.parse(rawConfig) as CliConfig;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
\`Unable to parse Swell CLI config at \${configPath}: \${String(error)}\`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const defaultStore = configJson.defaultStore;
|
|
62
|
+
const stores = configJson.stores || [];
|
|
63
|
+
|
|
64
|
+
const store = defaultStore
|
|
65
|
+
? stores.find((item) => item.storeId === defaultStore)
|
|
66
|
+
: undefined;
|
|
67
|
+
|
|
68
|
+
if (!defaultStore || !store?.sessionId) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
"No active Swell CLI session found. Run \\\`swell login\\\` or set SWELL_STORE_ID and SWELL_SESSION_ID.",
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const envPath = path.join(home, ".swell", "env.json");
|
|
75
|
+
let apiBaseUrl = \`https://\${defaultStore}.swell.store/admin/api\`;
|
|
76
|
+
|
|
77
|
+
if (existsSync(envPath)) {
|
|
78
|
+
try {
|
|
79
|
+
const rawEnv = readFileSync(envPath, "utf-8");
|
|
80
|
+
const envJson = JSON.parse(rawEnv) as { ADMIN_API_BASE_URL?: string };
|
|
81
|
+
if (envJson.ADMIN_API_BASE_URL) {
|
|
82
|
+
apiBaseUrl = envJson.ADMIN_API_BASE_URL.replace("\${STORE_ID}", defaultStore);
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
// Ignore env parsing errors and fall back to default
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
storeId: defaultStore,
|
|
91
|
+
sessionId: store.sessionId!,
|
|
92
|
+
apiBaseUrl,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const sdkAuth = loadSwellAuth();
|
|
97
|
+
|
|
98
|
+
export default defineWorkersConfig({
|
|
99
|
+
test: {
|
|
100
|
+
globals: true,
|
|
101
|
+
include: ["test/**/*.test.ts"],
|
|
102
|
+
exclude: ["node_modules", "docs"],
|
|
103
|
+
setupFiles: ["./test/setup-globals.ts"],
|
|
104
|
+
poolOptions: {
|
|
105
|
+
workers: {
|
|
106
|
+
singleWorker: true,
|
|
107
|
+
isolatedStorage: true,
|
|
108
|
+
miniflare: {
|
|
109
|
+
bindings: {
|
|
110
|
+
SWELL_STORE_ID: sdkAuth.storeId,
|
|
111
|
+
SWELL_SESSION_ID: sdkAuth.sessionId,
|
|
112
|
+
SWELL_API_BASE_URL: sdkAuth.apiBaseUrl,
|
|
113
|
+
SWELL_APP_ID: "${options.appId}",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface TestTemplateOptions {
|
|
2
|
+
/** App id from swell.json, used for SWELL_APP_ID and settings() */
|
|
3
|
+
appId: string;
|
|
4
|
+
}
|
|
5
|
+
interface WriteTestsOptions extends TestTemplateOptions {
|
|
6
|
+
/** Absolute path to the app root */
|
|
7
|
+
appPath: string;
|
|
8
|
+
/** Overwrite existing files if present */
|
|
9
|
+
overwrite: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface ScaffoldResult {
|
|
12
|
+
/** Files that were created or overwritten */
|
|
13
|
+
createdFiles: string[];
|
|
14
|
+
/** Files that were skipped because they already exist */
|
|
15
|
+
skippedFiles: string[];
|
|
16
|
+
/** Whether package.json was updated */
|
|
17
|
+
packageJsonUpdated: boolean;
|
|
18
|
+
/** Warning messages to display to user */
|
|
19
|
+
warnings: string[];
|
|
20
|
+
}
|
|
21
|
+
export type { ScaffoldResult, TestTemplateOptions, WriteTestsOptions };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { filePathExists, writeFile } from '../apps/index.js';
|
|
4
|
+
import { envDtsTemplate, integrationTestTemplate, mockRequestTemplate, setupGlobalsTemplate, swellClientTemplate, tsconfigTemplate, unitTestTemplate, vitestConfigTemplate, } from './tests/templates/index.js';
|
|
5
|
+
const TEST_DEV_DEPENDENCIES = {
|
|
6
|
+
'@cloudflare/vitest-pool-workers': 'latest',
|
|
7
|
+
vitest: 'latest',
|
|
8
|
+
'@swell/app-types': 'latest',
|
|
9
|
+
};
|
|
10
|
+
async function writeTextFile(filePath, contents, overwrite) {
|
|
11
|
+
if (!overwrite && filePathExists(filePath)) {
|
|
12
|
+
return { created: false, skipped: true };
|
|
13
|
+
}
|
|
14
|
+
await writeFile(filePath, contents);
|
|
15
|
+
return { created: true, skipped: false };
|
|
16
|
+
}
|
|
17
|
+
function updatePackageJson(appPath) {
|
|
18
|
+
const pkgPath = path.join(appPath, 'package.json');
|
|
19
|
+
if (!filePathExists(pkgPath)) {
|
|
20
|
+
return {
|
|
21
|
+
updated: false,
|
|
22
|
+
warning: 'package.json not found. Run `npm init` first.',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
let pkgRaw;
|
|
26
|
+
try {
|
|
27
|
+
pkgRaw = fs.readFileSync(pkgPath, 'utf8');
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return {
|
|
31
|
+
updated: false,
|
|
32
|
+
warning: `Could not read package.json: ${error instanceof Error ? error.message : String(error)}`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
let pkg;
|
|
36
|
+
try {
|
|
37
|
+
pkg = JSON.parse(pkgRaw);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
updated: false,
|
|
42
|
+
warning: `Could not parse package.json: ${error instanceof Error ? error.message : String(error)}`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const devDeps = (pkg.devDependencies || {});
|
|
46
|
+
const deps = (pkg.dependencies || {});
|
|
47
|
+
devDeps['@cloudflare/vitest-pool-workers'] =
|
|
48
|
+
devDeps['@cloudflare/vitest-pool-workers'] ||
|
|
49
|
+
TEST_DEV_DEPENDENCIES['@cloudflare/vitest-pool-workers'];
|
|
50
|
+
devDeps.vitest = devDeps.vitest || TEST_DEV_DEPENDENCIES.vitest;
|
|
51
|
+
if (!devDeps['@swell/app-types'] && !deps['@swell/app-types']) {
|
|
52
|
+
devDeps['@swell/app-types'] = TEST_DEV_DEPENDENCIES['@swell/app-types'];
|
|
53
|
+
}
|
|
54
|
+
pkg.devDependencies = devDeps;
|
|
55
|
+
const scripts = (pkg.scripts || {});
|
|
56
|
+
scripts.test = scripts.test || 'vitest run';
|
|
57
|
+
pkg.scripts = scripts;
|
|
58
|
+
try {
|
|
59
|
+
const next = `${JSON.stringify(pkg, null, 2)}\n`;
|
|
60
|
+
fs.writeFileSync(pkgPath, next, 'utf8');
|
|
61
|
+
return { updated: true };
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return {
|
|
65
|
+
updated: false,
|
|
66
|
+
warning: `Could not write package.json: ${error instanceof Error ? error.message : String(error)}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function validatePrerequisites(appPath) {
|
|
71
|
+
const warnings = [];
|
|
72
|
+
const tsconfigPath = path.join(appPath, 'tsconfig.json');
|
|
73
|
+
if (!filePathExists(tsconfigPath)) {
|
|
74
|
+
warnings.push('tsconfig.json not found. test/tsconfig.json extends it; create one or tests may not compile.');
|
|
75
|
+
}
|
|
76
|
+
return warnings;
|
|
77
|
+
}
|
|
78
|
+
async function createTestsScaffold(options) {
|
|
79
|
+
const { appPath, appId, overwrite } = options;
|
|
80
|
+
const result = {
|
|
81
|
+
createdFiles: [],
|
|
82
|
+
skippedFiles: [],
|
|
83
|
+
packageJsonUpdated: false,
|
|
84
|
+
warnings: [],
|
|
85
|
+
};
|
|
86
|
+
const prereqWarnings = validatePrerequisites(appPath);
|
|
87
|
+
result.warnings.push(...prereqWarnings);
|
|
88
|
+
const trackFile = async (relativePath, contents) => {
|
|
89
|
+
const fullPath = path.join(appPath, relativePath);
|
|
90
|
+
const writeResult = await writeTextFile(fullPath, contents, overwrite);
|
|
91
|
+
if (writeResult.created) {
|
|
92
|
+
result.createdFiles.push(relativePath);
|
|
93
|
+
}
|
|
94
|
+
else if (writeResult.skipped) {
|
|
95
|
+
result.skippedFiles.push(relativePath);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
await trackFile('vitest.config.ts', vitestConfigTemplate({ appId }));
|
|
99
|
+
await trackFile('test/tsconfig.json', tsconfigTemplate());
|
|
100
|
+
await trackFile('test/env.d.ts', envDtsTemplate());
|
|
101
|
+
await trackFile('test/setup-globals.ts', setupGlobalsTemplate());
|
|
102
|
+
await trackFile('test/helpers/swell-client.ts', swellClientTemplate());
|
|
103
|
+
await trackFile('test/helpers/mock-request.ts', mockRequestTemplate({ appId }));
|
|
104
|
+
await trackFile('test/unit/example.test.ts', unitTestTemplate());
|
|
105
|
+
await trackFile('test/integration/example.test.ts', integrationTestTemplate());
|
|
106
|
+
const pkgResult = updatePackageJson(appPath);
|
|
107
|
+
result.packageJsonUpdated = pkgResult.updated;
|
|
108
|
+
if (pkgResult.warning) {
|
|
109
|
+
result.warnings.push(pkgResult.warning);
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
export { createTestsScaffold };
|