214 lines
6.2 KiB
TypeScript
214 lines
6.2 KiB
TypeScript
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';
|
|
|
|
/**
|
|
* 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> => {
|
|
// Parse package name and version, handling scoped packages like @scope/name@version
|
|
const isScoped = dependencyArg.startsWith('@');
|
|
const lastAtIndex = dependencyArg.lastIndexOf('@');
|
|
|
|
// For scoped packages, the version @ must come after the /
|
|
// For unscoped packages, any @ indicates a version
|
|
const hasVersion = isScoped
|
|
? lastAtIndex > dependencyArg.indexOf('/')
|
|
: lastAtIndex >= 0;
|
|
|
|
const packageName = hasVersion ? dependencyArg.slice(0, lastAtIndex) : dependencyArg;
|
|
const version = hasVersion ? dependencyArg.slice(lastAtIndex + 1) : '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');
|
|
}
|
|
}
|