feat(mod_format): Refactor formatting modules to new BaseFormatter and implement concrete analyze/apply logic
This commit is contained in:
@@ -1,8 +1,204 @@
|
||||
import { LegacyFormatter } from './legacy.formatter.js';
|
||||
import * as formatPackageJson from '../format.packagejson.js';
|
||||
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||
import type { IPlannedChange } from '../interfaces.format.js';
|
||||
import * as plugins from '../mod.plugins.js';
|
||||
import * as paths from '../../paths.js';
|
||||
import { logger, logVerbose } from '../../gitzone.logging.js';
|
||||
|
||||
export class PackageJsonFormatter extends LegacyFormatter {
|
||||
constructor(context: any, project: any) {
|
||||
super(context, project, 'packagejson', formatPackageJson);
|
||||
/**
|
||||
* Ensures a certain dependency exists or is excluded
|
||||
*/
|
||||
const ensureDependency = async (
|
||||
packageJsonObject: any,
|
||||
position: 'dep' | 'devDep' | 'everywhere',
|
||||
constraint: 'exclude' | 'include' | 'latest',
|
||||
dependencyArg: string,
|
||||
): Promise<void> => {
|
||||
const [packageName, version] = dependencyArg.includes('@')
|
||||
? dependencyArg.split('@').filter(Boolean)
|
||||
: [dependencyArg, 'latest'];
|
||||
|
||||
const targetSections: string[] = [];
|
||||
|
||||
switch (position) {
|
||||
case 'dep':
|
||||
targetSections.push('dependencies');
|
||||
break;
|
||||
case 'devDep':
|
||||
targetSections.push('devDependencies');
|
||||
break;
|
||||
case 'everywhere':
|
||||
targetSections.push('dependencies', 'devDependencies');
|
||||
break;
|
||||
}
|
||||
|
||||
for (const section of targetSections) {
|
||||
if (!packageJsonObject[section]) {
|
||||
packageJsonObject[section] = {};
|
||||
}
|
||||
|
||||
switch (constraint) {
|
||||
case 'exclude':
|
||||
delete packageJsonObject[section][packageName];
|
||||
break;
|
||||
case 'include':
|
||||
if (!packageJsonObject[section][packageName]) {
|
||||
packageJsonObject[section][packageName] =
|
||||
version === 'latest' ? '^1.0.0' : version;
|
||||
}
|
||||
break;
|
||||
case 'latest':
|
||||
try {
|
||||
const registry = new plugins.smartnpm.NpmRegistry();
|
||||
const packageInfo = await registry.getPackageInfo(packageName);
|
||||
const latestVersion = packageInfo['dist-tags'].latest;
|
||||
packageJsonObject[section][packageName] = `^${latestVersion}`;
|
||||
} catch (error) {
|
||||
logVerbose(
|
||||
`Could not fetch latest version for ${packageName}, using existing or default`,
|
||||
);
|
||||
if (!packageJsonObject[section][packageName]) {
|
||||
packageJsonObject[section][packageName] =
|
||||
version === 'latest' ? '^1.0.0' : version;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class PackageJsonFormatter extends BaseFormatter {
|
||||
get name(): string {
|
||||
return 'packagejson';
|
||||
}
|
||||
|
||||
async analyze(): Promise<IPlannedChange[]> {
|
||||
const changes: IPlannedChange[] = [];
|
||||
const packageJsonPath = 'package.json';
|
||||
|
||||
// Check if file exists
|
||||
const exists = await plugins.smartfs.file(packageJsonPath).exists();
|
||||
if (!exists) {
|
||||
logVerbose('package.json does not exist, skipping');
|
||||
return changes;
|
||||
}
|
||||
|
||||
// Read current content
|
||||
const currentContent = (await plugins.smartfs
|
||||
.file(packageJsonPath)
|
||||
.encoding('utf8')
|
||||
.read()) as string;
|
||||
|
||||
// Parse and compute new content
|
||||
const packageJson = JSON.parse(currentContent);
|
||||
|
||||
// Get gitzone config from npmextra
|
||||
const npmextraConfig = new plugins.npmextra.Npmextra(paths.cwd);
|
||||
const gitzoneData: any = npmextraConfig.dataFor('@git.zone/cli', {});
|
||||
|
||||
// Set metadata from gitzone config
|
||||
if (gitzoneData.module) {
|
||||
packageJson.repository = {
|
||||
type: 'git',
|
||||
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}.git`,
|
||||
};
|
||||
packageJson.bugs = {
|
||||
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}/issues`,
|
||||
};
|
||||
packageJson.homepage = `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}#readme`;
|
||||
}
|
||||
|
||||
// Ensure module type
|
||||
if (!packageJson.type) {
|
||||
packageJson.type = 'module';
|
||||
}
|
||||
|
||||
// Ensure private field exists
|
||||
if (packageJson.private === undefined) {
|
||||
packageJson.private = true;
|
||||
}
|
||||
|
||||
// Ensure license field exists
|
||||
if (!packageJson.license) {
|
||||
packageJson.license = 'UNLICENSED';
|
||||
}
|
||||
|
||||
// Ensure scripts object exists
|
||||
if (!packageJson.scripts) {
|
||||
packageJson.scripts = {};
|
||||
}
|
||||
|
||||
// Ensure build script exists
|
||||
if (!packageJson.scripts.build) {
|
||||
packageJson.scripts.build = `echo "Not needed for now"`;
|
||||
}
|
||||
|
||||
// Ensure buildDocs script exists
|
||||
if (!packageJson.scripts.buildDocs) {
|
||||
packageJson.scripts.buildDocs = `tsdoc`;
|
||||
}
|
||||
|
||||
// Set files array
|
||||
packageJson.files = [
|
||||
'ts/**/*',
|
||||
'ts_web/**/*',
|
||||
'dist/**/*',
|
||||
'dist_*/**/*',
|
||||
'dist_ts/**/*',
|
||||
'dist_ts_web/**/*',
|
||||
'assets/**/*',
|
||||
'cli.js',
|
||||
'npmextra.json',
|
||||
'readme.md',
|
||||
];
|
||||
|
||||
// Handle dependencies
|
||||
await ensureDependency(
|
||||
packageJson,
|
||||
'devDep',
|
||||
'exclude',
|
||||
'@push.rocks/tapbundle',
|
||||
);
|
||||
await ensureDependency(packageJson, 'devDep', 'latest', '@git.zone/tstest');
|
||||
await ensureDependency(
|
||||
packageJson,
|
||||
'devDep',
|
||||
'latest',
|
||||
'@git.zone/tsbuild',
|
||||
);
|
||||
|
||||
// Set pnpm overrides from assets
|
||||
try {
|
||||
const overridesContent = (await plugins.smartfs
|
||||
.file(plugins.path.join(paths.assetsDir, 'overrides.json'))
|
||||
.encoding('utf8')
|
||||
.read()) as string;
|
||||
const overrides = JSON.parse(overridesContent);
|
||||
packageJson.pnpm = packageJson.pnpm || {};
|
||||
packageJson.pnpm.overrides = overrides;
|
||||
} catch (error) {
|
||||
logVerbose(`Could not read overrides.json: ${error.message}`);
|
||||
}
|
||||
|
||||
const newContent = JSON.stringify(packageJson, null, 2);
|
||||
|
||||
// Only add change if content differs
|
||||
if (newContent !== currentContent) {
|
||||
changes.push({
|
||||
type: 'modify',
|
||||
path: packageJsonPath,
|
||||
module: this.name,
|
||||
description: 'Format package.json',
|
||||
content: newContent,
|
||||
});
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
async applyChange(change: IPlannedChange): Promise<void> {
|
||||
if (change.type !== 'modify' || !change.content) return;
|
||||
|
||||
await this.modifyFile(change.path, change.content);
|
||||
logger.log('info', 'Updated package.json');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user