@vltpkg/workspaces
Advanced tools
+28
-24
| { | ||
| "name": "@vltpkg/workspaces", | ||
| "description": "Utility for working with vlt workspaces", | ||
| "version": "1.0.0-rc.18", | ||
| "version": "1.0.0-rc.22", | ||
| "repository": { | ||
@@ -10,13 +10,16 @@ "type": "git", | ||
| }, | ||
| "author": "vlt technology inc. <support@vlt.sh> (http://vlt.sh)", | ||
| "author": { | ||
| "name": "vlt technology inc.", | ||
| "email": "support@vlt.sh" | ||
| }, | ||
| "dependencies": { | ||
| "@vltpkg/dep-id": "1.0.0-rc.22", | ||
| "@vltpkg/error-cause": "1.0.0-rc.22", | ||
| "@vltpkg/graph-run": "1.0.0-rc.22", | ||
| "@vltpkg/package-json": "1.0.0-rc.22", | ||
| "@vltpkg/types": "1.0.0-rc.22", | ||
| "@vltpkg/vlt-json": "1.0.0-rc.22", | ||
| "glob": "^13.0.0", | ||
| "minimatch": "^10.1.1", | ||
| "path-scurry": "^2.0.1", | ||
| "@vltpkg/dep-id": "1.0.0-rc.18", | ||
| "@vltpkg/graph-run": "1.0.0-rc.18", | ||
| "@vltpkg/error-cause": "1.0.0-rc.18", | ||
| "@vltpkg/package-json": "1.0.0-rc.18", | ||
| "@vltpkg/types": "1.0.0-rc.18", | ||
| "@vltpkg/vlt-json": "1.0.0-rc.18" | ||
| "path-scurry": "^2.0.1" | ||
| }, | ||
@@ -35,4 +38,15 @@ "devDependencies": { | ||
| "engines": { | ||
| "node": ">=22.9.0" | ||
| "node": ">=22.22.0" | ||
| }, | ||
| "scripts": { | ||
| "format": "prettier --write . --log-level warn --ignore-path ../../.prettierignore --cache", | ||
| "format:check": "prettier --check . --ignore-path ../../.prettierignore --cache", | ||
| "lint": "eslint . --fix", | ||
| "lint:check": "eslint .", | ||
| "prepack": "tsc -p tsconfig.publish.json && ../../scripts/update-dist-exports.ts", | ||
| "snap": "tap", | ||
| "test": "tap", | ||
| "posttest": "tsc --noEmit", | ||
| "typecheck": "tsc --noEmit" | ||
| }, | ||
| "tap": { | ||
@@ -42,3 +56,3 @@ "extends": "../../tap-config.yaml" | ||
| "prettier": "../../.prettierrc.js", | ||
| "module": "./dist/index.js", | ||
| "module": "./src/index.ts", | ||
| "type": "module", | ||
@@ -49,3 +63,3 @@ "exports": { | ||
| "import": { | ||
| "default": "./dist/index.js" | ||
| "default": "./src/index.ts" | ||
| } | ||
@@ -56,13 +70,3 @@ } | ||
| "dist" | ||
| ], | ||
| "scripts": { | ||
| "format": "prettier --write . --log-level warn --ignore-path ../../.prettierignore --cache", | ||
| "format:check": "prettier --check . --ignore-path ../../.prettierignore --cache", | ||
| "lint": "eslint . --fix", | ||
| "lint:check": "eslint .", | ||
| "snap": "tap", | ||
| "test": "tap", | ||
| "posttest": "tsc --noEmit", | ||
| "typecheck": "tsc --noEmit" | ||
| } | ||
| } | ||
| ] | ||
| } |
-221
| import type { DepID } from '@vltpkg/dep-id'; | ||
| import { PackageJson } from '@vltpkg/package-json'; | ||
| import type { NormalizedManifest } from '@vltpkg/types'; | ||
| import type { DepResults } from '@vltpkg/graph-run'; | ||
| import { PathScurry } from 'path-scurry'; | ||
| export type WorkspacesLoadedConfig = { | ||
| workspace?: string[]; | ||
| 'workspace-group'?: string[]; | ||
| }; | ||
| /** | ||
| * The object passed to the constructor or {@link Monorepo#load} to limit which | ||
| * {@link Workspace Workspaces} get loaded. | ||
| */ | ||
| export type LoadQuery = { | ||
| /** | ||
| * A glob pattern string, or an array of them. Only workspaces found | ||
| * in paths matched will be loaded. | ||
| */ | ||
| paths?: string[] | string; | ||
| /** | ||
| * A string, or an array of strings. If set, only workspaces in the | ||
| * specified groups named will be included, if set. | ||
| */ | ||
| groups?: string[] | string; | ||
| }; | ||
| /** | ||
| * Canonical form of the {@link WorkspaceConfig}, used | ||
| * internally for consistency. | ||
| */ | ||
| export type WorkspaceConfigObject = Record<string, string[]>; | ||
| /** | ||
| * Allowed datatype in the `workspaces` field of the `vlt.json` file. | ||
| */ | ||
| export type WorkspaceConfig = string[] | WorkspaceConfigObject | string; | ||
| /** | ||
| * Turn a {@link WorkspaceConfig} into a | ||
| * {@link WorkspaceConfigObject}, or throw if it's not valid. | ||
| */ | ||
| export declare const asWSConfig: (conf: unknown, path?: string) => WorkspaceConfigObject; | ||
| /** | ||
| * Throw if the provided value is not a valid {@link WorkspaceConfig} | ||
| */ | ||
| export declare const assertWSConfig: (conf: unknown, path?: string) => asserts conf is WorkspaceConfig; | ||
| export type MonorepoOptions = { | ||
| /** | ||
| * A {@link PackageJson} object, for sharing manifest caches | ||
| */ | ||
| packageJson?: PackageJson; | ||
| /** | ||
| * A {@link PathScurry} object, for use in globs | ||
| */ | ||
| scurry?: PathScurry; | ||
| /** | ||
| * Parsed normalized contents of the workspaces from a `vlt.json` | ||
| * file | ||
| */ | ||
| config?: WorkspaceConfigObject; | ||
| /** | ||
| * If set, then {@link Monorepo#load} will be called immediately with | ||
| * this argument. | ||
| */ | ||
| load?: LoadQuery; | ||
| }; | ||
| /** | ||
| * Class representing a Monorepo containing multiple workspaces. | ||
| * | ||
| * Does not automatically look up the root, but that can be provided by | ||
| * running `Config.load()`, since it stops seeking the route when a | ||
| * `vlt.json` file is encountered. | ||
| */ | ||
| export declare class Monorepo { | ||
| #private; | ||
| /** The project root where vlt.json is found */ | ||
| projectRoot: string; | ||
| /** Scurry object to cache all filesystem calls (mostly globs) */ | ||
| scurry: PathScurry; | ||
| packageJson: PackageJson; | ||
| /** | ||
| * Number of {@link Workspace} objects loaded in this Monorepo | ||
| */ | ||
| get size(): number; | ||
| constructor(projectRoot: string, options?: MonorepoOptions); | ||
| /** | ||
| * Load the workspace definitions from vlt.json, | ||
| * canonicalizing the result into the effective `{[group:string]:string[]}` | ||
| * form. | ||
| * | ||
| * Eg: | ||
| * - `"src/*"` => `{packages:["src/*"]}` | ||
| * - `{"apps": "src/*"}` => `{apps: ["src/*"]}` | ||
| */ | ||
| get config(): WorkspaceConfigObject; | ||
| /** | ||
| * Iterating the Monorepo object yields the workspace objects, in as close to | ||
| * topological dependency order as possible. | ||
| */ | ||
| [Symbol.iterator](): Generator<Workspace, void, void>; | ||
| /** | ||
| * Iterating the Monorepo object yields the workspace objects, in as close to | ||
| * topological dependency order as possible. | ||
| */ | ||
| [Symbol.asyncIterator](): AsyncGenerator<Workspace, void, void>; | ||
| /** | ||
| * By default, loads all workspaces reachable in the Monorepo. | ||
| * | ||
| * If provided with one (`string`)or more (`string[]`) group names in | ||
| * the {@link LoadQuery#groups} field, then only Workspaces in the named | ||
| * group(s) will be considered. Note that group names are unique string | ||
| * matches, not globs. | ||
| * | ||
| * If provided with a set of arbitrary path arguments, then only paths | ||
| * patching the provided pattern(s) will be included. | ||
| * | ||
| * These two options intersect, so | ||
| * `load({groups:'foo', paths:'./foo/[xy]*'})` will only load the workspaces | ||
| * in the group `foo` that match the paths glob. | ||
| */ | ||
| load(query?: LoadQuery): this; | ||
| /** | ||
| * Return the array of workspace dependencies that are found in | ||
| * the loaded set, for use in calculating dependency graph order for | ||
| * build operations. | ||
| * | ||
| * This does *not* get the full set of dependencies, or expand any | ||
| * `workspace:` dependencies that are not loaded. | ||
| * | ||
| * Call with the `forceLoad` param set to `true` to attempt a full | ||
| * load if any deps are not currently loaded. | ||
| */ | ||
| getDeps(ws: Workspace, forceLoad?: boolean): Workspace[]; | ||
| onCycle(_ws: Workspace, _cycle: Workspace[], _depPath: Workspace[]): void; | ||
| /** | ||
| * Return the set of workspaces in the named group. | ||
| * If the group is not one we know about, then undefined is returned. | ||
| */ | ||
| group(group: string): Set<Workspace> | undefined; | ||
| /** | ||
| * Get a loaded workspace by path or name. | ||
| * | ||
| * Note that this can only return workspaces that were ingested via a | ||
| * previous call to {@link Monorepo#load}. | ||
| */ | ||
| get(nameOrPath: string): Workspace | undefined; | ||
| /** | ||
| * get the list of all loaded workspace names used as keys | ||
| */ | ||
| names(): Generator<string, void, unknown>; | ||
| /** | ||
| * get the list of all loaded workspace paths used as keys | ||
| */ | ||
| paths(): Generator<string, void, unknown>; | ||
| /** | ||
| * get the workspace objects in no particular order. | ||
| * this is ever so slightly faster than iterating, because it doesn't | ||
| * explore the graph to yield results in topological dependency order, | ||
| * and should be used instead when order doesn't matter. | ||
| */ | ||
| values(): Generator<Workspace, void, unknown>; | ||
| /** | ||
| * Get all the keys (package names and paths) for loaded workspaces. | ||
| * Union of {@link Monorepo#names} and {@link Monorepo#paths} | ||
| */ | ||
| keys(): Generator<string, void, unknown>; | ||
| /** | ||
| * Filter the monorepo object yielding the workspace objects that matches | ||
| * either of the {@link WorkspacesLoadedConfig} options provided, in as close | ||
| * to topological dependency order as possible. | ||
| */ | ||
| filter({ workspace: namesOrPaths, 'workspace-group': groupName, }: WorkspacesLoadedConfig): Generator<Workspace, void, unknown>; | ||
| /** | ||
| * Run an operation asynchronously over all loaded workspaces | ||
| * | ||
| * If the `forceLoad` param is true, then it will attempt to do a full load | ||
| * when encountering a `workspace:` dependency that isn't loaded. | ||
| * | ||
| * Note that because the return type appears in the parameters of the | ||
| * operation function, it must be set explicitly either in the operation | ||
| * function signature or by calling `run<MyType>` or it'll fall back to | ||
| * `unknown`, similar to `Array.reduce()`, and for the same reason. | ||
| */ | ||
| run<R>(operation: (s: Workspace, signal: AbortSignal, depResults: DepResults<Workspace, R>) => Promise<R> | R, forceLoad?: boolean): Promise<Map<Workspace, R>>; | ||
| /** | ||
| * Run an operation synchronously over all loaded workspaces | ||
| * | ||
| * If the `forceLoad` param is true, then it will attempt to do a full load | ||
| * when encountering a `workspace:` dependency that isn't loaded. | ||
| * | ||
| * Note that because the return type appears in the parameters of the | ||
| * operation function, it must be set explicitly either in the operation | ||
| * function signature or by calling `runSync<MyType>` or it'll fall back to | ||
| * `unknown`, similar to `Array.reduce()`, and for the same reason. | ||
| */ | ||
| runSync<R>(operation: (s: Workspace, signal: AbortSignal, depResults: DepResults<Workspace, R>) => R, forceLoad?: boolean): Map<Workspace, R>; | ||
| /** | ||
| * Convenience method to instantiate and load in one call. | ||
| * Returns undefined if the project is not a monorepo workspaces | ||
| * root, otherwise returns the loaded Monorepo. | ||
| */ | ||
| static maybeLoad(projectRoot: string, options?: MonorepoOptions): Monorepo | undefined; | ||
| /** | ||
| * Convenience method to instantiate and load in one call. | ||
| * Throws if called on a directory that is not a workspaces root. | ||
| */ | ||
| static load(projectRoot: string, options?: MonorepoOptions): Monorepo; | ||
| } | ||
| export declare const workspaceCache: Map<string, Workspace>; | ||
| /** | ||
| * Class representing a single Workspace in a {@link Monorepo} | ||
| */ | ||
| export declare class Workspace { | ||
| #private; | ||
| id: DepID; | ||
| path: string; | ||
| fullpath: string; | ||
| manifest: NormalizedManifest; | ||
| groups: string[]; | ||
| name: string; | ||
| constructor(path: string, manifest: NormalizedManifest, fullpath: string); | ||
| get keys(): string[]; | ||
| } | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} |
-512
| import { joinDepIDTuple } from '@vltpkg/dep-id'; | ||
| import { error } from '@vltpkg/error-cause'; | ||
| import { PackageJson } from '@vltpkg/package-json'; | ||
| import { load } from '@vltpkg/vlt-json'; | ||
| import { globSync } from 'glob'; | ||
| import { graphRun, graphRunSync } from '@vltpkg/graph-run'; | ||
| import { minimatch } from 'minimatch'; | ||
| import { basename, posix, resolve } from 'node:path'; | ||
| import { PathScurry } from 'path-scurry'; | ||
| /** | ||
| * Turn a {@link WorkspaceConfig} into a | ||
| * {@link WorkspaceConfigObject}, or throw if it's not valid. | ||
| */ | ||
| export const asWSConfig = (conf, path) => { | ||
| assertWSConfig(conf, path); | ||
| return (typeof conf === 'string' ? { packages: [conf] } | ||
| : Array.isArray(conf) ? { packages: conf } | ||
| : Object.fromEntries(Object.entries(conf).map(([k, v]) => [ | ||
| k, | ||
| typeof v === 'string' ? [v] : v, | ||
| ]))); | ||
| }; | ||
| /** | ||
| * Throw if the provided value is not a valid {@link WorkspaceConfig} | ||
| */ | ||
| export const assertWSConfig = (conf, path) => { | ||
| if (typeof conf === 'string') | ||
| return; | ||
| if (Array.isArray(conf)) { | ||
| for (const c of conf) { | ||
| if (typeof c !== 'string') { | ||
| throw error('Invalid workspace definition', { | ||
| path, | ||
| found: c, | ||
| wanted: 'string', | ||
| }); | ||
| } | ||
| } | ||
| return; | ||
| } | ||
| if (conf && typeof conf === 'object') { | ||
| for (const [group, value] of Object.entries(conf)) { | ||
| if (typeof value === 'string') | ||
| continue; | ||
| if (Array.isArray(value)) { | ||
| for (const c of value) { | ||
| if (typeof c !== 'string') { | ||
| throw error('Invalid workspace definition', { | ||
| path, | ||
| name: group, | ||
| found: c, | ||
| wanted: 'string', | ||
| }); | ||
| } | ||
| } | ||
| continue; | ||
| } | ||
| throw error('Invalid workspace definition', { | ||
| path, | ||
| name: group, | ||
| found: value, | ||
| wanted: 'string | string[]', | ||
| }); | ||
| } | ||
| return; | ||
| } | ||
| throw error('Invalid workspace definition', { | ||
| path, | ||
| found: conf, | ||
| wanted: 'string | string[] | { [group: string]: string | string[] }', | ||
| }); | ||
| }; | ||
| /** | ||
| * Class representing a Monorepo containing multiple workspaces. | ||
| * | ||
| * Does not automatically look up the root, but that can be provided by | ||
| * running `Config.load()`, since it stops seeking the route when a | ||
| * `vlt.json` file is encountered. | ||
| */ | ||
| export class Monorepo { | ||
| /** The project root where vlt.json is found */ | ||
| projectRoot; | ||
| /** Scurry object to cache all filesystem calls (mostly globs) */ | ||
| scurry; | ||
| // maps both name and path to the workspace objects | ||
| #workspaces = new Map(); | ||
| #groups = new Map(); | ||
| #config; | ||
| packageJson; | ||
| /** | ||
| * Number of {@link Workspace} objects loaded in this Monorepo | ||
| */ | ||
| get size() { | ||
| return [...this.values()].length; | ||
| } | ||
| constructor(projectRoot, options = {}) { | ||
| this.projectRoot = resolve(projectRoot); | ||
| this.scurry = options.scurry ?? new PathScurry(projectRoot); | ||
| this.packageJson = options.packageJson ?? new PackageJson(); | ||
| this.#config = options.config; | ||
| if (options.load) | ||
| this.load(options.load); | ||
| } | ||
| /** | ||
| * Load the workspace definitions from vlt.json, | ||
| * canonicalizing the result into the effective `{[group:string]:string[]}` | ||
| * form. | ||
| * | ||
| * Eg: | ||
| * - `"src/*"` => `{packages:["src/*"]}` | ||
| * - `{"apps": "src/*"}` => `{apps: ["src/*"]}` | ||
| */ | ||
| get config() { | ||
| if (this.#config) | ||
| return this.#config; | ||
| this.#config = asWSConfig(load('workspaces', assertWSConfig) ?? {}); | ||
| return this.#config; | ||
| } | ||
| /** | ||
| * Iterating the Monorepo object yields the workspace objects, in as close to | ||
| * topological dependency order as possible. | ||
| */ | ||
| *[Symbol.iterator]() { | ||
| const [ws] = [...this.values()]; | ||
| if (!ws) | ||
| return; | ||
| // leverage the fact that graphRun returns results in | ||
| // as close to topological order as possible. | ||
| for (const workspace of this.runSync(() => { }).keys()) { | ||
| yield workspace; | ||
| } | ||
| } | ||
| /** | ||
| * Iterating the Monorepo object yields the workspace objects, in as close to | ||
| * topological dependency order as possible. | ||
| */ | ||
| async *[Symbol.asyncIterator]() { | ||
| const [ws] = [...this.values()]; | ||
| if (!ws) | ||
| return; | ||
| for (const workspace of (await this.run(() => { })).keys()) { | ||
| yield workspace; | ||
| } | ||
| } | ||
| /** | ||
| * By default, loads all workspaces reachable in the Monorepo. | ||
| * | ||
| * If provided with one (`string`)or more (`string[]`) group names in | ||
| * the {@link LoadQuery#groups} field, then only Workspaces in the named | ||
| * group(s) will be considered. Note that group names are unique string | ||
| * matches, not globs. | ||
| * | ||
| * If provided with a set of arbitrary path arguments, then only paths | ||
| * patching the provided pattern(s) will be included. | ||
| * | ||
| * These two options intersect, so | ||
| * `load({groups:'foo', paths:'./foo/[xy]*'})` will only load the workspaces | ||
| * in the group `foo` that match the paths glob. | ||
| */ | ||
| load(query = {}) { | ||
| const paths = new Set(typeof query.paths === 'string' ? | ||
| [query.paths] | ||
| : (query.paths ?? [])); | ||
| const groups = new Set(typeof query.groups === 'string' ? | ||
| [query.groups] | ||
| : (query.groups ?? [])); | ||
| const groupsExpanded = {}; | ||
| for (const [group, pattern] of Object.entries(this.config)) { | ||
| if (groups.size && !groups.has(group)) | ||
| continue; | ||
| groupsExpanded[group] = this.#glob(pattern); | ||
| } | ||
| const filter = paths.size ? this.#glob([...paths]) : paths; | ||
| // if we specified paths, but none matched, nothing to do | ||
| if (paths.size && !filter.size) | ||
| return this; | ||
| for (const [group, matches] of Object.entries(groupsExpanded)) { | ||
| for (const path of matches) { | ||
| if (filter.size && !filter.has(path)) | ||
| continue; | ||
| this.#loadWS(path, group); | ||
| } | ||
| } | ||
| return this; | ||
| } | ||
| // Either load a workspace from disk, or from our internal set, | ||
| // and assign it to the named group | ||
| #loadWS(path, group) { | ||
| const fullpath = resolve(this.projectRoot, path); | ||
| const loaded = this.#workspaces.get(fullpath); | ||
| if (loaded) | ||
| return loaded; | ||
| const fromCache = workspaceCache.get(fullpath); | ||
| const manifest = fromCache?.manifest ?? this.packageJson.read(fullpath); | ||
| const ws = fromCache ?? new Workspace(path, manifest, fullpath); | ||
| if (group) | ||
| ws.groups.push(group); | ||
| // Check for duplicate workspace names | ||
| const existingWS = this.#workspaces.get(ws.name); | ||
| if (existingWS && existingWS.fullpath !== ws.fullpath) { | ||
| throw error('Duplicate workspace name found', { | ||
| name: ws.name, | ||
| path: this.projectRoot, | ||
| wanted: ws.fullpath, | ||
| found: existingWS.fullpath, | ||
| }); | ||
| } | ||
| this.#workspaces.set(ws.fullpath, ws); | ||
| this.#workspaces.set(ws.path, ws); | ||
| this.#workspaces.set(ws.name, ws); | ||
| for (const name of ws.groups) { | ||
| const group = this.#groups.get(name) ?? new Set(); | ||
| group.add(ws); | ||
| this.#groups.set(name, group); | ||
| } | ||
| return ws; | ||
| } | ||
| // can't be cached, because it's dependent on the matches set | ||
| // but still worthwhile to have it defined in one place | ||
| #globOptions(matches) { | ||
| // if the entry or any of its parent dirs are already matched, | ||
| // then we should not explore further down that directory tree. | ||
| // if we hit the projectRoot then stop searching. | ||
| const inMatches = (p) => { | ||
| return (!!p?.relativePosix() && | ||
| (matches.has(p.relativePosix()) || inMatches(p.parent))); | ||
| }; | ||
| return { | ||
| root: this.projectRoot, | ||
| cwd: this.projectRoot, | ||
| posix: true, | ||
| scurry: this.scurry, | ||
| withFileTypes: false, | ||
| ignore: { | ||
| childrenIgnored: p => basename(p.relativePosix()) === 'node_modules' || | ||
| inMatches(p), | ||
| // ignore if fails to load package.json | ||
| ignored: p => { | ||
| p.lstatSync(); | ||
| const rel = p.relativePosix(); | ||
| if (!rel) | ||
| return true; | ||
| const maybeDelete = []; | ||
| for (const m of matches) { | ||
| if (rel.startsWith(m + '/')) | ||
| return true; | ||
| if (m.startsWith(rel + '/')) { | ||
| maybeDelete.push(m); | ||
| } | ||
| } | ||
| if (!p.isDirectory()) | ||
| return true; | ||
| const pj = p.resolve('package.json').lstatSync(); | ||
| if (!pj?.isFile()) | ||
| return true; | ||
| try { | ||
| this.packageJson.read(p.fullpath()); | ||
| } | ||
| catch { | ||
| return true; | ||
| } | ||
| for (const m of maybeDelete) { | ||
| matches.delete(m); | ||
| } | ||
| matches.add(rel); | ||
| return false; | ||
| }, | ||
| }, | ||
| }; | ||
| } | ||
| #glob(pattern) { | ||
| const matches = new Set(); | ||
| globSync(pattern, this.#globOptions(matches)); | ||
| return matches; | ||
| } | ||
| /** | ||
| * Return the array of workspace dependencies that are found in | ||
| * the loaded set, for use in calculating dependency graph order for | ||
| * build operations. | ||
| * | ||
| * This does *not* get the full set of dependencies, or expand any | ||
| * `workspace:` dependencies that are not loaded. | ||
| * | ||
| * Call with the `forceLoad` param set to `true` to attempt a full | ||
| * load if any deps are not currently loaded. | ||
| */ | ||
| getDeps(ws, forceLoad = false) { | ||
| // load manifest and find workspace: deps | ||
| // filter by those loaded | ||
| const { manifest } = ws; | ||
| const depWorkspaces = []; | ||
| let didForceLoad = false; | ||
| for (const depType of [ | ||
| 'dependencies', | ||
| 'devDependencies', | ||
| 'optionalDependencies', | ||
| 'peerDependencies', | ||
| ]) { | ||
| const deps = manifest[depType]; | ||
| if (!deps) | ||
| continue; | ||
| for (const [dep, spec] of Object.entries(deps)) { | ||
| if (spec.startsWith('workspace:')) { | ||
| let depWS = this.#workspaces.get(dep); | ||
| if (!depWS) { | ||
| if (!forceLoad) | ||
| continue; | ||
| if (didForceLoad) | ||
| continue; | ||
| didForceLoad = true; | ||
| this.load(); | ||
| depWS = this.#workspaces.get(dep); | ||
| if (!depWS) | ||
| continue; | ||
| } | ||
| depWorkspaces.push(depWS); | ||
| } | ||
| } | ||
| } | ||
| return depWorkspaces; | ||
| } | ||
| onCycle(_ws, _cycle, _depPath) { | ||
| // XXX - process logging? Need to say something like: | ||
| // Cyclical workspace dependency warning! | ||
| // When evaluating dependency ${ws.name} via ${ | ||
| // path.map(ws => ws.name).join(' -> ') | ||
| // }, a dependency cycle was detected: ${ | ||
| // cycle.map(ws => ws.name).join(' -> ') | ||
| // }. Operation will continue, but dependency order not guaranteed.` | ||
| } | ||
| /** | ||
| * Return the set of workspaces in the named group. | ||
| * If the group is not one we know about, then undefined is returned. | ||
| */ | ||
| group(group) { | ||
| return this.#groups.get(group); | ||
| } | ||
| /** | ||
| * Get a loaded workspace by path or name. | ||
| * | ||
| * Note that this can only return workspaces that were ingested via a | ||
| * previous call to {@link Monorepo#load}. | ||
| */ | ||
| get(nameOrPath) { | ||
| return this.#workspaces.get(nameOrPath); | ||
| } | ||
| /** | ||
| * get the list of all loaded workspace names used as keys | ||
| */ | ||
| *names() { | ||
| for (const [key, ws] of this.#workspaces) { | ||
| if (key === ws.name) | ||
| yield key; | ||
| } | ||
| } | ||
| /** | ||
| * get the list of all loaded workspace paths used as keys | ||
| */ | ||
| *paths() { | ||
| for (const [key, ws] of this.#workspaces) { | ||
| if (key === ws.path) | ||
| yield key; | ||
| } | ||
| } | ||
| /** | ||
| * get the workspace objects in no particular order. | ||
| * this is ever so slightly faster than iterating, because it doesn't | ||
| * explore the graph to yield results in topological dependency order, | ||
| * and should be used instead when order doesn't matter. | ||
| */ | ||
| *values() { | ||
| const seen = new Set(); | ||
| for (const ws of this.#workspaces.values()) { | ||
| if (seen.has(ws.fullpath)) | ||
| continue; | ||
| seen.add(ws.fullpath); | ||
| yield ws; | ||
| } | ||
| } | ||
| /** | ||
| * Get all the keys (package names and paths) for loaded workspaces. | ||
| * Union of {@link Monorepo#names} and {@link Monorepo#paths} | ||
| */ | ||
| *keys() { | ||
| for (const ws of this.values()) { | ||
| yield ws.path; | ||
| if (ws.name !== ws.path) | ||
| yield ws.name; | ||
| } | ||
| } | ||
| /** | ||
| * Filter the monorepo object yielding the workspace objects that matches | ||
| * either of the {@link WorkspacesLoadedConfig} options provided, in as close | ||
| * to topological dependency order as possible. | ||
| */ | ||
| *filter({ workspace: namesOrPaths, 'workspace-group': groupName, }) { | ||
| const globPatternChecks = namesOrPaths?.map(glob => minimatch.filter(posix.join(glob))); | ||
| for (const ws of this) { | ||
| // check if any group has any of the provided group names | ||
| if (groupName?.some(i => ws.groups.includes(i))) { | ||
| yield ws; | ||
| continue; | ||
| } | ||
| // check if any workspace-provided name directly matches any of the | ||
| // configured workspaces by either name or file path | ||
| if (namesOrPaths | ||
| ?.map(i => posix.join(i)) | ||
| .some(i => ws.keys.includes(i))) { | ||
| yield ws; | ||
| continue; | ||
| } | ||
| // check if one of the workspace values are matching glob patterns | ||
| if (ws.keys.some(key => globPatternChecks?.some(fn => fn(key)))) { | ||
| yield ws; | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Run an operation asynchronously over all loaded workspaces | ||
| * | ||
| * If the `forceLoad` param is true, then it will attempt to do a full load | ||
| * when encountering a `workspace:` dependency that isn't loaded. | ||
| * | ||
| * Note that because the return type appears in the parameters of the | ||
| * operation function, it must be set explicitly either in the operation | ||
| * function signature or by calling `run<MyType>` or it'll fall back to | ||
| * `unknown`, similar to `Array.reduce()`, and for the same reason. | ||
| */ | ||
| async run(operation, forceLoad = false) { | ||
| const [ws, ...rest] = [...this.#workspaces.values()]; | ||
| if (!ws) { | ||
| throw error('No workspaces loaded', undefined, this.run); | ||
| } | ||
| return graphRun({ | ||
| graph: [ws, ...rest], | ||
| getDeps: ws => this.getDeps(ws, forceLoad), | ||
| visit: async (ws, signal, _, depResults) => await operation(ws, signal, depResults), | ||
| onCycle: (ws, cycle, path) => this.onCycle(ws, cycle, path), | ||
| }); | ||
| } | ||
| /** | ||
| * Run an operation synchronously over all loaded workspaces | ||
| * | ||
| * If the `forceLoad` param is true, then it will attempt to do a full load | ||
| * when encountering a `workspace:` dependency that isn't loaded. | ||
| * | ||
| * Note that because the return type appears in the parameters of the | ||
| * operation function, it must be set explicitly either in the operation | ||
| * function signature or by calling `runSync<MyType>` or it'll fall back to | ||
| * `unknown`, similar to `Array.reduce()`, and for the same reason. | ||
| */ | ||
| runSync(operation, forceLoad = false) { | ||
| const [ws, ...rest] = [...this.#workspaces.values()]; | ||
| if (!ws) { | ||
| throw error('No workspaces loaded', undefined, this.run); | ||
| } | ||
| return graphRunSync({ | ||
| graph: [ws, ...rest], | ||
| getDeps: ws => this.getDeps(ws, forceLoad), | ||
| visit: (ws, signal, _, depResults) => operation(ws, signal, depResults), | ||
| onCycle: (ws, cycle, path) => this.onCycle(ws, cycle, path), | ||
| }); | ||
| } | ||
| /** | ||
| * Convenience method to instantiate and load in one call. | ||
| * Returns undefined if the project is not a monorepo workspaces | ||
| * root, otherwise returns the loaded Monorepo. | ||
| */ | ||
| static maybeLoad(projectRoot, options = { load: {} }) { | ||
| const config = load('workspaces', assertWSConfig); | ||
| if (!config) | ||
| return; | ||
| return new Monorepo(projectRoot, { load: {}, ...options }); | ||
| } | ||
| /** | ||
| * Convenience method to instantiate and load in one call. | ||
| * Throws if called on a directory that is not a workspaces root. | ||
| */ | ||
| static load(projectRoot, options = { load: {} }) { | ||
| const { load = {} } = options; | ||
| return new Monorepo(projectRoot, { ...options, load }); | ||
| } | ||
| } | ||
| export const workspaceCache = new Map(); | ||
| /** | ||
| * Class representing a single Workspace in a {@link Monorepo} | ||
| */ | ||
| export class Workspace { | ||
| id; | ||
| path; | ||
| fullpath; | ||
| manifest; | ||
| groups = []; | ||
| name; | ||
| #keys; | ||
| constructor(path, manifest, fullpath) { | ||
| this.id = joinDepIDTuple(['workspace', path]); | ||
| workspaceCache.set(fullpath, this); | ||
| this.path = path; | ||
| this.fullpath = fullpath; | ||
| this.manifest = manifest; | ||
| this.name = manifest.name ?? path; | ||
| } | ||
| get keys() { | ||
| if (this.#keys) { | ||
| return this.#keys; | ||
| } | ||
| this.#keys = [this.name, this.path, this.fullpath]; | ||
| return this.#keys; | ||
| } | ||
| } | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DepID } from '@vltpkg/dep-id'\nimport { joinDepIDTuple } from '@vltpkg/dep-id'\nimport { error } from '@vltpkg/error-cause'\nimport { PackageJson } from '@vltpkg/package-json'\nimport type { NormalizedManifest } from '@vltpkg/types'\nimport { load } from '@vltpkg/vlt-json'\nimport type { GlobOptionsWithFileTypesFalse } from 'glob'\nimport { globSync } from 'glob'\nimport type { DepResults } from '@vltpkg/graph-run'\nimport { graphRun, graphRunSync } from '@vltpkg/graph-run'\nimport { minimatch } from 'minimatch'\nimport { basename, posix, resolve } from 'node:path'\nimport type { Path } from 'path-scurry'\nimport { PathScurry } from 'path-scurry'\n\nexport type WorkspacesLoadedConfig = {\n workspace?: string[]\n 'workspace-group'?: string[]\n}\n\n/**\n * The object passed to the constructor or {@link Monorepo#load} to limit which\n * {@link Workspace Workspaces} get loaded.\n */\nexport type LoadQuery = {\n /**\n * A glob pattern string, or an array of them. Only workspaces found\n * in paths matched will be loaded.\n */\n paths?: string[] | string\n /**\n * A string, or an array of strings. If set, only workspaces in the\n * specified groups named will be included, if set.\n */\n groups?: string[] | string\n}\n\n/**\n * Canonical form of the {@link WorkspaceConfig}, used\n * internally for consistency.\n */\nexport type WorkspaceConfigObject = Record<string, string[]>\n\n/**\n * Allowed datatype in the `workspaces` field of the `vlt.json` file.\n */\nexport type WorkspaceConfig =\n | string[]\n | WorkspaceConfigObject\n | string\n\n/**\n * Turn a {@link WorkspaceConfig} into a\n * {@link WorkspaceConfigObject}, or throw if it's not valid.\n */\nexport const asWSConfig = (\n conf: unknown,\n path?: string,\n): WorkspaceConfigObject => {\n assertWSConfig(conf, path)\n return (\n typeof conf === 'string' ? { packages: [conf] }\n : Array.isArray(conf) ? { packages: conf }\n : Object.fromEntries(\n Object.entries(conf).map(([k, v]) => [\n k,\n typeof v === 'string' ? [v] : v,\n ]),\n )\n )\n}\n\n/**\n * Throw if the provided value is not a valid {@link WorkspaceConfig}\n */\nexport const assertWSConfig: (\n conf: unknown,\n path?: string,\n) => asserts conf is WorkspaceConfig = (\n conf: unknown,\n path?: string,\n) => {\n if (typeof conf === 'string') return\n\n if (Array.isArray(conf)) {\n for (const c of conf) {\n if (typeof c !== 'string') {\n throw error('Invalid workspace definition', {\n path,\n found: c as unknown,\n wanted: 'string',\n })\n }\n }\n return\n }\n\n if (conf && typeof conf === 'object') {\n for (const [group, value] of Object.entries(conf)) {\n if (typeof value === 'string') continue\n if (Array.isArray(value)) {\n for (const c of value) {\n if (typeof c !== 'string') {\n throw error('Invalid workspace definition', {\n path,\n name: group,\n found: c as unknown,\n wanted: 'string',\n })\n }\n }\n continue\n }\n throw error('Invalid workspace definition', {\n path,\n name: group,\n found: value as unknown,\n wanted: 'string | string[]',\n })\n }\n return\n }\n\n throw error('Invalid workspace definition', {\n path,\n found: conf,\n wanted:\n 'string | string[] | { [group: string]: string | string[] }',\n })\n}\n\nexport type MonorepoOptions = {\n /**\n * A {@link PackageJson} object, for sharing manifest caches\n */\n packageJson?: PackageJson\n /**\n * A {@link PathScurry} object, for use in globs\n */\n scurry?: PathScurry\n /**\n * Parsed normalized contents of the workspaces from a `vlt.json`\n * file\n */\n config?: WorkspaceConfigObject\n /**\n * If set, then {@link Monorepo#load} will be called immediately with\n * this argument.\n */\n load?: LoadQuery\n}\n\n/**\n * Class representing a Monorepo containing multiple workspaces.\n *\n * Does not automatically look up the root, but that can be provided by\n * running `Config.load()`, since it stops seeking the route when a\n * `vlt.json` file is encountered.\n */\nexport class Monorepo {\n /** The project root where vlt.json is found */\n projectRoot: string\n /** Scurry object to cache all filesystem calls (mostly globs) */\n scurry: PathScurry\n\n // maps both name and path to the workspace objects\n #workspaces = new Map<string, Workspace>()\n #groups = new Map<string, Set<Workspace>>()\n #config?: WorkspaceConfigObject\n packageJson: PackageJson\n\n /**\n * Number of {@link Workspace} objects loaded in this Monorepo\n */\n get size(): number {\n return [...this.values()].length\n }\n\n constructor(projectRoot: string, options: MonorepoOptions = {}) {\n this.projectRoot = resolve(projectRoot)\n this.scurry = options.scurry ?? new PathScurry(projectRoot)\n this.packageJson = options.packageJson ?? new PackageJson()\n this.#config = options.config\n if (options.load) this.load(options.load)\n }\n\n /**\n * Load the workspace definitions from vlt.json,\n * canonicalizing the result into the effective `{[group:string]:string[]}`\n * form.\n *\n * Eg:\n * - `\"src/*\"` => `{packages:[\"src/*\"]}`\n * - `{\"apps\": \"src/*\"}` => `{apps: [\"src/*\"]}`\n */\n get config(): WorkspaceConfigObject {\n if (this.#config) return this.#config\n this.#config = asWSConfig(\n load('workspaces', assertWSConfig) ?? {},\n )\n return this.#config\n }\n\n /**\n * Iterating the Monorepo object yields the workspace objects, in as close to\n * topological dependency order as possible.\n */\n *[Symbol.iterator](): Generator<Workspace, void, void> {\n const [ws] = [...this.values()]\n if (!ws) return\n // leverage the fact that graphRun returns results in\n // as close to topological order as possible.\n for (const workspace of this.runSync(() => {}).keys()) {\n yield workspace\n }\n }\n\n /**\n * Iterating the Monorepo object yields the workspace objects, in as close to\n * topological dependency order as possible.\n */\n async *[Symbol.asyncIterator](): AsyncGenerator<\n Workspace,\n void,\n void\n > {\n const [ws] = [...this.values()]\n if (!ws) return\n for (const workspace of (await this.run(() => {})).keys()) {\n yield workspace\n }\n }\n\n /**\n * By default, loads all workspaces reachable in the Monorepo.\n *\n * If provided with one (`string`)or more (`string[]`) group names in\n * the {@link LoadQuery#groups} field, then only Workspaces in the named\n * group(s) will be considered. Note that group names are unique string\n * matches, not globs.\n *\n * If provided with a set of arbitrary path arguments, then only paths\n * patching the provided pattern(s) will be included.\n *\n * These two options intersect, so\n * `load({groups:'foo', paths:'./foo/[xy]*'})` will only load the workspaces\n * in the group `foo` that match the paths glob.\n */\n load(query: LoadQuery = {}): this {\n const paths = new Set(\n typeof query.paths === 'string' ?\n [query.paths]\n : (query.paths ?? []),\n )\n const groups = new Set(\n typeof query.groups === 'string' ?\n [query.groups]\n : (query.groups ?? []),\n )\n\n const groupsExpanded: Record<string, Set<string>> = {}\n for (const [group, pattern] of Object.entries(this.config)) {\n if (groups.size && !groups.has(group)) continue\n groupsExpanded[group] = this.#glob(pattern)\n }\n const filter = paths.size ? this.#glob([...paths]) : paths\n\n // if we specified paths, but none matched, nothing to do\n if (paths.size && !filter.size) return this\n\n for (const [group, matches] of Object.entries(groupsExpanded)) {\n for (const path of matches) {\n if (filter.size && !filter.has(path)) continue\n this.#loadWS(path, group)\n }\n }\n\n return this\n }\n\n // Either load a workspace from disk, or from our internal set,\n // and assign it to the named group\n #loadWS(path: string, group?: string): Workspace {\n const fullpath = resolve(this.projectRoot, path)\n const loaded = this.#workspaces.get(fullpath)\n if (loaded) return loaded\n const fromCache = workspaceCache.get(fullpath)\n const manifest =\n fromCache?.manifest ?? this.packageJson.read(fullpath)\n const ws = fromCache ?? new Workspace(path, manifest, fullpath)\n if (group) ws.groups.push(group)\n\n // Check for duplicate workspace names\n const existingWS = this.#workspaces.get(ws.name)\n if (existingWS && existingWS.fullpath !== ws.fullpath) {\n throw error('Duplicate workspace name found', {\n name: ws.name,\n path: this.projectRoot,\n wanted: ws.fullpath,\n found: existingWS.fullpath,\n })\n }\n\n this.#workspaces.set(ws.fullpath, ws)\n this.#workspaces.set(ws.path, ws)\n this.#workspaces.set(ws.name, ws)\n for (const name of ws.groups) {\n const group = this.#groups.get(name) ?? new Set()\n group.add(ws)\n this.#groups.set(name, group)\n }\n return ws\n }\n\n // can't be cached, because it's dependent on the matches set\n // but still worthwhile to have it defined in one place\n #globOptions(matches: Set<string>): GlobOptionsWithFileTypesFalse {\n // if the entry or any of its parent dirs are already matched,\n // then we should not explore further down that directory tree.\n // if we hit the projectRoot then stop searching.\n const inMatches = (p?: Path): boolean => {\n return (\n !!p?.relativePosix() &&\n (matches.has(p.relativePosix()) || inMatches(p.parent))\n )\n }\n\n return {\n root: this.projectRoot,\n cwd: this.projectRoot,\n posix: true,\n scurry: this.scurry,\n withFileTypes: false,\n ignore: {\n childrenIgnored: p =>\n basename(p.relativePosix()) === 'node_modules' ||\n inMatches(p),\n // ignore if fails to load package.json\n ignored: p => {\n p.lstatSync()\n const rel = p.relativePosix()\n if (!rel) return true\n const maybeDelete: string[] = []\n for (const m of matches) {\n if (rel.startsWith(m + '/')) return true\n if (m.startsWith(rel + '/')) {\n maybeDelete.push(m)\n }\n }\n if (!p.isDirectory()) return true\n const pj = p.resolve('package.json').lstatSync()\n if (!pj?.isFile()) return true\n try {\n this.packageJson.read(p.fullpath())\n } catch {\n return true\n }\n for (const m of maybeDelete) {\n matches.delete(m)\n }\n matches.add(rel)\n return false\n },\n },\n }\n }\n\n #glob(pattern: string[] | string) {\n const matches = new Set<string>()\n globSync(pattern, this.#globOptions(matches))\n return matches\n }\n\n /**\n * Return the array of workspace dependencies that are found in\n * the loaded set, for use in calculating dependency graph order for\n * build operations.\n *\n * This does *not* get the full set of dependencies, or expand any\n * `workspace:` dependencies that are not loaded.\n *\n * Call with the `forceLoad` param set to `true` to attempt a full\n * load if any deps are not currently loaded.\n */\n getDeps(ws: Workspace, forceLoad = false): Workspace[] {\n // load manifest and find workspace: deps\n // filter by those loaded\n const { manifest } = ws\n const depWorkspaces: Workspace[] = []\n let didForceLoad = false\n for (const depType of [\n 'dependencies',\n 'devDependencies',\n 'optionalDependencies',\n 'peerDependencies',\n ] as const) {\n const deps = manifest[depType]\n if (!deps) continue\n for (const [dep, spec] of Object.entries(deps)) {\n if (spec.startsWith('workspace:')) {\n let depWS = this.#workspaces.get(dep)\n if (!depWS) {\n if (!forceLoad) continue\n if (didForceLoad) continue\n didForceLoad = true\n this.load()\n depWS = this.#workspaces.get(dep)\n if (!depWS) continue\n }\n depWorkspaces.push(depWS)\n }\n }\n }\n return depWorkspaces\n }\n\n onCycle(\n _ws: Workspace,\n _cycle: Workspace[],\n _depPath: Workspace[],\n ) {\n // XXX - process logging? Need to say something like:\n // Cyclical workspace dependency warning!\n // When evaluating dependency ${ws.name} via ${\n // path.map(ws => ws.name).join(' -> ')\n // }, a dependency cycle was detected: ${\n // cycle.map(ws => ws.name).join(' -> ')\n // }. Operation will continue, but dependency order not guaranteed.`\n }\n\n /**\n * Return the set of workspaces in the named group.\n * If the group is not one we know about, then undefined is returned.\n */\n group(group: string) {\n return this.#groups.get(group)\n }\n\n /**\n * Get a loaded workspace by path or name.\n *\n * Note that this can only return workspaces that were ingested via a\n * previous call to {@link Monorepo#load}.\n */\n get(nameOrPath: string) {\n return this.#workspaces.get(nameOrPath)\n }\n\n /**\n * get the list of all loaded workspace names used as keys\n */\n *names() {\n for (const [key, ws] of this.#workspaces) {\n if (key === ws.name) yield key\n }\n }\n\n /**\n * get the list of all loaded workspace paths used as keys\n */\n *paths() {\n for (const [key, ws] of this.#workspaces) {\n if (key === ws.path) yield key\n }\n }\n\n /**\n * get the workspace objects in no particular order.\n * this is ever so slightly faster than iterating, because it doesn't\n * explore the graph to yield results in topological dependency order,\n * and should be used instead when order doesn't matter.\n */\n *values() {\n const seen = new Set<string>()\n for (const ws of this.#workspaces.values()) {\n if (seen.has(ws.fullpath)) continue\n seen.add(ws.fullpath)\n yield ws\n }\n }\n\n /**\n * Get all the keys (package names and paths) for loaded workspaces.\n * Union of {@link Monorepo#names} and {@link Monorepo#paths}\n */\n *keys() {\n for (const ws of this.values()) {\n yield ws.path\n if (ws.name !== ws.path) yield ws.name\n }\n }\n\n /**\n * Filter the monorepo object yielding the workspace objects that matches\n * either of the {@link WorkspacesLoadedConfig} options provided, in as close\n * to topological dependency order as possible.\n */\n *filter({\n workspace: namesOrPaths,\n 'workspace-group': groupName,\n }: WorkspacesLoadedConfig) {\n const globPatternChecks = namesOrPaths?.map(glob =>\n minimatch.filter(posix.join(glob)),\n )\n for (const ws of this) {\n // check if any group has any of the provided group names\n if (groupName?.some(i => ws.groups.includes(i))) {\n yield ws\n continue\n }\n // check if any workspace-provided name directly matches any of the\n // configured workspaces by either name or file path\n if (\n namesOrPaths\n ?.map(i => posix.join(i))\n .some(i => ws.keys.includes(i))\n ) {\n yield ws\n continue\n }\n // check if one of the workspace values are matching glob patterns\n if (\n ws.keys.some(key => globPatternChecks?.some(fn => fn(key)))\n ) {\n yield ws\n }\n }\n }\n\n /**\n * Run an operation asynchronously over all loaded workspaces\n *\n * If the `forceLoad` param is true, then it will attempt to do a full load\n * when encountering a `workspace:` dependency that isn't loaded.\n *\n * Note that because the return type appears in the parameters of the\n * operation function, it must be set explicitly either in the operation\n * function signature or by calling `run<MyType>` or it'll fall back to\n * `unknown`, similar to `Array.reduce()`, and for the same reason.\n */\n async run<R>(\n operation: (\n s: Workspace,\n signal: AbortSignal,\n depResults: DepResults<Workspace, R>,\n ) => Promise<R> | R,\n forceLoad = false,\n ) {\n const [ws, ...rest] = [...this.#workspaces.values()]\n if (!ws) {\n throw error('No workspaces loaded', undefined, this.run)\n }\n\n return graphRun<Workspace, R>({\n graph: [ws, ...rest],\n getDeps: ws => this.getDeps(ws, forceLoad),\n visit: async (ws, signal, _, depResults) =>\n await operation(ws, signal, depResults),\n onCycle: (ws, cycle, path) => this.onCycle(ws, cycle, path),\n })\n }\n\n /**\n * Run an operation synchronously over all loaded workspaces\n *\n * If the `forceLoad` param is true, then it will attempt to do a full load\n * when encountering a `workspace:` dependency that isn't loaded.\n *\n * Note that because the return type appears in the parameters of the\n * operation function, it must be set explicitly either in the operation\n * function signature or by calling `runSync<MyType>` or it'll fall back to\n * `unknown`, similar to `Array.reduce()`, and for the same reason.\n */\n runSync<R>(\n operation: (\n s: Workspace,\n signal: AbortSignal,\n depResults: DepResults<Workspace, R>,\n ) => R,\n forceLoad = false,\n ) {\n const [ws, ...rest] = [...this.#workspaces.values()]\n if (!ws) {\n throw error('No workspaces loaded', undefined, this.run)\n }\n\n return graphRunSync<Workspace, R>({\n graph: [ws, ...rest],\n getDeps: ws => this.getDeps(ws, forceLoad),\n visit: (ws, signal, _, depResults) =>\n operation(ws, signal, depResults),\n onCycle: (ws, cycle, path) => this.onCycle(ws, cycle, path),\n })\n }\n\n /**\n * Convenience method to instantiate and load in one call.\n * Returns undefined if the project is not a monorepo workspaces\n * root, otherwise returns the loaded Monorepo.\n */\n static maybeLoad(\n projectRoot: string,\n options: MonorepoOptions = { load: {} },\n ) {\n const config = load('workspaces', assertWSConfig)\n if (!config) return\n return new Monorepo(projectRoot, { load: {}, ...options })\n }\n\n /**\n * Convenience method to instantiate and load in one call.\n * Throws if called on a directory that is not a workspaces root.\n */\n static load(\n projectRoot: string,\n options: MonorepoOptions = { load: {} },\n ) {\n const { load = {} } = options\n return new Monorepo(projectRoot, { ...options, load })\n }\n}\n\nexport const workspaceCache = new Map<string, Workspace>()\n\n/**\n * Class representing a single Workspace in a {@link Monorepo}\n */\nexport class Workspace {\n id: DepID\n path: string\n fullpath: string\n manifest: NormalizedManifest\n groups: string[] = []\n name: string\n #keys?: string[]\n\n constructor(\n path: string,\n manifest: NormalizedManifest,\n fullpath: string,\n ) {\n this.id = joinDepIDTuple(['workspace', path])\n workspaceCache.set(fullpath, this)\n this.path = path\n this.fullpath = fullpath\n this.manifest = manifest\n this.name = manifest.name ?? path\n }\n\n get keys(): string[] {\n if (this.#keys) {\n return this.#keys\n }\n this.#keys = [this.name, this.path, this.fullpath]\n return this.#keys\n }\n}\n"]} |
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 8 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
0
-100%8006
-88.8%3
-57.14%0
-100%10
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated