Skip to content

Commit

Permalink
Ensure we are installing all required artifacts through trasitive mon…
Browse files Browse the repository at this point in the history
…orepo deps
  • Loading branch information
etrepum committed Apr 22, 2024
1 parent 9885ef6 commit 56528f2
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 38 deletions.
69 changes: 37 additions & 32 deletions scripts/__tests__/integration/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,31 +63,24 @@ exports.expectSuccessfulExec = expectSuccessfulExec;
* @property {Record<string, any>} packageJson
*/

/** @param {ctx} ExampleContext */
/**
* @param {ctx} ExampleContext
* @returns {Promise<Map<string, PackageMetadata>>} The installed monorepo dependency map
*/
async function buildExample({packageJson, exampleDir}) {
const lexicalPackages = new Map(
packagesManager.getPublicPackages().map((pkg) => [pkg.getNpmName(), pkg]),
);
let hasPlaywright = false;
const installDeps = [
'dependencies',
'devDependencies',
'peerDependencies',
].flatMap((depType) => {
const deps = packageJson[depType] || {};
hasPlaywright ||= '@playwright/test' in deps;
return Object.keys(deps).flatMap((k) => {
const pkg = lexicalPackages.get(k);
return pkg
? [
path.resolve(
'npm',
`${pkg.getDirectoryName()}-${pkg.packageJson.version}.tgz`,
),
]
: [];
});
});
const depsMap = packagesManager.computedMonorepoDependencyMap(
['dependencies', 'devDependencies', 'peerDependencies'].flatMap(
(depType) => {
const deps = packageJson[depType] || {};
hasPlaywright ||= '@playwright/test' in deps;
return Object.keys(deps);
},
),
);
const installDeps = [...depsMap.values()].map((pkg) =>
path.resolve('npm', `${pkg.getDirectoryName()}-${monorepoVersion}.tgz`),
);
if (installDeps.length === 0) {
throw new Error(`No lexical dependencies detected: ${exampleDir}`);
}
Expand All @@ -105,6 +98,7 @@ async function buildExample({packageJson, exampleDir}) {
await exec('npx playwright install');
}
});
return depsMap;
}

/**
Expand All @@ -119,19 +113,30 @@ function describeExample(packageJsonPath, bodyFun = undefined) {
/** @type {ExampleContext} */
const ctx = {exampleDir, packageJson, packageJsonPath};
describe(exampleDir, () => {
beforeAll(async () => buildExample(ctx), LONG_TIMEOUT);
/** @type {PackageMetadata[]} */
const deps = [];
beforeAll(async () => {
deps.push(...(await buildExample(ctx)).values());
}, LONG_TIMEOUT);
test('install & build succeeded', () => {
expect(true).toBe(true);
});
test(`installed lexical ${monorepoVersion}`, () => {
expect(
fs.existsSync(path.join(exampleDir, 'node_modules', 'lexical')),
).toBe(true);
expect(
fs.readJsonSync(
path.join(exampleDir, 'node_modules', 'lexical', 'package.json'),
),
).toMatchObject({version: monorepoVersion});
const packageNames = deps.map((pkg) => pkg.getNpmName());
expect(packageNames).toContain('lexical');
for (const pkg of deps) {
const installedPath = path.join(
exampleDir,
'node_modules',
pkg.getNpmName(),
);
expect({[installedPath]: fs.existsSync(installedPath)}).toEqual({
[installedPath]: true,
});
expect(
fs.readJsonSync(path.join(installedPath, 'package.json')),
).toMatchObject({name: pkg.getNpmName(), version: monorepoVersion});
}
});
if (packageJson.scripts.test) {
test(
Expand Down
47 changes: 41 additions & 6 deletions scripts/shared/packagesManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ function packageSort(a, b) {
class PackagesManager {
/** @type {Array<PackageMetadata>} */
packages;
/** @type {Map<string, PackageMetadata>} */
packagesByNpmName = new Map();
/** @type {Map<string, PackageMetadata>} */
packagesByDirectoryName = new Map();

/**
* @param {Array<string>} packagePaths
Expand All @@ -35,6 +39,10 @@ class PackagesManager {
this.packages = packagePaths
.map((packagePath) => new PackageMetadata(packagePath))
.sort(packageSort);
for (const pkg of this.packages) {
this.packagesByNpmName.set(pkg.getNpmName(), pkg);
this.packagesByDirectoryName.set(pkg.getDirectoryName(), pkg);
}
}

/**
Expand All @@ -43,9 +51,7 @@ class PackagesManager {
* @returns {PackageMetadata}
*/
getPackageByNpmName(name) {
const pkg = this.packages.find(
(candidate) => candidate.getNpmName() === name,
);
const pkg = this.packagesByNpmName.get(name);
if (!pkg) {
throw new Error(`Missing package with npm name '${name}'`);
}
Expand All @@ -58,9 +64,7 @@ class PackagesManager {
* @returns {PackageMetadata}
*/
getPackageByDirectoryName(name) {
const pkg = this.packages.find(
(candidate) => candidate.getDirectoryName() === name,
);
const pkg = this.packagesByDirectoryName(name);
if (!pkg) {
throw new Error(`Missing package with directory name '${name}'`);
}
Expand All @@ -85,6 +89,37 @@ class PackagesManager {
getPublicPackages() {
return this.packages.filter((pkg) => !pkg.isPrivate());
}

/**
* Given an array of npm dependencies (may include non-Lexical names),
* return all required transitive monorepo dependencies to have those
* packages (in a topologically ordered Map).
*
* @param {Array<string>} npmDependencies
* @returns {Map<string, PackageMetadata>}
*/
computedMonorepoDependencyMap(npmDependencies) {
/** @type {Map<string, PackageMetadata>} */
const depsMap = new Map();
const visited = new Set();
const traverse = (deps) => {
for (const dep of deps) {
if (visited.has(dep)) {
continue;
}
visited.add(dep);
if (!depsMap.has(dep)) {
const pkg = this.packagesByNpmName.get(dep);
if (pkg) {
traverse(Object.keys(pkg.packageJson.dependencies || {}));
depsMap.set(dep, pkg);
}
}
}
};
traverse(npmDependencies);
return depsMap;
}
}

exports.packagesManager = new PackagesManager(
Expand Down

0 comments on commit 56528f2

Please sign in to comment.