@swell/cli
Version:
Swell's command line interface/utility
114 lines (113 loc) • 4.4 kB
JavaScript
import * as fs from 'node:fs';
import * as path from 'node:path';
import { filePathExists, writeFile } from '../apps/index.js';
import { envDtsTemplate, integrationTestTemplate, mockRequestTemplate, setupGlobalsTemplate, swellClientTemplate, tsconfigTemplate, unitTestTemplate, vitestConfigTemplate, } from './tests/templates/index.js';
const TEST_DEV_DEPENDENCIES = {
'@cloudflare/vitest-pool-workers': 'latest',
vitest: 'latest',
'@swell/app-types': 'latest',
};
async function writeTextFile(filePath, contents, overwrite) {
if (!overwrite && filePathExists(filePath)) {
return { created: false, skipped: true };
}
await writeFile(filePath, contents);
return { created: true, skipped: false };
}
function updatePackageJson(appPath) {
const pkgPath = path.join(appPath, 'package.json');
if (!filePathExists(pkgPath)) {
return {
updated: false,
warning: 'package.json not found. Run `npm init` first.',
};
}
let pkgRaw;
try {
pkgRaw = fs.readFileSync(pkgPath, 'utf8');
}
catch (error) {
return {
updated: false,
warning: `Could not read package.json: ${error instanceof Error ? error.message : String(error)}`,
};
}
let pkg;
try {
pkg = JSON.parse(pkgRaw);
}
catch (error) {
return {
updated: false,
warning: `Could not parse package.json: ${error instanceof Error ? error.message : String(error)}`,
};
}
const devDeps = (pkg.devDependencies || {});
const deps = (pkg.dependencies || {});
devDeps['@cloudflare/vitest-pool-workers'] =
devDeps['@cloudflare/vitest-pool-workers'] ||
TEST_DEV_DEPENDENCIES['@cloudflare/vitest-pool-workers'];
devDeps.vitest = devDeps.vitest || TEST_DEV_DEPENDENCIES.vitest;
if (!devDeps['@swell/app-types'] && !deps['@swell/app-types']) {
devDeps['@swell/app-types'] = TEST_DEV_DEPENDENCIES['@swell/app-types'];
}
pkg.devDependencies = devDeps;
const scripts = (pkg.scripts || {});
scripts.test = scripts.test || 'vitest run';
pkg.scripts = scripts;
try {
const next = `${JSON.stringify(pkg, null, 2)}\n`;
fs.writeFileSync(pkgPath, next, 'utf8');
return { updated: true };
}
catch (error) {
return {
updated: false,
warning: `Could not write package.json: ${error instanceof Error ? error.message : String(error)}`,
};
}
}
function validatePrerequisites(appPath) {
const warnings = [];
const tsconfigPath = path.join(appPath, 'tsconfig.json');
if (!filePathExists(tsconfigPath)) {
warnings.push('tsconfig.json not found. test/tsconfig.json extends it; create one or tests may not compile.');
}
return warnings;
}
async function createTestsScaffold(options) {
const { appPath, appId, overwrite } = options;
const result = {
createdFiles: [],
skippedFiles: [],
packageJsonUpdated: false,
warnings: [],
};
const prereqWarnings = validatePrerequisites(appPath);
result.warnings.push(...prereqWarnings);
const trackFile = async (relativePath, contents) => {
const fullPath = path.join(appPath, relativePath);
const writeResult = await writeTextFile(fullPath, contents, overwrite);
if (writeResult.created) {
result.createdFiles.push(relativePath);
}
else if (writeResult.skipped) {
result.skippedFiles.push(relativePath);
}
};
await trackFile('vitest.config.ts', vitestConfigTemplate({ appId }));
await trackFile('test/tsconfig.json', tsconfigTemplate());
await trackFile('test/env.d.ts', envDtsTemplate());
await trackFile('test/setup-globals.ts', setupGlobalsTemplate());
await trackFile('test/helpers/swell-client.ts', swellClientTemplate());
await trackFile('test/helpers/mock-request.ts', mockRequestTemplate({ appId }));
await trackFile('test/unit/example.test.ts', unitTestTemplate());
await trackFile('test/integration/example.test.ts', integrationTestTemplate());
const pkgResult = updatePackageJson(appPath);
result.packageJsonUpdated = pkgResult.updated;
if (pkgResult.warning) {
result.warnings.push(pkgResult.warning);
}
return result;
}
export { createTestsScaffold };