Skip to content

Commit

Permalink
write scoped workspace-root deps
Browse files Browse the repository at this point in the history
  • Loading branch information
arshaw committed Dec 5, 2024
1 parent 1556401 commit 0c6c69d
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 2 deletions.
91 changes: 90 additions & 1 deletion premium/packages/pnpm-make-dedicated-lockfile/lib.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// derived from https://github.com/pnpm/pnpm/blob/main/packages/make-dedicated-lockfile/src/index.ts

import { join as joinPaths, sep as pathSep, isAbsolute } from 'path'
import { join as joinPaths, sep as pathSep, isAbsolute, relative as getRelative } from 'path'
import { getLockfileImporterId, readWantedLockfile, writeWantedLockfile } from '@pnpm/lockfile-file'
import { pruneSharedLockfile } from '@pnpm/prune-lockfile'
import { DEPENDENCIES_FIELDS } from '@pnpm/types'
import { readFile, lstat } from 'fs/promises'

export function readLockfile(rootDir, ignoreIncompatible = true) {
return readWantedLockfile(rootDir, { ignoreIncompatible }) // needs options or fails!
Expand Down Expand Up @@ -59,6 +60,66 @@ export async function makeDedicatedLockfile(rootDir, scopedDir, verbose) {
}
}

// transfer over workspace-root dependencies,
// using scopedDir's manifest as a whitelist
if (
!lockfile.importers[getRelative(rootDir, scopedDir)] && // NOT already a workspace
await hasManifest(scopedDir)
) {
const scopedManifest = await readManifest(scopedDir)
const scopedRootSnapshot = { specifiers: {} }
const rootImporter = lockfile.importers['.']

for (const depField of DEPENDENCIES_FIELDS) {
const depSpecifiers = scopedManifest[depField]

if (depSpecifiers) {
for (const depName in depSpecifiers) {
const depSpecifier = depSpecifiers[depName]
let depLocator = rootImporter[depField]?.[depName]

if (!depLocator) {
throw new Error(
`Scope '${scopedDir}' cannot depend on ${depName} without root also depending on it`
)
}

const depLocatorLink =
depLocator.startsWith(linkPrefix) &&
depLocator.slice(linkPrefix.length)

if (depLocatorLink) {
const scopedDepLocatorLink =
depLocatorLink.startsWith(`${baseImporterId}/`) &&
depLocatorLink.slice(baseImporterId.length + 1)

if (!scopedDepLocatorLink) {
throw new Error(
`Scope '${scopedDir}' cannot depend on ${depLocatorLink} if it is outside`
)
}

depLocator = linkPrefix + scopedDepLocatorLink
} else {
/*
TODO: for registry deps, throw error if specified version conflicts.
if rootImporter.specifiers[depName] !== depSpecifier
*/
}

;(
scopedRootSnapshot[depField] ||
(scopedRootSnapshot[depField] = {})
)[depName] = depLocator

scopedRootSnapshot.specifiers[depName] = depSpecifier
}
}
}

transformedImporters['.'] = scopedRootSnapshot
}

lockfile.importers = transformedImporters

const dedicatedLockfile = pruneSharedLockfile(lockfile)
Expand All @@ -83,6 +144,9 @@ const linkPrefix = 'link:'
function filterLinkedDepsOutOfScope(importerId, snapshot, scopedDir, pkgsOutOfScope) {
const transformedSnapshot = {}

/*
snapshotVal is the hash of dependends/devDependencies/etc
*/
for (const [snapshotKey, snapshotVal] of Object.entries(snapshot)) {
if (!DEPENDENCIES_FIELDS.includes(snapshotKey)) {
// not a dependency-related field. copy as-is
Expand Down Expand Up @@ -161,3 +225,28 @@ async function relinkDeps(rootDir, importerId, snapshot, readManifest) {
specifiers: transformedSpecifiers,
}
}

// UTILS... TODO: make DRY
// -------------------------------------------------------------------------------------------------

function hasManifest(dir) {
return fileExists(joinPaths(dir, 'package.json'))
}

async function readManifest(dir) {
const manifestPath = joinPaths(dir, 'package.json')
return await readJson(manifestPath)
}

function fileExists(path) {
return lstat(path).then(
() => true,
() => false,
)
}

async function readJson(path) {
const srcJson = await readFile(path, 'utf8')
const srcMeta = JSON.parse(srcJson)
return srcMeta
}
2 changes: 1 addition & 1 deletion scripts/src/meta/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as yaml from 'js-yaml'
import { makeDedicatedLockfile, readLockfile, relinkLockfile, writeLockfile } from 'pnpm-make-dedicated-lockfile'
import { addFile, assumeUnchanged } from '@fullcalendar-scripts/standard/utils/git'
import { boolPromise } from '@fullcalendar-scripts/standard/utils/lang'
import { querySubrepoPkgs, readManifest, writeManifest } from './utils.js'
import { querySubrepoPkgs, readManifest } from './utils.js'
import { lockFilename, workspaceFilename, turboFilename, miscSubpaths } from './config.js'

const verbose = true
Expand Down
4 changes: 4 additions & 0 deletions scripts/src/meta/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export async function getSubrepos(monorepoDir: string, singleName?: string) {
// -------------------------------------------------------------------------------------------------
// TODO: DRY with writeDistPkgJsons maybe?

export async function hasManifest(dir: string): Promise<boolean> {
return fileExists(joinPaths(dir, 'package.json'))
}

export async function readManifest(dir: string): Promise<any> {
const manifestPath = joinPaths(dir, 'package.json')
return await readJson(manifestPath)
Expand Down

0 comments on commit 0c6c69d

Please sign in to comment.