|
| 1 | +#!/usr/bin/env node |
| 2 | + |
| 3 | +/** |
| 4 | +This script uses the JSON file api-snapshot/api.json to validate that previously present symbols |
| 5 | +are still exported by the packages within the assessed group. |
| 6 | +
|
| 7 | +Data may only be deleted from api.json in an intentional backwards-incompatible change. |
| 8 | + */ |
| 9 | + |
| 10 | +const fs = require("node:fs"); |
| 11 | +const path = require("node:path"); |
| 12 | + |
| 13 | +const root = path.join(__dirname, "..", ".."); |
| 14 | +const dataPath = path.join(root, "scripts", "validation", "api.json"); |
| 15 | +const api = require(dataPath); |
| 16 | + |
| 17 | +api.$schema = "https://json-schema.org/draft/2020-12/schema"; |
| 18 | + |
| 19 | +const packageDirs = [ |
| 20 | + ...fs.readdirSync(path.join(root, "packages")).map((f) => path.join(root, "packages", f)), |
| 21 | + ...fs.readdirSync(path.join(root, "lib")).map((f) => path.join(root, "lib", f)), |
| 22 | + path.join(root, "clients", "client-s3"), // rest xml |
| 23 | + path.join(root, "clients", "client-s3-control"), // rest xml |
| 24 | + path.join(root, "clients", "client-dynamodb"), // json rpc |
| 25 | + path.join(root, "clients", "client-cloudwatch"), // query |
| 26 | + path.join(root, "clients", "client-sts"), // query |
| 27 | + path.join(root, "clients", "client-sagemaker"), // json rpc |
| 28 | + path.join(root, "clients", "client-bedrock"), // rest json |
| 29 | +]; |
| 30 | +const errors = []; |
| 31 | + |
| 32 | +for (const packageRoot of packageDirs) { |
| 33 | + const pkgJsonPath = path.join(packageRoot, "package.json"); |
| 34 | + const cjsPath = path.join(packageRoot, "dist-cjs", "index.js"); |
| 35 | + |
| 36 | + if (fs.existsSync(pkgJsonPath) && fs.existsSync(cjsPath)) { |
| 37 | + const packageJson = require(pkgJsonPath); |
| 38 | + const { name, version } = packageJson; |
| 39 | + const module = require(cjsPath); |
| 40 | + |
| 41 | + for (const key of Object.keys(module)) { |
| 42 | + if (module[key] === undefined) { |
| 43 | + console.warn(`symbol ${key} in ${name}@${version} has a value of undefined.`); |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + if (!api[name]) { |
| 48 | + api[name] = {}; |
| 49 | + for (const key of Object.keys(module)) { |
| 50 | + api[name][key] = [typeof module[key], `<=${version}`].join(", since "); |
| 51 | + } |
| 52 | + } else { |
| 53 | + for (const symbol of [...new Set([...Object.keys(api[name]), ...Object.keys(module)])]) { |
| 54 | + if (symbol in module && !(symbol in api[name])) { |
| 55 | + errors.push(`You must commit changes in api.json.`); |
| 56 | + api[name][symbol] = [typeof module[symbol], version].join(", since "); |
| 57 | + } |
| 58 | + if (!(symbol in module) && symbol in api[name]) { |
| 59 | + errors.push(`Symbol [${symbol}] is missing from ${name}, (${api[name][symbol]}).`); |
| 60 | + } |
| 61 | + if (symbol in module && symbol in api[name]) { |
| 62 | + if (api[name][symbol].split(", ")[0] !== typeof module[symbol]) { |
| 63 | + errors.push( |
| 64 | + `Symbol [${symbol}] has a different type than expected in ${name}, actual=${typeof module[ |
| 65 | + symbol |
| 66 | + ]} expected=${api[name][symbol]}.` |
| 67 | + ); |
| 68 | + } |
| 69 | + } |
| 70 | + } |
| 71 | + } |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +fs.writeFileSync(dataPath, JSON.stringify(api, null, 2) + "\n"); |
| 76 | + |
| 77 | +if (errors.length) { |
| 78 | + throw new Error(errors.join("\n")); |
| 79 | +} else { |
| 80 | + console.log(`✅ API snapshot test passed.`); |
| 81 | +} |
0 commit comments