JamieMason/syncpack

View on GitHub
src/get-package-json-files/package-json-file.ts

Summary

Maintainability
A
1 hr
Test Coverage
A
98%
import { Effect, pipe } from 'effect';
import type { Strategy } from '../config/get-custom-types.js';
import type { Ctx } from '../get-context/index.js';
import { Instance } from '../get-instances/instance.js';
import type { RcFile } from '../index.js';
import type { JsonFile } from '../io/read-json-file-sync.js';

export type PackageJson = {
  bugs?: { url: string } | string;
  config?: { syncpack?: RcFile };
  dependencies?: Record<string, string>;
  description?: string;
  devDependencies?: Record<string, string>;
  keywords?: string[];
  name?: string;
  overrides?: Record<string, string>;
  peerDependencies?: Record<string, string>;
  pnpm?: {
    overrides?: Record<string, string>;
  };
  repository?: { directory?: string; type: string; url: string } | string;
  resolutions?: Record<string, string>;
  scripts?: Record<string, string>;
  version?: string;
  workspaces?: string[] | { packages?: string[] };
} & Record<
  string,
  | Record<string, string | string[] | Record<string, string | string[]>>
  | string
  | string[]
  | undefined
>;

export class PackageJsonFile {
  /** resolved configuration */
  readonly config: Ctx['config'];
  /** ensure only one set of instances is ever created and shared */
  private _instances: Instance[] | null;

  /** the wrapped package.json file */
  jsonFile: JsonFile<PackageJson>;

  /** the .name property from the package.json file */
  name: string | undefined;

  constructor(jsonFile: JsonFile<PackageJson>, config: Ctx['config']) {
    this._instances = null;
    this.config = config;
    this.jsonFile = jsonFile;
    this.name = jsonFile.contents.name;
  }

  getInstances(enabledTypes: Strategy.Any[]): Effect.Effect<Instance[]> {
    if (!this._instances) {
      return pipe(
        Effect.all(
          enabledTypes.map((strategy) =>
            pipe(
              strategy.read(this),
              Effect.map((entries) =>
                entries.map(
                  ([name, rawSpecifier]) => new Instance(name, rawSpecifier, this, strategy),
                ),
              ),
            ),
          ),
        ),
        Effect.map((array) => array.flat()),
        Effect.tapBoth({
          onSuccess: (instances) =>
            Effect.logDebug(`found ${instances.length} instances in <${this.jsonFile.shortPath}>`),
          onFailure: () =>
            Effect.logError(`failed to get instances from <${this.jsonFile.shortPath}>`),
        }),
        Effect.catchAll(() => Effect.succeed([])),
        Effect.tap((instances) =>
          Effect.sync(() => {
            this._instances = instances;
          }),
        ),
      );
    }
    return Effect.succeed(this._instances);
  }
}