Skip to content

Commit

Permalink
feat: workspace runtime argument
Browse files Browse the repository at this point in the history
  • Loading branch information
marcus-pousette committed Apr 7, 2024
1 parent b468ff1 commit 1d08a81
Show file tree
Hide file tree
Showing 12 changed files with 64 additions and 26 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@
"scripts": {
"clean": "node src/index.js clean",
"build": "node src/index.js build --no-bundle",
"lint": "node src/index.js lint",
"lint": "node src/index.js lint --fix",
"test": "node src/index.js test",
"docs": "node src/index.js docs",
"dep-check": "node src/index.js dep-check",
Expand Down
2 changes: 1 addition & 1 deletion src/check-project/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ async function processMonorepo (projectDir, manifest, branchName, repoUrl, ciFil

const projectDirs = []

for (const subProjectDir of await getSubprojectDirectories(projectDir, workspaces)) {
for (const subProjectDir of await getSubprojectDirectories(workspaces, projectDir)) {
const stat = await fs.stat(subProjectDir)

if (!stat.isDirectory()) {
Expand Down
9 changes: 9 additions & 0 deletions src/cmds/run.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { loadUserConfig } from '../config/user.js'
import runCmd from '../run.js'
import { listWorkspaces } from '../utils.js'

/**
* @typedef {import("yargs").Argv} Argv
Expand Down Expand Up @@ -39,6 +40,14 @@ export default {
type: 'number',
describe: 'How many scripts to run at the same time',
default: userConfig.run.concurrency
},

workspaces: {
// an array of strings
array: true,
describe: 'Run the script in a specific workspace',
default: await listWorkspaces(process.cwd()),
alias: ['workspace', 'roots']
}
})
.positional('script', {
Expand Down
2 changes: 1 addition & 1 deletion src/docs/readme-updater-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function load (app) {
let projects = {}

if (isMonorepoParent) {
projects = parseProjects(process.cwd(), pkg.workspaces)
projects = parseProjects(pkg.workspaces)
}

// when rendering has finished, work out which UrlMappings refer to the index
Expand Down
2 changes: 1 addition & 1 deletion src/exec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default {
async run (ctx) {
const forwardArgs = ctx['--'] ? ctx['--'] : []

await everyMonorepoProject(process.cwd(), async (project) => {
await everyMonorepoProject(async (project) => {
console.info('') // eslint-disable-line no-console
console.info(kleur.grey(`${project.manifest.name}:`), `> ${ctx.command}${forwardArgs.length > 0 ? ` ${forwardArgs.join(' ')}` : ''}`) // eslint-disable-line no-console

Expand Down
4 changes: 2 additions & 2 deletions src/release-rc.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async function releaseMonorepoRcs (commit, ctx) {
/** @type {Record<string, string>} */
const versions = {}

await everyMonorepoProject(process.cwd(), async (project) => {
await everyMonorepoProject(async (project) => {
if (project.manifest.private === true) {
console.info(`Skipping private package ${project.manifest.name}`)
return
Expand All @@ -43,7 +43,7 @@ async function releaseMonorepoRcs (commit, ctx) {
console.info('')

// publish packages
await everyMonorepoProject(process.cwd(), async (project) => {
await everyMonorepoProject(async (project) => {
if (project.manifest.private === true) {
console.info(`Skipping private package ${project.manifest.name}`)
return
Expand Down
8 changes: 4 additions & 4 deletions src/release.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const tasks = new Listr([
const {
siblingVersions,
packageDirs
} = await calculateSiblingVersions(rootDir, workspaces)
} = await calculateSiblingVersions(workspaces, rootDir)

// check these dependency types for monorepo siblings
const dependencyTypes = [
Expand Down Expand Up @@ -142,16 +142,16 @@ const tasks = new Listr([
], { renderer: 'verbose' })

/**
* @param {string} rootDir
* @param {string[]} workspaces
* @param {string} rootDir
*/
async function calculateSiblingVersions (rootDir, workspaces) {
async function calculateSiblingVersions (workspaces, rootDir) {
const packageDirs = []

/** @type {Record<string, string>} */
const siblingVersions = {}

for (const subProjectDir of await getSubprojectDirectories(rootDir, workspaces)) {
for (const subProjectDir of await getSubprojectDirectories(workspaces, rootDir)) {
const pkg = JSON.parse(fs.readFileSync(path.join(subProjectDir, 'package.json'), {
encoding: 'utf-8'
}))
Expand Down
3 changes: 2 additions & 1 deletion src/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default {

const forwardArgs = ctx['--'] == null ? [] : ['--', ...ctx['--']]

await everyMonorepoProject(process.cwd(), async (project) => {
await everyMonorepoProject(async (project) => {
for (const script of scripts) {
if (project.manifest.scripts[script] == null) {
continue
Expand All @@ -45,6 +45,7 @@ export default {
}
}
}, {
workspaces: ctx.workspaces,
concurrency: ctx.concurrency
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/test-dependant/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ const testMonoRepo = async (targetDir, deps, scriptName) => {
}

// test each package that depends on passed deps
for (const match of await getSubprojectDirectories(targetDir, config.workspaces)) {
for (const match of await getSubprojectDirectories(config.workspaces, targetDir)) {
await testModule(path.join(targetDir, match), deps, scriptName)
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ interface GlobalOptions {
* Full config from configuration file
*/
fileConfig: Options

}

interface BuildOptions {
Expand Down Expand Up @@ -391,9 +392,11 @@ interface ExecOptions {
* Run commands in parallel up to this limit
*/
concurrency?: number

}

interface RunOptions {

/**
* If false, the script will continue to be run in other packages
*/
Expand All @@ -408,6 +411,12 @@ interface RunOptions {
* Run scripts in parallel up to this limit
*/
concurrency?: number

/**
* Workspaces to run the command in
*/
workspaces?: string[]

}

export type {
Expand Down
33 changes: 19 additions & 14 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,14 @@ export function findBinary (bin) {
return bin
}

/**
* @param {string} projectDir
*/
export const listWorkspaces = async (projectDir) => {
const manifest = fs.readJSONSync(path.join(projectDir, 'package.json'))
return manifest.workspaces ?? undefined
}

/**
* @typedef {object} Project
* @property {any} manifest
Expand All @@ -341,21 +349,20 @@ export function findBinary (bin) {
*/

/**
* @param {string} projectDir
* @param {(project: Project) => Promise<void>} fn
* @param {object} [opts]
* @param {string[]?} [opts.workspaces]
* @param {number} [opts.concurrency]
*/
export async function everyMonorepoProject (projectDir, fn, opts) {
const manifest = fs.readJSONSync(path.join(projectDir, 'package.json'))
const workspaces = manifest.workspaces

if (!workspaces || !Array.isArray(workspaces)) {
export async function everyMonorepoProject (fn, opts) {
const workspaces = (opts?.workspaces ?? await listWorkspaces(process.cwd())).filter((/** @type {string | null} */ workspace) => workspace != null)
if (!workspaces || !Array.isArray(workspaces) || workspaces.length === 0) {
throw new Error('No monorepo workspaces found')
}

/** @type {Record<string, Project>} */
const projects = await parseProjects(projectDir, workspaces)

const projects = await parseProjects(workspaces)

checkForCircularDependencies(projects)

Expand Down Expand Up @@ -398,25 +405,23 @@ export async function everyMonorepoProject (projectDir, fn, opts) {
}

/**
*
* @param {string} projectDir
* @param {string[]} workspaces
* @param {string | undefined} cwd
*/
export const getSubprojectDirectories = async (projectDir, workspaces) => fg.glob(workspaces, {
cwd: projectDir,
export const getSubprojectDirectories = async (workspaces, cwd = process.cwd()) => fg.glob(workspaces, {
cwd,
onlyFiles: false
})

/**
*
* @param {string} projectDir
* @param {string[]} workspaces
*/
export async function parseProjects (projectDir, workspaces) {
export async function parseProjects (workspaces) {
/** @type {Record<string, Project>} */
const projects = {}

for (const subProjectDir of await getSubprojectDirectories(projectDir, workspaces)) {
for (const subProjectDir of await getSubprojectDirectories(workspaces)) {
const stat = fs.statSync(subProjectDir)

if (!stat.isDirectory()) {
Expand Down
14 changes: 14 additions & 0 deletions test/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,18 @@ very test`)
expect(out.indexOf('b: very test')).to.be.lt(out.indexOf('d: npm run test'))
expect(out.indexOf('c: very test')).to.be.lt(out.indexOf('d: npm run test'))
})

it('can run in specifc workspaces', async function () {
this.timeout(120 * 1000) // slow ci is slow

const result = await execa(bin, ['run', 'test', '--workspaces=**/a-workspace-project'], {
cwd: projectDir
})

expect(result.stdout).to.equal(`
a-workspace-project: npm run test
a-workspace-project: > [email protected] test
a-workspace-project: > echo very test
a-workspace-project: very test`)
})
})

0 comments on commit 1d08a81

Please sign in to comment.