diff --git a/.bundlemonrc.cjs b/.bundlemonrc.cjs new file mode 100644 index 000000000..4d2fb8bf1 --- /dev/null +++ b/.bundlemonrc.cjs @@ -0,0 +1,31 @@ +// @ts-check + +const path = require('node:path'); +const { mkdirSync, existsSync } = require('node:fs'); +const { Compression } = require('bundlemon-utils'); + +/** @typedef {import("bundlemon/lib/main/types").Config} Config */ + +const packagePath = path.join(__dirname, 'ember-resources'); +const manifest = require(path.join(packagePath, 'package.json')); + +const jsFiles = Object.values(manifest.exports) + .map(distFile => distFile.replace(/^\.\/dist\//, '')) + .filter(distFile => distFile !== 'addon-main.cjs') + .filter(distFile => distFile !== 'index.js') + .filter(distFile => !distFile.includes('core/')); + +/** @type {Config} */ +module.exports = { + baseDir: path.join(__dirname, './ember-resources/dist'), + groups: [ + { path: 'index.bundled.js', friendlyName: 'index.js' }, + ], + files: jsFiles.map(distFile => ({ path: distFile })), + defaultCompression: Compression.Brotli, + includeCommitMessage: true, + reportOutput: [ + ['json', { fileName: 'bundlemon.json' }], + ['github', { checkRun: true, commitStatus: true, prComment: true }], + ], +}; diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml new file mode 100644 index 000000000..e7644fbc6 --- /dev/null +++ b/.github/workflows/size.yml @@ -0,0 +1,20 @@ +name: Bundle Size + +on: + push: + branches: [main] + pull_request: + types: [synchronize, opened, reopened] + +jobs: + bundle-size: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: NullVoxPopuli/action-setup-pnpm@v2.1.0 + - name: Production Build (not shipped to npm) + run: TERSER=1 pnpm build + - name: Bundle the index.js + run: node ./dev/estimate-bytes/bundle.js + - name: Run BundleMon + run: pnpm bundlemon diff --git a/.gitignore b/.gitignore index a2ebde9f6..f11dfcc1c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ dist/ tmp/ tsconfig.tsbuildinfo +bundlemon.json # dependencies node_modules/ diff --git a/dev/estimate-bytes/bundle.js b/dev/estimate-bytes/bundle.js new file mode 100644 index 000000000..837fcc24d --- /dev/null +++ b/dev/estimate-bytes/bundle.js @@ -0,0 +1,12 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { bundle } from './index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const packagePath = path.join(__dirname, '../../ember-resources'); +const packageJsonPath = path.join(packagePath, 'package.json'); +const packageJson = JSON.parse((await fs.readFile(packageJsonPath)).toString()); + +await bundle(path.join(packagePath, 'dist/index.js'), path.join(packagePath, 'dist/index.bundled.js')); diff --git a/dev/estimate-bytes/index.js b/dev/estimate-bytes/index.js index 13638c2d5..acdb7a97e 100644 --- a/dev/estimate-bytes/index.js +++ b/dev/estimate-bytes/index.js @@ -1,13 +1,16 @@ import fs from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; +import url from 'url'; import * as terser from 'terser'; import { globby } from 'globby'; import esbuild from 'esbuild'; +import { createRequire } from "node:module" import { dir as tmpDir } from 'tmp-promise'; import { gzip } from 'gzip-cli'; import { filesize } from 'filesize'; +const require = createRequire(import.meta.url) const __dirname = path.dirname(fileURLToPath(import.meta.url)); const root = path.join(__dirname, '../..'); const dist = path.join(root, 'ember-resources/dist'); @@ -99,7 +102,9 @@ async function collectStats() { await fs.writeFile(path.join(__dirname, 'comment.txt'), output); } -async function bundle(entry, outFile) { +export async function bundle(entry, outFile) { + packageJson ||= JSON.parse((await fs.readFile(packageJsonPath)).toString()); + let externals = [ ...Object.keys(packageJson.dependencies || {}), ...Object.keys(packageJson.peerDependencies || {}) @@ -162,4 +167,11 @@ async function statsFor(outFile, { tmp }) { return result; } -collectStats(); + +if (import.meta.url.startsWith('file:')) { // (A) + const modulePath = url.fileURLToPath(import.meta.url); + if (process.argv[1] === modulePath) { // (B) + // Main ESM module + collectStats(); + } +} diff --git a/ember-resources/package.json b/ember-resources/package.json index c7a9a23cf..8e89d2ff0 100644 --- a/ember-resources/package.json +++ b/ember-resources/package.json @@ -138,6 +138,7 @@ "@glimmer/tracking": "^1.1.2", "@glint/template": "^1.0.0-beta.3", "@nullvoxpopuli/eslint-configs": "^3.0.0", + "@rollup/plugin-terser": "^0.4.2", "@tsconfig/ember": "^2.0.0", "@types/ember__application": "^4.0.0", "@types/ember__component": "^4.0.8", diff --git a/ember-resources/rollup.config.mjs b/ember-resources/rollup.config.mjs index 3ea65fbf1..772c2c209 100644 --- a/ember-resources/rollup.config.mjs +++ b/ember-resources/rollup.config.mjs @@ -3,6 +3,7 @@ import ts from 'rollup-plugin-ts'; import { Addon } from '@embroider/addon-dev/rollup'; import copy from 'rollup-plugin-copy'; import { defineConfig } from 'rollup'; +import terser from '@rollup/plugin-terser'; const addon = new Addon({ srcDir: 'src', @@ -26,6 +27,18 @@ export default defineConfig({ hoistTransitiveImports: false, }, plugins: [ + // Used for bundle impact estimation + // not shipped to npm. + ...(process.env['TERSER'] + ? [ + terser({ + ecma: 2016, + module: true, + toplevel: true, + }), + ] + : []), + // These are the modules that users should be able to import from your // addon. Anything not listed here may get optimized away. addon.publicEntrypoints(['**/*.ts']), diff --git a/package.json b/package.json index bfe4c648b..dbbf63962 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,8 @@ "@changesets/changelog-github": "^0.4.8", "@changesets/cli": "^2.26.0", "@nullvoxpopuli/eslint-configs": "^3.1.3", + "bundlemon": "^2.0.1", + "bundlemon-utils": "^1.2.0", "concurrently": "^8.0.0", "eslint": "^8.35.0", "loader.js": "^4.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63eeb1b07..841975823 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,12 @@ importers: '@nullvoxpopuli/eslint-configs': specifier: ^3.1.3 version: 3.1.3(@babel/core@7.21.4)(@babel/eslint-parser@7.19.1)(@typescript-eslint/eslint-plugin@5.54.0)(@typescript-eslint/parser@5.54.0)(eslint-config-prettier@8.3.0)(eslint-plugin-ember@11.4.7)(eslint-plugin-qunit@7.3.4)(eslint@8.35.0)(prettier@2.8.4) + bundlemon: + specifier: ^2.0.1 + version: 2.0.1 + bundlemon-utils: + specifier: ^1.2.0 + version: 1.2.0 concurrently: specifier: ^8.0.0 version: 8.0.0 @@ -157,6 +163,9 @@ importers: '@nullvoxpopuli/eslint-configs': specifier: ^3.0.0 version: 3.1.3(@babel/core@7.21.4)(@babel/eslint-parser@7.19.1)(@typescript-eslint/eslint-plugin@5.54.0)(@typescript-eslint/parser@5.54.0)(eslint-config-prettier@8.3.0)(eslint-plugin-ember@11.4.7)(eslint-plugin-qunit@7.3.4)(eslint@8.35.0)(prettier@2.8.4) + '@rollup/plugin-terser': + specifier: ^0.4.2 + version: 0.4.2(rollup@3.21.7) '@tsconfig/ember': specifier: ^2.0.0 version: 2.0.0 @@ -3883,6 +3892,21 @@ packages: tslib: 2.5.0 dev: true + /@rollup/plugin-terser@0.4.2(rollup@3.21.7): + resolution: {integrity: sha512-jfUVQ4MxzIB0mz8QhDA1xiLT+pTF3WEWXeIqcwhoF84WhLWscPpxjJgjYMyAq0Po4UXqw2D9C64tD0gRDzJzfA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.x || ^3.x + peerDependenciesMeta: + rollup: + optional: true + dependencies: + rollup: 3.21.7 + serialize-javascript: 6.0.1 + smob: 0.0.6 + terser: 5.17.1 + dev: true + /@rollup/pluginutils@4.2.1: resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} @@ -4465,6 +4489,10 @@ packages: '@types/node': 18.16.0 dev: true + /@types/lodash@4.14.194: + resolution: {integrity: sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==} + dev: true + /@types/mime@3.0.1: resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} dev: true @@ -4502,6 +4530,10 @@ packages: resolution: {integrity: sha512-219LSCO9HPcoXcRTC6DbCs0FRhZgBnEMzf16RRqkT40WbkKx3mOeQuz3e2XqbfhOz/AHfbru0kzB1n1RCAsIIg==} dev: true + /@types/parse-json@4.0.0: + resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + dev: true + /@types/qs@6.9.7: resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} dev: true @@ -5332,6 +5364,23 @@ packages: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} + /axios-retry@3.4.0: + resolution: {integrity: sha512-VdgaP+gHH4iQYCCNUWF2pcqeciVOdGrBBAYUfTY+wPcO5Ltvp/37MLFNCmJKo7Gj3SHvCSdL8ouI1qLYJN3liA==} + dependencies: + '@babel/runtime': 7.21.0 + is-retry-allowed: 2.2.0 + dev: true + + /axios@1.4.0: + resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==} + dependencies: + follow-redirects: 1.15.2 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: true + /babel-code-frame@6.26.0: resolution: {integrity: sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==} dependencies: @@ -6559,6 +6608,13 @@ packages: - supports-color dev: true + /brotli-size@4.0.0: + resolution: {integrity: sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==} + engines: {node: '>= 10.16.0'} + dependencies: + duplexer: 0.1.1 + dev: true + /browser-process-hrtime@1.0.0: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} dev: true @@ -6619,6 +6675,33 @@ packages: semver: 7.5.0 dev: true + /bundlemon-utils@1.2.0: + resolution: {integrity: sha512-yC8DPG8y6WyBxLYbwRs6yydPtcsWfgGmbUI8+LDPUa+zcMVCGSZOszysJ7SJGlnN7dI7PW+8Ed7RtWpOZI2hOQ==} + engines: {node: '>=14.16'} + dependencies: + bytes: 3.1.2 + dev: true + + /bundlemon@2.0.1: + resolution: {integrity: sha512-Mo3A9y6hdLjBPZd3O57xz4zJ0F9DMHiCL9dWdjwAn6mWISUSeaJF1fsNsKTGmOy/jEMzWHvkuidm0tFL0Fq04g==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + axios: 1.4.0 + axios-retry: 3.4.0 + brotli-size: 4.0.0 + bundlemon-utils: 1.2.0 + bytes: 3.1.2 + chalk: 4.1.2 + commander: 9.5.0 + cosmiconfig: 7.1.0 + gzip-size: 6.0.0 + micromatch: 4.0.5 + yup: 0.32.11 + transitivePeerDependencies: + - debug + dev: true + /bytes@1.0.0: resolution: {integrity: sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ==} dev: true @@ -6980,6 +7063,11 @@ packages: engines: {node: '>= 12'} dev: true + /commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + dev: true + /common-tags@1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} @@ -7341,6 +7429,17 @@ packages: vary: 1.1.2 dev: true + /cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + /cosmiconfig@8.1.3: resolution: {integrity: sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==} engines: {node: '>=14'} @@ -7745,6 +7844,14 @@ packages: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} dev: true + /duplexer@0.1.1: + resolution: {integrity: sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==} + dev: true + + /duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + dev: true + /editions@1.3.4: resolution: {integrity: sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==} engines: {node: '>=0.8'} @@ -10216,6 +10323,15 @@ packages: mime-types: 2.1.35 dev: true + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + /forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -10669,6 +10785,13 @@ packages: minimist: 1.2.8 dev: true + /gzip-size@6.0.0: + resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} + engines: {node: '>=10'} + dependencies: + duplexer: 0.1.2 + dev: true + /handlebars@4.7.7: resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} engines: {node: '>=0.4.7'} @@ -11343,6 +11466,11 @@ packages: call-bind: 1.0.2 has-tostringtag: 1.0.0 + /is-retry-allowed@2.2.0: + resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} + engines: {node: '>=10'} + dev: true + /is-set@2.0.2: resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} dev: true @@ -11811,6 +11939,10 @@ packages: p-locate: 6.0.0 dev: true + /lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: true + /lodash._baseassign@3.2.0: resolution: {integrity: sha512-t3N26QR2IdSN+gqSy9Ds9pBu/J1EAFEshKlUHpJG3rvyJOYgcELIxcIeKKfZk7sjOz11cFfzJRsyFry/JyabJQ==} dependencies: @@ -12457,6 +12589,10 @@ packages: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} dev: true + /nanoclone@0.2.1: + resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==} + dev: true + /nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -13265,6 +13401,10 @@ packages: signal-exit: 3.0.7 dev: true + /property-expr@2.0.5: + resolution: {integrity: sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==} + dev: true + /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -13273,6 +13413,10 @@ packages: ipaddr.js: 1.9.1 dev: true + /proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: true + /pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true @@ -14128,6 +14272,10 @@ packages: yargs: 15.4.1 dev: true + /smob@0.0.6: + resolution: {integrity: sha512-V21+XeNni+tTyiST1MHsa84AQhT1aFZipzPpOFAVB8DkHzwJyjjAmt9bgwnuZiZWnIbMo2duE29wybxv/7HWUw==} + dev: true + /snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} dependencies: @@ -14915,6 +15063,10 @@ packages: engines: {node: '>=0.6'} dev: true + /toposort@2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + dev: true + /tough-cookie@4.1.2: resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} engines: {node: '>=6'} @@ -15828,6 +15980,11 @@ packages: lodash.merge: 4.6.2 dev: true + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + /yaml@2.2.2: resolution: {integrity: sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==} engines: {node: '>= 14'} @@ -15885,6 +16042,19 @@ packages: engines: {node: '>=12.20'} dev: true + /yup@0.32.11: + resolution: {integrity: sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==} + engines: {node: '>=10'} + dependencies: + '@babel/runtime': 7.21.0 + '@types/lodash': 4.14.194 + lodash: 4.17.21 + lodash-es: 4.17.21 + nanoclone: 0.2.1 + property-expr: 2.0.5 + toposort: 2.0.2 + dev: true + file:ember-resources(@ember/test-waiters@3.0.2)(@glimmer/component@1.1.2)(@glimmer/tracking@1.1.2)(@glint/template@1.0.0-beta.3)(ember-concurrency@2.3.7)(ember-source@5.0.0): resolution: {directory: ember-resources, type: directory} id: file:ember-resources