Skip to content

Commit

Permalink
fix: centralize component types into subpath
Browse files Browse the repository at this point in the history
- Separate component types from their schemas. Schemas should not be used outside of `midnight-smoker`; types can.
- Rename `NormalizePackageJson` to `PackageJson` since it's the only type we care about.
- Fix `readSmokerPkgJson()` to return a `PackageJson`
  • Loading branch information
boneskull committed Oct 9, 2024
1 parent 076c8d2 commit 8130682
Show file tree
Hide file tree
Showing 97 changed files with 1,013 additions and 788 deletions.
1 change: 1 addition & 0 deletions packages/midnight-smoker/.config/tsconfig.paths.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"#config/*": ["../src/config/*.js"],
"#constants": ["../src/constants/index.js"],
"#constants/*": ["../src/constants/*.js"],
"#defs/*": ["../src/defs/*.js"],
"#error/*": ["../src/error/*.js"],
"#error/meta/*": ["../src/error/meta/*.js"],
"#event/*": ["../src/event/*.js"],
Expand Down
1 change: 1 addition & 0 deletions packages/midnight-smoker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"#capabilities": "./dist/src/capabilities.js",
"#cli/*": "./dist/src/cli/*.js",
"#cli/command/*": "./dist/src/cli/command/*.js",
"#defs/*": "./dist/src/defs/*.js",
"#config": "./dist/src/config/index.js",
"#config/*": "./dist/src/config/*.js",
"#constants": "./dist/src/constants/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/midnight-smoker/src/config/midconfig/midconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
* @todo Copy license notice
*/
import {PACKAGE_JSON} from '#constants';
import {type DenormalizedPackageJson as PackageJson} from '#schema/package-json';
import * as assert from '#util/assert';
import {createDebug} from '#util/debug';
import {FileManager} from '#util/filemanager';
import {mimport} from '#util/importer';
import path from 'node:path';
import {type PackageJson} from 'type-fest';

