Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: run lerna scripts based on scope
- Loading branch information
Showing
4 changed files
with
206 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { findRoot } from './paths.mjs' | ||
import { getPackageNames, getScopes } from './scopes.mjs' | ||
import { join } from 'path' | ||
import { log } from './cli.mjs' | ||
import { spawn } from 'child_process' | ||
|
||
const serializeFilter = filter => { | ||
return JSON.stringify(filter) | ||
} | ||
|
||
const printAvailableScopes = async script => { | ||
const scopes = await getScopes({ withScript: script }) | ||
log(`Available "${script}" project scopes:`) | ||
scopes.map(s => log(s, { padding: 2 })) | ||
} | ||
|
||
const printAvailablePackages = async (scope, script) => { | ||
const scopes = await getPackageNames({ scope, withScript: script }) | ||
log(`Available "${script}" packages${scope ? ` from scope "${scope}"` : ''}:`) | ||
scopes.map(s => log(s, { padding: 2 })) | ||
} | ||
|
||
export const runScopeCommand = async ({ all, scope, pkg, script } = {}) => { | ||
if (!pkg && !all) { | ||
return printAvailablePackages(scope, script) | ||
} | ||
if (!scope && !all) { | ||
return printAvailableScopes(script) | ||
} | ||
const packages = await getPackageNames({ | ||
exact: pkg, | ||
scope, | ||
withScript: script, | ||
}) | ||
|
||
if (!packages.length) { | ||
log( | ||
`No packages after filtering for ${serializeFilter({ | ||
scope, | ||
pkg, | ||
script, | ||
})}` | ||
) | ||
process.exit(1) | ||
} | ||
|
||
log(`Starting ${packages.length} projects\n`) | ||
for (const pack of packages) { | ||
log(`* ${pack}\n`) | ||
} | ||
const baseDir = await findRoot() | ||
const lerna = join(baseDir, 'node_modules', '.bin', 'lerna') | ||
const noPrefix = packages.length <= 1 | ||
const lernaArgs = [ | ||
'run', | ||
script, | ||
noPrefix && '--no-prefix', | ||
'--stream', | ||
'--parallel', | ||
...packages.map(p => ['--scope', p]).flat(), | ||
].filter(Boolean) | ||
return await runCommand({ baseDir, binary: lerna, args: lernaArgs }) | ||
} | ||
|
||
const runCommand = async ({ baseDir, binary, args }) => { | ||
const cmd = await spawn(binary, args, { | ||
cwd: baseDir, | ||
env: process.env, | ||
}) | ||
|
||
const terminate = signal => () => { | ||
cmd.kill(signal) | ||
} | ||
|
||
process.on('exit', terminate('SIGINT')) | ||
process.on('SIGTERM', terminate('SIGTERM')) | ||
process.on('SIGINT', terminate('SIGINT')) | ||
|
||
cmd.stdout.on('data', data => log(data)) | ||
cmd.stderr.on('data', data => process.stderr.write(data)) | ||
cmd.on('close', code => { | ||
log(`Lerna terminated with code ${code}\n`) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { findRoot } from './paths.mjs' | ||
import { IsolatedProject } from './IsolatedProject.mjs' | ||
import { log } from './cli.mjs' | ||
|
||
const extractProjectScope = p => p.name.split('/')[0] | ||
const extractPackageName = p => p.name.split('/')[1] | ||
const filterUnique = (item, index, src) => src.indexOf(item) === index | ||
|
||
export const padScope = scope => { | ||
if (!scope) { | ||
return null | ||
} | ||
return scope.startsWith('@') ? scope : `@${scope}` | ||
} | ||
|
||
export const getPackages = async ({ exact, scope, withScript } = {}) => { | ||
const root = await findRoot() | ||
const project = new IsolatedProject(root) | ||
let packages = await project.getPackages() | ||
if (scope) { | ||
const projectScope = padScope(scope) | ||
packages = packages.filter(p => p.name.startsWith(`${projectScope}/`)) | ||
} | ||
if (withScript) { | ||
packages = packages.filter(p => p.scripts[withScript]) | ||
} | ||
if (exact) { | ||
packages = packages.filter(p => extractPackageName(p).startsWith(exact)) | ||
} | ||
return packages | ||
} | ||
|
||
export const getPackageNames = async ({ noScope = false, ...args } = {}) => { | ||
const packages = await getPackages(args) | ||
if (noScope) { | ||
return packages.map(extractPackageName) | ||
} | ||
return packages.map(p => p.name) | ||
} | ||
|
||
export const getScopes = async args => { | ||
const packages = await getPackages(args) | ||
return packages.map(extractProjectScope).filter(filterUnique) | ||
} | ||
|
||
export const printScopes = async args => { | ||
const scopes = await getScopes(args) | ||
scopes.map(log) | ||
} | ||
|
||
export const printPackages = async args => { | ||
const packages = await getPackageNames({ | ||
...args, | ||
noScope: Boolean(args.scope), | ||
}) | ||
packages.map(log) | ||
} |