diff --git a/packages/angular/src/builders/utilities/buildable-libs.ts b/packages/angular/src/builders/utilities/buildable-libs.ts index 02ccdaadb6..6f6e635468 100644 --- a/packages/angular/src/builders/utilities/buildable-libs.ts +++ b/packages/angular/src/builders/utilities/buildable-libs.ts @@ -2,7 +2,7 @@ import { calculateProjectDependencies, createTmpTsConfig, DependentBuildableProjectNode, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import { ProjectGraph, readCachedProjectGraph } from '@nrwl/devkit'; import { join } from 'path'; diff --git a/packages/angular/src/builders/webpack-browser/webpack-browser.impl.ts b/packages/angular/src/builders/webpack-browser/webpack-browser.impl.ts index db7c54ab28..3b05d7b0fa 100644 --- a/packages/angular/src/builders/webpack-browser/webpack-browser.impl.ts +++ b/packages/angular/src/builders/webpack-browser/webpack-browser.impl.ts @@ -5,7 +5,7 @@ import { stripIndents, } from '@nrwl/devkit'; import { WebpackNxBuildCoordinationPlugin } from '@nrwl/webpack/src/plugins/webpack-nx-build-coordination-plugin'; -import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { DependentBuildableProjectNode } from '@nrwl/js/src/utils/buildable-libs-utils'; import { existsSync } from 'fs'; import { readNxJson } from 'nx/src/project-graph/file-utils'; import { isNpmProject } from 'nx/src/project-graph/operators'; diff --git a/packages/angular/src/builders/webpack-dev-server/webpack-dev-server.impl.ts b/packages/angular/src/builders/webpack-dev-server/webpack-dev-server.impl.ts index af86dc691f..27215a8e70 100644 --- a/packages/angular/src/builders/webpack-dev-server/webpack-dev-server.impl.ts +++ b/packages/angular/src/builders/webpack-dev-server/webpack-dev-server.impl.ts @@ -4,7 +4,7 @@ import { readCachedProjectGraph, } from '@nrwl/devkit'; import { WebpackNxBuildCoordinationPlugin } from '@nrwl/webpack/src/plugins/webpack-nx-build-coordination-plugin'; -import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { DependentBuildableProjectNode } from '@nrwl/js/src/utils/buildable-libs-utils'; import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph'; import { existsSync } from 'fs'; import { isNpmProject } from 'nx/src/project-graph/operators'; diff --git a/packages/angular/src/executors/delegate-build/delegate-build.impl.spec.ts b/packages/angular/src/executors/delegate-build/delegate-build.impl.spec.ts index c4eb89ba4e..92e6e3c057 100644 --- a/packages/angular/src/executors/delegate-build/delegate-build.impl.spec.ts +++ b/packages/angular/src/executors/delegate-build/delegate-build.impl.spec.ts @@ -1,6 +1,6 @@ jest.mock('@nrwl/devkit'); jest.mock('@nrwl/devkit'); -jest.mock('@nrwl/workspace/src/utilities/buildable-libs-utils'); +jest.mock('@nrwl/js/src/utils/buildable-libs-utils'); // nested code imports graph from the repo, which might have innacurate graph version jest.mock('nx/src/project-graph/project-graph', () => ({ ...jest.requireActual('nx/src/project-graph/project-graph'), @@ -11,7 +11,7 @@ jest.mock('nx/src/project-graph/project-graph', () => ({ import type { ExecutorContext, Target } from '@nrwl/devkit'; import * as devkit from '@nrwl/devkit'; -import * as buildableLibsUtils from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import * as buildableLibsUtils from '@nrwl/js/src/utils/buildable-libs-utils'; import delegateBuildExecutor from './delegate-build.impl'; import type { DelegateBuildExecutorSchema } from './schema'; diff --git a/packages/angular/src/executors/delegate-build/delegate-build.impl.ts b/packages/angular/src/executors/delegate-build/delegate-build.impl.ts index 5ea2dfd8c8..9753034e8a 100644 --- a/packages/angular/src/executors/delegate-build/delegate-build.impl.ts +++ b/packages/angular/src/executors/delegate-build/delegate-build.impl.ts @@ -8,7 +8,7 @@ import { calculateProjectDependencies, checkDependentProjectsHaveBeenBuilt, createTmpTsConfig, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import type { DelegateBuildExecutorSchema } from './schema'; export async function* delegateBuildExecutor( diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.spec.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.spec.ts index a6c6ff35c7..7ab7162b60 100644 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.spec.ts +++ b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.spec.ts @@ -1,11 +1,11 @@ jest.mock('@nrwl/devkit'); -jest.mock('@nrwl/workspace/src/utilities/buildable-libs-utils'); +jest.mock('@nrwl/js/src/utils/buildable-libs-utils'); jest.mock('ng-packagr'); jest.mock('ng-packagr/lib/utils/ng-compiler-cli'); jest.mock('./ng-packagr-adjustments/ng-package/options.di'); import type { ExecutorContext } from '@nrwl/devkit'; -import * as buildableLibsUtils from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import * as buildableLibsUtils from '@nrwl/js/src/utils/buildable-libs-utils'; import * as ngPackagr from 'ng-packagr'; import { ngCompilerCli } from 'ng-packagr/lib/utils/ng-compiler-cli'; import { BehaviorSubject } from 'rxjs'; diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.ts index c3b205d79c..a53ea567b4 100644 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.ts +++ b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.ts @@ -2,7 +2,7 @@ import type { ExecutorContext } from '@nrwl/devkit'; import { createTmpTsConfig, DependentBuildableProjectNode, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import { NgPackagr } from 'ng-packagr'; import { resolve } from 'path'; import { createLibraryExecutor } from '../package/package.impl'; diff --git a/packages/angular/src/executors/package/package.impl.spec.ts b/packages/angular/src/executors/package/package.impl.spec.ts index 7f0be6f101..dfe9d972db 100644 --- a/packages/angular/src/executors/package/package.impl.spec.ts +++ b/packages/angular/src/executors/package/package.impl.spec.ts @@ -1,11 +1,11 @@ jest.mock('@nrwl/devkit'); -jest.mock('@nrwl/workspace/src/utilities/buildable-libs-utils'); +jest.mock('@nrwl/js/src/utils/buildable-libs-utils'); jest.mock('ng-packagr'); jest.mock('ng-packagr/lib/utils/ng-compiler-cli'); jest.mock('./ng-packagr-adjustments/ng-package/options.di'); import type { ExecutorContext } from '@nrwl/devkit'; -import * as buildableLibsUtils from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import * as buildableLibsUtils from '@nrwl/js/src/utils/buildable-libs-utils'; import * as ngPackagr from 'ng-packagr'; import { ngCompilerCli } from 'ng-packagr/lib/utils/ng-compiler-cli'; import { BehaviorSubject } from 'rxjs'; diff --git a/packages/angular/src/executors/package/package.impl.ts b/packages/angular/src/executors/package/package.impl.ts index d899141a76..88588e010d 100644 --- a/packages/angular/src/executors/package/package.impl.ts +++ b/packages/angular/src/executors/package/package.impl.ts @@ -6,7 +6,7 @@ import { createTmpTsConfig, DependentBuildableProjectNode, updateBuildableProjectPackageJsonDependencies, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import type { NgPackagr } from 'ng-packagr'; import { resolve } from 'path'; import { from } from 'rxjs'; diff --git a/packages/esbuild/src/executors/esbuild/esbuild.impl.ts b/packages/esbuild/src/executors/esbuild/esbuild.impl.ts index f192f02f76..717e881561 100644 --- a/packages/esbuild/src/executors/esbuild/esbuild.impl.ts +++ b/packages/esbuild/src/executors/esbuild/esbuild.impl.ts @@ -22,7 +22,7 @@ import { getOutfile, } from './lib/build-esbuild-options'; import { getExtraDependencies } from './lib/get-extra-dependencies'; -import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { DependentBuildableProjectNode } from '@nrwl/js/src/utils/buildable-libs-utils'; import { join } from 'path'; const BUILD_WATCH_FAILED = `[ ${chalk.red( diff --git a/packages/esbuild/src/executors/esbuild/lib/get-extra-dependencies.ts b/packages/esbuild/src/executors/esbuild/lib/get-extra-dependencies.ts index 00e18767ba..5f00a6ace1 100644 --- a/packages/esbuild/src/executors/esbuild/lib/get-extra-dependencies.ts +++ b/packages/esbuild/src/executors/esbuild/lib/get-extra-dependencies.ts @@ -1,5 +1,5 @@ import { ProjectGraph } from '@nrwl/devkit'; -import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { DependentBuildableProjectNode } from '@nrwl/js/src/utils/buildable-libs-utils'; export function getExtraDependencies( projectName: string, diff --git a/packages/js/src/executors/node/node.impl.ts b/packages/js/src/executors/node/node.impl.ts index 4d2b6c8638..8d106f87a7 100644 --- a/packages/js/src/executors/node/node.impl.ts +++ b/packages/js/src/executors/node/node.impl.ts @@ -5,7 +5,7 @@ import { parseTargetString, runExecutor, } from '@nrwl/devkit'; -import { calculateProjectDependencies } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { calculateProjectDependencies } from '../../utils/buildable-libs-utils'; import { ChildProcess, fork } from 'child_process'; import { randomUUID } from 'crypto'; import { HashingImpl } from 'nx/src/hasher/hashing-impl'; diff --git a/packages/js/src/utils/buildable-libs-utils.spec.ts b/packages/js/src/utils/buildable-libs-utils.spec.ts new file mode 100644 index 0000000000..2c4d3ee8fa --- /dev/null +++ b/packages/js/src/utils/buildable-libs-utils.spec.ts @@ -0,0 +1,196 @@ +import { DependencyType, ProjectGraph } from '@nrwl/devkit'; +import { + calculateProjectDependencies, + DependentBuildableProjectNode, + updatePaths, +} from './buildable-libs-utils'; + +describe('updatePaths', () => { + const deps: DependentBuildableProjectNode[] = [ + { name: '@proj/lib', node: {} as any, outputs: ['dist/libs/lib'] }, + ]; + + it('should add path', () => { + const paths: Record = { + '@proj/test': ['libs/test/src/index.ts'], + }; + updatePaths(deps, paths); + expect(paths).toEqual({ + '@proj/lib': ['dist/libs/lib'], + '@proj/test': ['libs/test/src/index.ts'], + }); + }); + + it('should replace paths', () => { + const paths: Record = { + '@proj/lib': ['libs/lib/src/index.ts'], + '@proj/lib/sub': ['libs/lib/sub/src/index.ts'], + }; + updatePaths(deps, paths); + expect(paths).toEqual({ + '@proj/lib': ['dist/libs/lib'], + '@proj/lib/sub': ['dist/libs/lib/sub'], + }); + }); +}); + +describe('calculateProjectDependencies', () => { + it('should include npm packages in dependency list', async () => { + const graph: ProjectGraph = { + nodes: { + example: { + type: 'lib', + name: 'example', + data: { + files: [], + root: '/root/example', + }, + }, + }, + externalNodes: { + 'npm:formik': { + type: 'npm', + name: 'npm:formik', + data: { + packageName: 'formik', + version: '0.0.0', + }, + }, + }, + dependencies: { + example: [ + { + source: 'example', + target: 'npm:formik', + type: DependencyType.static, + }, + ], + }, + }; + + const results = await calculateProjectDependencies( + graph, + 'root', + 'example', + 'build', + undefined + ); + expect(results).toMatchObject({ + target: { + type: 'lib', + name: 'example', + }, + dependencies: [{ name: 'formik' }], + }); + }); + + it('should include npm packages in dependency list and sort them correctly', async () => { + const graph: ProjectGraph = { + nodes: { + example: { + type: 'lib', + name: 'example', + data: { + files: [], + root: '/root/example', + }, + }, + }, + externalNodes: { + 'npm:some-lib': { + type: 'npm', + name: 'npm:some-lib', + data: { + packageName: 'some-lib', + version: '0.0.0', + }, + }, + 'npm:formik': { + type: 'npm', + name: 'npm:formik', + data: { + packageName: 'formik', + version: '0.0.0', + }, + }, + 'npm:@prefixed-lib': { + type: 'npm', + name: 'npm:@prefixed-lib', + data: { + packageName: '@prefixed-lib', + version: '0.0.0', + }, + }, + }, + dependencies: { + example: [ + { + source: 'example', + target: 'npm:some-lib', + type: DependencyType.static, + }, + { + source: 'example', + target: 'npm:formik', + type: DependencyType.static, + }, + { + source: 'example', + target: 'npm:@prefixed-lib', + type: DependencyType.static, + }, + ], + }, + }; + + const results = await calculateProjectDependencies( + graph, + 'root', + 'example', + 'build', + undefined + ); + expect(results).toMatchObject({ + target: { + type: 'lib', + name: 'example', + }, + dependencies: [ + { name: '@prefixed-lib' }, + { name: 'formik' }, + { name: 'some-lib' }, + ], + }); + }); +}); + +describe('missingDependencies', () => { + it('should throw an error if dependency is missing', async () => { + const graph: ProjectGraph = { + nodes: { + example: { + type: 'lib', + name: 'example', + data: { + files: [], + root: '/root/example', + }, + }, + }, + externalNodes: {}, + dependencies: { + example: [ + { + source: 'example', + target: 'missing', + type: DependencyType.static, + }, + ], + }, + }; + + expect(() => + calculateProjectDependencies(graph, 'root', 'example', 'build', undefined) + ).toThrow(); + }); +}); diff --git a/packages/js/src/utils/buildable-libs-utils.ts b/packages/js/src/utils/buildable-libs-utils.ts new file mode 100644 index 0000000000..d09edc55a8 --- /dev/null +++ b/packages/js/src/utils/buildable-libs-utils.ts @@ -0,0 +1,454 @@ +import { dirname, join, relative } from 'path'; +import { directoryExists, fileExists } from 'nx/src/utils/fileutils'; +import type { ProjectGraph, ProjectGraphProjectNode } from '@nrwl/devkit'; +import { + getOutputsForTargetAndConfiguration, + ProjectGraphExternalNode, + readJsonFile, + stripIndents, + writeJsonFile, +} from '@nrwl/devkit'; +import type * as ts from 'typescript'; +import { unlinkSync } from 'fs'; +import { output } from 'nx/src/utils/output'; +import { isNpmProject } from 'nx/src/project-graph/operators'; +import { ensureTypescript } from './typescript/ensure-typescript'; + +let tsModule: typeof import('typescript'); + +function isBuildable(target: string, node: ProjectGraphProjectNode): boolean { + return ( + node.data.targets && + node.data.targets[target] && + node.data.targets[target].executor !== '' + ); +} + +export type DependentBuildableProjectNode = { + name: string; + outputs: string[]; + node: ProjectGraphProjectNode | ProjectGraphExternalNode; +}; + +export function calculateProjectDependencies( + projGraph: ProjectGraph, + root: string, + projectName: string, + targetName: string, + configurationName: string, + shallow?: boolean +): { + target: ProjectGraphProjectNode; + dependencies: DependentBuildableProjectNode[]; + nonBuildableDependencies: string[]; + topLevelDependencies: DependentBuildableProjectNode[]; +} { + const target = projGraph.nodes[projectName]; + // gather the library dependencies + const nonBuildableDependencies = []; + const topLevelDependencies: DependentBuildableProjectNode[] = []; + const collectedDeps = collectDependencies( + projectName, + projGraph, + [], + shallow + ); + const missing = collectedDeps.reduce( + (missing: string[] | undefined, { name: dep }) => { + const depNode = projGraph.nodes[dep] || projGraph.externalNodes[dep]; + if (!depNode) { + missing = missing || []; + missing.push(dep); + } + return missing; + }, + null + ); + if (missing) { + throw new Error(`Unable to find ${missing.join(', ')} in project graph.`); + } + const dependencies = collectedDeps + .map(({ name: dep, isTopLevel }) => { + let project: DependentBuildableProjectNode = null; + const depNode = projGraph.nodes[dep] || projGraph.externalNodes[dep]; + if (depNode.type === 'lib') { + if (isBuildable(targetName, depNode)) { + const libPackageJsonPath = join( + root, + depNode.data.root, + 'package.json' + ); + + project = { + name: fileExists(libPackageJsonPath) + ? readJsonFile(libPackageJsonPath).name // i.e. @workspace/mylib + : dep, + outputs: getOutputsForTargetAndConfiguration( + { + overrides: {}, + target: { + project: projectName, + target: targetName, + configuration: configurationName, + }, + }, + depNode + ), + node: depNode, + }; + } else { + nonBuildableDependencies.push(dep); + } + } else if (depNode.type === 'npm') { + project = { + name: depNode.data.packageName, + outputs: [], + node: depNode, + }; + } + + if (project && isTopLevel) { + topLevelDependencies.push(project); + } + + return project; + }) + .filter((x) => !!x); + + dependencies.sort((a, b) => (a.name > b.name ? 1 : b.name > a.name ? -1 : 0)); + + return { + target, + dependencies, + nonBuildableDependencies, + topLevelDependencies, + }; +} + +function collectDependencies( + project: string, + projGraph: ProjectGraph, + acc: { name: string; isTopLevel: boolean }[], + shallow?: boolean, + areTopLevelDeps = true +): { name: string; isTopLevel: boolean }[] { + (projGraph.dependencies[project] || []).forEach((dependency) => { + if (!acc.some((dep) => dep.name === dependency.target)) { + // Temporary skip this. Currently the set of external nodes is built from package.json, not lock file. + // As a result, some nodes might be missing. This should not cause any issues, we can just skip them. + if ( + dependency.target.startsWith('npm:') && + !projGraph.externalNodes[dependency.target] + ) + return; + + acc.push({ name: dependency.target, isTopLevel: areTopLevelDeps }); + const isInternalTarget = projGraph.nodes[dependency.target]; + if (!shallow && isInternalTarget) { + collectDependencies(dependency.target, projGraph, acc, shallow, false); + } + } + }); + return acc; +} + +function readTsConfigWithRemappedPaths( + tsConfig: string, + generatedTsConfigPath: string, + dependencies: DependentBuildableProjectNode[] +) { + const generatedTsConfig: any = { compilerOptions: {} }; + generatedTsConfig.extends = relative( + dirname(generatedTsConfigPath), + tsConfig + ); + generatedTsConfig.compilerOptions.paths = computeCompilerOptionsPaths( + tsConfig, + dependencies + ); + + if (process.env.NX_VERBOSE_LOGGING_PATH_MAPPINGS === 'true') { + output.log({ + title: 'TypeScript path mappings have been rewritten.', + }); + console.log( + JSON.stringify(generatedTsConfig.compilerOptions.paths, null, 2) + ); + } + return generatedTsConfig; +} + +/** + * Util function to create tsconfig compilerOptions object with support for workspace libs paths. + * + * + * + * @param tsConfig String of config path or object parsed via ts.parseJsonConfigFileContent. + * @param dependencies Dependencies calculated by Nx. + */ +export function computeCompilerOptionsPaths( + tsConfig: string | ts.ParsedCommandLine, + dependencies: DependentBuildableProjectNode[] +) { + const paths = readPaths(tsConfig) || {}; + updatePaths(dependencies, paths); + return paths; +} + +function readPaths(tsConfig: string | ts.ParsedCommandLine) { + if (!tsModule) { + tsModule = ensureTypescript(); + } + try { + let config: ts.ParsedCommandLine; + if (typeof tsConfig === 'string') { + const configFile = tsModule.readConfigFile( + tsConfig, + tsModule.sys.readFile + ); + config = tsModule.parseJsonConfigFileContent( + configFile.config, + tsModule.sys, + dirname(tsConfig) + ); + } else { + config = tsConfig; + } + if (config.options?.paths) { + return config.options.paths; + } else { + return null; + } + } catch (e) { + return null; + } +} + +export function createTmpTsConfig( + tsconfigPath: string, + workspaceRoot: string, + projectRoot: string, + dependencies: DependentBuildableProjectNode[] +) { + const tmpTsConfigPath = join( + workspaceRoot, + 'tmp', + projectRoot, + 'tsconfig.generated.json' + ); + const parsedTSConfig = readTsConfigWithRemappedPaths( + tsconfigPath, + tmpTsConfigPath, + dependencies + ); + process.on('exit', () => cleanupTmpTsConfigFile(tmpTsConfigPath)); + writeJsonFile(tmpTsConfigPath, parsedTSConfig); + return join(tmpTsConfigPath); +} + +function cleanupTmpTsConfigFile(tmpTsConfigPath) { + try { + if (tmpTsConfigPath) { + unlinkSync(tmpTsConfigPath); + } + } catch (e) {} +} + +export function checkDependentProjectsHaveBeenBuilt( + root: string, + projectName: string, + targetName: string, + projectDependencies: DependentBuildableProjectNode[] +): boolean { + const missing = findMissingBuildDependencies( + root, + projectName, + targetName, + projectDependencies + ); + if (missing.length > 0) { + console.error(stripIndents` + It looks like all of ${projectName}'s dependencies have not been built yet: + ${missing.map((x) => ` - ${x.node.name}`).join('\n')} + + You might be missing a "targetDefaults" configuration in your root nx.json (https://nx.dev/reference/project-configuration#target-defaults), + or "dependsOn" configured in ${projectName}'s project.json (https://nx.dev/reference/project-configuration#dependson) + `); + return false; + } else { + return true; + } +} + +export function findMissingBuildDependencies( + root: string, + projectName: string, + targetName: string, + projectDependencies: DependentBuildableProjectNode[] +): DependentBuildableProjectNode[] { + const depLibsToBuildFirst: DependentBuildableProjectNode[] = []; + + // verify whether all dependent libraries have been built + projectDependencies.forEach((dep) => { + if (dep.node.type !== 'lib') { + return; + } + + const paths = dep.outputs.map((p) => join(root, p)); + + if (!paths.some(directoryExists)) { + depLibsToBuildFirst.push(dep); + } + }); + + return depLibsToBuildFirst; +} + +export function updatePaths( + dependencies: DependentBuildableProjectNode[], + paths: Record +) { + const pathsKeys = Object.keys(paths); + // For each registered dependency + dependencies.forEach((dep) => { + // If there are outputs + if (dep.outputs && dep.outputs.length > 0) { + // Directly map the dependency name to the output paths (dist/packages/..., etc.) + paths[dep.name] = dep.outputs; + + // check for secondary entrypoints + // For each registered path + for (const path of pathsKeys) { + const nestedName = `${dep.name}/`; + + // If the path points to the current dependency and is nested (/) + if (path.startsWith(nestedName)) { + const nestedPart = path.slice(nestedName.length); + + // Bind secondary endpoints for ng-packagr projects + let mappedPaths = dep.outputs.map( + (output) => `${output}/${nestedPart}` + ); + + // Get the dependency's package name + const { root } = (dep.node?.data || {}) as any; + if (root) { + // Update nested mappings to point to the dependency's output paths + mappedPaths = mappedPaths.concat( + paths[path].flatMap((path) => + dep.outputs.map((output) => path.replace(root, output)) + ) + ); + } + + paths[path] = mappedPaths; + } + } + } + }); +} + +/** + * Updates the peerDependencies section in the `dist/lib/xyz/package.json` with + * the proper dependency and version + */ +export function updateBuildableProjectPackageJsonDependencies( + root: string, + projectName: string, + targetName: string, + configurationName: string, + node: ProjectGraphProjectNode, + dependencies: DependentBuildableProjectNode[], + typeOfDependency: 'dependencies' | 'peerDependencies' = 'dependencies' +) { + const outputs = getOutputsForTargetAndConfiguration( + { + overrides: {}, + target: { + project: projectName, + target: targetName, + configuration: configurationName, + }, + }, + node + ); + + const packageJsonPath = `${outputs[0]}/package.json`; + let packageJson; + let workspacePackageJson; + try { + packageJson = readJsonFile(packageJsonPath); + workspacePackageJson = readJsonFile(`${root}/package.json`); + } catch (e) { + // cannot find or invalid package.json + return; + } + + packageJson.dependencies = packageJson.dependencies || {}; + packageJson.peerDependencies = packageJson.peerDependencies || {}; + + let updatePackageJson = false; + dependencies.forEach((entry) => { + const packageName = isNpmProject(entry.node) + ? entry.node.data.packageName + : entry.name; + + if ( + !hasDependency(packageJson, 'dependencies', packageName) && + !hasDependency(packageJson, 'devDependencies', packageName) && + !hasDependency(packageJson, 'peerDependencies', packageName) + ) { + try { + let depVersion; + if (entry.node.type === 'lib') { + const outputs = getOutputsForTargetAndConfiguration( + { + overrides: {}, + target: { + project: projectName, + target: targetName, + configuration: configurationName, + }, + }, + entry.node + ); + + const depPackageJsonPath = join(root, outputs[0], 'package.json'); + depVersion = readJsonFile(depPackageJsonPath).version; + + packageJson[typeOfDependency][packageName] = depVersion; + } else if (isNpmProject(entry.node)) { + // If an npm dep is part of the workspace devDependencies, do not include it the library + if ( + !!workspacePackageJson.devDependencies?.[ + entry.node.data.packageName + ] + ) { + return; + } + + depVersion = entry.node.data.version; + + packageJson[typeOfDependency][entry.node.data.packageName] = + depVersion; + } + updatePackageJson = true; + } catch (e) { + // skip if cannot find package.json + } + } + }); + + if (updatePackageJson) { + writeJsonFile(packageJsonPath, packageJson); + } +} + +// verify whether the package.json already specifies the dep +function hasDependency(outputJson, depConfigName: string, packageName: string) { + if (outputJson[depConfigName]) { + return outputJson[depConfigName][packageName]; + } else { + return false; + } +} diff --git a/packages/js/src/utils/check-dependencies.ts b/packages/js/src/utils/check-dependencies.ts index 06d004e402..ba41b41768 100644 --- a/packages/js/src/utils/check-dependencies.ts +++ b/packages/js/src/utils/check-dependencies.ts @@ -3,7 +3,7 @@ import { calculateProjectDependencies, createTmpTsConfig, DependentBuildableProjectNode, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from './buildable-libs-utils'; export function checkDependencies( context: ExecutorContext, diff --git a/packages/js/src/utils/compiler-helper-dependency.ts b/packages/js/src/utils/compiler-helper-dependency.ts index 8432d01837..05586e6b55 100644 --- a/packages/js/src/utils/compiler-helper-dependency.ts +++ b/packages/js/src/utils/compiler-helper-dependency.ts @@ -4,7 +4,7 @@ import { ProjectGraphDependency, readJsonFile, } from '@nrwl/devkit'; -import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { DependentBuildableProjectNode } from './buildable-libs-utils'; import { join } from 'path'; import { readTsConfig } from './typescript/ts-config'; import { ExecutorOptions, SwcExecutorOptions } from './schema'; diff --git a/packages/js/src/utils/package-json/index.ts b/packages/js/src/utils/package-json/index.ts index c3cca82436..7f889c08bb 100644 --- a/packages/js/src/utils/package-json/index.ts +++ b/packages/js/src/utils/package-json/index.ts @@ -1,6 +1,5 @@ -import { join } from 'path'; import { ExecutorContext } from '@nrwl/devkit'; -import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { DependentBuildableProjectNode } from '../buildable-libs-utils'; import { watchForSingleFileChanges } from '../watch-for-single-file-changes'; import type { UpdatePackageJsonOption } from './update-package-json'; diff --git a/packages/js/src/utils/package-json/update-package-json.spec.ts b/packages/js/src/utils/package-json/update-package-json.spec.ts index 12c19c083e..855e0596be 100644 --- a/packages/js/src/utils/package-json/update-package-json.spec.ts +++ b/packages/js/src/utils/package-json/update-package-json.spec.ts @@ -7,7 +7,7 @@ import { } from './update-package-json'; import { vol } from 'memfs'; import { ExecutorContext, ProjectGraph } from '@nrwl/devkit'; -import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { DependentBuildableProjectNode } from '../buildable-libs-utils'; jest.mock('nx/src/utils/workspace-root', () => ({ workspaceRoot: '/root', diff --git a/packages/js/src/utils/package-json/update-package-json.ts b/packages/js/src/utils/package-json/update-package-json.ts index 365b579659..d8ce4f9e5a 100644 --- a/packages/js/src/utils/package-json/update-package-json.ts +++ b/packages/js/src/utils/package-json/update-package-json.ts @@ -1,4 +1,4 @@ -import { createLockFile } from 'nx/src/lock-file/lock-file'; +import { createLockFile, getLockFileName } from 'nx/src/lock-file/lock-file'; import { createPackageJson } from 'nx/src/utils/create-package-json'; import { ExecutorContext, @@ -10,9 +10,8 @@ import { workspaceRoot, writeJsonFile, } from '@nrwl/devkit'; -import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { DependentBuildableProjectNode } from '../buildable-libs-utils'; import { basename, dirname, join, parse, relative } from 'path'; -import { getLockFileName } from 'nx/src/lock-file/lock-file'; import { writeFileSync } from 'fs-extra'; import { isNpmProject } from 'nx/src/project-graph/operators'; import { fileExists } from 'nx/src/utils/fileutils'; diff --git a/packages/next/src/executors/build/build.impl.ts b/packages/next/src/executors/build/build.impl.ts index 7fcef0d0d0..b07b2ee869 100644 --- a/packages/next/src/executors/build/build.impl.ts +++ b/packages/next/src/executors/build/build.impl.ts @@ -15,7 +15,7 @@ import { directoryExists } from '@nrwl/workspace/src/utilities/fileutils'; import { calculateProjectDependencies, DependentBuildableProjectNode, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import { checkAndCleanWithSemver } from '@nrwl/devkit/src/utils/semver'; import { prepareConfig } from '../../utils/config'; diff --git a/packages/next/src/executors/export/export.impl.ts b/packages/next/src/executors/export/export.impl.ts index d838836c1d..11dfc04e05 100644 --- a/packages/next/src/executors/export/export.impl.ts +++ b/packages/next/src/executors/export/export.impl.ts @@ -4,14 +4,14 @@ import { parseTargetString, readTargetOptions, runExecutor, + workspaceLayout, } from '@nrwl/devkit'; import exportApp from 'next/dist/export'; import { join, resolve } from 'path'; import { calculateProjectDependencies, DependentBuildableProjectNode, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; -import { workspaceLayout } from '@nrwl/devkit'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import { prepareConfig } from '../../utils/config'; import { diff --git a/packages/next/src/executors/server/server.impl.ts b/packages/next/src/executors/server/server.impl.ts index d2a18c6fe6..00d581803f 100644 --- a/packages/next/src/executors/server/server.impl.ts +++ b/packages/next/src/executors/server/server.impl.ts @@ -10,7 +10,7 @@ import { import * as chalk from 'chalk'; import { existsSync } from 'fs'; import { join, resolve } from 'path'; -import { calculateProjectDependencies } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { calculateProjectDependencies } from '@nrwl/js/src/utils/buildable-libs-utils'; import { prepareConfig } from '../../utils/config'; import { diff --git a/packages/next/src/utils/buildable-libs.ts b/packages/next/src/utils/buildable-libs.ts index a12ae0723d..38fdcbf004 100644 --- a/packages/next/src/utils/buildable-libs.ts +++ b/packages/next/src/utils/buildable-libs.ts @@ -1,7 +1,7 @@ import { DependentBuildableProjectNode, findMissingBuildDependencies, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import * as chalk from 'chalk'; import { ExecutorContext, stripIndents } from '@nrwl/devkit'; diff --git a/packages/next/src/utils/config.ts b/packages/next/src/utils/config.ts index 7c80f52c61..6aae860e09 100644 --- a/packages/next/src/utils/config.ts +++ b/packages/next/src/utils/config.ts @@ -22,7 +22,7 @@ import { WithNxOptions } from '../../plugins/with-nx'; import { createTmpTsConfig, DependentBuildableProjectNode, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; const loadConfig = require('next/dist/server/config').default; diff --git a/packages/rollup/src/executors/rollup/lib/update-package-json.ts b/packages/rollup/src/executors/rollup/lib/update-package-json.ts index cbc4f55d1b..02c7937a1c 100644 --- a/packages/rollup/src/executors/rollup/lib/update-package-json.ts +++ b/packages/rollup/src/executors/rollup/lib/update-package-json.ts @@ -4,7 +4,7 @@ import { ProjectGraphProjectNode } from 'nx/src/config/project-graph'; import { DependentBuildableProjectNode, updateBuildableProjectPackageJsonDependencies, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import { writeJsonFile } from 'nx/src/utils/fileutils'; import { PackageJson } from 'nx/src/utils/package-json'; import { NormalizedRollupExecutorOptions } from './normalize'; diff --git a/packages/rollup/src/executors/rollup/rollup.impl.ts b/packages/rollup/src/executors/rollup/rollup.impl.ts index 9114bcf92b..781052a87c 100644 --- a/packages/rollup/src/executors/rollup/rollup.impl.ts +++ b/packages/rollup/src/executors/rollup/rollup.impl.ts @@ -14,7 +14,7 @@ import { calculateProjectDependencies, computeCompilerOptionsPaths, DependentBuildableProjectNode, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import nodeResolve from '@rollup/plugin-node-resolve'; import { AssetGlobPattern, RollupExecutorOptions } from './schema'; diff --git a/packages/webpack/src/executors/dev-server/dev-server.impl.ts b/packages/webpack/src/executors/dev-server/dev-server.impl.ts index 0b2722e624..bf4c039b15 100644 --- a/packages/webpack/src/executors/dev-server/dev-server.impl.ts +++ b/packages/webpack/src/executors/dev-server/dev-server.impl.ts @@ -13,7 +13,7 @@ import { getDevServerConfig } from './lib/get-dev-server-config'; import { calculateProjectDependencies, createTmpTsConfig, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import { runWebpackDevServer } from '../../utils/run-webpack'; import { resolveCustomWebpackConfig } from '../../utils/webpack/custom-webpack'; import { normalizeOptions } from '../webpack/lib/normalize-options'; diff --git a/packages/webpack/src/executors/webpack/webpack.impl.ts b/packages/webpack/src/executors/webpack/webpack.impl.ts index 20463e8aa2..80aca12ad9 100644 --- a/packages/webpack/src/executors/webpack/webpack.impl.ts +++ b/packages/webpack/src/executors/webpack/webpack.impl.ts @@ -14,7 +14,7 @@ import { resolve } from 'path'; import { calculateProjectDependencies, createTmpTsConfig, -} from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +} from '@nrwl/js/src/utils/buildable-libs-utils'; import { getWebpackConfig } from './lib/get-webpack-config'; import { runWebpack } from './lib/run-webpack'; diff --git a/packages/workspace/src/utilities/buildable-libs-utils.ts b/packages/workspace/src/utilities/buildable-libs-utils.ts index a61b03bac9..e79cb6a6f9 100644 --- a/packages/workspace/src/utilities/buildable-libs-utils.ts +++ b/packages/workspace/src/utilities/buildable-libs-utils.ts @@ -24,12 +24,18 @@ function isBuildable(target: string, node: ProjectGraphProjectNode): boolean { ); } +/** + * @deprecated This type will be removed from @nrwl/workspace in version 17. Prefer importing from @nrwl/js. + */ export type DependentBuildableProjectNode = { name: string; outputs: string[]; node: ProjectGraphProjectNode | ProjectGraphExternalNode; }; +/** + * @deprecated This function will be removed from @nrwl/workspace in version 17. Prefer importing from @nrwl/js. + */ export function calculateProjectDependencies( projGraph: ProjectGraph, root: string, @@ -178,13 +184,7 @@ function readTsConfigWithRemappedPaths( return generatedTsConfig; } -/** - * Util function to create tsconfig compilerOptions object with support for workspace libs paths. - * - * @param tsConfig String of config path or object parsed via ts.parseJsonConfigFileContent. - * @param dependencies Dependencies calculated by Nx. - */ -export function computeCompilerOptionsPaths( +function computeCompilerOptionsPaths( tsConfig: string | ts.ParsedCommandLine, dependencies: DependentBuildableProjectNode[] ) { @@ -222,6 +222,9 @@ function readPaths(tsConfig: string | ts.ParsedCommandLine) { } } +/** + * @deprecated This function will be removed from @nrwl/workspace in version 17. Prefer importing from @nrwl/js. + */ export function createTmpTsConfig( tsconfigPath: string, workspaceRoot: string, @@ -252,6 +255,9 @@ function cleanupTmpTsConfigFile(tmpTsConfigPath) { } catch (e) {} } +/** + * @deprecated This function will be removed from @nrwl/workspace in version 17. Prefer importing from @nrwl/js. + */ export function checkDependentProjectsHaveBeenBuilt( root: string, projectName: string, @@ -278,6 +284,9 @@ export function checkDependentProjectsHaveBeenBuilt( } } +/** + * @deprecated This function will be removed from @nrwl/workspace in version 17. Prefer importing from @nrwl/js. + */ export function findMissingBuildDependencies( root: string, projectName: string, @@ -302,6 +311,9 @@ export function findMissingBuildDependencies( return depLibsToBuildFirst; } +/** + * @deprecated This function will be removed from @nrwl/workspace in version 17. Prefer importing from @nrwl/js. + */ export function updatePaths( dependencies: DependentBuildableProjectNode[], paths: Record @@ -349,6 +361,7 @@ export function updatePaths( /** * Updates the peerDependencies section in the `dist/lib/xyz/package.json` with * the proper dependency and version + * @deprecated This function will be removed from @nrwl/workspace in version 17. Prefer importing from @nrwl/js. */ export function updateBuildableProjectPackageJsonDependencies( root: string,