Skip to content

Commit

Permalink
recursively search upwards for pnp.cjs
Browse files Browse the repository at this point in the history
  • Loading branch information
lmiller1990 committed Apr 10, 2023
1 parent 01e94a7 commit 775314a
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 105 deletions.
41 changes: 1 addition & 40 deletions packages/scaffold-config/src/ct-detect-third-party.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { z } from 'zod'
import fs from 'fs-extra'
import Debug from 'debug'
import findUp from 'find-up'
import { isRepositoryRoot } from './searchUtils'

const debug = Debug('cypress:scaffold-config:ct-detect-third-party')

Expand All @@ -26,46 +27,6 @@ const thirdPartyDefinitionPrefixes = {
globalPrefix: 'cypress-ct-',
}

const ROOT_PATHS = [
'.git',

// https://pnpm.io/workspaces
'pnpm-workspace.yaml',

// https://rushjs.io/pages/advanced/config_files/
'rush.json',

// https://nx.dev/deprecated/workspace-json#workspace.json
// https://nx.dev/reference/nx-json#nx.json
'workspace.json',
'nx.json',

// https://lerna.js.org/docs/api-reference/configuration
'lerna.json',
]

async function hasWorkspacePackageJson (directory: string) {
try {
const pkg = await fs.readJson(path.join(directory, 'package.json'))

debug('package file for %s: %o', directory, pkg)

return !!pkg.workspaces
} catch (e) {
debug('error reading package.json in %s. this is not the repository root', directory)

return false
}
}

export async function isRepositoryRoot (directory: string) {
if (ROOT_PATHS.some((rootPath) => fs.existsSync(path.join(directory, rootPath)))) {
return true
}

return hasWorkspacePackageJson(directory)
}

