|
1 | 1 | import fs from 'node:fs'; |
2 | 2 | import semver from 'semver'; |
3 | 3 |
|
| 4 | +const SFDX_LOCAL_DEV_DIST_BASE = '@lwc/sfdx-local-dev-dist'; |
| 5 | +const SFDX_LOCAL_DEV_DIST_PATTERN = /^@lwc\/sfdx-local-dev-dist-([\d.]+)$/; |
| 6 | + |
| 7 | +/** |
| 8 | + * This script ensures that the aliased dependencies in package.json stay in sync |
| 9 | + * with the versions defined in apiVersionMetadata. |
| 10 | + * It also ensures @lwc/sfdx-local-dev-dist matches the version of the highest-numbered |
| 11 | + * @lwc/sfdx-local-dev-dist-<number> alias. |
| 12 | + */ |
| 13 | + |
4 | 14 | // Read package.json |
5 | 15 | const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); |
| 16 | +const apiVersionMetadata = packageJson.apiVersionMetadata; |
| 17 | + |
| 18 | +if (!apiVersionMetadata) { |
| 19 | + console.error('Error: missing apiVersionMetadata in package.json'); |
| 20 | + process.exit(1); |
| 21 | +} |
| 22 | + |
| 23 | +let hasError = false; |
| 24 | + |
| 25 | +// Check that @lwc/sfdx-local-dev-dist version matches the highest-numbered alias |
| 26 | +const baseVersion = packageJson.dependencies[SFDX_LOCAL_DEV_DIST_BASE]; |
| 27 | +if (baseVersion) { |
| 28 | + const aliasedDeps = Object.keys(packageJson.dependencies).filter((key) => SFDX_LOCAL_DEV_DIST_PATTERN.test(key)); |
| 29 | + if (aliasedDeps.length > 0) { |
| 30 | + const highestAlias = aliasedDeps |
| 31 | + .map((key) => { |
| 32 | + const m = key.match(SFDX_LOCAL_DEV_DIST_PATTERN); |
| 33 | + return { key, number: m ? parseFloat(m[1]) : -1 }; |
| 34 | + }) |
| 35 | + .sort((a, b) => b.number - a.number)[0]; |
| 36 | + |
| 37 | + const highestAliasValue = packageJson.dependencies[highestAlias.key]; |
| 38 | + const highestMatch = highestAliasValue?.match(/@([^@]+)$/); |
| 39 | + const highestRange = highestMatch ? highestMatch[1] : null; |
6 | 40 |
|
7 | | -// Extract versions |
8 | | -const devServerDependencyVersion = packageJson.dependencies['@lwc/lwc-dev-server']; |
9 | | -const devServerTargetVersionRange = packageJson.apiVersionMetadata?.target?.matchingDevServerVersion; |
| 41 | + if (!highestRange) { |
| 42 | + console.error(`Error: Could not parse version range from '${highestAlias.key}': ${highestAliasValue}`); |
| 43 | + hasError = true; |
| 44 | + } else if (baseVersion !== highestRange) { |
| 45 | + console.error( |
| 46 | + `Error: Version of '${SFDX_LOCAL_DEV_DIST_BASE}' (${baseVersion}) must be the same as the version of the highest-numbered alias '${highestAlias.key}' (${highestRange}).`, |
| 47 | + ); |
| 48 | + hasError = true; |
| 49 | + } |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +// Iterate through each API version defined in metadata |
| 54 | +for (const [apiVersion, metadata] of Object.entries(apiVersionMetadata)) { |
| 55 | + const expectedDeps = metadata.dependencies; |
| 56 | + if (!expectedDeps) continue; |
| 57 | + |
| 58 | + for (const [depName, expectedRange] of Object.entries(expectedDeps)) { |
| 59 | + // For each dependency in metadata, find its aliased counterpart in package.json dependencies |
| 60 | + // e.g. @lwc/lwc-dev-server -> @lwc/lwc-dev-server-65.0 |
| 61 | + const aliasName = `${depName}-${apiVersion}`; |
| 62 | + const actualAliasValue = packageJson.dependencies[aliasName]; |
| 63 | + |
| 64 | + if (!actualAliasValue) { |
| 65 | + console.error(`Error: Missing aliased dependency '${aliasName}' in package.json for API version ${apiVersion}`); |
| 66 | + hasError = true; |
| 67 | + continue; |
| 68 | + } |
10 | 69 |
|
11 | | -if (!devServerDependencyVersion || !devServerTargetVersionRange) { |
12 | | - console.error('Error: missing @lwc/lwc-dev-server or matchingDevServerVersion'); |
13 | | - process.exit(1); // Fail the check |
| 70 | + // actualAliasValue looks like "npm:@lwc/lwc-dev-server@~13.2.x" or "npm:lwc@~8.23.x" |
| 71 | + // We want to extract the version range after the last @ |
| 72 | + const match = actualAliasValue.match(/@([^@]+)$/); |
| 73 | + if (!match) { |
| 74 | + console.error(`Error: Could not parse version range from aliased dependency '${aliasName}': ${actualAliasValue}`); |
| 75 | + hasError = true; |
| 76 | + continue; |
| 77 | + } |
| 78 | + |
| 79 | + const actualRange = match[1]; |
| 80 | + |
| 81 | + // Compare the range in metadata with the range in the aliased dependency |
| 82 | + if (!semver.intersects(expectedRange, actualRange)) { |
| 83 | + console.error( |
| 84 | + `Error: Version mismatch for '${aliasName}'. ` + |
| 85 | + `Expected ${expectedRange} in apiVersionMetadata, but found ${actualRange} in dependencies.`, |
| 86 | + ); |
| 87 | + hasError = true; |
| 88 | + } |
| 89 | + } |
14 | 90 | } |
15 | 91 |
|
16 | | -// Compare versions |
17 | | -if (semver.intersects(devServerTargetVersionRange, devServerDependencyVersion)) { |
18 | | - process.exit(0); // Pass the check |
19 | | -} else { |
| 92 | +if (hasError) { |
20 | 93 | console.error( |
21 | | - `Error: @lwc/lwc-dev-server versions do not match between 'dependencies' and 'apiVersionMetadata' in package.json. Expected ${devServerDependencyVersion} in apiVersionMetadata > target > matchingDevServerVersion. Got ${devServerTargetVersionRange} instead. When updating the @lwc/lwc-dev-server dependency, you must ensure that it is compatible with the supported API version in this branch, then update apiVersionMetadata > target > matchingDevServerVersion to match, in order to "sign off" on this dependency change.` |
| 94 | + '\nWhen updating LWC dependencies, you must ensure that the versions in apiVersionMetadata match the aliased dependencies in package.json.', |
22 | 95 | ); |
23 | | - process.exit(1); // Fail the check |
| 96 | + process.exit(1); |
24 | 97 | } |
| 98 | + |
| 99 | +process.exit(0); |
0 commit comments