├── .eslintrc.js ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── rfc--design-spec.md ├── workflows-disabled │ └── chatops.yml └── workflows │ ├── docusaurus-pr.yml │ ├── docusaurus-release.yml │ ├── pr.yml │ └── release.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .swcrc ├── .vscode ├── launch.json └── settings.json ├── .yarn └── releases │ ├── yarn-4.8.0.cjs │ └── yarn-4.9.1.cjs ├── .yarnrc.yml ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── beachball.config.js ├── benchmark ├── .gitignore ├── index.js ├── package.json └── yarn.lock ├── decks ├── lage-deck.md └── monorepo.md ├── docs ├── .gitignore ├── .yarnrc.yml ├── README.md ├── docs │ ├── contributing.mdx │ ├── cookbook │ │ ├── _category_.json │ │ ├── make-jest-fast.mdx │ │ ├── make-lint-fast.mdx │ │ ├── make-ts-fast.mdx │ │ └── migration.mdx │ ├── guides │ │ ├── README.md │ │ ├── _category_.json │ │ ├── buildxl.md │ │ ├── cache.md │ │ ├── installation.md │ │ ├── pipeline.md │ │ ├── priority.md │ │ ├── profile.md │ │ ├── remote-cache.md │ │ └── scopes.md │ ├── introduction.mdx │ ├── quick-start.md │ └── reference │ │ ├── _category_.json │ │ ├── cli.md │ │ └── config.md ├── docusaurus.config.ts ├── package.json ├── src │ ├── components │ │ ├── Badge.tsx │ │ ├── classNames.ts │ │ └── home │ │ │ ├── Quote.tsx │ │ │ ├── Section.tsx │ │ │ ├── Tools.tsx │ │ │ └── WaveDivider.tsx │ ├── css │ │ ├── custom.css │ │ └── tailwind.css │ ├── data │ │ ├── TableContents.ts │ │ └── ToolList.ts │ └── pages │ │ └── index.tsx ├── static │ ├── .nojekyll │ ├── fonts │ │ ├── Bahnschrift.ttf │ │ └── LondrinaSolid.ttf │ └── img │ │ ├── Wave Shape 1.svg │ │ ├── Wave Shape 2.svg │ │ ├── backfill.svg │ │ ├── beachball1.svg │ │ ├── contributor-guide-dark.png │ │ ├── contributor-guide-light.png │ │ ├── frog-cloud0.png │ │ ├── frog-hero0.png │ │ ├── frog-monitor0.png │ │ ├── frog-sing0.png │ │ ├── frog-skip0.png │ │ ├── frog.webm │ │ ├── graph-diagram-dark.png │ │ ├── graph-diagram-light.png │ │ ├── just.svg │ │ ├── lage.png │ │ ├── p-chart.svg │ │ ├── package-graph-dark.png │ │ ├── package-graph.png │ │ ├── target-graph-dark.png │ │ ├── target-graph.png │ │ ├── task-graph-dark.png │ │ └── task-graph.png ├── tsconfig.json └── yarn.lock ├── lage.config.js ├── llms.txt ├── netlify.toml ├── package.json ├── packages ├── cache-github-actions │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── cacheProvider.ts │ │ └── index.ts │ └── tsconfig.json ├── cache │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── CredentialCache.ts │ │ ├── backfillWrapper.ts │ │ ├── chunkPromise.ts │ │ ├── getCacheDirectory.ts │ │ ├── index.ts │ │ ├── providers │ │ │ ├── BackfillCacheProvider.ts │ │ │ └── RemoteFallbackCacheProvider.ts │ │ └── types │ │ │ └── CacheProvider.ts │ ├── tests │ │ ├── BackfillCacheProvider.test.ts │ │ ├── RemoteFallbackCacheProvider.test.ts │ │ ├── backfillWrapper.test.ts │ │ ├── chunkPromise.test.ts │ │ └── fixtures │ │ │ └── backfill-config │ │ │ └── backfill.config.js │ └── tsconfig.json ├── cli │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── README.md │ ├── bin │ │ ├── lage-server.js │ │ └── lage.js │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── cache │ │ │ ├── createCacheProvider.ts │ │ │ └── isRunningFromCI.ts │ │ ├── cli.ts │ │ ├── commands │ │ │ ├── addOptions.ts │ │ │ ├── affected │ │ │ │ ├── action.ts │ │ │ │ └── index.ts │ │ │ ├── cache │ │ │ │ ├── action.ts │ │ │ │ ├── clearCache.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pruneCache.ts │ │ │ │ └── runners │ │ │ │ │ ├── ClearCacheRunner.ts │ │ │ │ │ └── PruneCacheRunner.ts │ │ │ ├── createReporter.ts │ │ │ ├── exec │ │ │ │ ├── action.ts │ │ │ │ ├── executeInProcess.ts │ │ │ │ ├── executeRemotely.ts │ │ │ │ ├── expandTargetDefinition.ts │ │ │ │ ├── index.ts │ │ │ │ └── simulateFileAccess.ts │ │ │ ├── info │ │ │ │ ├── action.ts │ │ │ │ └── index.ts │ │ │ ├── init │ │ │ │ ├── action.ts │ │ │ │ └── index.ts │ │ │ ├── initializeReporters.ts │ │ │ ├── isRunningFromCI.ts │ │ │ ├── launchServerInBackground.ts │ │ │ ├── options.ts │ │ │ ├── parseServerOption.ts │ │ │ ├── run │ │ │ │ ├── action.ts │ │ │ │ ├── createTargetGraph.ts │ │ │ │ ├── filterArgsForTasks.ts │ │ │ │ ├── filterPipelineDefinitions.ts │ │ │ │ ├── index.ts │ │ │ │ ├── runAction.ts │ │ │ │ ├── watchAction.ts │ │ │ │ └── watcher.ts │ │ │ ├── server │ │ │ │ ├── MemoryStream.ts │ │ │ │ ├── action.ts │ │ │ │ ├── getOutputFiles.ts │ │ │ │ ├── index.ts │ │ │ │ ├── lageService.ts │ │ │ │ └── singleTargetWorker.ts │ │ │ └── targetHashFilePath.ts │ │ ├── filter │ │ │ ├── getFilteredPackages.ts │ │ │ └── hasRepoChanged.ts │ │ ├── getBinPaths.ts │ │ ├── index.ts │ │ ├── optimizeTargetGraph.ts │ │ ├── runnerPickerOptions.ts │ │ ├── runners │ │ │ ├── NoOpRunner.ts │ │ │ ├── NpmScriptRunner.ts │ │ │ ├── README.md │ │ │ └── WorkerRunner.ts │ │ ├── server.ts │ │ ├── showHelp.ts │ │ └── types │ │ │ ├── FilterOptions.ts │ │ │ ├── ReporterInitOptions.ts │ │ │ └── errors.ts │ ├── tests │ │ ├── filterPackages.test.ts │ │ ├── getFilteredPackages.test.ts │ │ ├── initializeReporters.test.ts │ │ ├── jsonReporter.test.ts │ │ ├── parseServerOption.test.ts │ │ └── simulateFileAccess.test.ts │ └── tsconfig.json ├── config │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── getConcurrency.ts │ │ ├── getConfig.ts │ │ ├── getMaxWorkersPerTask.ts │ │ ├── index.ts │ │ ├── readConfigFile.ts │ │ └── types │ │ │ ├── CacheOptions.ts │ │ │ ├── ConfigOptions.ts │ │ │ ├── LoggerOptions.ts │ │ │ ├── PipelineDefinition.ts │ │ │ └── Priority.ts │ ├── tests │ │ ├── fixtures │ │ │ ├── async-config │ │ │ │ └── lage.config.js │ │ │ └── sync-config │ │ │ │ └── lage.config.js │ │ ├── getConfig.test.ts │ │ └── getMaxWorkersPerTask.test.ts │ └── tsconfig.json ├── e2e-tests │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── __snapshots__ │ │ │ └── info.test.ts.snap │ │ ├── basic.test.ts │ │ ├── basicFailure.test.ts │ │ ├── bigapp.test.ts │ │ ├── cacheClear.test.ts │ │ ├── info.test.ts │ │ ├── killDetachedProcess.ts │ │ ├── lageserver.test.ts │ │ ├── mock │ │ │ └── monorepo.ts │ │ ├── parseNdJson.ts │ │ ├── remoteFallback.test.ts │ │ └── transitiveTaskDeps.test.ts │ ├── tsconfig.json │ └── yarn │ │ └── yarn.js ├── format-hrtime │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── formatDuration.ts │ │ └── index.ts │ ├── tests │ │ └── formatDuration.test.ts │ └── tsconfig.json ├── globby │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── dts-bundle.config.js │ ├── package.json │ ├── scripts │ │ └── rename.js │ ├── src │ │ └── index.mts │ └── tsconfig.json ├── hasher │ ├── .eslintignore │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── FileHasher.ts │ │ ├── PackageTree.ts │ │ ├── TargetHasher.ts │ │ ├── __fixtures__ │ │ │ ├── .gitignore │ │ │ ├── basic │ │ │ │ ├── node_modules │ │ │ │ │ ├── .bin │ │ │ │ │ │ └── copy │ │ │ │ │ └── package-2 │ │ │ │ │ │ └── package.json │ │ │ │ ├── package.json │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ └── yarn.lock │ │ │ ├── config │ │ │ │ ├── backfill.config.js │ │ │ │ ├── package.json │ │ │ │ └── packages │ │ │ │ │ ├── package-1 │ │ │ │ │ ├── backfill.config.js │ │ │ │ │ └── package.json │ │ │ │ │ └── package-2 │ │ │ │ │ └── package.json │ │ │ ├── empty │ │ │ │ ├── package.json │ │ │ │ └── yarn.lock │ │ │ ├── monorepo-different │ │ │ │ ├── package.json │ │ │ │ ├── packages │ │ │ │ │ ├── package-a │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── package-b │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ └── yarn.lock │ │ │ ├── monorepo-nested │ │ │ │ ├── package.json │ │ │ │ ├── packages │ │ │ │ │ └── package-a │ │ │ │ │ │ ├── build │ │ │ │ │ │ ├── another │ │ │ │ │ │ │ └── file.ts │ │ │ │ │ │ └── package-b │ │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ └── yarn.lock │ │ │ ├── monorepo-pnpm │ │ │ │ ├── package.json │ │ │ │ ├── packages │ │ │ │ │ ├── package-a │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── package-b │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ ├── pnpm-lock.yaml │ │ │ │ └── pnpm-workspace.yaml │ │ │ ├── monorepo-rush-pnpm │ │ │ │ ├── common │ │ │ │ │ └── config │ │ │ │ │ │ └── rush │ │ │ │ │ │ ├── command-line.json │ │ │ │ │ │ └── pnpm-lock.yaml │ │ │ │ ├── packages │ │ │ │ │ ├── package-a │ │ │ │ │ │ ├── .rush │ │ │ │ │ │ │ └── temp │ │ │ │ │ │ │ │ ├── package-deps_compile.json │ │ │ │ │ │ │ │ └── shrinkwrap-deps.json │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── package-b │ │ │ │ │ │ ├── .rush │ │ │ │ │ │ └── temp │ │ │ │ │ │ │ ├── package-deps_compile.json │ │ │ │ │ │ │ └── shrinkwrap-deps.json │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ └── rush.json │ │ │ ├── monorepo-rush-yarn │ │ │ │ ├── common │ │ │ │ │ └── config │ │ │ │ │ │ └── rush │ │ │ │ │ │ ├── command-line.json │ │ │ │ │ │ └── yarn.lock │ │ │ │ ├── packages │ │ │ │ │ ├── package-a │ │ │ │ │ │ ├── .rush │ │ │ │ │ │ │ └── temp │ │ │ │ │ │ │ │ ├── package-deps_compile.json │ │ │ │ │ │ │ │ └── shrinkwrap-deps.json │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── package-b │ │ │ │ │ │ ├── .rush │ │ │ │ │ │ └── temp │ │ │ │ │ │ │ ├── package-deps_compile.json │ │ │ │ │ │ │ └── shrinkwrap-deps.json │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ └── rush.json │ │ │ ├── monorepo-with-deps │ │ │ │ ├── node_modules │ │ │ │ │ ├── .yarn-integrity │ │ │ │ │ ├── package-a │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── package-b │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ ├── package.json │ │ │ │ ├── packages │ │ │ │ │ ├── package-a │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── package-b │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ └── yarn.lock │ │ │ ├── monorepo-with-global-files-different-tasks │ │ │ │ ├── .eslintrc.js │ │ │ │ ├── package.json │ │ │ │ ├── packages │ │ │ │ │ ├── package-a │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── package-b │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ ├── some-global.config.js │ │ │ │ └── yarn.lock │ │ │ ├── monorepo-with-global-files │ │ │ │ ├── package.json │ │ │ │ ├── packages │ │ │ │ │ ├── package-a │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── package-b │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ ├── some-global.config.js │ │ │ │ ├── some-global2.config.js │ │ │ │ └── yarn.lock │ │ │ ├── monorepo │ │ │ │ ├── package.json │ │ │ │ ├── packages │ │ │ │ │ ├── package-a │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── package-b │ │ │ │ │ │ ├── node_modules │ │ │ │ │ │ └── .bin │ │ │ │ │ │ │ └── copy │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── src │ │ │ │ │ │ └── index.ts │ │ │ │ └── yarn.lock │ │ │ ├── nested-test-project │ │ │ │ ├── package.json │ │ │ │ └── src │ │ │ │ │ └── file 1.txt │ │ │ └── test-project │ │ │ │ ├── file 2.txt │ │ │ │ ├── file1.txt │ │ │ │ ├── file蝴蝶.txt │ │ │ │ └── package.json │ │ ├── __tests__ │ │ │ ├── PackageTree.test.ts │ │ │ ├── TargetHasher.test.ts │ │ │ ├── getPackageDeps.test.ts │ │ │ ├── resolveDependenciesHelper.ts │ │ │ ├── resolveExternalDependencies.test.ts │ │ │ └── resolveInternalDependencies.test.ts │ │ ├── expandInputPatterns.ts │ │ ├── getInputFiles.ts │ │ ├── getPackageDeps.ts │ │ ├── hashStrings.ts │ │ ├── index.ts │ │ ├── nameAtVersion.ts │ │ ├── resolveExternalDependencies.ts │ │ └── resolveInternalDependencies.ts │ └── tsconfig.json ├── lage │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── README.md │ ├── RELEASE.md │ ├── dts-bundle.config.js │ ├── index.js │ ├── package.json │ ├── scripts │ │ ├── bundle.mjs │ │ ├── prebuild.js │ │ ├── retain-dynamic-import-plugin.js │ │ └── update-dts-bundle.js │ ├── tsconfig.json │ └── v1-changelogs │ │ ├── CHANGELOG.json │ │ └── CHANGELOG.md ├── logger │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── Logger.ts │ │ ├── index.ts │ │ └── interfaces │ │ │ ├── LogEntry.ts │ │ │ ├── LogLevel.ts │ │ │ ├── LogStructuredData.ts │ │ │ └── Reporter.ts │ ├── tests │ │ └── logger.test.ts │ └── tsconfig.json ├── monorepo-fixture │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── monorepo.ts │ └── tsconfig.json ├── reporters │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── AdoReporter.ts │ │ ├── ChromeTraceEventsReporter.ts │ │ ├── JsonReporter.ts │ │ ├── LogReporter.ts │ │ ├── ProgressReporter.ts │ │ ├── VerboseFileLogReporter.ts │ │ ├── formatBytes.ts │ │ ├── index.ts │ │ ├── isTargetStatusLogEntry.ts │ │ ├── slowestTargetRuns.ts │ │ └── types │ │ │ ├── TargetLogEntry.ts │ │ │ └── progressBarTypes.ts │ ├── tests │ │ ├── AdoReporter.test.ts │ │ ├── ChromeTraceEventsReporter.test.ts │ │ ├── JsonReporter.test.ts │ │ ├── LogReporter.test.ts │ │ └── VerboseFileLogReporter.test.ts │ └── tsconfig.json ├── rpc │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── buf.gen.yaml │ ├── buf.yaml │ ├── package.json │ ├── proto │ │ └── lage │ │ │ └── v1 │ │ │ └── lage.proto │ ├── src │ │ ├── createClient.ts │ │ ├── createRoutes.ts │ │ ├── createServer.ts │ │ ├── gen │ │ │ └── lage │ │ │ │ └── v1 │ │ │ │ ├── lage_connect.ts │ │ │ │ └── lage_pb.ts │ │ ├── index.ts │ │ └── types │ │ │ └── ILageService.ts │ └── tsconfig.json ├── runners │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── NoOpRunner.ts │ │ ├── NpmScriptRunner.ts │ │ ├── TargetRunnerPicker.ts │ │ ├── WorkerRunner.ts │ │ ├── index.ts │ │ └── types │ │ │ ├── TargetRunner.ts │ │ │ └── TargetRunnerPickerOptions.ts │ └── tsconfig.json ├── scheduler-types │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── types │ │ │ ├── SchedulerRunSummary.ts │ │ │ ├── TargetRun.ts │ │ │ ├── TargetScheduler.ts │ │ │ └── TargetStatus.ts │ └── tsconfig.json ├── scheduler │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── SimpleScheduler.ts │ │ ├── WrappedTarget.ts │ │ ├── bufferTransform.ts │ │ ├── cache │ │ │ ├── createCacheProvider.ts │ │ │ └── isRunningFromCI.ts │ │ ├── categorizeTargetRuns.ts │ │ ├── formatBytes.ts │ │ ├── getLageOutputCacheLocation.ts │ │ ├── index.ts │ │ └── workers │ │ │ └── targetWorker.ts │ ├── tests │ │ ├── NpmScriptRunner.test.ts │ │ ├── SimpleScheduler.test.ts │ │ ├── SimpleScheduler.watchmode.test.ts │ │ ├── WrappedTarget.test.ts │ │ ├── fixtures │ │ │ ├── AbortEarlyRunner.js │ │ │ ├── FailOnPackageRunner.js │ │ │ ├── NoOpRunner.js │ │ │ ├── fakeNpm │ │ │ ├── fakeNpm.cmd │ │ │ ├── fakeNpm.js │ │ │ ├── package-a │ │ │ │ └── package.json │ │ │ ├── pools.ts │ │ │ └── worker.js │ │ └── waitFor.ts │ └── tsconfig.json ├── target-graph │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── TargetFactory.ts │ │ ├── TargetGraphBuilder.ts │ │ ├── WorkspaceTargetGraphBuilder.ts │ │ ├── detectCycles.ts │ │ ├── expandDepSpecs.ts │ │ ├── getWeight.ts │ │ ├── index.ts │ │ ├── prioritize.ts │ │ ├── removeNodes.ts │ │ ├── sortTargetsByPriority.ts │ │ ├── targetId.ts │ │ ├── transitiveReduction.ts │ │ └── types │ │ │ ├── Target.ts │ │ │ ├── TargetConfig.ts │ │ │ └── TargetGraph.ts │ ├── tests │ │ ├── TargetFactory.test.ts │ │ ├── TargetGraphBuilder.test.ts │ │ ├── WorkspaceTargetGraphBuilder.test.ts │ │ ├── detectCycles.test.ts │ │ ├── prioritize.test.ts │ │ ├── targetId.test.ts │ │ └── transitiveReduction.test.ts │ └── tsconfig.json ├── tsconfig.lage2.json └── worker-threads-pool │ ├── CHANGELOG.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ ├── AggregatedPool.ts │ ├── TaskInfo.ts │ ├── ThreadWorker.ts │ ├── WorkerPool.ts │ ├── createFilteredStreamTransform.ts │ ├── index.ts │ ├── pickTaskFromQueue.ts │ ├── registerWorker.ts │ ├── stdioStreamMarkers.ts │ └── types │ │ ├── Pool.ts │ │ ├── WorkerPoolOptions.ts │ │ └── WorkerQueue.ts │ ├── tests │ ├── AggregatedPool.test.ts │ ├── WorkerPool.test.ts │ ├── fixtures │ │ ├── my-5mb-worker.js │ │ ├── my-bad-worker.js │ │ ├── my-worker.js │ │ └── registerWorker.fixture.js │ └── pickTaskFromQueue.test.ts │ └── tsconfig.json ├── patches └── node-fetch+2.6.9.patch ├── renovate.json5 ├── scripts ├── bin │ └── monorepo-scripts.js ├── commands │ └── lint.js ├── config │ ├── eslintrc.js │ ├── jest-setup-after-env.js │ ├── jest.config.js │ └── tsconfig.eslint.json ├── debugTests.js ├── package.json └── worker │ ├── depcheck.js │ ├── jest.js │ ├── lint.js │ ├── transpile.js │ └── types.js ├── test.md ├── types └── global │ └── index.d.ts └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | // This is only used to enable eslint in the editor 4 | module.exports = { 5 | ...require("./scripts/config/eslintrc"), 6 | parserOptions: { 7 | project: path.join(__dirname, "./scripts/config/tsconfig.eslint.json"), 8 | }, 9 | ignorePatterns: ["**/*.js", "**/__fixtures__", "**/hasher/src/__tests__", "docs", "packages/*/scripts", "packages/*/src/gen", "lib/"], 10 | }; 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.png binary 3 | *.jpg binary 4 | *.gif binary 5 | *.jpeg binary 6 | *.ttf binary 7 | *.webm binary -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | /packages/lage2 @kenotron 2 | /packages/cli @kenotron 3 | /packages/cache @kenotron 4 | /packages/logger @kenotron 5 | /packages/monorepo-fixture @kenotron 6 | /packages/reporters @kenotron 7 | /packages/scheduler @kenotron 8 | /packages/target-graph @kenotron 9 | /packages/worker-thread-pool @kenotron 10 | /scripts @kenotron -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Environment/Troubleshooting** 24 | Please paste in the result of `npx envinfo` to help identify your run time environment 25 | 26 | If this is an issue with run order of tasks, please paste in the result of `lage info [your command(s)] [options]` 27 | -------------------------------------------------------------------------------- /.github/workflows/docusaurus-pr.yml: -------------------------------------------------------------------------------- 1 | name: Docs - PR 2 | 3 | # This doesn't actually deploy, just builds the docs folder when it's modified. 4 | 5 | on: 6 | pull_request: 7 | branches: 8 | - master 9 | paths: 10 | - .github/workflows/docusaurus-pr.yml 11 | - "docs/**" 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.ref }} 15 | cancel-in-progress: true 16 | 17 | permissions: {} 18 | 19 | jobs: 20 | test-build: 21 | name: Test build 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 25 | 26 | - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 27 | with: 28 | node-version-file: .nvmrc 29 | 30 | - run: yarn --immutable 31 | working-directory: docs 32 | 33 | - name: Build website 34 | working-directory: docs 35 | run: yarn build 36 | 37 | - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 38 | with: 39 | name: docs 40 | path: docs/build/** 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | src/**/*.js 3 | dist 4 | lib 5 | *.log 6 | .DS_Store 7 | *.pdf 8 | coverage 9 | .idea/ 10 | **/.docusaurus/ 11 | profile*.json 12 | .yarn/* 13 | !.yarn/releases/ 14 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged --allow-empty -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | change 3 | scripts 4 | decks 5 | docs 6 | tests 7 | .github 8 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Not formatted 2 | .*ignore 3 | *.proto 4 | 5 | # Generated or imported files 6 | change/*.json 7 | docs/build 8 | CHANGELOG.* 9 | LICENSE 10 | SECURITY.md 11 | *.snap 12 | yarn.lock 13 | **/.yarn 14 | # this is a test fixture 15 | yarn.js 16 | **/.rush/temp 17 | 18 | # These lines from .gitignore can be removed after prettier 3 update 19 | node_modules 20 | src/**/*.js 21 | dist 22 | lib 23 | *.log 24 | .DS_Store 25 | *.pdf 26 | coverage 27 | .idea/ 28 | **/.docusaurus/ 29 | profile*.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "semi": true, 4 | "singleQuote": false, 5 | "trailingComma": "es5", 6 | "tabWidth": 2, 7 | "overrides": [ 8 | { 9 | "files": "*.json5", 10 | "options": { 11 | "parser": "json" 12 | } 13 | } 14 | ], 15 | "endOfLine": "lf" 16 | } 17 | -------------------------------------------------------------------------------- /.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "jsc": { 4 | "parser": { 5 | "syntax": "typescript", 6 | "tsx": false, 7 | "dynamicImport": true 8 | }, 9 | "target": "es2020" 10 | }, 11 | "module": { 12 | "type": "commonjs", 13 | "ignoreDynamic": true 14 | }, 15 | "sourceMaps": true 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Attach", 9 | "port": 9229, 10 | "request": "attach", 11 | "skipFiles": ["/**"], 12 | "type": "node" 13 | }, 14 | { 15 | "name": "Current TS File", 16 | "type": "node", 17 | "request": "launch", 18 | "args": ["${relativeFile}"], 19 | "env": { 20 | "NODE_OPTIONS": "-r ts-node/register", 21 | "TS_NODE_COMPILER_OPTIONS": "{\"target\":\"ES2020\",\"module\":\"CommonJS\",\"moduleResolution\":\"node16\",\"sourceMap\":false}" 22 | }, 23 | "runtimeArgs": [], 24 | "sourceMaps": true, 25 | "cwd": "${workspaceRoot}", 26 | "protocol": "inspector" 27 | }, 28 | { 29 | "name": "Debug current open test", 30 | "type": "node", 31 | "request": "launch", 32 | "program": "${workspaceRoot}/scripts/debugTests.js", 33 | "cwd": "${fileDirname}", 34 | "args": ["--runInBand", "--watch", "${fileBasename}"], 35 | "sourceMaps": true, 36 | "outputCapture": "std", 37 | "console": "integratedTerminal" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true, 4 | "typescript.tsdk": "node_modules/typescript/lib", 5 | "files.associations": { 6 | "*.json5": "jsonc" 7 | }, 8 | "search.exclude": { 9 | "**/lib": true, 10 | "**/node_modules": true, 11 | "**/.docusaurus": true, 12 | "**/.yarn": true, 13 | "**/yarn/yarn.js": true, 14 | "**/docs/build": true, 15 | "**/*.code-search": true 16 | }, 17 | "[typescript]": { 18 | "editor.defaultFormatter": "esbenp.prettier-vscode" 19 | }, 20 | "[javascript]": { 21 | "editor.defaultFormatter": "vscode.typescript-language-features" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-4.9.1.cjs 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lage 2 | 3 | Documentation is here: https://microsoft.github.io/lage/ 4 | 5 | # Contributing 6 | 7 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 8 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 9 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 10 | 11 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 12 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 13 | provided by the bot. You will only need to do this once across all repos using our CLA. 14 | 15 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 16 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 17 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 18 | -------------------------------------------------------------------------------- /beachball.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | /** @type {import('beachball').BeachballConfig}*/ 3 | module.exports = { 4 | groupChanges: true, 5 | access: "public", 6 | ignorePatterns: [ 7 | "benchmark/**", 8 | ".*ignore", 9 | ".github/**", 10 | "beachball.config.js", 11 | "decks/**", 12 | "docs/**", 13 | "jasmine.json", 14 | "packages/*/jest.config.js", 15 | "packages/*/tests/**", 16 | // This one is especially important (otherwise dependabot would be blocked by change file requirements) 17 | "yarn.lock", 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /benchmark/.gitignore: -------------------------------------------------------------------------------- 1 | .yarn/* -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lage-run/benchmark", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "private": true, 7 | "dependencies": { 8 | "array-differ": "^4.0.0", 9 | "execa": "^9.3.0", 10 | "fast-glob": "^3.3.2", 11 | "glob-hasher": "^1.4.2", 12 | "globby": "^14.0.1", 13 | "micromatch": "^4.0.7", 14 | "multimatch": "^7.0.0", 15 | "workspace-tools": "^0.38.0" 16 | }, 17 | "type": "module", 18 | "module": "index.js" 19 | } 20 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | .yarn/ -------------------------------------------------------------------------------- /docs/.yarnrc.yml: -------------------------------------------------------------------------------- 1 | yarnPath: ../.yarn/releases/yarn-4.8.0.cjs 2 | nodeLinker: node-modules 3 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/docs/cookbook/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Cookbook", 3 | "position": 5 4 | } 5 | -------------------------------------------------------------------------------- /docs/docs/guides/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introducing Lage 3 | --- 4 | 5 | ## Overview 6 | 7 | Your JS repo has gotten large enough that you have turned to using a tool to help you manage multiple packages inside a repository. That's great! However, you realized quickly that the tasks defined inside the workspace have to be run in package dependency order. 8 | 9 | Lerna, Rush, wsrun and even pnpm will provide a simple way for you to run npm scripts to be run in a topological order. However, these tools will force you to run your tasks by script name one at a time. For example, all the `build` scripts will have to run first. Then all the `test` scripts run in the topological order. 10 | 11 | This usually means that there are wasted CPU cycles in between `build` and `test`. We can achieve better pipelining the npm scripts if we had a way to say that `test` can run as soon as `build` are done for the package. 12 | 13 | `lage` (Norwegian for "make", pronounced law-geh) solves this by providing a terse pipelining syntax. It has features specifically address large monorepos with high number of packages: 14 | 15 | - package and task scopes 16 | - output caching 17 | - sound scheduling with package task pipeline 18 | - prioritization 19 | -------------------------------------------------------------------------------- /docs/docs/guides/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Guides", 3 | "position": 3 4 | } 5 | -------------------------------------------------------------------------------- /docs/docs/guides/cache.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | 4 | title: Local caching 5 | --- 6 | 7 | `lage` by default will cache recent task results locally on disk. As long as the source file and the command arguments have not changed, those cached results will be restored. 8 | 9 | See [remote cache](./remote-cache.md) for details about speeding up local dev environment even further with a remote cache from Continuous Integration jobs. 10 | 11 | ## Turn off cache 12 | 13 | Sometimes, this incremental behavior is not desired. You can override the caching behavior by using the `--no-cache` argument. 14 | 15 | ``` 16 | lage build --no-cache 17 | ``` 18 | 19 | ## Resetting cache 20 | 21 | Once in a while, the cache might need to be recreated from scratch. In those situations, you can reset the cache by passing in the `--reset-cache` argument to the command line. 22 | 23 | ``` 24 | lage build --reset-cache 25 | ``` 26 | 27 | ## Cache Options 28 | 29 | Caching capability is provided by `backfill`. All of the configuration under the `cacheOptions` key is passed to `backfill`. For the complete documentation of `cacheOptions`, see the [`backfill` configuration documentation](https://github.com/microsoft/backfill#configuration). 30 | -------------------------------------------------------------------------------- /docs/docs/guides/priority.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | 4 | title: Priorities 5 | --- 6 | 7 | In a large monorepo, you'll need to do some [profiling](./profile.md) to understand bottlenecks. Sometimes, the package tasks are not scheduled in the order that will produce the most optimized run times. 8 | 9 | ## v2 styled configuration for priority 10 | 11 | As of `lage` v2, you can now configure the priority inside the target pipeline configuration: 12 | 13 | ```js 14 | module.exports = { 15 | pipeline: { 16 | build: ["^build"], 17 | test: ["build"], 18 | "foo#test": { 19 | priority: 100, 20 | dependsOn: ["build"] 21 | } 22 | } 23 | }; 24 | ``` 25 | 26 | ## Legacy (v1 + v2) way of configuring priority 27 | 28 | To manually pick a package task to be higher priority, simply place a [`priorities` configuration](../reference/config.md) in the `lage.config.js`: 29 | 30 | ```js 31 | module.exports = { 32 | pipeline: { ... }, 33 | priorities: [ 34 | { 35 | package: 'foo', 36 | task: 'test', 37 | priority: 100 38 | } 39 | ] 40 | } 41 | ``` 42 | 43 | The higher the priority number, the higher the priority. These numbers are relative to each other. Any task that is not listed in the priorities array is not prioritized. 44 | -------------------------------------------------------------------------------- /docs/docs/guides/profile.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | 4 | title: Profiling 5 | --- 6 | 7 | A particularly complex monorepo can present opportunities for optimization. For example, when there are really large packages, it might be more efficient to break those up so the build can be split across different CPU cores. `lage` greatly enhances the ability for developers to see where the bottlenecks are. 8 | 9 | To collect a profile of the `lage` run, simply add the `--profile` argument. 10 | 11 | ``` 12 | lage build test --profile 13 | ``` 14 | 15 | ## Using the profile 16 | 17 | When the run is finished, a profile JSON file is produced. This file is to be imported into a Chromium-based browser's devtools Performance tab. 18 | 19 | ## Sample of `lage` profile session 20 | 21 | For example, you can see the following `lage` run of the [Fluent UI](https://developer.microsoft.com/en-us/fluentui/#/components) repo. You can see that there is a dip in the concurrency when building the `office-ui-fabric-react` package. This makes sense, because many internal packages depends on that one large package. 22 | -------------------------------------------------------------------------------- /docs/docs/reference/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Reference", 3 | "position": 4 4 | } 5 | -------------------------------------------------------------------------------- /docs/src/components/Badge.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Badge = (props: { children: React.ReactNode }) => { 4 | const { children } = props; 5 | return ( 6 | 7 | {children} 8 | 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /docs/src/components/classNames.ts: -------------------------------------------------------------------------------- 1 | export function cx(...classNames: (string | boolean | null | undefined)[]) { 2 | return classNames.filter(Boolean).join(" "); 3 | } 4 | 5 | export const classNames = { 6 | /** rounded corner size for a box (button or tool, not the highlight bubbles) */ 7 | roundedBox: "rounded-lg", 8 | 9 | // text-lg to text-<2xl 10 | fontSm: "text-s1 md:text-s2 lg:text-s3", 11 | // // text-xl to text-2xl 12 | fontSmPlus: "text-s2 md:text-s3 lg:text-s4", 13 | // text-<2xl to text->2xl 14 | fontMd: "text-s3 md:text-s4 lg:text-s5", 15 | // text-2xl to text-3xl 16 | fontMdLg: "text-s4 md:text-s6 lg:text-s7", 17 | // text-2xl to text-4xl 18 | fontMdXl: "text-s4 md:text-s6 lg:text-s8", 19 | } as const; // so the values show in intellisense 20 | -------------------------------------------------------------------------------- /docs/src/components/home/Quote.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { cx, classNames } from "../classNames"; 3 | 4 | /** Quote aside, hidden on xs, provides its own padding x */ 5 | export const Quote = ( 6 | props: React.PropsWithChildren<{ 7 | author: string; 8 | organization: string; 9 | }> 10 | ) => { 11 | // pt-8 is for the big quote mark 12 | return ( 13 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /docs/src/components/home/Tools.tsx: -------------------------------------------------------------------------------- 1 | import Link from "@docusaurus/Link"; 2 | import React from "react"; 3 | import { ToolList, type ToolInfo } from "../../data/ToolList"; 4 | import { cx, classNames } from "../classNames"; 5 | 6 | const Tool = (props: ToolInfo) => { 7 | const { svg: Svg, title, description, link } = props; 8 | return ( 9 |
15 | 23 | 24 | {title} 25 | 26 |