export function isThirdPartyDefinition (definition: Cypress.ComponentFrameworkDefinition | Cypress.ThirdPartyComponentFrameworkDefinition): boolean {
return definition.type.startsWith(thirdPartyDefinitionPrefixes.globalPrefix) ||
thirdPartyDefinitionPrefixes.namespacedPrefixRe.test(definition.type)
Expand Down
10 changes: 6 additions & 4 deletions packages/scaffold-config/src/frameworks.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import fs from 'fs-extra'
import path from 'path'
import * as dependencies from './dependencies'
import componentIndexHtmlGenerator from './component-index-template'
import debugLib from 'debug'
import semver from 'semver'
import { isThirdPartyDefinition } from './ct-detect-third-party'
import { tryToFindPnpFile } from './searchUtils'

const debug = debugLib('cypress:scaffold-config:frameworks')

Expand Down Expand Up @@ -34,12 +34,14 @@ export async function isDependencyInstalled (dependency: Cypress.CypressComponen

// we only need to register this once, when the project check dependencies for the first time.
if (!yarnPnpRegistrationPath.has(projectPath)) {
try {
const pnpapi = require(path.join(projectPath, '.pnp.cjs'))
const pnpFile = await tryToFindPnpFile(projectPath)

if (pnpFile) {
const pnpapi = require(pnpFile)

pnpapi.setup()
yarnPnpRegistrationPath.set(projectPath, { usesYarnPnP: true })
} catch (e) {
} else {
// not using Yarn PnP
yarnPnpRegistrationPath.set(projectPath, { usesYarnPnP: false })
}
Expand Down
71 changes: 71 additions & 0 deletions packages/scaffold-config/src/searchUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import findUp from 'find-up'
import path from 'path'
import fs from 'fs-extra'
import Debug from 'debug'
const debug = Debug('cypress:scaffold-config:searchUtils')

const ROOT_PATHS = [
'.git',

// https://pnpm.io/workspaces
'pnpm-workspace.yaml',

// https://rushjs.io/pages/advanced/config_files/
'rush.json',

// https://nx.dev/deprecated/workspace-json#workspace.json
// https://nx.dev/reference/nx-json#nx.json
'workspace.json',
'nx.json',

// https://lerna.js.org/docs/api-reference/configuration
'lerna.json',
]

async function hasWorkspacePackageJson (directory: string) {
try {
const pkg = await fs.readJson(path.join(directory, 'package.json'))

debug('package file for %s: %o', directory, pkg)

return !!pkg.workspaces
} catch (e) {
debug('error reading package.json in %s. this is not the repository root', directory)

return false
}
}

export async function isRepositoryRoot (directory: string) {
if (ROOT_PATHS.some((rootPath) => fs.existsSync(path.join(directory, rootPath)))) {
return true
}

return hasWorkspacePackageJson(directory)
}

/**
* Recursing search upwards from projectPath until the repository root looking for .pnp.cjs.
* If `.pnp.cjs` is found, return it
*/
export async function tryToFindPnpFile (projectPath: string): Promise<string | undefined> {
return findUp(async (directory: string) => {
const isCurrentRepositoryRoot = await isRepositoryRoot(directory)

const file = path.join(directory, '.pnp.cjs')
const hasPnpCjs = await fs.pathExists(file)

if (hasPnpCjs) {
return file
}

if (isCurrentRepositoryRoot) {
debug('stopping search at %s because it is believed to be the repository root', directory)

return findUp.stop
}

// Return undefined to keep searching
return undefined
}, { cwd: projectPath })
}
62 changes: 1 addition & 61 deletions packages/scaffold-config/test/unit/ct-detect-third-party.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { scaffoldMigrationProject, fakeDepsInNodeModules } from './detect.spec'
import fs from 'fs-extra'
import path from 'path'
import { detectThirdPartyCTFrameworks, validateThirdPartyModule, isThirdPartyDefinition, isRepositoryRoot } from '../../src'
import { detectThirdPartyCTFrameworks, validateThirdPartyModule, isThirdPartyDefinition } from '../../src'
import { expect } from 'chai'
import os from 'os'
import solidJs from './fixtures'

async function copyNodeModule (root, moduleName) {
Expand Down Expand Up @@ -54,65 +53,6 @@ describe('isThirdPartyDefinition', () => {
})
})

describe('isRepositoryRoot', () => {
const TEMP_DIR = path.join(os.tmpdir(), 'is-repository-root-tmp')

beforeEach(async () => {
await fs.mkdir(TEMP_DIR)
})

afterEach(async () => {
await fs.rm(TEMP_DIR, { recursive: true })
})

it('returns false if there is nothing in the directory', async () => {
const isCurrentRepositoryRoot = await isRepositoryRoot(TEMP_DIR)

expect(isCurrentRepositoryRoot).to.be.false
})

it('returns true if there is a Git directory', async () => {
await fs.mkdir(path.join(TEMP_DIR, '.git'))

const isCurrentRepositoryRoot = await isRepositoryRoot(TEMP_DIR)

expect(isCurrentRepositoryRoot).to.be.true
})

it('returns false if there is a package.json without workspaces field', async () => {
await fs.writeFile(path.join(TEMP_DIR, 'package.json'), `{
"name": "@packages/foo",
"private": true,
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
`)

const isCurrentRepositoryRoot = await isRepositoryRoot(TEMP_DIR)

expect(isCurrentRepositoryRoot).to.be.false
})

it('returns true if there is a package.json with workspaces field', async () => {
await fs.writeFile(path.join(TEMP_DIR, 'package.json'), `{
"name": "monorepo-repo",
"private": true,
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"workspaces": [
"packages/*"
]
}
`)

const isCurrentRepositoryRoot = await isRepositoryRoot(TEMP_DIR)

expect(isCurrentRepositoryRoot).to.be.true
})
})

describe('detectThirdPartyCTFrameworks', () => {
it('detects third party frameworks in global namespace', async () => {
const projectRoot = await scaffoldQwikApp(['cypress-ct-qwik'])
Expand Down
105 changes: 105 additions & 0 deletions packages/scaffold-config/test/unit/searchUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import fs from 'fs-extra'
import path from 'path'
import { expect } from 'chai'
import os from 'os'
import { isRepositoryRoot, tryToFindPnpFile } from '../../src/searchUtils'
import dedent from 'dedent'

const TEMP_DIR = path.join(os.tmpdir(), 'is-repository-root-tmp')

beforeEach(async () => {
await fs.mkdir(TEMP_DIR)
})

afterEach(async () => {
await fs.rm(TEMP_DIR, { recursive: true })
})

describe('isRepositoryRoot', () => {
it('returns false if there is nothing in the directory', async () => {
const isCurrentRepositoryRoot = await isRepositoryRoot(TEMP_DIR)

expect(isCurrentRepositoryRoot).to.be.false
})

it('returns true if there is a Git directory', async () => {
await fs.mkdir(path.join(TEMP_DIR, '.git'))

const isCurrentRepositoryRoot = await isRepositoryRoot(TEMP_DIR)

expect(isCurrentRepositoryRoot).to.be.true
})

it('returns false if there is a package.json without workspaces field', async () => {
await fs.writeFile(path.join(TEMP_DIR, 'package.json'), `{
"name": "@packages/foo",
"private": true,
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
`)

const isCurrentRepositoryRoot = await isRepositoryRoot(TEMP_DIR)

expect(isCurrentRepositoryRoot).to.be.false
})

it('returns true if there is a package.json with workspaces field', async () => {
await fs.writeFile(path.join(TEMP_DIR, 'package.json'), `{
"name": "monorepo-repo",
"private": true,
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"workspaces": [
"packages/*"
]
}
`)

const isCurrentRepositoryRoot = await isRepositoryRoot(TEMP_DIR)

expect(isCurrentRepositoryRoot).to.be.true
})
})

describe('tryToFindPnpFile', () => {
it('finds pnp.cjs at repo root', async () => {
const projectPath = path.join(TEMP_DIR, 'packages', 'tests')
const pnpcjs = path.join(TEMP_DIR, '.pnp.cjs')

await Promise.all([
fs.ensureFile(path.join(projectPath, 'package.json')),
fs.writeFile(pnpcjs, '/* pnp api */'),
fs.writeFile(path.join(TEMP_DIR, 'package.json'), dedent`
{
"workspaces": [
"packages/*"
]
}
`),
])

const pnpPath = await tryToFindPnpFile(projectPath)

expect(pnpPath).to.eq(pnpcjs)
})

it('does not find pnp.cjs at repo root', async () => {
const projectPath = path.join(TEMP_DIR, 'packages', 'tests')

await fs.ensureFile(path.join(projectPath, 'package.json'))
await fs.writeFile(path.join(TEMP_DIR, 'package.json'), dedent`
{
"workspaces": [
"packages/*"
]
}
`)

const pnpPath = await tryToFindPnpFile(projectPath)

expect(pnpPath).to.eq(undefined)
})
})

0 comments on commit 775314a

Please sign in to comment.