Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(bazel): integration test rule not able to setup mappings for resolutions #286

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 51 additions & 8 deletions bazel/integration/test_runner/package_json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,18 @@ export function updateMappingsForPackageJson(
): PackageJson {
const newPackageJson = {...packageJson};

updateMappingForRecord(newPackageJson.dependencies ?? {}, mappings);
updateMappingForRecord(newPackageJson.devDependencies ?? {}, mappings);
updateMappingForRecord(newPackageJson.optionalDependencies ?? {}, mappings);
updateMappingForRecord(newPackageJson.resolutions ?? {}, mappings);
updateMappingForRecord(newPackageJson, 'dependencies', mappings);
updateMappingForRecord(newPackageJson, 'devDependencies', mappings);
updateMappingForRecord(newPackageJson, 'optionalDependencies', mappings);
// The object for Yarn resolutions will not directly match with the mapping keys
// specified here, as resolutions usually follow a format as followed:
// 1. `**/<pkg-name>`
// 2. `<other-pkg>/**/<pkg-name>`
// 3. `<pkg-name>`
// More details here: https://classic.yarnpkg.com/lang/en/docs/selective-version-resolutions/.
// We pass a regular expression which matches the `<pkg-name>` so that the mappings can
// be applied for resolutions as well.
updateMappingForRecord(newPackageJson, 'resolutions', mappings, /([^/]+)$/);

return newPackageJson;
}
Expand All @@ -64,15 +72,31 @@ export function updateMappingsForPackageJson(
* @throws An error if there is a dependency entry referring to a local file. Such
* entries should not use `file:` but instead configure a mapping through Bazel.
*/
function updateMappingForRecord(record: DependencyRecord, mappings: PackageMappings) {
for (const [pkgName, value] of Object.entries(record)) {
function updateMappingForRecord(
pkgJson: PackageJson,
recordName: keyof PackageJson,
mappings: PackageMappings,
nameMatchRegex?: RegExp,
) {
const record = pkgJson[recordName] ?? {};

for (const [entryKey, value] of Object.entries(record)) {
const pkgName = getPackageNameFromDependencyEntry(entryKey, nameMatchRegex);

if (pkgName === null) {
throw Error(`Could not determine package name for "${recordName}" entry: ${entryKey}.`);
}

// Print the resolved package name to ease debugging when packages are not mapped properly.
debug(`updateMappingForRecord: Resolved "${recordName}@${entryKey}" to package: ${pkgName}`);

const mappedAbsolutePath = mappings[pkgName];

// If the value of the dependency entry is referring to a local file, then we report
// an error as this is likely a missing mapping that should be set up through Bazel.
if (mappedAbsolutePath === undefined && value.startsWith(`file:`)) {
throw Error(
`Unexpected dependency entry for: ${pkgName}, pointing to: ${value}.` +
`Unexpected dependency entry for: ${entryKey}, pointing to: ${value}. ` +
`Instead, configure the mapping through the integration test Bazel target.`,
);
}
Expand All @@ -82,6 +106,25 @@ function updateMappingForRecord(record: DependencyRecord, mappings: PackageMappi
continue;
}

record[pkgName] = mappedAbsolutePath;
record[entryKey] = mappedAbsolutePath;
}
}

/**
* Gets the package name from a dependency record entry.
*
* @param entryKey Key of the dependency record entry.
* @param nameMatchRegex Optional regular expression that can be specified to match the package
* name in a dependency entry. This is useful for e.g. Yarn resolutions using patterns.
devversion marked this conversation as resolved.
Show resolved Hide resolved
* The first capturing group is expected to return the matched package name.
*/
function getPackageNameFromDependencyEntry(
entryKey: string,
nameMatchRegex?: RegExp,
): string | null {
if (nameMatchRegex) {
const matches = entryKey.match(nameMatchRegex);
return matches === null ? null : matches[1];
devversion marked this conversation as resolved.
Show resolved Hide resolved
}
return entryKey;
}
11 changes: 11 additions & 0 deletions bazel/integration/tests/package_mappings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,16 @@
"license": "MIT",
"dependencies": {
"fake_pkg": "0.0.0"
},
"devDependencies": {
"fake_pkg": "0.0.0"
},
"optionalDependencies": {
"fake_pkg": "0.0.0"
},
"resolutions": {
"**/fake_pkg": "0.0.0",
"some_pkg/**/fake_pkg": "0.0.0",
"fake_pkg": "0.0.0"
}
devversion marked this conversation as resolved.
Show resolved Hide resolved
}
11 changes: 11 additions & 0 deletions bazel/integration/tests/package_mappings/test.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import fakePkg from 'fake_pkg';
import fs from 'fs';

// Sanity check that the installed package matches the one we have
// built from source using `pkg_npm`.
if (fakePkg !== 'This is a fake package!') {
console.error('Fake package is not matching with locally-built one.');
process.exitCode = 1;
}

const pkgJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const recordsToCheck = ['dependencies', 'devDependencies', 'optionalDependencies', 'resolutions'];

for (const recordName of recordsToCheck) {
if (Object.values(pkgJson[recordName]).includes('0.0.0')) {
console.error(`The "${recordName}" field has not been replaced with mapped archives.`);
process.exitCode = 1;
}
}