{description}

27 |
28 | ); 29 | }; 30 | 31 | export const Tools = () => { 32 | return ( 33 |
34 | {ToolList.map((props) => ( 35 | 36 | ))} 37 |
38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /docs/src/components/home/WaveDivider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export const WaveDivider = () => { 4 | return ( 5 |
6 | 11 |
12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* code fonts are overly large */ 3 | --ifm-code-font-size: 95%; 4 | /* Use this background color for the top navbar in light or dark mode */ 5 | --navbar-background: rgb(28 88 106); 6 | } 7 | 8 | pre.prism-code code { 9 | font-size: var(--ifm-code-font-size); 10 | } 11 | 12 | /* top navbar */ 13 | .navbar { 14 | background-color: var(--navbar-background); 15 | } 16 | /* items and link items in top navbar (can't set on .navbar because it will 17 | color certain small-screen sidebar items too) */ 18 | .navbar__items, 19 | .navbar__link--active { 20 | color: white; 21 | --ifm-navbar-link-color: white; 22 | --ifm-navbar-link-hover-color: white; 23 | } 24 | /* add hover underline on links in top navbar (needs more specificity) */ 25 | .navbar__items .navbar__link:hover { 26 | text-decoration: underline; 27 | } 28 | /* top navbar when menu is open on small screens */ 29 | .navbar-sidebar__brand { 30 | background-color: var(--navbar-background); 31 | color: white; 32 | } 33 | 34 | html[data-theme="dark"] { 35 | --ifm-color-primary: rgb(70, 189, 226); 36 | } 37 | 38 | /* Make links in markdown text visible in light mode (add an underline because it was hard to get a 39 | color with enough contrast against both the background and other text) */ 40 | html[data-theme="light"] .markdown { 41 | --ifm-link-color: #357e95; 42 | --ifm-link-decoration: underline; 43 | } 44 | -------------------------------------------------------------------------------- /docs/src/data/ToolList.ts: -------------------------------------------------------------------------------- 1 | import justSvg from "../../static/img/just.svg"; 2 | import beachballSvg from "../../static/img/beachball1.svg"; 3 | import backfillSvg from "../../static/img/backfill.svg"; 4 | import pGraphSvg from "../../static/img/p-chart.svg"; 5 | 6 | export interface ToolInfo { 7 | title: string; 8 | svg: React.ComponentType>; 9 | description: string; 10 | link: string; 11 | } 12 | 13 | export const ToolList: ToolInfo[] = [ 14 | { 15 | title: "Beachball", 16 | svg: beachballSvg, 17 | description: 18 | "The sunniest semantic version bumper. Makes automating npm publishing a breeze.", 19 | link: "https://github.com/microsoft/beachball", 20 | }, 21 | { 22 | title: "Just ___", 23 | svg: justSvg, 24 | description: 25 | "The task library that just works. Use to build, test, and lint your frontend projects.", 26 | link: "https://github.com/microsoft/just", 27 | }, 28 | { 29 | title: "Backfill", 30 | svg: backfillSvg, 31 | description: 32 | "A JavaScript caching library for reducing build time. Used internally by Lage.", 33 | link: "https://github.com/microsoft/backfill", 34 | }, 35 | { 36 | title: "p-graph", 37 | svg: pGraphSvg, 38 | description: "Run a promise graph with concurrency control.", 39 | link: "https://github.com/microsoft/p-graph", 40 | }, 41 | ]; 42 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/fonts/Bahnschrift.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/fonts/Bahnschrift.ttf -------------------------------------------------------------------------------- /docs/static/fonts/LondrinaSolid.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/fonts/LondrinaSolid.ttf -------------------------------------------------------------------------------- /docs/static/img/Wave Shape 1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/static/img/Wave Shape 2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/static/img/contributor-guide-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/contributor-guide-dark.png -------------------------------------------------------------------------------- /docs/static/img/contributor-guide-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/contributor-guide-light.png -------------------------------------------------------------------------------- /docs/static/img/frog-cloud0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/frog-cloud0.png -------------------------------------------------------------------------------- /docs/static/img/frog-hero0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/frog-hero0.png -------------------------------------------------------------------------------- /docs/static/img/frog-monitor0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/frog-monitor0.png -------------------------------------------------------------------------------- /docs/static/img/frog-sing0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/frog-sing0.png -------------------------------------------------------------------------------- /docs/static/img/frog-skip0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/frog-skip0.png -------------------------------------------------------------------------------- /docs/static/img/frog.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/frog.webm -------------------------------------------------------------------------------- /docs/static/img/graph-diagram-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/graph-diagram-dark.png -------------------------------------------------------------------------------- /docs/static/img/graph-diagram-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/graph-diagram-light.png -------------------------------------------------------------------------------- /docs/static/img/lage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/lage.png -------------------------------------------------------------------------------- /docs/static/img/package-graph-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/package-graph-dark.png -------------------------------------------------------------------------------- /docs/static/img/package-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/package-graph.png -------------------------------------------------------------------------------- /docs/static/img/target-graph-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/target-graph-dark.png -------------------------------------------------------------------------------- /docs/static/img/target-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/target-graph.png -------------------------------------------------------------------------------- /docs/static/img/task-graph-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/task-graph-dark.png -------------------------------------------------------------------------------- /docs/static/img/task-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/lage/681710b4af96431c5245237ff7c9a50e56ce2fb9/docs/static/img/task-graph.png -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/docusaurus/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | "lib": ["ES2022", "DOM"], 6 | "strict": true, 7 | "noImplicitAny": false, 8 | "forceConsistentCasingInFileNames": true 9 | }, 10 | "include": ["src"] 11 | } 12 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/" 3 | to = "/lage" 4 | status = 301 5 | 6 | [build] 7 | base = "docs/" 8 | publish = "build/" -------------------------------------------------------------------------------- /packages/cache-github-actions/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@lage-run/monorepo-scripts/config/jest.config.js"); 2 | -------------------------------------------------------------------------------- /packages/cache-github-actions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lage-run/cache-github-actions", 3 | "version": "0.1.24", 4 | "description": "Github Action Cache for Lage", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/microsoft/lage" 8 | }, 9 | "homepage": "https://microsoft.github.io/lage/", 10 | "license": "MIT", 11 | "main": "lib/index.js", 12 | "types": "lib/index.d.ts", 13 | "scripts": { 14 | "build": "monorepo-scripts tsc", 15 | "start": "monorepo-scripts tsc -w --preserveWatchOutput", 16 | "lint": "monorepo-scripts lint" 17 | }, 18 | "dependencies": { 19 | "@actions/cache": "3.3.0", 20 | "backfill-config": "6.6.0", 21 | "backfill-logger": "5.3.0", 22 | "workspace-tools": "0.38.3" 23 | }, 24 | "devDependencies": { 25 | "@lage-run/monorepo-scripts": "workspace:^" 26 | }, 27 | "publishConfig": { 28 | "access": "public" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/cache-github-actions/src/cacheProvider.ts: -------------------------------------------------------------------------------- 1 | import cache from "@actions/cache"; 2 | import type { CustomStorageConfig } from "backfill-config"; 3 | import type { Logger } from "backfill-logger"; 4 | import path from "path"; 5 | import { getWorkspaceRoot } from "workspace-tools"; 6 | 7 | const root = getWorkspaceRoot(process.cwd())!; 8 | 9 | const cacheProvider: CustomStorageConfig = { 10 | provider: (_logger: Logger, cwd: string) => { 11 | return { 12 | async fetch(hash: string): Promise { 13 | if (!hash) { 14 | return false; 15 | } 16 | 17 | const paths = [path.relative(root, path.join(cwd, "**"))]; 18 | const restoreKeys = ["lage-"]; 19 | 20 | const cacheKey = await cache.restoreCache(paths, hash, restoreKeys); 21 | 22 | return !!cacheKey; 23 | }, 24 | 25 | async put(hash: string, filesToCache: string[]): Promise { 26 | if (!hash) { 27 | return; 28 | } 29 | 30 | const paths = filesToCache.map((files) => path.relative(root, path.join(cwd, files))); 31 | 32 | await cache.saveCache(paths, hash); 33 | }, 34 | }; 35 | }, 36 | name: "github-actions", 37 | }; 38 | 39 | export { cacheProvider }; 40 | -------------------------------------------------------------------------------- /packages/cache-github-actions/src/index.ts: -------------------------------------------------------------------------------- 1 | export { cacheProvider as default } from "./cacheProvider.js"; 2 | -------------------------------------------------------------------------------- /packages/cache-github-actions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.lage2.json", 3 | "compilerOptions": { 4 | "outDir": "./lib" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/cache/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@lage-run/monorepo-scripts/config/jest.config.js"); 2 | -------------------------------------------------------------------------------- /packages/cache/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lage-run/cache", 3 | "version": "1.3.13", 4 | "description": "Cache for Lage", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/microsoft/lage" 8 | }, 9 | "homepage": "https://microsoft.github.io/lage/", 10 | "license": "MIT", 11 | "main": "lib/index.js", 12 | "types": "lib/index.d.ts", 13 | "scripts": { 14 | "build": "monorepo-scripts tsc", 15 | "start": "monorepo-scripts tsc -w --preserveWatchOutput", 16 | "test": "monorepo-scripts jest", 17 | "lint": "monorepo-scripts lint" 18 | }, 19 | "dependencies": { 20 | "@azure/identity": "^4.0.1", 21 | "@lage-run/config": "workspace:^", 22 | "@lage-run/logger": "workspace:^", 23 | "@lage-run/target-graph": "workspace:^", 24 | "backfill-cache": "5.10.0", 25 | "backfill-config": "6.6.0", 26 | "backfill-logger": "5.3.0", 27 | "glob-hasher": "^1.4.2" 28 | }, 29 | "devDependencies": { 30 | "@lage-run/monorepo-fixture": "workspace:^", 31 | "@lage-run/monorepo-scripts": "workspace:^" 32 | }, 33 | "publishConfig": { 34 | "access": "public" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/cache/src/CredentialCache.ts: -------------------------------------------------------------------------------- 1 | import { DefaultAzureCredential } from "@azure/identity"; 2 | 3 | export class CredentialCache { 4 | private static credential: DefaultAzureCredential | null = null; 5 | 6 | public static getInstance(): DefaultAzureCredential { 7 | if (!this.credential) { 8 | this.credential = new DefaultAzureCredential(); 9 | } 10 | return this.credential; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/cache/src/chunkPromise.ts: -------------------------------------------------------------------------------- 1 | type PromiseFn = () => Promise; 2 | 3 | export async function chunkPromise(promises: (Promise | PromiseFn)[], limit = 5) { 4 | for (let i = 0; i < promises.length; i += limit) { 5 | await Promise.all(promises.slice(i, i + limit).map((p) => (typeof p === "function" ? p() : p))); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/cache/src/getCacheDirectory.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | 3 | export function getCacheDirectoryRoot(root: string) { 4 | return path.join(root, "node_modules", ".cache", "lage"); 5 | } 6 | 7 | export function getCacheDirectory(root: string, hash: string) { 8 | return path.join(getCacheDirectoryRoot(root), "cache", hash.substring(0, 4)); 9 | } 10 | 11 | export function getLogsCacheDirectory(root: string, hash: string) { 12 | return path.join(getCacheDirectoryRoot(root), "logs", hash.substring(0, 4)); 13 | } 14 | -------------------------------------------------------------------------------- /packages/cache/src/index.ts: -------------------------------------------------------------------------------- 1 | export { BackfillCacheProvider } from "./providers/BackfillCacheProvider.js"; 2 | export { RemoteFallbackCacheProvider } from "./providers/RemoteFallbackCacheProvider.js"; 3 | export type { CacheOptions } from "@lage-run/config"; 4 | export type { CacheProvider } from "./types/CacheProvider.js"; 5 | 6 | export { getCacheDirectory, getLogsCacheDirectory, getCacheDirectoryRoot } from "./getCacheDirectory.js"; 7 | -------------------------------------------------------------------------------- /packages/cache/src/types/CacheProvider.ts: -------------------------------------------------------------------------------- 1 | import type { Target } from "@lage-run/target-graph"; 2 | 3 | export interface CacheProvider { 4 | fetch(hash: string, target: Target): Promise; 5 | put(hash: string, target: Target): Promise; 6 | clear(): Promise; 7 | purge(sinceDays: number): Promise; 8 | isReadOnly?: boolean; 9 | } 10 | -------------------------------------------------------------------------------- /packages/cache/tests/chunkPromise.test.ts: -------------------------------------------------------------------------------- 1 | import { chunkPromise } from "../src/chunkPromise"; 2 | 3 | describe("chunking promises", () => { 4 | it("should chunk promises", async () => { 5 | const mockedPromiseFns = [ 6 | jest.fn().mockResolvedValue(1).mockName("1"), 7 | jest.fn().mockResolvedValue(2).mockName("2"), 8 | jest.fn().mockResolvedValue(3).mockName("3"), 9 | jest.fn().mockResolvedValue(4).mockName("4"), 10 | jest.fn().mockResolvedValue(5).mockName("5"), 11 | ]; 12 | 13 | await chunkPromise(mockedPromiseFns, 2); 14 | 15 | for (const mockedPromiseFn of mockedPromiseFns) { 16 | expect(mockedPromiseFn).toBeCalledTimes(1); 17 | } 18 | }); 19 | 20 | it("should throw, if one promise was rejected", async () => { 21 | const mockedPromiseFns = [ 22 | jest.fn().mockResolvedValue(1).mockName("1"), 23 | jest.fn().mockResolvedValue(2).mockName("2"), 24 | jest.fn().mockResolvedValue(3).mockName("3"), 25 | jest.fn().mockResolvedValue(4).mockName("4"), 26 | jest.fn().mockResolvedValue(5).mockName("5"), 27 | ]; 28 | 29 | expect(async () => await chunkPromise(mockedPromiseFns, 2)).rejects; 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/cache/tests/fixtures/backfill-config/backfill.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /packages/cache/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.lage2.json", 3 | "compilerOptions": { 4 | "outDir": "./lib" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # @lage-run/cli 2 | 3 | This is the command line interface (CLI) for lage. It contains the logic to tie everything together: 4 | 5 | 1. parses CLI arguments via `commander` 6 | 2. initializes the various commands 7 | 3. for running the targets, there are some reserved options for lage, but the rest are passed through to the scripts 8 | 4. figures out the filtered packages as entry points (dependencies are also run, unless --no-dependents are specified) 9 | 5. scheduler, reporter, cache, logger are initialized and run 10 | -------------------------------------------------------------------------------- /packages/cli/bin/lage-server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require("../lib/server.js"); 4 | -------------------------------------------------------------------------------- /packages/cli/bin/lage.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require("../lib/cli.js"); 4 | -------------------------------------------------------------------------------- /packages/cli/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@lage-run/monorepo-scripts/config/jest.config.js"); 2 | -------------------------------------------------------------------------------- /packages/cli/src/cache/createCacheProvider.ts: -------------------------------------------------------------------------------- 1 | import type { CacheOptions } from "@lage-run/cache"; 2 | import { TargetHasher } from "@lage-run/hasher"; 3 | import type { Logger } from "@lage-run/logger"; 4 | 5 | interface CreateCacheOptions { 6 | cacheOptions?: CacheOptions; 7 | logger: Logger; 8 | root: string; 9 | skipLocalCache: boolean; 10 | cliArgs: string[]; 11 | } 12 | 13 | export async function createCache(options: CreateCacheOptions) { 14 | const { cacheOptions, root, cliArgs, logger } = options; 15 | 16 | const hasher = new TargetHasher({ 17 | root, 18 | environmentGlob: cacheOptions?.environmentGlob ?? [], 19 | cacheKey: cacheOptions?.cacheKey, 20 | cliArgs, 21 | logger, 22 | }); 23 | 24 | await hasher.initialize(); 25 | 26 | return { hasher }; 27 | } 28 | -------------------------------------------------------------------------------- /packages/cli/src/cache/isRunningFromCI.ts: -------------------------------------------------------------------------------- 1 | export const isRunningFromCI = process.env.NODE_ENV !== "test" && (!!process.env.CI || !!process.env.TF_BUILD); 2 | -------------------------------------------------------------------------------- /packages/cli/src/cli.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | 3 | import { runCommand } from "./commands/run/index.js"; 4 | import { cacheCommand } from "./commands/cache/index.js"; 5 | import { NoTargetFoundError } from "./types/errors.js"; 6 | import { affectedCommand } from "./commands/affected/index.js"; 7 | import { initCommand } from "./commands/init/index.js"; 8 | import { infoCommand } from "./commands/info/index.js"; 9 | import { execCommand } from "./commands/exec/index.js"; 10 | 11 | async function main() { 12 | const program = new Command(); 13 | program.addCommand(runCommand, { isDefault: true }); 14 | program.addCommand(cacheCommand); 15 | program.addCommand(affectedCommand); 16 | program.addCommand(initCommand); 17 | program.addCommand(infoCommand); 18 | program.addCommand(execCommand); 19 | 20 | await program.parseAsync(process.argv); 21 | } 22 | 23 | main().catch((err) => { 24 | /* eslint-disable no-console */ 25 | switch (err) { 26 | case NoTargetFoundError: 27 | console.log("lage: no targets found that matches the given scope."); 28 | break; 29 | default: 30 | console.error(err); 31 | break; 32 | } 33 | /* eslint-enable no-console */ 34 | 35 | process.exitCode = 1; 36 | }); 37 | -------------------------------------------------------------------------------- /packages/cli/src/commands/addOptions.ts: -------------------------------------------------------------------------------- 1 | import type { Command, Option } from "commander"; 2 | import { options } from "./options.js"; 3 | 4 | export function addOptions(category: keyof typeof options, command: Command) { 5 | for (const option of Object.values