From 4d3c319c830cff93472f2d0e0ba1c3e2ef709d1b Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Wed, 20 Dec 2023 08:37:43 +0000 Subject: [PATCH] feat: add concurrency option to run and exec (#1312) To speed things up, add a `concurrency` option to `aegir run` and `aegir exec` - this will run commands up to the cocurrency limit while still taking monorepo sibling dependencies into account. --------- Co-authored-by: Cayman --- package.json | 1 + src/cmds/exec.js | 5 ++ src/cmds/release-rc.js | 5 ++ src/cmds/run.js | 5 ++ src/exec.js | 2 + src/release-rc.js | 4 ++ src/run.js | 2 + src/types.ts | 17 ++++++- src/utils.js | 50 ++++++++++++------- .../projects/a-large-monorepo/package.json | 12 +++++ .../a-large-monorepo/packages/a/package.json | 17 +++++++ .../a-large-monorepo/packages/a/src/index.js | 16 ++++++ .../a-large-monorepo/packages/a/src/types.ts | 3 ++ .../a-large-monorepo/packages/a/tsconfig.json | 39 +++++++++++++++ .../a-large-monorepo/packages/a/typedoc.json | 3 ++ .../a-large-monorepo/packages/b/package.json | 20 ++++++++ .../packages/b/src/dir/index.js | 7 +++ .../a-large-monorepo/packages/b/src/index.js | 18 +++++++ .../a-large-monorepo/packages/b/src/types.ts | 3 ++ .../a-large-monorepo/packages/b/tsconfig.json | 39 +++++++++++++++ .../a-large-monorepo/packages/b/typedoc.json | 3 ++ .../a-large-monorepo/packages/c/package.json | 20 ++++++++ .../packages/c/src/dir/index.js | 7 +++ .../a-large-monorepo/packages/c/src/index.js | 18 +++++++ .../a-large-monorepo/packages/c/src/types.ts | 3 ++ .../a-large-monorepo/packages/c/tsconfig.json | 39 +++++++++++++++ .../a-large-monorepo/packages/c/typedoc.json | 3 ++ .../a-large-monorepo/packages/d/package.json | 20 ++++++++ .../packages/d/src/dir/index.js | 7 +++ .../a-large-monorepo/packages/d/src/index.js | 18 +++++++ .../a-large-monorepo/packages/d/src/types.ts | 3 ++ .../a-large-monorepo/packages/d/tsconfig.json | 39 +++++++++++++++ .../a-large-monorepo/packages/d/typedoc.json | 3 ++ test/run.js | 31 ++++++++++++ 34 files changed, 463 insertions(+), 19 deletions(-) create mode 100644 test/fixtures/projects/a-large-monorepo/package.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/a/package.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/a/src/index.js create mode 100644 test/fixtures/projects/a-large-monorepo/packages/a/src/types.ts create mode 100644 test/fixtures/projects/a-large-monorepo/packages/a/tsconfig.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/a/typedoc.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/b/package.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/b/src/dir/index.js create mode 100644 test/fixtures/projects/a-large-monorepo/packages/b/src/index.js create mode 100644 test/fixtures/projects/a-large-monorepo/packages/b/src/types.ts create mode 100644 test/fixtures/projects/a-large-monorepo/packages/b/tsconfig.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/b/typedoc.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/c/package.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/c/src/dir/index.js create mode 100644 test/fixtures/projects/a-large-monorepo/packages/c/src/index.js create mode 100644 test/fixtures/projects/a-large-monorepo/packages/c/src/types.ts create mode 100644 test/fixtures/projects/a-large-monorepo/packages/c/tsconfig.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/c/typedoc.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/d/package.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/d/src/dir/index.js create mode 100644 test/fixtures/projects/a-large-monorepo/packages/d/src/index.js create mode 100644 test/fixtures/projects/a-large-monorepo/packages/d/src/types.ts create mode 100644 test/fixtures/projects/a-large-monorepo/packages/d/tsconfig.json create mode 100644 test/fixtures/projects/a-large-monorepo/packages/d/typedoc.json diff --git a/package.json b/package.json index 386a2307d..69ad93057 100644 --- a/package.json +++ b/package.json @@ -286,6 +286,7 @@ "npm-package-json-lint": "^7.0.0", "nyc": "^15.1.0", "p-map": "^6.0.0", + "p-queue": "^7.3.4", "p-retry": "^6.0.0", "pascalcase": "^2.0.0", "path": "^0.12.7", diff --git a/src/cmds/exec.js b/src/cmds/exec.js index 5eb176006..9a326327a 100644 --- a/src/cmds/exec.js +++ b/src/cmds/exec.js @@ -34,6 +34,11 @@ export default { type: 'boolean', describe: 'Prefix output with the package name', default: userConfig.exec.prefix + }, + concurrency: { + type: 'number', + describe: 'How many commands to run at the same time', + default: userConfig.exec.concurrency } }) }, diff --git a/src/cmds/release-rc.js b/src/cmds/release-rc.js index 103710c30..257e15d69 100644 --- a/src/cmds/release-rc.js +++ b/src/cmds/release-rc.js @@ -40,6 +40,11 @@ export default { type: 'boolean', describe: 'Prefix output with the package name', default: userConfig.releaseRc.prefix + }, + concurrency: { + type: 'number', + describe: 'How many modules to release at the same time', + default: userConfig.releaseRc.concurrency } }) }, diff --git a/src/cmds/run.js b/src/cmds/run.js index f575a2e00..33b0ff5ed 100644 --- a/src/cmds/run.js +++ b/src/cmds/run.js @@ -34,6 +34,11 @@ export default { type: 'boolean', describe: 'Prefix output with the package name', default: userConfig.run.prefix + }, + concurrency: { + type: 'number', + describe: 'How many scripts to run at the same time', + default: userConfig.run.concurrency } }) .positional('script', { diff --git a/src/exec.js b/src/exec.js index d57358fab..e785143ff 100644 --- a/src/exec.js +++ b/src/exec.js @@ -31,6 +31,8 @@ export default { console.info(kleur.red(err.stack)) // eslint-disable-line no-console } + }, { + concurrency: ctx.concurrency }) } } diff --git a/src/release-rc.js b/src/release-rc.js index cfde0e5a8..cfb39aafe 100644 --- a/src/release-rc.js +++ b/src/release-rc.js @@ -29,6 +29,8 @@ async function releaseMonorepoRcs (commit, ctx) { } versions[project.manifest.name] = `${project.manifest.version}-${commit}` + }, { + concurrency: ctx.concurrency }) console.info('Will release the following packages:') @@ -97,6 +99,8 @@ async function releaseMonorepoRcs (commit, ctx) { }) console.info('') + }, { + concurrency: ctx.concurrency }) } diff --git a/src/run.js b/src/run.js index dfbb7388e..b39462e68 100644 --- a/src/run.js +++ b/src/run.js @@ -44,6 +44,8 @@ export default { console.info(kleur.red(err.stack)) // eslint-disable-line no-console } } + }, { + concurrency: ctx.concurrency }) } } diff --git a/src/types.ts b/src/types.ts index 1741403d3..02007fa6e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -341,6 +341,11 @@ interface ReleaseRcOptions { * Prefix output with the package name */ prefix?: boolean + + /** + * Release modules in parallel up to this limit + */ + concurrency?: number } interface DependencyCheckOptions { @@ -365,11 +370,16 @@ interface ExecOptions { * Prefix output with the package name */ prefix?: boolean + + /** + * Run commands in parallel up to this limit + */ + concurrency?: number } interface RunOptions { /** - * If false, the command will continue to be run in other packages + * If false, the script will continue to be run in other packages */ bail?: boolean @@ -377,6 +387,11 @@ interface RunOptions { * Prefix output with the package name */ prefix?: boolean + + /** + * Run scripts in parallel up to this limit + */ + concurrency?: number } export type { diff --git a/src/utils.js b/src/utils.js index 976cce9cf..f358d6add 100644 --- a/src/utils.js +++ b/src/utils.js @@ -18,6 +18,7 @@ import fs from 'fs-extra' import kleur from 'kleur' import Listr from 'listr' import { minimatch } from 'minimatch' +import PQueue from 'p-queue' import lockfile from 'proper-lockfile' import { readPackageUpSync } from 'read-pkg-up' import stripBom from 'strip-bom' @@ -313,14 +314,15 @@ export function findBinary (bin) { * @property {string} dir * @property {string[]} siblingDependencies * @property {string[]} dependencies - * @property {boolean} run */ /** * @param {string} projectDir * @param {(project: Project) => Promise} fn + * @param {object} [opts] + * @param {number} [opts.concurrency] */ -export async function everyMonorepoProject (projectDir, fn) { +export async function everyMonorepoProject (projectDir, fn, opts) { const manifest = fs.readJSONSync(path.join(projectDir, 'package.json')) const workspaces = manifest.workspaces @@ -334,27 +336,40 @@ export async function everyMonorepoProject (projectDir, fn) { checkForCircularDependencies(projects) /** - * @param {Project} project + * @type {Map} Track the number of outstanding dependencies of each project + * + * This is mutated (decremented and deleted) as tasks are run for dependencies */ - async function run (project) { - if (project.run) { - return - } + const inDegree = new Map() + for (const [name, project] of Object.entries(projects)) { + inDegree.set(name, project.siblingDependencies.length) + } - for (const siblingDep of project.siblingDependencies) { - await run(projects[siblingDep]) - } + const queue = new PQueue({ + concurrency: opts?.concurrency ?? os.availableParallelism?.() ?? os.cpus().length + }) - if (project.run) { - return + while (inDegree.size) { + /** @type {string[]} */ + const toRun = [] + + for (const [name, d] of inDegree) { + // when there are no more dependencies + // the project can be added to the queue + // and removed from the tracker + if (d === 0) { + toRun.push(name) + inDegree.delete(name) + } } - project.run = true - await fn(project) - } + await Promise.all(toRun.map((name) => queue.add(() => fn(projects[name])))) - for (const project of Object.values(projects)) { - await run(project) + // decrement projects whose dependencies were just run + for (const [name, d] of inDegree) { + const decrement = projects[name].siblingDependencies.filter(dep => toRun.includes(dep)).length + inDegree.set(name, d - decrement) + } } } @@ -390,7 +405,6 @@ export function parseProjects (projectDir, workspaces) { manifest: pkg, dir: subProjectDir, siblingDependencies: [], - run: false, dependencies: [ ...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {}), diff --git a/test/fixtures/projects/a-large-monorepo/package.json b/test/fixtures/projects/a-large-monorepo/package.json new file mode 100644 index 000000000..3d6382fe4 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/package.json @@ -0,0 +1,12 @@ +{ + "name": "a-large-monorepo", + "version": "1.0.0", + "description": "", + "homepage": "https://github.com/ipfs/aegir#readme", + "scripts": { + "docs": "aegir docs" + }, + "workspaces": [ + "packages/*" + ] +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/a/package.json b/test/fixtures/projects/a-large-monorepo/packages/a/package.json new file mode 100644 index 000000000..e04b8e794 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/a/package.json @@ -0,0 +1,17 @@ +{ + "name": "a", + "version": "1.0.0", + "description": "", + "homepage": "https://github.com/ipfs/aegir#readme", + "exports": { + ".": { + "import": "./src/index.js" + } + }, + "scripts": { + "test": "echo very test" + }, + "type": "module", + "author": "", + "license": "ISC" +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/a/src/index.js b/test/fixtures/projects/a-large-monorepo/packages/a/src/index.js new file mode 100644 index 000000000..58dc564eb --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/a/src/index.js @@ -0,0 +1,16 @@ +/** + * @typedef {import('./types').ExportedButNotInExports} ExportedButNotInExports + */ + +/** + * @typedef {object} AnExportedInterface + * @property {() => void} AnExportedInterface.aMethod + */ + +export const useHerp = () => { + +} + +export const useDerp = () => { + +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/a/src/types.ts b/test/fixtures/projects/a-large-monorepo/packages/a/src/types.ts new file mode 100644 index 000000000..08c6ad031 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/a/src/types.ts @@ -0,0 +1,3 @@ +export interface ExportedButNotInExports { + aMethod(): void +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/a/tsconfig.json b/test/fixtures/projects/a-large-monorepo/packages/a/tsconfig.json new file mode 100644 index 000000000..6ddff411a --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/a/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "strict": true, + // project options + "outDir": "dist", + "allowJs": true, + "checkJs": true, + "target": "ES2020", + "module": "ES2020", + "lib": ["ES2021", "ES2021.Promise", "ES2021.String", "ES2020.BigInt", "DOM", "DOM.Iterable"], + "noEmit": false, + "noEmitOnError": true, + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "incremental": true, + "composite": true, + "isolatedModules": true, + "removeComments": false, + "sourceMap": true, + // module resolution + "esModuleInterop": true, + "moduleResolution": "node", + // linter checks + "noImplicitReturns": false, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + // advanced + "verbatimModuleSyntax": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "stripInternal": true, + "resolveJsonModule": true + }, + "include": [ + "src" + ] +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/a/typedoc.json b/test/fixtures/projects/a-large-monorepo/packages/a/typedoc.json new file mode 100644 index 000000000..c7d830493 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/a/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["./src/index.js"] +} \ No newline at end of file diff --git a/test/fixtures/projects/a-large-monorepo/packages/b/package.json b/test/fixtures/projects/a-large-monorepo/packages/b/package.json new file mode 100644 index 000000000..847eaec34 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/b/package.json @@ -0,0 +1,20 @@ +{ + "name": "b", + "version": "1.0.0", + "description": "", + "homepage": "https://github.com/ipfs/aegir#readme", + "exports": { + ".": { + "import": "./src/index.js" + } + }, + "dependencies": { + "a": "1.0.0" + }, + "scripts": { + "test": "echo very test" + }, + "type": "module", + "author": "", + "license": "ISC" +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/b/src/dir/index.js b/test/fixtures/projects/a-large-monorepo/packages/b/src/dir/index.js new file mode 100644 index 000000000..50c309ccb --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/b/src/dir/index.js @@ -0,0 +1,7 @@ +/** + * @param {string} arg + * @returns {boolean} + */ +export function garply (arg) { + return true +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/b/src/index.js b/test/fixtures/projects/a-large-monorepo/packages/b/src/index.js new file mode 100644 index 000000000..fc34cfdef --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/b/src/index.js @@ -0,0 +1,18 @@ +/** + * @typedef {import('./types.js').ExportedButNotInExports} ExportedButNotInExports + */ + +/** + * @typedef {object} AnExportedInterface + * @property {() => void} AnExportedInterface.aMethod + */ + +export const useHerp = () => { + +} + +export const useDerp = () => { + +} + +export { garply } from './dir/index.js' diff --git a/test/fixtures/projects/a-large-monorepo/packages/b/src/types.ts b/test/fixtures/projects/a-large-monorepo/packages/b/src/types.ts new file mode 100644 index 000000000..08c6ad031 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/b/src/types.ts @@ -0,0 +1,3 @@ +export interface ExportedButNotInExports { + aMethod(): void +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/b/tsconfig.json b/test/fixtures/projects/a-large-monorepo/packages/b/tsconfig.json new file mode 100644 index 000000000..6ddff411a --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/b/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "strict": true, + // project options + "outDir": "dist", + "allowJs": true, + "checkJs": true, + "target": "ES2020", + "module": "ES2020", + "lib": ["ES2021", "ES2021.Promise", "ES2021.String", "ES2020.BigInt", "DOM", "DOM.Iterable"], + "noEmit": false, + "noEmitOnError": true, + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "incremental": true, + "composite": true, + "isolatedModules": true, + "removeComments": false, + "sourceMap": true, + // module resolution + "esModuleInterop": true, + "moduleResolution": "node", + // linter checks + "noImplicitReturns": false, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + // advanced + "verbatimModuleSyntax": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "stripInternal": true, + "resolveJsonModule": true + }, + "include": [ + "src" + ] +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/b/typedoc.json b/test/fixtures/projects/a-large-monorepo/packages/b/typedoc.json new file mode 100644 index 000000000..c7d830493 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/b/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["./src/index.js"] +} \ No newline at end of file diff --git a/test/fixtures/projects/a-large-monorepo/packages/c/package.json b/test/fixtures/projects/a-large-monorepo/packages/c/package.json new file mode 100644 index 000000000..dd8405e93 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/c/package.json @@ -0,0 +1,20 @@ +{ + "name": "c", + "version": "1.0.0", + "description": "", + "homepage": "https://github.com/ipfs/aegir#readme", + "exports": { + ".": { + "import": "./src/index.js" + } + }, + "dependencies": { + "a": "1.0.0" + }, + "scripts": { + "test": "echo very test" + }, + "type": "module", + "author": "", + "license": "ISC" +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/c/src/dir/index.js b/test/fixtures/projects/a-large-monorepo/packages/c/src/dir/index.js new file mode 100644 index 000000000..50c309ccb --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/c/src/dir/index.js @@ -0,0 +1,7 @@ +/** + * @param {string} arg + * @returns {boolean} + */ +export function garply (arg) { + return true +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/c/src/index.js b/test/fixtures/projects/a-large-monorepo/packages/c/src/index.js new file mode 100644 index 000000000..fc34cfdef --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/c/src/index.js @@ -0,0 +1,18 @@ +/** + * @typedef {import('./types.js').ExportedButNotInExports} ExportedButNotInExports + */ + +/** + * @typedef {object} AnExportedInterface + * @property {() => void} AnExportedInterface.aMethod + */ + +export const useHerp = () => { + +} + +export const useDerp = () => { + +} + +export { garply } from './dir/index.js' diff --git a/test/fixtures/projects/a-large-monorepo/packages/c/src/types.ts b/test/fixtures/projects/a-large-monorepo/packages/c/src/types.ts new file mode 100644 index 000000000..08c6ad031 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/c/src/types.ts @@ -0,0 +1,3 @@ +export interface ExportedButNotInExports { + aMethod(): void +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/c/tsconfig.json b/test/fixtures/projects/a-large-monorepo/packages/c/tsconfig.json new file mode 100644 index 000000000..6ddff411a --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/c/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "strict": true, + // project options + "outDir": "dist", + "allowJs": true, + "checkJs": true, + "target": "ES2020", + "module": "ES2020", + "lib": ["ES2021", "ES2021.Promise", "ES2021.String", "ES2020.BigInt", "DOM", "DOM.Iterable"], + "noEmit": false, + "noEmitOnError": true, + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "incremental": true, + "composite": true, + "isolatedModules": true, + "removeComments": false, + "sourceMap": true, + // module resolution + "esModuleInterop": true, + "moduleResolution": "node", + // linter checks + "noImplicitReturns": false, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + // advanced + "verbatimModuleSyntax": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "stripInternal": true, + "resolveJsonModule": true + }, + "include": [ + "src" + ] +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/c/typedoc.json b/test/fixtures/projects/a-large-monorepo/packages/c/typedoc.json new file mode 100644 index 000000000..c7d830493 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/c/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["./src/index.js"] +} \ No newline at end of file diff --git a/test/fixtures/projects/a-large-monorepo/packages/d/package.json b/test/fixtures/projects/a-large-monorepo/packages/d/package.json new file mode 100644 index 000000000..5215c495d --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/d/package.json @@ -0,0 +1,20 @@ +{ + "name": "d", + "version": "1.0.0", + "description": "", + "homepage": "https://github.com/ipfs/aegir#readme", + "exports": { + ".": { + "import": "./src/index.js" + } + }, + "dependencies": { + "c": "1.0.0" + }, + "scripts": { + "test": "echo very test" + }, + "type": "module", + "author": "", + "license": "ISC" +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/d/src/dir/index.js b/test/fixtures/projects/a-large-monorepo/packages/d/src/dir/index.js new file mode 100644 index 000000000..50c309ccb --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/d/src/dir/index.js @@ -0,0 +1,7 @@ +/** + * @param {string} arg + * @returns {boolean} + */ +export function garply (arg) { + return true +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/d/src/index.js b/test/fixtures/projects/a-large-monorepo/packages/d/src/index.js new file mode 100644 index 000000000..fc34cfdef --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/d/src/index.js @@ -0,0 +1,18 @@ +/** + * @typedef {import('./types.js').ExportedButNotInExports} ExportedButNotInExports + */ + +/** + * @typedef {object} AnExportedInterface + * @property {() => void} AnExportedInterface.aMethod + */ + +export const useHerp = () => { + +} + +export const useDerp = () => { + +} + +export { garply } from './dir/index.js' diff --git a/test/fixtures/projects/a-large-monorepo/packages/d/src/types.ts b/test/fixtures/projects/a-large-monorepo/packages/d/src/types.ts new file mode 100644 index 000000000..08c6ad031 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/d/src/types.ts @@ -0,0 +1,3 @@ +export interface ExportedButNotInExports { + aMethod(): void +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/d/tsconfig.json b/test/fixtures/projects/a-large-monorepo/packages/d/tsconfig.json new file mode 100644 index 000000000..6ddff411a --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/d/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "strict": true, + // project options + "outDir": "dist", + "allowJs": true, + "checkJs": true, + "target": "ES2020", + "module": "ES2020", + "lib": ["ES2021", "ES2021.Promise", "ES2021.String", "ES2020.BigInt", "DOM", "DOM.Iterable"], + "noEmit": false, + "noEmitOnError": true, + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "incremental": true, + "composite": true, + "isolatedModules": true, + "removeComments": false, + "sourceMap": true, + // module resolution + "esModuleInterop": true, + "moduleResolution": "node", + // linter checks + "noImplicitReturns": false, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + // advanced + "verbatimModuleSyntax": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "stripInternal": true, + "resolveJsonModule": true + }, + "include": [ + "src" + ] +} diff --git a/test/fixtures/projects/a-large-monorepo/packages/d/typedoc.json b/test/fixtures/projects/a-large-monorepo/packages/d/typedoc.json new file mode 100644 index 000000000..c7d830493 --- /dev/null +++ b/test/fixtures/projects/a-large-monorepo/packages/d/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["./src/index.js"] +} \ No newline at end of file diff --git a/test/run.js b/test/run.js index 1fdcea5a9..355bc1033 100644 --- a/test/run.js +++ b/test/run.js @@ -86,4 +86,35 @@ a-workspace-project: npm run test very test`) }) + + it('should execute commands in dependency order', async function () { + this.timeout(120 * 1000) // slow ci is slow + + /* + This testcase has the following dependencies: + + d -> c + c -> a + b -> a + + This means the test runner should batch the test runs like so: + + [a] + [b, c] + [d] + + */ + const result = await execa(bin, ['run', 'test'], { + cwd: await setUpProject('a-large-monorepo') + }) + + const out = result.stdout + // a finishes before b or c starts + expect(out.indexOf('a: very test')).to.be.lt(out.indexOf('b: npm run test')) + expect(out.indexOf('a: very test')).to.be.lt(out.indexOf('c: npm run test')) + + // b and c finish before d starts + 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')) + }) })