diff --git a/apps/ts-minbar-test-react-components/package.json b/apps/ts-minbar-test-react-components/package.json index 0daa0c00ca3648..7d634a1864999c 100644 --- a/apps/ts-minbar-test-react-components/package.json +++ b/apps/ts-minbar-test-react-components/package.json @@ -9,7 +9,7 @@ }, "scripts": { "type-check": "tsc -p .", - "test": "ts-node ./src/index.ts" + "test": "ts-node --transpileOnly ./src/index.ts" }, "devDependencies": { "@fluentui/scripts-tasks": "*", diff --git a/apps/ts-minbar-test-react-components/src/index.ts b/apps/ts-minbar-test-react-components/src/index.ts index b06bee4e7da945..cc783f58401558 100644 --- a/apps/ts-minbar-test-react-components/src/index.ts +++ b/apps/ts-minbar-test-react-components/src/index.ts @@ -4,7 +4,6 @@ import { log, shEcho, TempPaths, - workspaceRoot, generateFiles, addResolutionPathsForProjectPackages, packProjectPackages, @@ -34,8 +33,7 @@ async function performTest() { await shEcho(`yarn add ${dependencies}`, tempPaths.testApp); logger(`✔️ Dependencies were installed`); - const lernaRoot = workspaceRoot; - const packedPackages = await packProjectPackages(logger, lernaRoot, ['@fluentui/react-components']); + const packedPackages = await packProjectPackages(logger, '@fluentui/react-components'); await addResolutionPathsForProjectPackages(tempPaths.testApp); await shEcho(`yarn add ${packedPackages['@fluentui/react-components']}`, tempPaths.testApp); diff --git a/apps/ts-minbar-test-react/package.json b/apps/ts-minbar-test-react/package.json index 2aa76bc5489f64..90edc02f0ddfdc 100644 --- a/apps/ts-minbar-test-react/package.json +++ b/apps/ts-minbar-test-react/package.json @@ -9,7 +9,7 @@ }, "scripts": { "type-check": "tsc -p .", - "test": "ts-node ./src/index.ts" + "test": "ts-node --transpileOnly ./src/index.ts" }, "devDependencies": { "@fluentui/scripts-tasks": "*", diff --git a/apps/ts-minbar-test-react/src/index.ts b/apps/ts-minbar-test-react/src/index.ts index 0297b04c04fb5c..05193387ef14a7 100644 --- a/apps/ts-minbar-test-react/src/index.ts +++ b/apps/ts-minbar-test-react/src/index.ts @@ -7,7 +7,6 @@ import { log, shEcho, TempPaths, - workspaceRoot, generateFiles, } from '@fluentui/scripts-projects-test'; @@ -36,8 +35,7 @@ async function performTest() { await shEcho(`yarn add ${dependencies}`, tempPaths.testApp); logger(`✔️ Dependencies were installed`); - const lernaRoot = workspaceRoot; - const packedPackages = await packProjectPackages(logger, lernaRoot, ['@fluentui/react']); + const packedPackages = await packProjectPackages(logger, '@fluentui/react'); await addResolutionPathsForProjectPackages(tempPaths.testApp); await shEcho(`yarn add ${packedPackages['@fluentui/react']}`, tempPaths.testApp); diff --git a/change/@fluentui-cra-template-be360b59-bfe0-4e5e-9fcf-8b5ec9154ac5.json b/change/@fluentui-cra-template-be360b59-bfe0-4e5e-9fcf-8b5ec9154ac5.json new file mode 100644 index 00000000000000..52a2857e6a7aaa --- /dev/null +++ b/change/@fluentui-cra-template-be360b59-bfe0-4e5e-9fcf-8b5ec9154ac5.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "chore: refactor test to use latest APIs", + "packageName": "@fluentui/cra-template", + "email": "martinhochel@microsoft.com", + "dependentChangeType": "none" +} diff --git a/jest.preset.js b/jest.preset.js index e58a6359660e6d..1a4492c352a218 100644 --- a/jest.preset.js +++ b/jest.preset.js @@ -17,7 +17,7 @@ const baseConfig = { '^.+\\.ts$': 'ts-jest', }, testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], testPathIgnorePatterns: ['/node_modules/', '/lib/', '/lib-commonjs/', '/dist/'], testEnvironment: 'jsdom', moduleNameMapper: { ...tsPathAliases }, diff --git a/nx.json b/nx.json index 09ecb831d2f7e3..5efd3cd15e108b 100644 --- a/nx.json +++ b/nx.json @@ -20,7 +20,7 @@ }, "pluginsConfig": { "@nx/js": { - "analyzeSourceFiles": true + "analyzeSourceFiles": false } }, "targetDefaults": { diff --git a/package.json b/package.json index b004696e42d56c..46beb1724f03c9 100644 --- a/package.json +++ b/package.json @@ -163,8 +163,6 @@ "@types/jju": "1.4.1", "@types/json-schema": "^7.0.8", "@types/lerna-alias": "3.0.0", - "@types/lerna__package-graph": "5.1.0", - "@types/lerna__project": "5.1.0", "@types/loader-utils": "2.0.3", "@types/lodash": "4.14.182", "@types/markdown-table": "2.0.0", @@ -274,8 +272,6 @@ "just-scripts": "1.8.2", "lage": "1.8.8", "lerna": "7.1.3", - "@lerna/project": "6.4.1", - "@lerna/package-graph": "6.4.1", "lerna-alias": "3.0.3-0", "license-webpack-plugin": "2.3.10", "lint-staged": "10.2.10", diff --git a/packages/cra-template/package.json b/packages/cra-template/package.json index 6249be058e6261..8ae80dcc0384e9 100644 --- a/packages/cra-template/package.json +++ b/packages/cra-template/package.json @@ -19,7 +19,6 @@ ], "devDependencies": { "@fluentui/react": "*", - "@fluentui/scripts-projects-test": "*", - "@fluentui/scripts-monorepo": "*" + "@fluentui/scripts-projects-test": "*" } } diff --git a/packages/cra-template/scripts/test.ts b/packages/cra-template/scripts/test.ts index aa095081dbb2ac..09be42bcc9f153 100644 --- a/packages/cra-template/scripts/test.ts +++ b/packages/cra-template/scripts/test.ts @@ -1,4 +1,4 @@ -import fs from 'fs-extra'; +import fs from 'fs'; import path from 'path'; import semver from 'semver'; @@ -12,8 +12,11 @@ import { prepareCreateReactApp, TempPaths, } from '@fluentui/scripts-projects-test'; -import { findGitRoot } from '@fluentui/scripts-monorepo'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function readJson(filePath: string): Record { + return JSON.parse(fs.readFileSync(filePath, 'utf-8')); +} // This test is sort of like `packages/fluentui/projects-test/src/createReactApp.ts`, but it uses // a custom local template rather than making a generic TS project and adding our project as a dep. // So they share some logic, but can't be completely merged (and this test shouldn't go under @@ -24,9 +27,9 @@ import { findGitRoot } from '@fluentui/scripts-monorepo'; * This will probably only be an issue if there's a major version bump. */ function verifyVersion() { - const templateJson = fs.readJSONSync(path.resolve(__dirname, '../template.json')); + const templateJson = readJson(path.resolve(__dirname, '../template.json')); const templateVersion = templateJson.package.dependencies['@fluentui/react']; - const reactPackageJson = fs.readJSONSync(path.resolve(__dirname, '../../react/package.json')); + const reactPackageJson = readJson(path.resolve(__dirname, '../../react/package.json')); const actualVersion = reactPackageJson.version; if (!semver.satisfies(actualVersion, templateVersion)) { console.error( @@ -42,15 +45,16 @@ function verifyVersion() { * Instead, pack up the locally-built packages and make a copy of the template which references them. */ async function prepareTemplate(logger: Function, tempPaths: TempPaths) { - await packProjectPackages(logger, findGitRoot(), ['@fluentui/react']); + await packProjectPackages(logger, '@fluentui/react'); const templatePath = path.join(tempPaths.root, 'cra-template'); - const packageJson = fs.readJSONSync(path.resolve(__dirname, '../package.json')); + const packageJson = readJson(path.resolve(__dirname, '../package.json')); // Copy only the template files that would be installed from npm const filesToCopy = [...packageJson.files, 'package.json']; + const rootDir = path.resolve(__dirname, '..'); for (const file of filesToCopy) { - await fs.copy(path.resolve(__dirname, '..', file), path.join(templatePath, file)); + fs.cpSync(path.resolve(rootDir, file), path.join(templatePath, file), { recursive: true }); } await addResolutionPathsForProjectPackages(templatePath, true /*isTemplateJson*/); diff --git a/packages/fluentui/projects-test/src/createReactApp.ts b/packages/fluentui/projects-test/src/createReactApp.ts index 35d1a5970ccc73..d8b81ba6e11ac3 100644 --- a/packages/fluentui/projects-test/src/createReactApp.ts +++ b/packages/fluentui/projects-test/src/createReactApp.ts @@ -8,7 +8,6 @@ import { shEcho, performBrowserTest, prepareCreateReactApp, - workspaceRoot, generateFiles, } from '@fluentui/scripts-projects-test'; @@ -34,7 +33,7 @@ export async function createReactApp() { logger('STEP 2. Add Fluent UI dependency to test project..'); - const packedPackages = await packProjectPackages(logger, workspaceRoot, ['@fluentui/react-northstar']); + const packedPackages = await packProjectPackages(logger, '@fluentui/react-northstar'); await addResolutionPathsForProjectPackages(testAppPathRoot); await shEcho(`yarn add ${packedPackages['@fluentui/react-northstar']}`, testAppPathRoot); diff --git a/packages/fluentui/projects-test/src/nextjs.ts b/packages/fluentui/projects-test/src/nextjs.ts index 89ffe9b5bca077..0cf59efae8b7c7 100644 --- a/packages/fluentui/projects-test/src/nextjs.ts +++ b/packages/fluentui/projects-test/src/nextjs.ts @@ -7,7 +7,6 @@ import { log, shEcho, performBrowserTest, - workspaceRoot, generateFiles, } from '@fluentui/scripts-projects-test'; @@ -25,7 +24,7 @@ export async function nextjs() { logger('STEP 2. Add Fluent UI dependency to test project'); - const packedPackages = await packProjectPackages(logger, workspaceRoot, ['@fluentui/react-northstar']); + const packedPackages = await packProjectPackages(logger, '@fluentui/react-northstar'); await addResolutionPathsForProjectPackages(tempPaths.testApp); await shEcho(`yarn add ${packedPackages['@fluentui/react-northstar']}`, tempPaths.testApp); diff --git a/packages/fluentui/projects-test/src/rollup.ts b/packages/fluentui/projects-test/src/rollup.ts index db992e4e96be7a..503886369cc7bb 100644 --- a/packages/fluentui/projects-test/src/rollup.ts +++ b/packages/fluentui/projects-test/src/rollup.ts @@ -7,7 +7,6 @@ import { prepareTempDirs, log, shEcho, - workspaceRoot, generateFiles, } from '@fluentui/scripts-projects-test'; @@ -36,7 +35,7 @@ export async function rollup() { logger('STEP 2. Add Fluent UI dependency to test project'); - const packedPackages = await packProjectPackages(logger, workspaceRoot, ['@fluentui/react-northstar']); + const packedPackages = await packProjectPackages(logger, '@fluentui/react-northstar'); await addResolutionPathsForProjectPackages(tempPaths.testApp); await shEcho(`yarn add ${packedPackages['@fluentui/react-northstar']}`, tempPaths.testApp); diff --git a/packages/fluentui/projects-test/src/typings.ts b/packages/fluentui/projects-test/src/typings.ts index 3b35c19abb16cd..9a812b1aa5861a 100644 --- a/packages/fluentui/projects-test/src/typings.ts +++ b/packages/fluentui/projects-test/src/typings.ts @@ -34,7 +34,7 @@ export async function typings() { await shEcho(`yarn add ${dependencies}`, tempPaths.testApp); logger(`✔️ Dependencies were installed`); - const packedPackages = await packProjectPackages(logger, workspaceRoot, ['@fluentui/react-northstar']); + const packedPackages = await packProjectPackages(logger, '@fluentui/react-northstar'); await addResolutionPathsForProjectPackages(tempPaths.testApp); await shEcho(`yarn add ${packedPackages['@fluentui/react-northstar']}`, tempPaths.testApp); diff --git a/scripts/generators/copy-notices.js b/scripts/generators/copy-notices.js index 85a81db3233f86..7e9129008a1488 100644 --- a/scripts/generators/copy-notices.js +++ b/scripts/generators/copy-notices.js @@ -1,6 +1,7 @@ -const monorepo = require('@fluentui/scripts-monorepo'); -const path = require('path'); const fs = require('fs'); +const path = require('path'); + +const monorepo = require('@fluentui/scripts-monorepo'); /** * Copies NOTICE.txt files from `@fluentui/react-components` to all of its internal production dependencies @@ -18,9 +19,9 @@ async function copyNotices() { console.log(`NOTICE.txt exists in ${noticeFilePath}`); - const dependencyNames = await monorepo.getDependencies('@fluentui/react-components', { production: true }); - const copyLocations = dependencyNames.map(dependencyName => - path.resolve(monorepo.findGitRoot(), 'packages', dependencyName.replace('@fluentui/', ''), 'NOTICE.txt'), + const dependencies = (await monorepo.getDependencies('@fluentui/react-components')).dependencies; + const copyLocations = dependencies.map(dep => + path.resolve(monorepo.findGitRoot(), 'packages', dep.name.replace('@fluentui/', ''), 'NOTICE.txt'), ); console.log(`reading ${noticeFilePath}`); diff --git a/scripts/monorepo/src/getDependencies.d.ts b/scripts/monorepo/src/getDependencies.d.ts new file mode 100644 index 00000000000000..710fff0fdd16cc --- /dev/null +++ b/scripts/monorepo/src/getDependencies.d.ts @@ -0,0 +1,17 @@ +// Why is this manual type declaration needed? +// - modules(projects) written in TS not having enabled `checkJS:true` wont infer types from .js files, thus the API will have `any` type. +// This errors in strict ts check mode. + +/// +export declare function getDependencies(packageName: string): Promise<{ + dependencies: Dependency[]; + devDependencies: Dependency[]; + all: Dependency[]; + projectGraph: import('lerna/utils').ProjectGraphWithPackages; +}>; + +type Dependency = { + name: string; + isTopLevel: boolean; + dependencyType: import('lerna/utils').ProjectGraphWorkspacePackageDependency['dependencyCollection']; +}; diff --git a/scripts/monorepo/src/getDependencies.js b/scripts/monorepo/src/getDependencies.js index 99f471bc127682..75d44dab5b1f34 100644 --- a/scripts/monorepo/src/getDependencies.js +++ b/scripts/monorepo/src/getDependencies.js @@ -1,58 +1,82 @@ -const path = require('path'); +const lernaUtils = require('lerna/utils'); -const { Project } = require('@lerna/project'); - -const { PackageGraph } = require('@lerna/package-graph'); - -const findGitRoot = require('./findGitRoot'); +/** +* @typedef {{ + name: string, + isTopLevel: boolean, + dependencyType: lernaUtils.ProjectGraphWorkspacePackageDependency['dependencyCollection'], + }} Dependency + */ /** * - * @param {string[]} rootPackages - * @param {Map} projectGraph - * @param {string[]} packageList - * @returns + * @param {string} project + * @param {lernaUtils.ProjectGraphWithPackages} projectGraph + * @param {*} options + * @param {Dependency[]} _acc + * @param {boolean} _areTopLevelDeps + * @returns {Dependency[]} */ -function flattenPackageGraph(rootPackages, projectGraph, packageList = []) { - rootPackages.forEach(packageName => { - packageList.push(packageName); +function collectDependencies( + project, + projectGraph, + options = { + shallow: true, + dependenciesOnly: false, + }, + _acc = [], + _areTopLevelDeps = true, +) { + if (!projectGraph.localPackageDependencies[project]) { + return _acc; + } - flattenPackageGraph([...projectGraph.get(packageName).localDependencies.keys()], projectGraph, packageList); - }); + projectGraph.localPackageDependencies[project].forEach(dependency => { + const isDependencyAlreadyCollected = _acc.some(dep => dep.name === dependency.target); - return packageList.sort().filter((v, i, a) => a.indexOf(v) === i); -} + if (isDependencyAlreadyCollected) { + return; + } -/** - * Returns all the dependencies of a given package name - * @param {string} packageName including `@fluentui/` prefix - * @param {Object} options - * @param {boolean} [options.dev] include dev dependencies - * @param {boolean} [options.production] include production dependencies - */ -async function getDependencies(packageName, options = { production: true }) { - const lernaProject = new Project(path.resolve(findGitRoot(), 'packages')); - const projectPackages = await lernaProject.getPackages(); + if ( + options.dependenciesOnly && + dependency.dependencyCollection && + dependency.dependencyCollection !== 'dependencies' + ) { + return; + } - const allDepsGraph = flattenPackageGraph([packageName], new PackageGraph(projectPackages)); - const productionDepsGraph = flattenPackageGraph([packageName], new PackageGraph(projectPackages, 'dependencies')); + _acc.push({ + name: dependency.target, + dependencyType: dependency.dependencyCollection, + isTopLevel: _areTopLevelDeps, + }); - const devDependencies = allDepsGraph.filter(dep => !productionDepsGraph.includes(dep)); + if (!options.shallow) { + collectDependencies(dependency.target, projectGraph, options, _acc, false); + } + }); - /** - * @type {string[]} - */ - let res = []; + return _acc; +} - if (options.dev) { - res = res.concat(devDependencies); - } +/** + * Returns dependencies metadata build from dependency graph for provided package + * @param {string} packageName - including `@fluentui/` prefix + */ +async function getDependencies(packageName) { + const { projectGraph } = await lernaUtils.detectProjects(); - if (options.production) { - res = res.concat(productionDepsGraph); - } + const allDepsGraph = collectDependencies(packageName, projectGraph, { shallow: false, dependenciesOnly: false }); + const depsGraph = collectDependencies(packageName, projectGraph, { shallow: false, dependenciesOnly: true }); + const devDepsGraph = allDepsGraph.filter(anyDep => !depsGraph.find(prodDep => prodDep.name === anyDep.name)); - return res; + return { + dependencies: depsGraph, + devDependencies: devDepsGraph, + all: allDepsGraph, + projectGraph, + }; } -module.exports = getDependencies; +exports.getDependencies = getDependencies; diff --git a/scripts/monorepo/src/getDependencies.spec.js b/scripts/monorepo/src/getDependencies.spec.js new file mode 100644 index 00000000000000..4fc69c2f41ff26 --- /dev/null +++ b/scripts/monorepo/src/getDependencies.spec.js @@ -0,0 +1,108 @@ +// NOTE: +// - this spec file needs to be a javascript file otherwise there will be jest error: 'Cannot find module 'spdx-license-ids' from 'scan.js'' +// also to make this work `moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],` was modified with added `json` +// not 100% same error - but issue with same package ( that lerna uses under the hood ) - https://github.com/storybookjs/storybook/issues/3728#issuecomment-396190777 + +const { getDependencies } = require('./getDependencies'); + +describe(`#getDependencies`, () => { + const packageName = '@fluentui/react-text'; + it(`should return package/s dependency tree array for all,devDeps and production dependencies`, async () => { + const deps = await getDependencies(packageName); + + expect(deps.dependencies).toMatchInlineSnapshot(` + Array [ + Object { + "dependencyType": "dependencies", + "isTopLevel": true, + "name": "@fluentui/react-shared-contexts", + }, + Object { + "dependencyType": "dependencies", + "isTopLevel": false, + "name": "@fluentui/react-theme", + }, + Object { + "dependencyType": "dependencies", + "isTopLevel": false, + "name": "@fluentui/tokens", + }, + Object { + "dependencyType": "dependencies", + "isTopLevel": true, + "name": "@fluentui/react-utilities", + }, + Object { + "dependencyType": "dependencies", + "isTopLevel": false, + "name": "@fluentui/keyboard-keys", + }, + Object { + "dependencyType": "dependencies", + "isTopLevel": true, + "name": "@fluentui/react-jsx-runtime", + }, + ] + `); + + expect(deps.devDependencies).toMatchInlineSnapshot(` + Array [ + Object { + "dependencyType": "devDependencies", + "isTopLevel": false, + "name": "@fluentui/eslint-plugin", + }, + Object { + "dependencyType": "devDependencies", + "isTopLevel": false, + "name": "@fluentui/scripts-api-extractor", + }, + Object { + "dependencyType": "devDependencies", + "isTopLevel": false, + "name": "@fluentui/scripts-tasks", + }, + Object { + "dependencyType": "dependencies", + "isTopLevel": false, + "name": "@fluentui/scripts-monorepo", + }, + Object { + "dependencyType": "dependencies", + "isTopLevel": false, + "name": "@fluentui/scripts-utils", + }, + Object { + "dependencyType": "dependencies", + "isTopLevel": false, + "name": "@fluentui/scripts-prettier", + }, + Object { + "dependencyType": "devDependencies", + "isTopLevel": false, + "name": "@fluentui/react-conformance", + }, + Object { + "dependencyType": "devDependencies", + "isTopLevel": false, + "name": "@fluentui/scripts-jest", + }, + Object { + "dependencyType": "devDependencies", + "isTopLevel": false, + "name": "@fluentui/react-conformance-griffel", + }, + ] + `); + }); + + it(`should provide similar api like 'new Project(root).getPackages()'`, async () => { + const { projectGraph } = await getDependencies(packageName); + const packageInfo = projectGraph.nodes[packageName].package; + + expect(packageInfo?.location).toEqual(expect.stringContaining('packages/react-components/react-text')); + expect(packageInfo?.dependencies).toEqual(expect.any(Object)); + expect(packageInfo?.get('main')).toEqual('lib-commonjs/index.js'); + expect(packageInfo?.get('module')).toEqual('lib/index.js'); + }); +}); diff --git a/scripts/monorepo/src/index.d.ts b/scripts/monorepo/src/index.d.ts index ef25502a1a83b0..53d016949011cf 100644 --- a/scripts/monorepo/src/index.d.ts +++ b/scripts/monorepo/src/index.d.ts @@ -1,5 +1,5 @@ export type { PackageJson, AllPackageInfo } from './types'; -export { default as getDependencies } from './getDependencies'; +export { getDependencies } from './getDependencies'; export { default as findGitRoot } from './findGitRoot'; export { default as findRepoDeps } from './findRepoDeps'; export { default as getAllPackageInfo } from './getAllPackageInfo'; diff --git a/scripts/monorepo/src/index.js b/scripts/monorepo/src/index.js index bfbd2af551f6ec..0b55f2b4196bae 100644 --- a/scripts/monorepo/src/index.js +++ b/scripts/monorepo/src/index.js @@ -1,9 +1,9 @@ module.exports = { - getDependencies: require('./getDependencies'), findGitRoot: require('./findGitRoot'), findRepoDeps: require('./findRepoDeps'), getAllPackageInfo: require('./getAllPackageInfo'), eslintConstants: require('./eslint-constants'), + ...require('./getDependencies'), ...require('./isConvergedPackage'), ...require('./getAffectedPackages'), ...require('./getNthCommit'), diff --git a/scripts/projects-test/src/packPackages.ts b/scripts/projects-test/src/packPackages.ts index 6d458ac172ef63..30176ff5ccb58e 100644 --- a/scripts/projects-test/src/packPackages.ts +++ b/scripts/projects-test/src/packPackages.ts @@ -1,8 +1,7 @@ import path from 'path'; +import { getDependencies } from '@fluentui/scripts-monorepo'; import { sh } from '@fluentui/scripts-utils'; -import { PackageGraph } from '@lerna/package-graph'; -import { Project } from '@lerna/project'; import fs from 'fs-extra'; import { createTempDir, shEcho } from './utils'; @@ -12,17 +11,6 @@ type PackedPackages = Record; /** Shared packed packages between tests since they're not modified by any test */ let packedPackages: PackedPackages; -function flattenPackageGraph(rootPackages: string[], projectGraph: PackageGraph, packageList: string[] = []): string[] { - rootPackages.forEach(packageName => { - packageList.push(packageName); - - // NOTE: we need to use Array.from instead of spread to new array because v0 packages have target `es5` thus this would trigger TS error - flattenPackageGraph(Array.from(projectGraph.get(packageName).localDependencies.keys()), projectGraph, packageList); - }); - - return packageList.sort().filter((v, i, a) => a.indexOf(v) === i); -} - export async function addResolutionPathsForProjectPackages(testProjectDir: string, isTemplateJson?: boolean) { const jsonPath = path.resolve(testProjectDir, isTemplateJson ? 'template.json' : 'package.json'); const json = fs.readJSONSync(jsonPath); @@ -36,11 +24,7 @@ export async function addResolutionPathsForProjectPackages(testProjectDir: strin fs.writeJSONSync(jsonPath, json, { spaces: 2 }); } -export async function packProjectPackages( - logger: Function, - lernaRoot: string, - rootPackages: string[], -): Promise { +export async function packProjectPackages(logger: Function, project: string): Promise { if (packedPackages) { logger(`✔️ Packages already packed`); return packedPackages; @@ -48,16 +32,15 @@ export async function packProjectPackages( packedPackages = {}; - const lernaProject = new Project(lernaRoot); - // this is the list of package.json contents with some extra properties - const projectPackages = await lernaProject.getPackages(); - - logger(`✔️ Used lerna config: ${lernaProject.rootConfigLocation}`); - - const projectPackagesGraph = new PackageGraph(projectPackages, 'dependencies'); - const requiredPackages = flattenPackageGraph(rootPackages, projectPackagesGraph); + const { dependencies: projectDependencies, projectGraph } = await getDependencies(project); + // add provided package to be packaged + projectDependencies.unshift({ + name: project, + dependencyType: 'dependencies', + isTopLevel: true, + }); - logger(`✔️ Following packages will be packed:${requiredPackages.map(p => `\n${' '.repeat(30)}- ${p}`)}`); + logger(`✔️ Following packages will be packed:${projectDependencies.map(pkg => `\n${' '.repeat(30)}- ${pkg.name}`)}`); const tmpDirectory = createTempDir('project-packed-'); logger(`✔️ Temporary directory for packed packages was created: ${tmpDirectory}`); @@ -66,19 +49,20 @@ export async function packProjectPackages( await shEcho('npm --version', tmpDirectory); await Promise.all( - requiredPackages.map(async packageName => { - const packageInfo = projectPackages.find(pkg => pkg.name === packageName); + projectDependencies.map(async projectConfig => { + const packageName = projectConfig.name; + const packageInfo = projectGraph.nodes[packageName].package; if (!packageInfo) { throw new Error(`Package ${packageName} doesn't exist`); } const packagePath = packageInfo.location; - const packageMain: string | undefined = packageInfo.get('main'); + const packageMain = packageInfo.get('main') as string | undefined; const entryPointPath = packageMain ? path.join(packagePath, packageMain) : ''; if (!fs.existsSync(entryPointPath)) { throw new Error( - `Package ${packageName} does not appear to have been built yet. Please ensure that root package(s) ` + - `${rootPackages.join(', ')} are listed in devDependencies of the package running the test.`, + `Package ${packageName} does not appear to have been built yet.` + + `Please ensure that package:"${project}" has properly defined dependencies in its package.json`, ); } diff --git a/typings/lerna/index.d.ts b/typings/lerna/index.d.ts new file mode 100644 index 00000000000000..791c6ee22eb007 --- /dev/null +++ b/typings/lerna/index.d.ts @@ -0,0 +1,75 @@ +// Type definitions for lerna 7.0.0 + +// NOTE: types taken from @see https://github.com/lerna/lerna/blob/main/libs/core/src/lib/project-graph-with-packages.ts +// ISSUE: https://github.com/lerna/lerna/issues/3851 + +declare module 'lerna/utils' { + import type { ProjectFileMap, ProjectGraph, ProjectGraphDependency, ProjectGraphProjectNode } from '@nx/devkit'; + + interface RawManifest { + name: string; + version: string; + description?: string; + private?: boolean; + bin?: Record | string; + scripts?: Record; + dependencies?: Record; + devDependencies?: Record; + optionalDependencies?: Record; + peerDependencies?: Record; + publishConfig?: Record<'directory' | 'registry' | 'tag', string>; + workspaces?: string[]; + nx?: Record; + gitHead?: string; + lerna?: RawManifestLernaConfig; + } + + interface RawManifestLernaConfig { + command?: { + publish?: { + directory?: string; + assets?: AssetDefinition[]; + }; + }; + } + type AssetDefinition = string | { from: string; to: string }; + + interface Package extends Omit { + /** + * Map-like retrieval of arbitrary values from package.json + */ + get(key: string): unknown; + binLocation: string; + + resolved: Record; + /** + * path to package.json + */ + manifestLocation: string; + location: string; + lernaConfig: RawManifest['lerna'] | undefined; + } + + type ExtendedNpaResult = { + workspaceSpec?: string; + workspaceAlias?: string; + }; + + interface ProjectGraphProjectNodeWithPackage extends ProjectGraphProjectNode { + package: Package | null; + } + interface ProjectGraphWorkspacePackageDependency extends ProjectGraphDependency { + targetVersionMatchesDependencyRequirement: boolean; + targetResolvedNpaResult: ExtendedNpaResult; + dependencyCollection: 'dependencies' | 'devDependencies' | 'optionalDependencies'; // lerna doesn't manage peer dependencies + } + export interface ProjectGraphWithPackages extends ProjectGraph { + nodes: Record; + localPackageDependencies: Record; + } + + export function detectProjects(): Promise<{ + projectGraph: ProjectGraphWithPackages; + projectFileMap: ProjectFileMap; + }>; +} diff --git a/typings/lerna/tsconfig.json b/typings/lerna/tsconfig.json new file mode 100644 index 00000000000000..c6f8b0a935c4c2 --- /dev/null +++ b/typings/lerna/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": {}, + "include": ["*.d.ts"] +} diff --git a/typings/tsconfig.json b/typings/tsconfig.json index d2324f1f1c570d..66aabec9b543cb 100644 --- a/typings/tsconfig.json +++ b/typings/tsconfig.json @@ -30,6 +30,9 @@ }, { "path": "./find-free-port/tsconfig.json" + }, + { + "path": "./lerna/tsconfig.json" } ] } diff --git a/yarn.lock b/yarn.lock index 2a174e8a2c7087..c6da86f241e4f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2312,59 +2312,6 @@ validate-npm-package-name "5.0.0" yargs-parser "20.2.4" -"@lerna/package-graph@6.4.1": - version "6.4.1" - resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-6.4.1.tgz#7a18024d531f0bd88609944e572b4861f0f8868f" - integrity sha512-fQvc59stRYOqxT3Mn7g/yI9/Kw5XetJoKcW5l8XeqKqcTNDURqKnN0qaNBY6lTTLOe4cR7gfXF2l1u3HOz0qEg== - dependencies: - "@lerna/prerelease-id-from-version" "6.4.1" - "@lerna/validation-error" "6.4.1" - npm-package-arg "8.1.1" - npmlog "^6.0.2" - semver "^7.3.4" - -"@lerna/package@6.4.1": - version "6.4.1" - resolved "https://registry.yarnpkg.com/@lerna/package/-/package-6.4.1.tgz#ebbd4c5f58f4b6cf77019271a686be9585272a3b" - integrity sha512-TrOah58RnwS9R8d3+WgFFTu5lqgZs7M+e1dvcRga7oSJeKscqpEK57G0xspvF3ycjfXQwRMmEtwPmpkeEVLMzA== - dependencies: - load-json-file "^6.2.0" - npm-package-arg "8.1.1" - write-pkg "^4.0.0" - -"@lerna/prerelease-id-from-version@6.4.1": - version "6.4.1" - resolved "https://registry.yarnpkg.com/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-6.4.1.tgz#65eb1835cdfd112783eea6b596812c64f535386b" - integrity sha512-uGicdMFrmfHXeC0FTosnUKRgUjrBJdZwrmw7ZWMb5DAJGOuTzrvJIcz5f0/eL3XqypC/7g+9DoTgKjX3hlxPZA== - dependencies: - semver "^7.3.4" - -"@lerna/project@6.4.1": - version "6.4.1" - resolved "https://registry.yarnpkg.com/@lerna/project/-/project-6.4.1.tgz#0519323aa8bde5b73fc0bf1c428385a556a445f0" - integrity sha512-BPFYr4A0mNZ2jZymlcwwh7PfIC+I6r52xgGtJ4KIrIOB6mVKo9u30dgYJbUQxmSuMRTOnX7PJZttQQzSda4gEg== - dependencies: - "@lerna/package" "6.4.1" - "@lerna/validation-error" "6.4.1" - cosmiconfig "^7.0.0" - dedent "^0.7.0" - dot-prop "^6.0.1" - glob-parent "^5.1.1" - globby "^11.0.2" - js-yaml "^4.1.0" - load-json-file "^6.2.0" - npmlog "^6.0.2" - p-map "^4.0.0" - resolve-from "^5.0.0" - write-json-file "^4.3.0" - -"@lerna/validation-error@6.4.1": - version "6.4.1" - resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-6.4.1.tgz#2cab92c2be395158c3d65fa57ddb73892617d7e8" - integrity sha512-fxfJvl3VgFd7eBfVMRX6Yal9omDLs2mcGKkNYeCEyt4Uwlz1B5tPAXyk/sNMfkKV2Aat/mlK5tnY13vUrMKkyA== - dependencies: - npmlog "^6.0.2" - "@linaria/babel-preset@^3.0.0-beta.22", "@linaria/babel-preset@^3.0.0-beta.23": version "3.0.0-beta.23" resolved "https://registry.yarnpkg.com/@linaria/babel-preset/-/babel-preset-3.0.0-beta.23.tgz#1e8c32c85495fdd9db4d3f1ccd4d29fe17cb1d16" @@ -5335,28 +5282,6 @@ resolved "https://registry.yarnpkg.com/@types/lerna-alias/-/lerna-alias-3.0.0.tgz#e4d70f75bad9f5a7b697f76b7d7b3fa97aa57844" integrity sha512-EfwEzSWxAxrZgUJT5sECi4RPFCIgOm/K9e5EWXp2hR9H6wzV1QIBgjGtGEl/OpOCcglNWzmqrVRX/qjvNwli9Q== -"@types/lerna__package-graph@5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/lerna__package-graph/-/lerna__package-graph-5.1.0.tgz#070b88cec0c7a51e1f80f4b3a97490491f81a92f" - integrity sha512-/n50KTl3cjvjF70th6DXsAicYV+MBGkjcRuVZn1cAeQivAg5TS9aYiUWuGY1S6v7i8FdPF+Ysf5YYUvniZmyIQ== - dependencies: - "@types/lerna__package" "*" - "@types/npm-package-arg" "*" - -"@types/lerna__package@*": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/lerna__package/-/lerna__package-5.1.0.tgz#870a1fa82356dc9d5cd2a60d8deb45a54755fdc5" - integrity sha512-afNRkz6zwZZRR/5JRZ48R2t7kekQcX2Dq0JMvnsh0NiAVOdP9PYghNGhwiIdBau3cFmdU7wwxXupWAnzC4rZSQ== - dependencies: - "@types/npm-package-arg" "*" - -"@types/lerna__project@5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/lerna__project/-/lerna__project-5.1.0.tgz#0418629d42a5b8f35b7ad95fc944fc54e8c9b1af" - integrity sha512-J41EBQZOD7CL7Aqwriifb63paXePjBBkDEfGdTGs65Yrn0624ekNcLRG8UH+4mfbcog4zCoaHZHMjcSMytEzjA== - dependencies: - "@types/lerna__package" "*" - "@types/loader-utils@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/loader-utils/-/loader-utils-2.0.3.tgz#fbc2337358f8f4a7dc532ac0a3646c74275edf2d" @@ -5447,11 +5372,6 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== -"@types/npm-package-arg@*": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@types/npm-package-arg/-/npm-package-arg-6.1.1.tgz#9e2d8adc04d39824a3d9f36f738010a3f7da3c1a" - integrity sha512-452/1Kp9IdM/oR10AyqAgZOxUt7eLbm+EMJ194L6oarMYdZNiFIFAOJ7IIr0OrZXTySgfHjJezh2oiyk2kc3ag== - "@types/npmlog@^4.1.2": version "4.1.2" resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.2.tgz#d070fe6a6b78755d1092a3dc492d34c3d8f871c4" @@ -10661,11 +10581,6 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-indent@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" - integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== - detect-libc@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -10970,13 +10885,6 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" -dot-prop@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" - integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== - dependencies: - is-obj "^2.0.0" - dotenv-expand@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" @@ -17327,7 +17235,7 @@ listr2@^3.8.3: through "^2.3.8" wrap-ansi "^7.0.0" -load-json-file@6.2.0, load-json-file@^6.2.0: +load-json-file@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== @@ -23134,13 +23042,6 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -sort-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" - integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== - dependencies: - is-plain-obj "^2.0.0" - source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -26286,19 +26187,7 @@ write-json-file@^3.2.0: sort-keys "^2.0.0" write-file-atomic "^2.4.2" -write-json-file@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" - integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== - dependencies: - detect-indent "^6.0.0" - graceful-fs "^4.1.15" - is-plain-obj "^2.0.0" - make-dir "^3.0.0" - sort-keys "^4.0.0" - write-file-atomic "^3.0.0" - -write-pkg@4.0.0, write-pkg@^4.0.0: +write-pkg@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==