|
|
|
|
@@ -16,6 +16,11 @@ export interface IPublishModuleOptions {
|
|
|
|
|
dependencies?: { [key: string]: string };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface IResolvedRegistry {
|
|
|
|
|
url: string;
|
|
|
|
|
accessLevel: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class PublishModule {
|
|
|
|
|
tsPublishRef: TsPublish;
|
|
|
|
|
public options: IPublishModuleOptions;
|
|
|
|
|
@@ -34,14 +39,14 @@ export class PublishModule {
|
|
|
|
|
if (!this.options.packageSubFolder.startsWith('ts')) {
|
|
|
|
|
throw new Error('subFolder must start with "ts"');
|
|
|
|
|
}
|
|
|
|
|
this.options.tsPublishJson = plugins.smartfile.fs.toObjectSync(
|
|
|
|
|
plugins.path.join(this.options.packageSubFolderFullPath, 'tspublish.json')
|
|
|
|
|
);
|
|
|
|
|
const tspublishJsonPath = plugins.path.join(this.options.packageSubFolderFullPath, 'tspublish.json');
|
|
|
|
|
const tspublishJsonContent = await plugins.smartfs.file(tspublishJsonPath).encoding('utf8').read();
|
|
|
|
|
this.options.tsPublishJson = JSON.parse(tspublishJsonContent as string);
|
|
|
|
|
|
|
|
|
|
// the package.json of the parent mono repo
|
|
|
|
|
const monoRepoPackageJson = JSON.parse(
|
|
|
|
|
plugins.smartfile.fs.toStringSync(plugins.path.join(this.options.monoRepoDir, 'package.json'))
|
|
|
|
|
);
|
|
|
|
|
const packageJsonPath = plugins.path.join(this.options.monoRepoDir, 'package.json');
|
|
|
|
|
const packageJsonContent = await plugins.smartfs.file(packageJsonPath).encoding('utf8').read();
|
|
|
|
|
const monoRepoPackageJson = JSON.parse(packageJsonContent as string);
|
|
|
|
|
|
|
|
|
|
this.options.dependencies = {
|
|
|
|
|
...this.options.dependencies,
|
|
|
|
|
@@ -91,9 +96,12 @@ export class PublishModule {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async createTsconfigJson() {
|
|
|
|
|
const originalTsConfig = plugins.smartfile.fs.toObjectSync(
|
|
|
|
|
plugins.path.join(paths.cwd, 'tsconfig.json')
|
|
|
|
|
);
|
|
|
|
|
const tsconfigPath = plugins.path.join(paths.cwd, 'tsconfig.json');
|
|
|
|
|
let originalTsConfig: any = null;
|
|
|
|
|
if (await plugins.smartfs.file(tsconfigPath).exists()) {
|
|
|
|
|
const tsconfigContent = await plugins.smartfs.file(tsconfigPath).encoding('utf8').read();
|
|
|
|
|
originalTsConfig = JSON.parse(tsconfigContent as string);
|
|
|
|
|
}
|
|
|
|
|
if (originalTsConfig?.compilerOptions?.paths) {
|
|
|
|
|
for (const path of Object.keys(originalTsConfig.compilerOptions.paths)) {
|
|
|
|
|
originalTsConfig.compilerOptions.paths[
|
|
|
|
|
@@ -166,41 +174,35 @@ export class PublishModule {
|
|
|
|
|
this.options.monoRepoDir,
|
|
|
|
|
`dist_publish_${this.options.packageSubFolder}`
|
|
|
|
|
);
|
|
|
|
|
await plugins.smartfile.fs.ensureEmptyDir(this.options.publishModDirFullPath);
|
|
|
|
|
|
|
|
|
|
// Ensure empty directory
|
|
|
|
|
const publishDir = plugins.smartfs.directory(this.options.publishModDirFullPath);
|
|
|
|
|
if (await publishDir.exists()) {
|
|
|
|
|
await publishDir.recursive().delete();
|
|
|
|
|
}
|
|
|
|
|
await publishDir.recursive().create();
|
|
|
|
|
|
|
|
|
|
// package.json
|
|
|
|
|
const packageJson = await plugins.smartfile.SmartFile.fromString(
|
|
|
|
|
plugins.path.join(this.options.publishModDirFullPath, 'package.json'),
|
|
|
|
|
await this.createPackageJson(),
|
|
|
|
|
'utf8'
|
|
|
|
|
);
|
|
|
|
|
await packageJson.write();
|
|
|
|
|
const packageJsonPath = plugins.path.join(this.options.publishModDirFullPath, 'package.json');
|
|
|
|
|
await plugins.smartfs.file(packageJsonPath).encoding('utf8').write(await this.createPackageJson());
|
|
|
|
|
|
|
|
|
|
// tsconfig.json
|
|
|
|
|
const tsconfigJson = await plugins.smartfile.SmartFile.fromString(
|
|
|
|
|
plugins.path.join(this.options.publishModDirFullPath, 'tsconfig.json'),
|
|
|
|
|
await this.createTsconfigJson(),
|
|
|
|
|
'utf8'
|
|
|
|
|
);
|
|
|
|
|
await tsconfigJson.write();
|
|
|
|
|
const tsconfigJsonPath = plugins.path.join(this.options.publishModDirFullPath, 'tsconfig.json');
|
|
|
|
|
await plugins.smartfs.file(tsconfigJsonPath).encoding('utf8').write(await this.createTsconfigJson());
|
|
|
|
|
|
|
|
|
|
// ts subfolder, the folder that contains the source code and is being transpiled
|
|
|
|
|
await plugins.smartfile.fs.copy(
|
|
|
|
|
this.options.packageSubFolderFullPath,
|
|
|
|
|
plugins.path.join(this.options.publishModDirFullPath, this.options.packageSubFolder)
|
|
|
|
|
);
|
|
|
|
|
const destSubFolder = plugins.path.join(this.options.publishModDirFullPath, this.options.packageSubFolder);
|
|
|
|
|
await plugins.smartfs.directory(this.options.packageSubFolderFullPath).recursive().copy(destSubFolder);
|
|
|
|
|
|
|
|
|
|
// readme
|
|
|
|
|
await plugins.smartfile.fs.copy(
|
|
|
|
|
plugins.path.join(this.options.packageSubFolderFullPath, 'readme.md'),
|
|
|
|
|
plugins.path.join(this.options.publishModDirFullPath, 'readme.md')
|
|
|
|
|
);
|
|
|
|
|
const readmeSrc = plugins.path.join(this.options.packageSubFolderFullPath, 'readme.md');
|
|
|
|
|
const readmeDest = plugins.path.join(this.options.publishModDirFullPath, 'readme.md');
|
|
|
|
|
await plugins.smartfs.file(readmeSrc).copy(readmeDest);
|
|
|
|
|
|
|
|
|
|
// license
|
|
|
|
|
await plugins.smartfile.fs.copy(
|
|
|
|
|
plugins.path.join(this.options.monoRepoDir, 'license'),
|
|
|
|
|
plugins.path.join(this.options.publishModDirFullPath, 'license')
|
|
|
|
|
);
|
|
|
|
|
const licenseSrc = plugins.path.join(this.options.monoRepoDir, 'license');
|
|
|
|
|
const licenseDest = plugins.path.join(this.options.publishModDirFullPath, 'license');
|
|
|
|
|
await plugins.smartfs.file(licenseSrc).copy(licenseDest);
|
|
|
|
|
|
|
|
|
|
// cli stuff
|
|
|
|
|
this.createBinCliSetup();
|
|
|
|
|
@@ -227,22 +229,104 @@ export class PublishModule {
|
|
|
|
|
);
|
|
|
|
|
const indexPath = `./dist_${this.options.packageSubFolder}/index.js`;
|
|
|
|
|
const fileContent = atob(files[0].base64Content).replace('./dist_ts/index.js', indexPath);
|
|
|
|
|
await plugins.smartfile.memory.toFs(fileContent, plugins.path.join(this.options.publishModDirFullPath, 'cli.js'));
|
|
|
|
|
const cliJsPath = plugins.path.join(this.options.publishModDirFullPath, 'cli.js');
|
|
|
|
|
await plugins.smartfs.file(cliJsPath).encoding('utf8').write(fileContent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resolves the registries to publish to based on tspublish.json configuration.
|
|
|
|
|
* Supports:
|
|
|
|
|
* - "useBase": Use only registries from npmextra.json
|
|
|
|
|
* - "extendBase": Use base registries + additions, with exclusions via "-" prefix
|
|
|
|
|
* - Explicit registries: Direct registry URLs in format "url:accessLevel"
|
|
|
|
|
*/
|
|
|
|
|
private async resolveRegistries(): Promise<IResolvedRegistry[]> {
|
|
|
|
|
const rawRegistries = this.options.tsPublishJson?.registries || [];
|
|
|
|
|
|
|
|
|
|
// Empty → skip publishing
|
|
|
|
|
if (rawRegistries.length === 0) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const hasUseBase = rawRegistries.includes('useBase');
|
|
|
|
|
const hasExtendBase = rawRegistries.includes('extendBase');
|
|
|
|
|
|
|
|
|
|
let baseRegistries: string[] = [];
|
|
|
|
|
let baseAccessLevel = 'public';
|
|
|
|
|
|
|
|
|
|
// Load base registries from npmextra.json if needed
|
|
|
|
|
if (hasUseBase || hasExtendBase) {
|
|
|
|
|
const npmextraInstance = new plugins.npmextra.Npmextra(this.options.monoRepoDir);
|
|
|
|
|
const gitzoneConfig = npmextraInstance.dataFor<any>('@git.zone/cli', {});
|
|
|
|
|
baseRegistries = gitzoneConfig?.release?.registries || [];
|
|
|
|
|
baseAccessLevel = gitzoneConfig?.release?.accessLevel || 'public';
|
|
|
|
|
|
|
|
|
|
if (baseRegistries.length === 0) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`useBase/extendBase specified in tspublish.json but no registries configured in npmextra.json at @git.zone/cli.release.registries`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// useBase: Only base registries
|
|
|
|
|
if (hasUseBase) {
|
|
|
|
|
return baseRegistries.map((url) => ({ url, accessLevel: baseAccessLevel }));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// extendBase: Base registries + additions - exclusions
|
|
|
|
|
if (hasExtendBase) {
|
|
|
|
|
const exclusions = rawRegistries
|
|
|
|
|
.filter((r) => r.startsWith('-'))
|
|
|
|
|
.map((r) => r.slice(1)); // remove '-' prefix
|
|
|
|
|
|
|
|
|
|
const additions = rawRegistries.filter((r) => r !== 'extendBase' && !r.startsWith('-'));
|
|
|
|
|
|
|
|
|
|
// Filter out excluded base registries
|
|
|
|
|
const result: IResolvedRegistry[] = baseRegistries
|
|
|
|
|
.filter((url) => !exclusions.includes(url))
|
|
|
|
|
.map((url) => ({ url, accessLevel: baseAccessLevel }));
|
|
|
|
|
|
|
|
|
|
// Add explicit registries
|
|
|
|
|
for (const addition of additions) {
|
|
|
|
|
const parts = addition.split(':');
|
|
|
|
|
const url = parts[0];
|
|
|
|
|
const access = parts[1] || 'public';
|
|
|
|
|
result.push({ url, accessLevel: access });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Explicit registries only (original behavior)
|
|
|
|
|
return rawRegistries.map((r) => {
|
|
|
|
|
const parts = r.split(':');
|
|
|
|
|
const url = parts[0];
|
|
|
|
|
const access = parts[1] || 'public';
|
|
|
|
|
return { url, accessLevel: access };
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async publish() {
|
|
|
|
|
logPublish(`Publishing ${this.options.name} v${this.options.version}...`);
|
|
|
|
|
const registries = await this.resolveRegistries();
|
|
|
|
|
|
|
|
|
|
// Handle empty registries
|
|
|
|
|
if (registries.length === 0) {
|
|
|
|
|
logWarn(`No registries configured for ${this.options.name}. Skipping publish.`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logPublish(`Publishing ${this.options.name} v${this.options.version} to ${registries.length} registry(ies)...`);
|
|
|
|
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
|
|
|
|
executor: 'bash',
|
|
|
|
|
});
|
|
|
|
|
for (const registry of this.options.tsPublishJson.registries) {
|
|
|
|
|
const registryArray = registry.split(':');
|
|
|
|
|
const registryUrl = registryArray[0];
|
|
|
|
|
const registryAccessLevel = registryArray[1];
|
|
|
|
|
|
|
|
|
|
for (const registry of registries) {
|
|
|
|
|
const registryUrl = registry.url.startsWith('https://') ? registry.url : `https://${registry.url}`;
|
|
|
|
|
logOngoing(`Publishing to ${registryUrl}...`);
|
|
|
|
|
await smartshellInstance.exec(
|
|
|
|
|
`cd ${this.options.publishModDirFullPath} && pnpm publish ${
|
|
|
|
|
registryAccessLevel === 'public' ? '--access public' : ''
|
|
|
|
|
} --no-git-checks --registry https://${registryUrl}`
|
|
|
|
|
registry.accessLevel === 'public' ? '--access public' : ''
|
|
|
|
|
} --no-git-checks --registry ${registryUrl}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
logSuccess(`Successfully published ${this.options.name} v${this.options.version}!`);
|
|
|
|
|
|