import {
type AsyncSearcher,
Expand Down
4 changes: 2 additions & 2 deletions packages/midnight-smoker/src/constants/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const LintEvents = constant({
});

/**
* Core event names
* Mapping of core event names to event types
*
* @enum
*/
Expand All @@ -101,7 +101,7 @@ export const CoreEvents = constant({
});

/**
* All event names
* Mapping of event names to event types
*
* @enum
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/midnight-smoker/src/defs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# `midnight-smoker/defs`: Core Types for Plugins

This directory contains the types of _components_; a plugin is a collection of components. Plugin authors will find these types helpful.

Note that this directory should not contain any Zod schemas! The schemas are not public use, and should be considered an implementation detail.
49 changes: 49 additions & 0 deletions packages/midnight-smoker/src/defs/executor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Declares the {@link Executor} interface
*
* An implementation of `Executor` can then transform commands as needed.
*
* @packageDocumentation
*/

import {type ExecOptions, type ExecOutput} from '#schema/exec-result';
import {type StaticPkgManagerSpec} from '#schema/static-pkg-manager-spec';
import {type SpawnOptions as NodeOptions} from 'node:child_process';

/**
* Options to pass along to the underlying child process spawner
*/
export type {NodeOptions};

export type {ExecOptions, ExecOutput, StaticPkgManagerSpec};

/**
* Options for an {@link Executor}
*/

export type ExecutorOpts = ExecOptions | undefined;

/**
* An `Executor` is responsible for invoking package manager commands.
*
* A package manager calls its `Executor` instance with the proper arguments to
* run.
*
* An `Executor` can be thought of as the final "transform" before the package
* manager process gets spawned.
*
* @remarks
* This can be thought of as a wrapper around `exec()` from
* `midnight-smoker/util`, allowing greater control over the spawned process and
* its return value.
* @param spec - The package manager spec
* @param args - The arguments to the package manager executable, likely
* including a command
* @param options - Options for the `Executor`
* @param nodeOptions - Options for `child_process.spawn()` by way of `exec()`
*/
export type Executor = (
spec: StaticPkgManagerSpec,
args: string[],
options?: ExecutorOpts,
) => Promise<ExecOutput>;
256 changes: 256 additions & 0 deletions packages/midnight-smoker/src/defs/pkg-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
/**
* Contains the schema and type def for a {@link PkgManager}, which is a
* component that a plugin can provide.
*
* A `PkgManager` is an adapter for a package manager executable at a given
* version (or range).
*
* @module midnight-smoker/defs/pkg-manager
*/

import {type ExecOutput, type Executor} from '#defs/executor';
import {type RunScriptResult} from '#schema/run-script-result';
import {type StaticPkgManagerSpec} from '#schema/static-pkg-manager-spec';
import {type RawPkgManagerVersionData} from '#schema/version';
import {type WorkspaceInfo} from '#schema/workspace-info';
import {type Range} from 'semver';
import {type Merge, type SetOptional} from 'type-fest';

/**
* The context for a package manager.
*
* This is the base chunk of state that a {@link PkgManager} gets to work with.
* Each operation function will receive this context object or a more specific
* one which extends this.
*/
export type PkgManagerContext = Merge<
{
executor: Executor;
spec: StaticPkgManagerSpec;
tmpdir: string;
useWorkspaces?: boolean;
workspaceInfo: WorkspaceInfo[];
},
PkgManagerOpts
>;

/**
* A Package Manager definition which is an adapter for a package manager
* executable.
*
* `midnight-smoker` only cares about a select few operations (packing,
* installation, and running a script), but they all must be provided.
*
* A `PkgManager` is defined via a plugin, and is considered a Component. Its
* `ComponentKind` is `PkgManager`.
*
* This can be a plain object or a singleton-like instance of some class. If a
* class is used, it's the responsibility of the plugin author to instantiate.
*/
export type PkgManager = {
/**
* Whatever else the plugin author needs in here is fine; it is ignored by
* `midnight-smoker`
*/
[x: string]: unknown;

/**
* The name of the package manager's executable. End-users will refer to this
* when choosing a package manager
*/
bin: string;

/**
* Description of the package manager
*/
description?: string;

/**
* A function that installs a package. It receives a context object and
* returns a the raw {@link ExecResult}, returned by its
* {@link PkgManagerContext.executor Executor}.
*/
install: PkgManagerInstallFn;

/**
* The name of the lockfile for this package manager. Used for auto-detection
* of package manager
*/
lockfile?: string;

/**
* The human-readable name for display
*/
name: string;

/**
* A function that packs a package into a tarball into a
* {@link PkgManagerContext.tmpdir temp dir} (the path to which is determined
* by `midnight-smoker`). It receives a context object and returns an
* {@link InstallManifest}.
*/
pack: PkgManagerPackFn;

/**
* A function that runs a script against a package. It receives a context
* object and returns a {@link RunScriptResult}.
*/
runScript: PkgManagerRunScriptFn;

/**
* A "setup" lifecycle function which is called before any operations are run.
* It receives the context object, which it can mutate.
*/
setup?: PkgManagerSetupFn;

/**
* The range of versions supported by this package manager; used to determine
* if the `PkgManager` can handle a given version
*/
supportedVersionRange: PkgManagerSupportedVersionRange;

/**
* A "teardown" lifecycle function which is called after all operations are
* run. It receives the context object, which it can mutate.
*/
teardown?: PkgManagerTeardownFn;

/**
* An array of versions or an object containing versions and tags,
* representing known package manager versions
*
* This is needed to validate user input.
*/
versions: RawPkgManagerVersionData;
};

/**
* The context object for {@link PkgManagerInstallFn}.
*
* The `PkgManagerInstallFn` will use the
* {@link PkgManagerInstallContext.installManifest installManifest property} to
* determine what to install. Usually, this will be a tarball which was created
* by {@link PkgManagerPackFn}.
*/
export type PkgManagerInstallContext = {
installManifest: InstallManifest;
signal: AbortSignal;
} & PkgManagerContext;

/**
* Installs a package given the {@link PkgManagerInstallContext context object}.
*/
export type PkgManagerInstallFn = (
context: PkgManagerInstallContext,
) => Promise<ExecOutput>;

/**
* Extra options for package manager operations.
*/
export type PkgManagerOpts = {
/**
* If `true`, ignore missing scripts in {@link PkgManagerRunScriptFn}.
*/
loose?: boolean;

/**
* If `true`, the package manager should pipe its STDERR/STDOUT to the console
*/
verbose?: boolean;
};

/**
* The context object for {@link PkgManagerPackFn}.
*
* The `PkgManagerPackFn` will use the properties from {@link WorkspaceInfo} to
* determine what to pack; {@link PkgManagerContext.tmpdir tmpdir} is the target
* directory.
*/
export type PkgManagerPackContext = {
signal: AbortSignal;
timeout?: number;
} & PkgManagerContext &
WorkspaceInfo;

/**
* Packs a package into a tarball given a {@link PkgManagerPackContext context}
* object
*/
export type PkgManagerPackFn = (
context: PkgManagerPackContext,
) => Promise<InstallManifest>;

/**
* The context object for {@link PkgManagerRunScriptFn}.
*
* The `PkgManagerRunScriptFn` will use the
* {@link PkgManagerRunScriptContext.manifest RunScriptManifest} to determine
* what scripts to run within the installed packages (and where).
*/
export type PkgManagerRunScriptContext = {
manifest: RunScriptManifest;
signal: AbortSignal;
} & PkgManagerContext;

/**
* Runs a script against a package given a {@link PkgManagerRunScriptContext}
* object.
*/
export type PkgManagerRunScriptFn = (
context: PkgManagerRunScriptContext,
) => Promise<RunScriptResult>;

/**
* A "lifeycle" function that sets up a package manager.
*/
export type PkgManagerSetupFn =
| ((context: PkgManagerContext) => Promise<void>)
| ((context: PkgManagerContext) => void);

/**
* A value which indicates what version or versions a package manager can
* support.
*
* Must be parseable by `semver`.
*/
export type PkgManagerSupportedVersionRange = Range | string;

/**
* A "lifeycle" function that tears down a package manager.
*
* Use this to dispose of resources or clean up after a package manager.
* `midnight-smoker` will handle the temporary directory.
*/
export type PkgManagerTeardownFn =
| ((context: PkgManagerContext) => Promise<void>)
| ((context: PkgManagerContext) => void);

export type InstallManifest = Readonly<
{
cwd: string;
installPath?: string;
isAdditional?: boolean;
pkgSpec: string;
} & SetOptional<
WorkspaceInfo,
'localPath' | 'pkgJson' | 'pkgJsonPath' | 'rawPkgJson'
>
>;

/**
* An install manifest referencing a workspace (_not_ an additional dependency)
*/

export type WorkspaceInstallManifest = Merge<
InstallManifest,
{
installPath: string;
isAdditional?: false;
localPath: string;
}
>;

export type RunScriptManifest = {
cwd: string;
script: string;
} & WorkspaceInfo;
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* Provides {@link StaticPluginMetadata}, which plugins have access to.
*
* @packageDocumentation
*/

/**
* Public information about a plugin suitable for serialization.
*/
Expand Down
Loading

0 comments on commit 8130682

Please sign in to comment.