diff --git a/.gitignore b/.gitignore index 104af9a157ef..47c2fc037ca4 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ workspace .turbo turbo.dev.json +.rollup.cache coverage dist-* diff --git a/package.json b/package.json index 8f2193b81963..47e123d71f08 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,9 @@ "@fastify/formbody": "^7.4.0", "@microsoft/api-extractor": "7.52.7", "@mixer/parallel-prettier": "2.0.3", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.2", + "@rollup/plugin-typescript": "^12.1.4", "@tsconfig/recommended": "1.0.1", "@types/fs-extra": "^8.0.1", "@types/jest": "29.5.11", diff --git a/scripts/compilation/Inliner.js b/scripts/compilation/Inliner.js index 580a236da389..12d4b2efdb16 100644 --- a/scripts/compilation/Inliner.js +++ b/scripts/compilation/Inliner.js @@ -2,7 +2,10 @@ const fs = require("fs"); const path = require("path"); const { spawnProcess } = require("./../utils/spawn-process"); const walk = require("./../utils/walk"); -const esbuild = require("esbuild"); +const rollup = require("rollup"); +const { nodeResolve } = require("@rollup/plugin-node-resolve"); +const typescript = require("@rollup/plugin-typescript"); +const json = require("@rollup/plugin-json"); const root = path.join(__dirname, "..", ".."); @@ -143,6 +146,8 @@ module.exports = class Inliner { } } + this.variantExternals = [...new Set(this.variantExternals)]; + return this; } @@ -155,30 +160,67 @@ module.exports = class Inliner { return this; } - this.variantExternalsForEsBuild = this.variantExternals.map( - (variant) => "*/" + path.basename(variant).replace(/.js$/, "") - ); + const variantExternalsForRollup = this.variantExternals.map((variant) => variant.replace(/.js$/, "")); + + const entryPoint = path.join(root, this.subfolder, this.package, "src", "index.ts"); + + const inputOptions = (externals) => ({ + input: [entryPoint], + plugins: [ + nodeResolve(), + json(), + typescript({ + compilerOptions: { + importHelpers: true, + noEmitHelpers: false, + module: "esnext", + target: "es2022", + noCheck: true, + removeComments: true, + }, + }), + ], + external: (id) => { + const relative = !!id.match(/^\.?\.?\//); + if (!relative) { + this.verbose && console.log("EXTERN (pkg)", id); + return true; + } - const buildOptions = { - platform: this.platform, - target: ["node18"], - bundle: true, - format: "cjs", - mainFields: ["main"], - allowOverwrite: true, - entryPoints: [path.join(root, this.subfolder, this.package, "src", "index.ts")], - supported: { - "dynamic-import": false, + const local = id.includes(`/packages/`) && id.includes(`/dist-es/`); + if (local) { + this.verbose && console.log("EXTERN (local)", id); + return true; + } + + if (id === entryPoint) { + this.verbose && console.log("INTERN (entry point)", id); + return false; + } + + for (const file of externals) { + const idWithoutExtension = id.replace(/\.ts$/, ""); + if (idWithoutExtension.endsWith(path.basename(file))) { + this.verbose && console.log("EXTERN (variant)", id); + return true; + } + } + + this.verbose && console.log("INTERN (invariant)", id); + return false; }, - outfile: this.outfile, - keepNames: true, - packages: "external", - external: ["@smithy/*", "@aws-sdk/*", "node_modules/*", ...this.variantExternalsForEsBuild], + }); + + const outputOptions = { + dir: path.dirname(this.outfile), + format: "cjs", + exports: "named", + preserveModules: false, }; - if (!this.hasSubmodules) { - await esbuild.build(buildOptions); - } + const bundle = await rollup.rollup(inputOptions(variantExternalsForRollup)); + await bundle.write(outputOptions); + await bundle.close(); if (this.hasSubmodules) { const submodules = fs.readdirSync(path.join(root, this.subfolder, this.package, "src", "submodules")); @@ -206,19 +248,41 @@ module.exports = class Inliner { } } - await esbuild.build({ - ...buildOptions, - entryPoints: [path.join(root, this.subfolder, this.package, "src", "submodules", submodule, "index.ts")], - outfile: path.join(root, this.subfolder, this.package, "dist-cjs", "submodules", submodule, "index.js"), - external: [ - "@smithy/*", - "@aws-sdk/*", - "node_modules/*", - ...this.variantExternals - .filter((variant) => variant.includes(`submodules/${submodule}`)) - .map((variant) => "*/" + path.basename(variant).replace(/.js$/, "")), - ], + // remove remaining empty directories. + const submoduleFolder = path.join(root, this.subfolder, this.package, "dist-cjs", "submodules", submodule); + function rmdirEmpty(dir) { + for (const entry of fs.readdirSync(dir)) { + const fullPath = path.join(dir, entry); + if (fs.lstatSync(fullPath).isDirectory()) { + if (fs.readdirSync(fullPath).length) { + rmdirEmpty(fullPath); + } else { + fs.rmdirSync(fullPath); + } + } + } + } + rmdirEmpty(submoduleFolder); + + const submoduleVariants = variantExternalsForRollup.filter((external) => + external.includes(`submodules/${submodule}`) + ); + + const submoduleOptions = inputOptions(submoduleVariants); + + const submoduleBundle = await rollup.rollup({ + ...submoduleOptions, + input: path.join(root, this.subfolder, this.package, "src", "submodules", submodule, "index.ts"), + }); + + await submoduleBundle.write({ + ...outputOptions, + dir: path.dirname( + path.join(root, this.subfolder, this.package, "dist-cjs", "submodules", submodule, "index.js") + ), }); + + await submoduleBundle.close(); } } @@ -271,7 +335,9 @@ module.exports = class Inliner { .join("/")) + "/index.js"; if (!this.reExportStubs) { - fs.rmSync(file); + if (fs.readFileSync(file, "utf-8").includes(`Object.defineProperty(exports, "__esModule", { value: true });`)) { + fs.rmSync(file); + } const files = fs.readdirSync(path.dirname(file)); if (files.length === 0) { fs.rmdirSync(path.dirname(file)); @@ -463,33 +529,51 @@ module.exports = class Inliner { } // check ESM compat. - const tmpFileContents = this.canonicalExports - .filter((sym) => !sym.includes(":")) - .map((sym) => `import { ${sym} } from "${this.pkgJson.name}";`) - .join("\n"); + const tmpFileContents = + `import assert from "node:assert"; + + const namingExceptions = [ + "paginateOperation" // name for all paginators. + ]; + ` + + this.canonicalExports + .filter((sym) => !sym.includes(":")) + .map((sym) => { + if ( + [ + "AWSSDKSigV4Signer", // deprecated alias for AwsSdkSigV4Signer + "resolveAWSSDKSigV4Config", // deprecated alias for resolveAwsSdkSigV4Config + "__Client", // base Client in SDK clients + "$Command", // base Command in SDK clients + "getDefaultClientConfiguration", // renamed to getDefaultExtensionConfiguration + "generateIdempotencyToken", // sometimes called v4 + "defaultUserAgent", // renamed to createDefaultUserAgentProvider + "getSigV4AuthPlugin", // legacy auth, getAwsAuthPlugin + "NumberValueImpl", // name of NumberValue + + "WorkSpacesThin", // alias of WorkSpacesThinClient + + "HostResolver", // alias of NodeDnsLookupHostResolver + "expectInt", // aliased to expectLong + "handleFloat", // aliased to limitedParseDouble + "limitedParseFloat", // aliased to limitedParseDouble + "strictParseFloat", // aliased to strictParseDouble + "strictParseInt", // aliased to strictParseLong + ].includes(sym) + ) { + return `import { ${sym} } from "${this.pkgJson.name}";`; + } + return `import { ${sym} } from "${this.pkgJson.name}"; +if (typeof ${sym} === "function") { + if (${sym}.name !== "${sym}" && !namingExceptions.includes(${sym}.name)) { + throw new Error(${sym}.name + " does not equal expected ${sym}.") + } +} + `; + }) + .join("\n"); fs.writeFileSync(path.join(__dirname, "tmp", this.package + ".mjs"), tmpFileContents, "utf-8"); await spawnProcess("node", [path.join(__dirname, "tmp", this.package + ".mjs")]); - - if (this.hasSubmodules) { - const submodules = fs.readdirSync(path.join(root, this.subfolder, this.package, "src", "submodules")); - for (const submodule of submodules) { - const canonicalExports = Object.keys( - require(path.join(root, this.subfolder, this.package, "dist-cjs", "submodules", submodule, "index.js")) - ); - const tmpFileContents = canonicalExports - .filter((sym) => !sym.includes(":")) - .map((sym) => `import { ${sym} } from "${this.pkgJson.name}/${submodule}";`) - .join("\n"); - const tmpFilePath = path.join(__dirname, "tmp", this.package + "_" + submodule + ".mjs"); - fs.writeFileSync(tmpFilePath, tmpFileContents, "utf-8"); - await spawnProcess("node", [tmpFilePath]); - fs.rmSync(tmpFilePath); - if (this.verbose) { - console.log("ESM compatibility verified for submodule", submodule); - } - } - } - if (this.verbose) { console.log("ESM compatibility verified."); } diff --git a/yarn.lock b/yarn.lock index 3c584b34df1d..9eb15138eea5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28332,6 +28332,73 @@ __metadata: languageName: node linkType: hard +"@rollup/plugin-json@npm:^6.1.0": + version: 6.1.0 + resolution: "@rollup/plugin-json@npm:6.1.0" + dependencies: + "@rollup/pluginutils": "npm:^5.1.0" + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10c0/9400c431b5e0cf3088ba2eb2d038809a2b0fb2a84ed004997da85582f48cd64958ed3168893c4f2c8109e38652400ed68282d0c92bf8ec07a3b2ef2e1ceab0b7 + languageName: node + linkType: hard + +"@rollup/plugin-node-resolve@npm:^16.0.2": + version: 16.0.2 + resolution: "@rollup/plugin-node-resolve@npm:16.0.2" + dependencies: + "@rollup/pluginutils": "npm:^5.0.1" + "@types/resolve": "npm:1.20.2" + deepmerge: "npm:^4.2.2" + is-module: "npm:^1.0.0" + resolve: "npm:^1.22.1" + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10c0/483b678a78decddb2597abcaf3b7e92c54c296a7362224ed6791bd7d46a7ee1cc0d652c96dadd0ea3b1f30d0f304b2bf7bb02b6748b6ba2067e7c157b8a8ca70 + languageName: node + linkType: hard + +"@rollup/plugin-typescript@npm:^12.1.4": + version: 12.1.4 + resolution: "@rollup/plugin-typescript@npm:12.1.4" + dependencies: + "@rollup/pluginutils": "npm:^5.1.0" + resolve: "npm:^1.22.1" + peerDependencies: + rollup: ^2.14.0||^3.0.0||^4.0.0 + tslib: "*" + typescript: ">=3.7.0" + peerDependenciesMeta: + rollup: + optional: true + tslib: + optional: true + checksum: 10c0/b5bf7f54794d0b33ae5441c5aa202a95beb7068c206f40102f94997e888756c06c2bfe00517eb74a58771078432f94e8a34e99f5c6dbf89a22b49431b83c4798 + languageName: node + linkType: hard + +"@rollup/pluginutils@npm:^5.0.1, @rollup/pluginutils@npm:^5.1.0": + version: 5.3.0 + resolution: "@rollup/pluginutils@npm:5.3.0" + dependencies: + "@types/estree": "npm:^1.0.0" + estree-walker: "npm:^2.0.2" + picomatch: "npm:^4.0.2" + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10c0/001834bf62d7cf5bac424d2617c113f7f7d3b2bf3c1778cbcccb72cdc957b68989f8e7747c782c2b911f1dde8257f56f8ac1e779e29e74e638e3f1e2cac2bcd0 + languageName: node + linkType: hard + "@rollup/rollup-android-arm-eabi@npm:4.46.2": version: 4.46.2 resolution: "@rollup/rollup-android-arm-eabi@npm:4.46.2" @@ -29711,6 +29778,13 @@ __metadata: languageName: node linkType: hard +"@types/resolve@npm:1.20.2": + version: 1.20.2 + resolution: "@types/resolve@npm:1.20.2" + checksum: 10c0/c5b7e1770feb5ccfb6802f6ad82a7b0d50874c99331e0c9b259e415e55a38d7a86ad0901c57665d93f75938be2a6a0bc9aa06c9749192cadb2e4512800bbc6e6 + languageName: node + linkType: hard + "@types/semver@npm:^7.3.12": version: 7.5.8 resolution: "@types/semver@npm:7.5.8" @@ -30917,6 +30991,9 @@ __metadata: "@fastify/formbody": "npm:^7.4.0" "@microsoft/api-extractor": "npm:7.52.7" "@mixer/parallel-prettier": "npm:2.0.3" + "@rollup/plugin-json": "npm:^6.1.0" + "@rollup/plugin-node-resolve": "npm:^16.0.2" + "@rollup/plugin-typescript": "npm:^12.1.4" "@tsconfig/recommended": "npm:1.0.1" "@types/fs-extra": "npm:^8.0.1" "@types/jest": "npm:29.5.11" @@ -33442,6 +33519,13 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^2.0.2": + version: 2.0.2 + resolution: "estree-walker@npm:2.0.2" + checksum: 10c0/53a6c54e2019b8c914dc395890153ffdc2322781acf4bd7d1a32d7aedc1710807bdcd866ac133903d5629ec601fbb50abe8c2e5553c7f5a0afdd9b6af6c945af + languageName: node + linkType: hard + "estree-walker@npm:^3.0.3": version: 3.0.3 resolution: "estree-walker@npm:3.0.3" @@ -35190,6 +35274,13 @@ __metadata: languageName: node linkType: hard +"is-module@npm:^1.0.0": + version: 1.0.0 + resolution: "is-module@npm:1.0.0" + checksum: 10c0/795a3914bcae7c26a1c23a1e5574c42eac13429625045737bf3e324ce865c0601d61aee7a5afbca1bee8cb300c7d9647e7dc98860c9bdbc3b7fdc51d8ac0bffc + languageName: node + linkType: hard + "is-negated-glob@npm:^1.0.0": version: 1.0.0 resolution: "is-negated-glob@npm:1.0.0" @@ -39523,7 +39614,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.9.0, resolve@npm:~1.22.1, resolve@npm:~1.22.2": +"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.9.0, resolve@npm:~1.22.1, resolve@npm:~1.22.2": version: 1.22.10 resolution: "resolve@npm:1.22.10" dependencies: @@ -39546,7 +39637,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.9.0#optional!builtin, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin, resolve@patch:resolve@npm%3A~1.22.2#optional!builtin": +"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.9.0#optional!builtin, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin, resolve@patch:resolve@npm%3A~1.22.2#optional!builtin": version: 1.22.10 resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin::version=1.22.10&hash=c3c19d" dependencies: