diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index bc94a7a980..15d4f3abf0 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -52,11 +52,11 @@ jobs:
os: [ubuntu-latest, macOS-latest, windows-latest]
node-version: ['*']
# Must include the minimum deno version from the `DENO_VERSION_RANGE` constant in `node/bridge.ts`.
- deno-version: ['v1.37.0', 'v1.44.4']
+ deno-version: ['v1.39.0', 'v1.46.3']
include:
- os: ubuntu-latest
node-version: '14.16.0'
- deno-version: 'v1.44.4'
+ deno-version: 'v1.46.3'
fail-fast: false
steps:
# Increasing the maximum number of open files. See:
@@ -192,7 +192,7 @@ jobs:
- name: Setup Deno
uses: denoland/setup-deno@v1
with:
- deno-version: v1.44.4
+ deno-version: v1.46.3
if: ${{ !steps.release-check.outputs.IS_RELEASE }}
- name: Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index afc3eb64d7..3373fd9e33 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,11 +1,11 @@
{
- "packages/build-info": "7.14.3",
- "packages/build": "29.54.4",
+ "packages/build-info": "7.15.2",
+ "packages/build": "29.56.1",
"packages/edge-bundler": "12.2.3",
"packages/cache-utils": "5.1.6",
- "packages/config": "20.19.0",
+ "packages/config": "20.19.1",
"packages/framework-info": "9.8.13",
- "packages/functions-utils": "5.2.83",
+ "packages/functions-utils": "5.2.93",
"packages/git-utils": "5.1.1",
"packages/headers-parser": "7.1.4",
"packages/js-client": "13.1.21",
@@ -14,5 +14,5 @@
"packages/run-utils": "5.1.1",
"packages/opentelemetry-sdk-setup": "1.1.3",
"packages/opentelemetry-utils": "1.2.1",
- "packages/zip-it-and-ship-it": "9.39.2"
+ "packages/zip-it-and-ship-it": "9.41.1"
}
diff --git a/package-lock.json b/package-lock.json
index 6ff818c3e5..8731f30c43 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -300,18 +300,18 @@
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.24.8",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
- "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
- "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -399,12 +399,12 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.25.6",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz",
- "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==",
+ "version": "7.26.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
+ "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.25.6"
+ "@babel/types": "^7.26.3"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -458,14 +458,13 @@
}
},
"node_modules/@babel/types": {
- "version": "7.25.6",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz",
- "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==",
+ "version": "7.26.3",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
+ "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
"license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.24.8",
- "@babel/helper-validator-identifier": "^7.24.7",
- "to-fast-properties": "^2.0.0"
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -1560,10 +1559,11 @@
}
},
"node_modules/@eslint/js": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
- "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
+ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
@@ -1678,12 +1678,14 @@
}
},
"node_modules/@humanwhocodes/config-array": {
- "version": "0.11.14",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
- "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
+ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
+ "deprecated": "Use @eslint/config-array instead",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "@humanwhocodes/object-schema": "^2.0.2",
+ "@humanwhocodes/object-schema": "^2.0.3",
"debug": "^4.3.1",
"minimatch": "^3.0.5"
},
@@ -6675,9 +6677,9 @@
}
},
"node_modules/@netlify/open-api": {
- "version": "2.34.0",
- "resolved": "https://registry.npmjs.org/@netlify/open-api/-/open-api-2.34.0.tgz",
- "integrity": "sha512-C4v7Od/vnGgZ1P4JK3Fn9uUi9HkTxeUqUtj4OLnGD+rGyaVrl4JY89xMCoVksijDtO8XylYFU59CSTnQNeNw7g==",
+ "version": "2.35.0",
+ "resolved": "https://registry.npmjs.org/@netlify/open-api/-/open-api-2.35.0.tgz",
+ "integrity": "sha512-c6LpV29CKMgq6/eViItE6L2ova9UldBO9tHRvvwpJfSBgCwWaFhmiepe07E3xIW4GfTCGoWE816mNzXB/2ceZg==",
"license": "MIT",
"engines": {
"node": ">=14"
@@ -6705,9 +6707,9 @@
"link": true
},
"node_modules/@netlify/serverless-functions-api": {
- "version": "1.24.0",
- "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.24.0.tgz",
- "integrity": "sha512-W5LL8EJaZWmPXtjk99C/C6HOEtkQvtBgScQIVNgYPjATyGnYWo8KqJJ45zKxsLuby7jO1+YsRijBJm0nNRR3Nw==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.31.1.tgz",
+ "integrity": "sha512-SkNxzfCwctS5ETnCqJOJfZZ/jB0pTkbWEAsApHoL7HzUQGWoRM6wYf4baJAJVMTfZBQu534SbKuwRs7WDAs43A==",
"license": "MIT",
"dependencies": {
"@netlify/node-cookies": "^0.1.0",
@@ -8429,13 +8431,13 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.47.1",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.1.tgz",
- "integrity": "sha512-dbWpcNQZ5nj16m+A5UNScYx7HX5trIy7g4phrcitn+Nk83S32EBX/CLU4hiF4RGKX/yRc93AAqtfaXB7JWBd4Q==",
+ "version": "1.49.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz",
+ "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright": "1.47.1"
+ "playwright": "1.49.1"
},
"bin": {
"playwright": "cli.js"
@@ -9099,12 +9101,12 @@
"license": "ISC"
},
"node_modules/@vercel/nft": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.27.4.tgz",
- "integrity": "sha512-Rioz3LJkEKicKCi9BSyc1RXZ5R6GmXosFMeBSThh6msWSOiArKhb7c75MiWwZEgPL7x0/l3TAfH/l0cxKNuUFA==",
+ "version": "0.27.6",
+ "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.27.6.tgz",
+ "integrity": "sha512-mwuyUxskdcV8dd7N7JnxBgvFEz1D9UOePI/WyLLzktv6HSCwgPNQGit/UJ2IykAWGlypKw4pBQjOKWvIbXITSg==",
"license": "MIT",
"dependencies": {
- "@mapbox/node-pre-gyp": "^1.0.5",
+ "@mapbox/node-pre-gyp": "^1.0.11",
"@rollup/pluginutils": "^4.0.0",
"acorn": "^8.6.0",
"acorn-import-attributes": "^1.9.5",
@@ -9113,7 +9115,7 @@
"estree-walker": "2.0.2",
"glob": "^7.1.3",
"graceful-fs": "^4.2.9",
- "micromatch": "^4.0.2",
+ "micromatch": "^4.0.8",
"node-gyp-build": "^4.2.2",
"resolve-from": "^5.0.0"
},
@@ -10450,12 +10452,14 @@
"license": "ISC"
},
"node_modules/babel-loader": {
- "version": "8.3.0",
+ "version": "8.4.1",
+ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz",
+ "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"find-cache-dir": "^3.3.1",
- "loader-utils": "^2.0.0",
+ "loader-utils": "^2.0.4",
"make-dir": "^3.1.0",
"schema-utils": "^2.6.5"
},
@@ -14461,16 +14465,17 @@
}
},
"node_modules/eslint": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
- "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
+ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.4",
- "@eslint/js": "8.57.0",
- "@humanwhocodes/config-array": "^0.11.14",
+ "@eslint/js": "8.57.1",
+ "@humanwhocodes/config-array": "^0.13.0",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"@ungap/structured-clone": "^1.2.0",
@@ -14717,9 +14722,9 @@
}
},
"node_modules/eslint-module-utils": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz",
- "integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==",
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
+ "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -14743,9 +14748,9 @@
}
},
"node_modules/eslint-plugin-import": {
- "version": "2.30.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz",
- "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==",
+ "version": "2.31.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz",
+ "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -14757,7 +14762,7 @@
"debug": "^3.2.7",
"doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.9",
- "eslint-module-utils": "^2.9.0",
+ "eslint-module-utils": "^2.12.0",
"hasown": "^2.0.2",
"is-core-module": "^2.15.1",
"is-glob": "^4.0.3",
@@ -14766,13 +14771,14 @@
"object.groupby": "^1.0.3",
"object.values": "^1.2.0",
"semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.8",
"tsconfig-paths": "^3.15.0"
},
"engines": {
"node": ">=4"
},
"peerDependencies": {
- "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
}
},
"node_modules/eslint-plugin-import/node_modules/brace-expansion": {
@@ -14864,6 +14870,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -14880,6 +14887,7 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -14889,6 +14897,7 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -14904,6 +14913,7 @@
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -14914,6 +14924,7 @@
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -14930,6 +14941,7 @@
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -14942,6 +14954,7 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -14954,6 +14967,7 @@
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@@ -14970,6 +14984,7 @@
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
}
@@ -14979,6 +14994,7 @@
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
@@ -14995,6 +15011,7 @@
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -15007,6 +15024,7 @@
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -15015,13 +15033,15 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/eslint/node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"p-locate": "^5.0.0"
},
@@ -15037,6 +15057,7 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -15049,6 +15070,7 @@
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -15064,6 +15086,7 @@
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"p-limit": "^3.0.2"
},
@@ -15079,6 +15102,7 @@
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -15088,6 +15112,7 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -15100,6 +15125,7 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -15112,6 +15138,7 @@
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -15341,9 +15368,9 @@
}
},
"node_modules/fdir": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.3.0.tgz",
- "integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==",
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz",
+ "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==",
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
@@ -16800,7 +16827,9 @@
}
},
"node_modules/hot-shots": {
- "version": "10.0.0",
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-10.2.1.tgz",
+ "integrity": "sha512-tmjcyZkG/qADhcdC7UjAp8D7v7W2DOYFgaZ48fYMuayMQmVVUg8fntKmrjes/b40ef6yZ+qt1lB8kuEDfLC4zw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -20065,9 +20094,9 @@
}
},
"node_modules/nock": {
- "version": "13.5.5",
- "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.5.tgz",
- "integrity": "sha512-XKYnqUrCwXC8DGG1xX4YH5yNIrlh9c065uaMZZHUoeUUINTOyt+x/G+ezYk0Ft6ExSREVIs+qBJDK503viTfFA==",
+ "version": "13.5.6",
+ "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz",
+ "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -22006,13 +22035,13 @@
}
},
"node_modules/playwright": {
- "version": "1.47.1",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.1.tgz",
- "integrity": "sha512-SUEKi6947IqYbKxRiqnbUobVZY4bF1uu+ZnZNJX9DfU1tlf2UhWfvVjLf01pQx9URsOr18bFVUKXmanYWhbfkw==",
+ "version": "1.49.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz",
+ "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.47.1"
+ "playwright-core": "1.49.1"
},
"bin": {
"playwright": "cli.js"
@@ -22025,9 +22054,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.47.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.1.tgz",
- "integrity": "sha512-i1iyJdLftqtt51mEk6AhYFaAJCDx0xQ/O5NU8EKaWFgMjItPVma542Nh/Aq8aLCjIJSzjaiEQGW/nyqLkGF1OQ==",
+ "version": "1.49.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz",
+ "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -22437,9 +22466,9 @@
}
},
"node_modules/qs": {
- "version": "6.13.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
- "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+ "version": "6.13.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz",
+ "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
@@ -24533,13 +24562,6 @@
"integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==",
"dev": true
},
- "node_modules/to-fast-properties": {
- "version": "2.0.0",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -24910,9 +24932,9 @@
}
},
"node_modules/typescript": {
- "version": "5.6.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
- "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
+ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -25181,10 +25203,11 @@
}
},
"node_modules/vite": {
- "version": "4.5.3",
- "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
- "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
+ "version": "4.5.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.5.tgz",
+ "integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"esbuild": "^0.18.10",
"postcss": "^8.4.27",
@@ -25266,6 +25289,7 @@
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -25280,6 +25304,7 @@
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
"dev": true,
"hasInstallScript": true,
+ "license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
@@ -26087,9 +26112,9 @@
"license": "ISC"
},
"node_modules/yaml": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
- "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
+ "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
@@ -26312,30 +26337,31 @@
}
},
"node_modules/zod": {
- "version": "3.23.8",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
- "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
+ "version": "3.24.1",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
+ "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"packages/build": {
"name": "@netlify/build",
- "version": "29.54.4",
+ "version": "29.56.1",
"license": "MIT",
"dependencies": {
"@bugsnag/js": "^7.0.0",
"@netlify/blobs": "^7.4.0",
"@netlify/cache-utils": "^5.1.6",
- "@netlify/config": "^20.19.0",
+ "@netlify/config": "^20.19.1",
"@netlify/edge-bundler": "12.2.3",
"@netlify/framework-info": "^9.8.13",
- "@netlify/functions-utils": "^5.2.83",
+ "@netlify/functions-utils": "^5.2.93",
"@netlify/git-utils": "^5.1.1",
"@netlify/opentelemetry-utils": "^1.2.1",
"@netlify/plugins-list": "^6.80.0",
"@netlify/run-utils": "^5.1.1",
- "@netlify/zip-it-and-ship-it": "9.39.2",
+ "@netlify/zip-it-and-ship-it": "9.41.1",
"@sindresorhus/slugify": "^2.0.0",
"ansi-escapes": "^6.0.0",
"chalk": "^5.0.0",
@@ -26345,7 +26371,7 @@
"figures": "^5.0.0",
"filter-obj": "^5.0.0",
"got": "^12.0.0",
- "hot-shots": "10.0.0",
+ "hot-shots": "10.2.1",
"indent-string": "^5.0.0",
"is-plain-obj": "^4.0.0",
"js-yaml": "^4.0.0",
@@ -26428,7 +26454,7 @@
},
"packages/build-info": {
"name": "@netlify/build-info",
- "version": "7.14.3",
+ "version": "7.15.2",
"license": "MIT",
"dependencies": {
"@bugsnag/js": "^7.20.0",
@@ -26438,7 +26464,7 @@
"minimatch": "^9.0.0",
"read-pkg": "^7.1.0",
"semver": "^7.3.8",
- "yaml": "^2.1.3",
+ "yaml": "^2.6.1",
"yargs": "^17.6.0"
},
"bin": {
@@ -26643,7 +26669,7 @@
},
"packages/config": {
"name": "@netlify/config",
- "version": "20.19.0",
+ "version": "20.19.1",
"license": "MIT",
"dependencies": {
"@iarna/toml": "^2.2.5",
@@ -27133,10 +27159,10 @@
},
"packages/functions-utils": {
"name": "@netlify/functions-utils",
- "version": "5.2.83",
+ "version": "5.2.93",
"license": "MIT",
"dependencies": {
- "@netlify/zip-it-and-ship-it": "9.39.2",
+ "@netlify/zip-it-and-ship-it": "9.41.1",
"cpy": "^9.0.0",
"path-exists": "^5.0.0"
},
@@ -27271,7 +27297,7 @@
"version": "13.1.21",
"license": "MIT",
"dependencies": {
- "@netlify/open-api": "^2.34.0",
+ "@netlify/open-api": "^2.35.0",
"lodash-es": "^4.17.21",
"micro-api-client": "^3.3.0",
"node-fetch": "^3.0.0",
@@ -27588,13 +27614,13 @@
},
"packages/zip-it-and-ship-it": {
"name": "@netlify/zip-it-and-ship-it",
- "version": "9.39.2",
+ "version": "9.41.1",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.22.5",
- "@babel/types": "7.25.6",
+ "@babel/parser": "^7.26.3",
+ "@babel/types": "7.26.3",
"@netlify/binary-info": "^1.0.0",
- "@netlify/serverless-functions-api": "^1.24.0",
+ "@netlify/serverless-functions-api": "^1.31.1",
"@vercel/nft": "^0.27.1",
"archiver": "^7.0.0",
"common-path-prefix": "^3.0.0",
diff --git a/packages/build-info/CHANGELOG.md b/packages/build-info/CHANGELOG.md
index 7b4c441ee3..e7e975bb48 100644
--- a/packages/build-info/CHANGELOG.md
+++ b/packages/build-info/CHANGELOG.md
@@ -64,6 +64,35 @@
* dependencies
* @netlify/framework-info bumped from ^9.8.8 to ^9.8.9
+## [7.15.2](https://github.com/netlify/build/compare/build-info-v7.15.1...build-info-v7.15.2) (2024-10-25)
+
+
+### Bug Fixes
+
+* detect next if typescript config file is used ([#5893](https://github.com/netlify/build/issues/5893)) ([d07e0d5](https://github.com/netlify/build/commit/d07e0d52bf1e3d8410625e216e08914b4f0ae8cc))
+
+## [7.15.1](https://github.com/netlify/build/compare/build-info-v7.15.0...build-info-v7.15.1) (2024-10-01)
+
+
+### Bug Fixes
+
+* add some support for dotnet runtime detection ([#5858](https://github.com/netlify/build/issues/5858)) ([75eb6a2](https://github.com/netlify/build/commit/75eb6a2a5f902320dd70a808ec30256fcd8df29a))
+* framework detection for solidjs and solidstart ([#5856](https://github.com/netlify/build/issues/5856)) ([2002b42](https://github.com/netlify/build/commit/2002b42751bc0bd120086ab32c685f9bf6703237))
+
+## [7.15.0](https://github.com/netlify/build/compare/build-info-v7.14.4...build-info-v7.15.0) (2024-10-01)
+
+
+### Features
+
+* framework detection for tanstack router and tanstack start ([#5857](https://github.com/netlify/build/issues/5857)) ([1aa78f4](https://github.com/netlify/build/commit/1aa78f45de2a97001083bb3a29eceb2afc5064f7))
+
+## [7.14.4](https://github.com/netlify/build/compare/build-info-v7.14.3...build-info-v7.14.4) (2024-09-23)
+
+
+### Bug Fixes
+
+* set default nodejs version for redwoodjs ([#5842](https://github.com/netlify/build/issues/5842)) ([a3dfb7e](https://github.com/netlify/build/commit/a3dfb7e9664a9968e7575e43ab653a089a4ede0b))
+
## [7.14.3](https://github.com/netlify/build/compare/build-info-v7.14.2...build-info-v7.14.3) (2024-09-20)
diff --git a/packages/build-info/assets/logos/solid-start/dark.svg b/packages/build-info/assets/logos/solid-start/dark.svg
new file mode 100644
index 0000000000..fc61e36c94
--- /dev/null
+++ b/packages/build-info/assets/logos/solid-start/dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/build-info/assets/logos/tanstack-router/default.png b/packages/build-info/assets/logos/tanstack-router/default.png
new file mode 100644
index 0000000000..ec5d621427
Binary files /dev/null and b/packages/build-info/assets/logos/tanstack-router/default.png differ
diff --git a/packages/build-info/assets/logos/tanstack-start/default.png b/packages/build-info/assets/logos/tanstack-start/default.png
new file mode 100644
index 0000000000..ec5d621427
Binary files /dev/null and b/packages/build-info/assets/logos/tanstack-start/default.png differ
diff --git a/packages/build-info/package.json b/packages/build-info/package.json
index eb08f999d0..943fd21bf7 100644
--- a/packages/build-info/package.json
+++ b/packages/build-info/package.json
@@ -1,6 +1,6 @@
{
"name": "@netlify/build-info",
- "version": "7.14.3",
+ "version": "7.15.2",
"description": "Build info utility",
"type": "module",
"exports": {
diff --git a/packages/build-info/src/frameworks/index.ts b/packages/build-info/src/frameworks/index.ts
index 624ddd522b..9cd1b52edc 100644
--- a/packages/build-info/src/frameworks/index.ts
+++ b/packages/build-info/src/frameworks/index.ts
@@ -34,11 +34,13 @@ import { RedwoodJS } from './redwoodjs.js'
import { Remix } from './remix.js'
import { Roots } from './roots.js'
import { Sapper } from './sapper.js'
+import { SolidJs } from './solid-js.js'
import { SolidStart } from './solid-start.js'
-import { Solid } from './solid.js'
import { Stencil } from './stencil.js'
import { SvelteKit } from './svelte-kit.js'
import { Svelte } from './svelte.js'
+import { TanStackRouter } from './tanstack-router.js'
+import { TanStackStart } from './tanstack-start.js'
import { Vite } from './vite.js'
import { Vue } from './vue.js'
import { VuePress } from './vuepress.js'
@@ -66,9 +68,11 @@ export const frameworks = [
ReactStatic,
RedwoodJS,
Remix,
- Solid,
+ SolidJs,
SolidStart,
Stencil,
+ TanStackRouter,
+ TanStackStart,
VuePress,
Assemble,
DocPad,
diff --git a/packages/build-info/src/frameworks/next.ts b/packages/build-info/src/frameworks/next.ts
index be9c0bc2fc..15eee5c62e 100644
--- a/packages/build-info/src/frameworks/next.ts
+++ b/packages/build-info/src/frameworks/next.ts
@@ -8,7 +8,7 @@ export class Next extends BaseFramework implements Framework {
category = Category.SSG
npmDependencies = ['next']
excludedNpmDependencies = ['@nrwl/next']
- configFiles = ['next.config.js', 'next.config.mjs']
+ configFiles = ['next.config.js', 'next.config.mjs', 'next.config.ts']
dev = {
command: 'next',
diff --git a/packages/build-info/src/frameworks/solid-js.test.ts b/packages/build-info/src/frameworks/solid-js.test.ts
new file mode 100644
index 0000000000..3b703fbcff
--- /dev/null
+++ b/packages/build-info/src/frameworks/solid-js.test.ts
@@ -0,0 +1,42 @@
+import { beforeEach, expect, test } from 'vitest'
+
+import { mockFileSystem } from '../../tests/mock-file-system.js'
+import { NodeFS } from '../node/file-system.js'
+import { Project } from '../project.js'
+
+beforeEach((ctx) => {
+ ctx.fs = new NodeFS()
+})
+
+test('detects a Solid.js site', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'package.json': JSON.stringify({
+ scripts: {
+ start: 'vite',
+ dev: 'vite',
+ build: 'vite build',
+ serve: 'vite preview',
+ },
+ dependencies: {
+ 'solid-js': '^1.8.11',
+ },
+ devDependencies: {
+ 'solid-devtools': '^0.29.2',
+ vite: '^5.0.11',
+ 'vite-plugin-solid': '^2.8.2',
+ },
+ }),
+ })
+ const detected = await new Project(fs, cwd).detectFrameworks()
+
+ const detectedFrameworks = (detected ?? []).map((framework) => framework.id)
+ expect(detectedFrameworks).not.toContain('vinxi')
+ expect(detectedFrameworks).not.toContain('vite')
+ expect(detectedFrameworks).not.toContain('solid-start')
+
+ expect(detected?.[0]?.id).toBe('solid-js')
+ expect(detected?.[0]?.build?.command).toBe('vite build')
+ expect(detected?.[0]?.build?.directory).toBe('dist')
+ expect(detected?.[0]?.dev?.command).toBe('vite dev')
+ expect(detected?.[0]?.dev?.port).toBe(3000)
+})
diff --git a/packages/build-info/src/frameworks/solid.ts b/packages/build-info/src/frameworks/solid-js.ts
similarity index 66%
rename from packages/build-info/src/frameworks/solid.ts
rename to packages/build-info/src/frameworks/solid-js.ts
index 36b11ac9f8..93da954c6f 100644
--- a/packages/build-info/src/frameworks/solid.ts
+++ b/packages/build-info/src/frameworks/solid-js.ts
@@ -1,21 +1,21 @@
import { BaseFramework, Category, Framework } from './framework.js'
-export class Solid extends BaseFramework implements Framework {
+export class SolidJs extends BaseFramework implements Framework {
readonly id = 'solid-js'
name = 'SolidJS'
npmDependencies = ['solid-js']
- excludedNpmDependencies = ['solid-start']
+ excludedNpmDependencies = ['solid-start', '@solidjs/start']
category = Category.SSG
dev = {
- command: 'npm run dev',
+ command: 'vite dev',
port: 3000,
pollingStrategies: [{ name: 'TCP' }],
}
build = {
- command: 'npm run build',
- directory: 'netlify',
+ command: 'vite build',
+ directory: 'dist',
}
logo = {
diff --git a/packages/build-info/src/frameworks/solid-start.test.ts b/packages/build-info/src/frameworks/solid-start.test.ts
new file mode 100644
index 0000000000..d427f23067
--- /dev/null
+++ b/packages/build-info/src/frameworks/solid-start.test.ts
@@ -0,0 +1,76 @@
+import { beforeEach, expect, test } from 'vitest'
+
+import { mockFileSystem } from '../../tests/mock-file-system.js'
+import { NodeFS } from '../node/file-system.js'
+import { Project } from '../project.js'
+
+beforeEach((ctx) => {
+ ctx.fs = new NodeFS()
+})
+
+test('detects and configures a SolidStart site that uses the old `solid-start` package', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'package.json': JSON.stringify({
+ scripts: {
+ dev: 'solid-start dev',
+ build: 'solid-start build',
+ start: 'solid-start start',
+ },
+ dependencies: {
+ '@solidjs/meta': '^0.28.5',
+ '@solidjs/router': '^0.8.2',
+ 'solid-js': '^1.7.8',
+ 'solid-start': '^0.2.26',
+ },
+ devDependencies: {
+ 'solid-start-netlify': '^0.2.26',
+ 'solid-start-node': '^0.2.26',
+ vite: '3.2.5',
+ },
+ }),
+ })
+ const detected = await new Project(fs, cwd).detectFrameworks()
+
+ const detectedFrameworks = (detected ?? []).map((framework) => framework.id)
+ expect(detectedFrameworks).not.toContain('vinxi')
+ expect(detectedFrameworks).not.toContain('vite')
+ expect(detectedFrameworks).not.toContain('solidjs')
+
+ expect(detected?.[0]?.id).toBe('solid-start')
+ expect(detected?.[0]?.build?.command).toBe('solid-start build')
+ expect(detected?.[0]?.build?.directory).toBe('netlify')
+ expect(detected?.[0]?.dev?.command).toBe('solid-start dev')
+ expect(detected?.[0]?.dev?.port).toBe(3000)
+})
+
+test('detects and configures a SolidStart site that uses the new `@solidjs/start` package', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'package.json': JSON.stringify({
+ scripts: {
+ dev: 'vinxi dev',
+ build: 'vinxi build',
+ start: 'vinxi start',
+ },
+ dependencies: {
+ '@solidjs/router': '^0.14.1',
+ '@solidjs/start': '^1.0.6',
+ 'solid-js': '^1.8.18',
+ },
+ devDependencies: {
+ vinxi: '^0.4.1',
+ },
+ }),
+ })
+ const detected = await new Project(fs, cwd).detectFrameworks()
+
+ const detectedFrameworks = (detected ?? []).map((framework) => framework.id)
+ expect(detectedFrameworks).not.toContain('vinxi')
+ expect(detectedFrameworks).not.toContain('vite')
+ expect(detectedFrameworks).not.toContain('solidjs')
+
+ expect(detected?.[0]?.id).toBe('solid-start')
+ expect(detected?.[0]?.build?.command).toBe('vinxi build')
+ expect(detected?.[0]?.build?.directory).toBe('dist')
+ expect(detected?.[0]?.dev?.command).toBe('vinxi dev')
+ expect(detected?.[0]?.dev?.port).toBe(3000)
+})
diff --git a/packages/build-info/src/frameworks/solid-start.ts b/packages/build-info/src/frameworks/solid-start.ts
index 7b290b8c5d..fe78c161a0 100644
--- a/packages/build-info/src/frameworks/solid-start.ts
+++ b/packages/build-info/src/frameworks/solid-start.ts
@@ -1,25 +1,52 @@
-import { BaseFramework, Category, Framework } from './framework.js'
+import { BaseFramework, Category, type DetectedFramework, type Framework } from './framework.js'
+
+const LEGACY_PACKAGE_NAME = 'solid-start'
+const LEGACY_DEV = {
+ command: 'solid-start dev',
+ port: 3000,
+}
+const LEGACY_BUILD = {
+ command: 'solid-start build',
+ directory: 'netlify',
+}
export class SolidStart extends BaseFramework implements Framework {
readonly id = 'solid-start'
name = 'Solid Start'
- npmDependencies = ['solid-start']
+ npmDependencies = [
+ // Used this name up to 0.3.11
+ 'solid-start',
+ // Renamed starting at 0.4.0
+ '@solidjs/start',
+ ]
category = Category.SSG
dev = {
- command: 'solid-start dev',
+ command: 'vinxi dev',
port: 3000,
- pollingStrategies: [{ name: 'TCP' }],
}
build = {
- command: 'solid-start build',
- directory: 'netlify',
+ command: 'vinxi build',
+ directory: 'dist',
}
logo = {
default: '/logos/solid-start/default.svg',
light: '/logos/solid-start/default.svg',
- dark: '/logos/solid-start/default.svg',
+ dark: '/logos/solid-start/dark.svg',
+ }
+
+ async detect(): Promise {
+ await super.detect()
+
+ if (this.detected) {
+ if (this.detected.package?.name === LEGACY_PACKAGE_NAME) {
+ this.dev = LEGACY_DEV
+ this.build = LEGACY_BUILD
+ return this as DetectedFramework
+ }
+ return this as DetectedFramework
+ }
}
}
diff --git a/packages/build-info/src/frameworks/tanstack-router.test.ts b/packages/build-info/src/frameworks/tanstack-router.test.ts
new file mode 100644
index 0000000000..0ae21dffd3
--- /dev/null
+++ b/packages/build-info/src/frameworks/tanstack-router.test.ts
@@ -0,0 +1,44 @@
+import { beforeEach, expect, test } from 'vitest'
+
+import { mockFileSystem } from '../../tests/mock-file-system.js'
+import { NodeFS } from '../node/file-system.js'
+import { Project } from '../project.js'
+
+beforeEach((ctx) => {
+ ctx.fs = new NodeFS()
+})
+
+test('detects a TanStack Router site', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'package.json': JSON.stringify({
+ scripts: {
+ dev: 'vite --port=3001',
+ build: 'vite build && tsc --noEmit',
+ serve: 'vite preview',
+ start: 'vite',
+ },
+ dependencies: {
+ '@tanstack/react-router': '^1.58.15',
+ '@tanstack/router-devtools': '^1.58.15',
+ '@tanstack/router-plugin': '^1.58.12',
+ react: '^18.2.0',
+ 'react-dom': '^18.2.0',
+ },
+ devDependencies: {
+ vite: '^5.4.5',
+ },
+ }),
+ })
+ const detected = await new Project(fs, cwd).detectFrameworks()
+
+ const detectedFrameworks = (detected ?? []).map((framework) => framework.id)
+ expect(detectedFrameworks).not.toContain('vinxi')
+ expect(detectedFrameworks).not.toContain('vite')
+ expect(detectedFrameworks).not.toContain('tanstack-start')
+
+ expect(detected?.[0]?.id).toBe('tanstack-router')
+ expect(detected?.[0]?.build?.command).toBe('vite build')
+ expect(detected?.[0]?.build?.directory).toBe('dist')
+ expect(detected?.[0]?.dev?.command).toBe('vite dev')
+ expect(detected?.[0]?.dev?.port).toBe(3000)
+})
diff --git a/packages/build-info/src/frameworks/tanstack-router.ts b/packages/build-info/src/frameworks/tanstack-router.ts
new file mode 100644
index 0000000000..6dd1820aa2
--- /dev/null
+++ b/packages/build-info/src/frameworks/tanstack-router.ts
@@ -0,0 +1,26 @@
+import { BaseFramework, Category, Framework } from './framework.js'
+
+export class TanStackRouter extends BaseFramework implements Framework {
+ readonly id = 'tanstack-router'
+ name = 'TanStack Router'
+ npmDependencies = ['@tanstack/react-router']
+ excludedNpmDependencies = ['@tanstack/start']
+ category = Category.SSG
+
+ dev = {
+ command: 'vite dev',
+ port: 3000,
+ pollingStrategies: [{ name: 'TCP' }],
+ }
+
+ build = {
+ command: 'vite build',
+ directory: 'dist',
+ }
+
+ logo = {
+ default: '/logos/tanstack-router/default.png',
+ light: '/logos/tanstack-router/default.png',
+ dark: '/logos/tanstack-router/default.png',
+ }
+}
diff --git a/packages/build-info/src/frameworks/tanstack-start.test.ts b/packages/build-info/src/frameworks/tanstack-start.test.ts
new file mode 100644
index 0000000000..e739b9263f
--- /dev/null
+++ b/packages/build-info/src/frameworks/tanstack-start.test.ts
@@ -0,0 +1,43 @@
+import { beforeEach, expect, test } from 'vitest'
+
+import { mockFileSystem } from '../../tests/mock-file-system.js'
+import { NodeFS } from '../node/file-system.js'
+import { Project } from '../project.js'
+
+beforeEach((ctx) => {
+ ctx.fs = new NodeFS()
+})
+
+test('detects a TanStack Start site', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'package.json': JSON.stringify({
+ scripts: {
+ dev: 'vinxi dev',
+ build: 'vinxi build',
+ start: 'vinxi start',
+ },
+ dependencies: {
+ '@tanstack/react-router': '^1.58.15',
+ '@tanstack/router-devtools': '^1.58.15',
+ '@tanstack/start': '^1.58.15',
+ react: '^18.3.1',
+ 'react-dom': '^18.3.1',
+ },
+ devDependencies: {
+ vinxi: '0.4.3',
+ },
+ }),
+ })
+ const detected = await new Project(fs, cwd).detectFrameworks()
+
+ const detectedFrameworks = (detected ?? []).map((framework) => framework.id)
+ expect(detectedFrameworks).not.toContain('vinxi')
+ expect(detectedFrameworks).not.toContain('vite')
+ expect(detectedFrameworks).not.toContain('tanstack-router')
+
+ expect(detected?.[0]?.id).toBe('tanstack-start')
+ expect(detected?.[0]?.build?.command).toBe('vinxi build')
+ expect(detected?.[0]?.build?.directory).toBe('dist')
+ expect(detected?.[0]?.dev?.command).toBe('vinxi dev')
+ expect(detected?.[0]?.dev?.port).toBe(3000)
+})
diff --git a/packages/build-info/src/frameworks/tanstack-start.ts b/packages/build-info/src/frameworks/tanstack-start.ts
new file mode 100644
index 0000000000..d148af6538
--- /dev/null
+++ b/packages/build-info/src/frameworks/tanstack-start.ts
@@ -0,0 +1,25 @@
+import { BaseFramework, Category, Framework } from './framework.js'
+
+export class TanStackStart extends BaseFramework implements Framework {
+ readonly id = 'tanstack-start'
+ name = 'TanStack Start'
+ npmDependencies = ['@tanstack/start']
+ category = Category.SSG
+
+ dev = {
+ command: 'vinxi dev',
+ port: 3000,
+ pollingStrategies: [{ name: 'TCP' }],
+ }
+
+ build = {
+ command: 'vinxi build',
+ directory: 'dist',
+ }
+
+ logo = {
+ default: '/logos/tanstack-start/default.png',
+ light: '/logos/tanstack-start/default.png',
+ dark: '/logos/tanstack-start/default.png',
+ }
+}
diff --git a/packages/build-info/src/frameworks/vite.ts b/packages/build-info/src/frameworks/vite.ts
index 52f2a258c1..ec83c9e2e2 100644
--- a/packages/build-info/src/frameworks/vite.ts
+++ b/packages/build-info/src/frameworks/vite.ts
@@ -10,8 +10,13 @@ export class Vite extends BaseFramework implements Framework {
'@remix-run/server-runtime',
'@shopify/hydrogen',
'@builder.io/qwik',
+ // Used this name up to 0.3.11
'solid-start',
+ // Renamed starting at 0.4.0
+ '@solidjs/start',
'solid-js',
+ '@tanstack/react-router',
+ '@tanstack/start',
'@sveltejs/kit',
'@analogjs/platform',
]
diff --git a/packages/build-info/src/runtime/bun.test.ts b/packages/build-info/src/runtime/bun.test.ts
index 65e7d4e789..07f4e102a8 100644
--- a/packages/build-info/src/runtime/bun.test.ts
+++ b/packages/build-info/src/runtime/bun.test.ts
@@ -17,6 +17,15 @@ test('detects node when bunfig.toml is present', async ({ fs }) => {
expect(detected[0].name).toBe('Bun')
})
+test('detects node when bun.lock is present', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'bun.lock': '',
+ })
+
+ const detected = await new Project(fs, cwd).detectRuntime()
+ expect(detected[0].name).toBe('Bun')
+})
+
test('detects node when bun.lockb is present', async ({ fs }) => {
const cwd = mockFileSystem({
'bun.lockb': '',
diff --git a/packages/build-info/src/runtime/bun.ts b/packages/build-info/src/runtime/bun.ts
index 8b11e6f667..3112d4c7a3 100644
--- a/packages/build-info/src/runtime/bun.ts
+++ b/packages/build-info/src/runtime/bun.ts
@@ -3,5 +3,5 @@ import { LangRuntime } from './runtime.js'
export class Bun extends LangRuntime {
id = 'bun'
name = 'Bun'
- configFiles = ['bun.lockb', 'bunfig.toml']
+ configFiles = ['bun.lock', 'bun.lockb', 'bunfig.toml']
}
diff --git a/packages/build-info/src/runtime/dotnet.test.ts b/packages/build-info/src/runtime/dotnet.test.ts
new file mode 100644
index 0000000000..6fbf44ffa5
--- /dev/null
+++ b/packages/build-info/src/runtime/dotnet.test.ts
@@ -0,0 +1,27 @@
+import { beforeEach, expect, test } from 'vitest'
+
+import { mockFileSystem } from '../../tests/mock-file-system.js'
+import { NodeFS } from '../node/file-system.js'
+import { Project } from '../project.js'
+
+beforeEach((ctx) => {
+ ctx.fs = new NodeFS()
+})
+
+test('detects dotnet when Program.cs is present', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'Program.cs': '',
+ })
+
+ const detected = await new Project(fs, cwd).detectRuntime()
+ expect(detected[0].name).toBe('Dotnet')
+})
+
+test('detects dotnet when appsettings.json is present', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'appsettings.json': '',
+ })
+
+ const detected = await new Project(fs, cwd).detectRuntime()
+ expect(detected[0].name).toBe('Dotnet')
+})
diff --git a/packages/build-info/src/runtime/dotnet.ts b/packages/build-info/src/runtime/dotnet.ts
new file mode 100644
index 0000000000..e8fb7445a5
--- /dev/null
+++ b/packages/build-info/src/runtime/dotnet.ts
@@ -0,0 +1,7 @@
+import { LangRuntime } from './runtime.js'
+
+export class Dotnet extends LangRuntime {
+ id = 'dotnet'
+ name = 'Dotnet'
+ configFiles = ['Program.cs', 'appsettings.json']
+}
diff --git a/packages/build-info/src/runtime/erlang.test.ts b/packages/build-info/src/runtime/erlang.test.ts
new file mode 100644
index 0000000000..1a46882ca7
--- /dev/null
+++ b/packages/build-info/src/runtime/erlang.test.ts
@@ -0,0 +1,29 @@
+import { beforeEach, expect, test } from 'vitest'
+
+import { mockFileSystem } from '../../tests/mock-file-system.js'
+import { NodeFS } from '../node/file-system.js'
+import { Project } from '../project.js'
+
+beforeEach((ctx) => {
+ ctx.fs = new NodeFS()
+})
+
+test('detects erlang when rebar.config is present', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'rebar.config': '',
+ })
+
+ const detected = await new Project(fs, cwd).detectRuntime()
+ expect(detected.length).toBe(1)
+ expect(detected[0].name).toBe('Erlang')
+})
+
+test('detects erlang when rebar.lock is present', async ({ fs }) => {
+ const cwd = mockFileSystem({
+ 'rebar.lock': '',
+ })
+
+ const detected = await new Project(fs, cwd).detectRuntime()
+ expect(detected.length).toBe(1)
+ expect(detected[0].name).toBe('Erlang')
+})
diff --git a/packages/build-info/src/runtime/erlang.ts b/packages/build-info/src/runtime/erlang.ts
new file mode 100644
index 0000000000..322f761603
--- /dev/null
+++ b/packages/build-info/src/runtime/erlang.ts
@@ -0,0 +1,7 @@
+import { LangRuntime } from './runtime.js'
+
+export class Erlang extends LangRuntime {
+ id = 'erlang'
+ name = 'Erlang'
+ configFiles = ['rebar.config', 'rebar.lock']
+}
diff --git a/packages/build-info/src/runtime/index.ts b/packages/build-info/src/runtime/index.ts
index 080500ba4c..0c321ca4be 100644
--- a/packages/build-info/src/runtime/index.ts
+++ b/packages/build-info/src/runtime/index.ts
@@ -1,6 +1,8 @@
import { Brew } from './brew.js'
import { Bun } from './bun.js'
import { Emacs } from './cask.js'
+import { Dotnet } from './dotnet.js'
+import { Erlang } from './erlang.js'
import { Go } from './go.js'
import { Java } from './java.js'
import { Node } from './node.js'
@@ -10,4 +12,4 @@ import { Ruby } from './ruby.js'
import { Rust } from './rust.js'
import { Swift } from './swift.js'
-export const runtimes = [Node, Ruby, Brew, Bun, Emacs, Go, Java, Php, Rust, Swift, Python]
+export const runtimes = [Node, Ruby, Brew, Bun, Dotnet, Emacs, Erlang, Go, Java, Php, Rust, Swift, Python]
diff --git a/packages/build/CHANGELOG.md b/packages/build/CHANGELOG.md
index 1e649a7a93..23d39219bc 100644
--- a/packages/build/CHANGELOG.md
+++ b/packages/build/CHANGELOG.md
@@ -105,6 +105,149 @@
* dependencies
* @netlify/config bumped from ^20.8.0 to ^20.8.1
+## [29.56.1](https://github.com/netlify/build/compare/build-v29.56.0...build-v29.56.1) (2024-11-15)
+
+
+### Bug Fixes
+
+* **deps:** update dependency hot-shots to v10.2.1 ([#5906](https://github.com/netlify/build/issues/5906)) ([ced3f33](https://github.com/netlify/build/commit/ced3f33658b54855ac5f7cbb5ec12ee234ef1370))
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/config bumped from ^20.19.0 to ^20.19.1
+
+## [29.56.0](https://github.com/netlify/build/compare/build-v29.55.5...build-v29.56.0) (2024-10-29)
+
+
+### Features
+
+* keep logging pipe for trusted plugins ([#5899](https://github.com/netlify/build/issues/5899)) ([a631d8c](https://github.com/netlify/build/commit/a631d8c71316b083017cfd702f138e723043c6da))
+
+## [29.55.5](https://github.com/netlify/build/compare/build-v29.55.4...build-v29.55.5) (2024-10-25)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.92 to ^5.2.93
+ * @netlify/zip-it-and-ship-it bumped from 9.41.0 to 9.41.1
+
+## [29.55.4](https://github.com/netlify/build/compare/build-v29.55.3...build-v29.55.4) (2024-10-21)
+
+
+### Bug Fixes
+
+* various fixes to extension dev/test flow ([#5885](https://github.com/netlify/build/issues/5885)) ([07e567c](https://github.com/netlify/build/commit/07e567c61d25dbade69c3f913a300c055d8f465a))
+
+## [29.55.3](https://github.com/netlify/build/compare/build-v29.55.2...build-v29.55.3) (2024-10-21)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.91 to ^5.2.92
+ * @netlify/zip-it-and-ship-it bumped from 9.40.2 to 9.41.0
+
+## [29.55.2](https://github.com/netlify/build/compare/build-v29.55.1...build-v29.55.2) (2024-10-11)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.90 to ^5.2.91
+ * @netlify/zip-it-and-ship-it bumped from 9.40.1 to 9.40.2
+
+## [29.55.1](https://github.com/netlify/build/compare/build-v29.55.0...build-v29.55.1) (2024-10-11)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.89 to ^5.2.90
+ * @netlify/zip-it-and-ship-it bumped from 9.40.0 to 9.40.1
+
+## [29.55.0](https://github.com/netlify/build/compare/build-v29.54.9...build-v29.55.0) (2024-10-10)
+
+
+### Features
+
+* write metadata file ([#5875](https://github.com/netlify/build/issues/5875)) ([7dfac4c](https://github.com/netlify/build/commit/7dfac4cc38058e135c4952d53ef6b63c8034f962))
+
+
+### Bug Fixes
+
+* reading data from spawned process ([#5877](https://github.com/netlify/build/issues/5877)) ([5860e72](https://github.com/netlify/build/commit/5860e7272263dd092c97add354dfa2021518eb5c))
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.88 to ^5.2.89
+ * @netlify/zip-it-and-ship-it bumped from 9.39.7 to 9.40.0
+
+## [29.54.9](https://github.com/netlify/build/compare/build-v29.54.8...build-v29.54.9) (2024-10-01)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.87 to ^5.2.88
+ * @netlify/zip-it-and-ship-it bumped from 9.39.6 to 9.39.7
+
+## [29.54.8](https://github.com/netlify/build/compare/build-v29.54.7...build-v29.54.8) (2024-10-01)
+
+
+### Bug Fixes
+
+* **deps:** update dependency hot-shots to v10.1.1 ([#5861](https://github.com/netlify/build/issues/5861)) ([c6a3f3b](https://github.com/netlify/build/commit/c6a3f3b2a5ead444601459909449c63236ba7b5d))
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.86 to ^5.2.87
+ * @netlify/zip-it-and-ship-it bumped from 9.39.5 to 9.39.6
+
+## [29.54.7](https://github.com/netlify/build/compare/build-v29.54.6...build-v29.54.7) (2024-09-26)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.85 to ^5.2.86
+ * @netlify/zip-it-and-ship-it bumped from 9.39.4 to 9.39.5
+
+## [29.54.6](https://github.com/netlify/build/compare/build-v29.54.5...build-v29.54.6) (2024-09-24)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.84 to ^5.2.85
+ * @netlify/zip-it-and-ship-it bumped from 9.39.3 to 9.39.4
+
+## [29.54.5](https://github.com/netlify/build/compare/build-v29.54.4...build-v29.54.5) (2024-09-23)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/functions-utils bumped from ^5.2.83 to ^5.2.84
+ * @netlify/zip-it-and-ship-it bumped from 9.39.2 to 9.39.3
+
## [29.54.4](https://github.com/netlify/build/compare/build-v29.54.3...build-v29.54.4) (2024-09-20)
diff --git a/packages/build/package.json b/packages/build/package.json
index 7f23d7ff5c..92c5c87979 100644
--- a/packages/build/package.json
+++ b/packages/build/package.json
@@ -1,6 +1,6 @@
{
"name": "@netlify/build",
- "version": "29.54.4",
+ "version": "29.56.1",
"description": "Netlify build module",
"type": "module",
"exports": "./lib/index.js",
@@ -70,15 +70,15 @@
"@bugsnag/js": "^7.0.0",
"@netlify/blobs": "^7.4.0",
"@netlify/cache-utils": "^5.1.6",
- "@netlify/config": "^20.19.0",
+ "@netlify/config": "^20.19.1",
"@netlify/edge-bundler": "12.2.3",
"@netlify/framework-info": "^9.8.13",
- "@netlify/functions-utils": "^5.2.83",
+ "@netlify/functions-utils": "^5.2.93",
"@netlify/git-utils": "^5.1.1",
"@netlify/opentelemetry-utils": "^1.2.1",
"@netlify/plugins-list": "^6.80.0",
"@netlify/run-utils": "^5.1.1",
- "@netlify/zip-it-and-ship-it": "9.39.2",
+ "@netlify/zip-it-and-ship-it": "9.41.1",
"@sindresorhus/slugify": "^2.0.0",
"ansi-escapes": "^6.0.0",
"chalk": "^5.0.0",
@@ -88,7 +88,7 @@
"figures": "^5.0.0",
"filter-obj": "^5.0.0",
"got": "^12.0.0",
- "hot-shots": "10.0.0",
+ "hot-shots": "10.2.1",
"indent-string": "^5.0.0",
"is-plain-obj": "^4.0.0",
"js-yaml": "^4.0.0",
diff --git a/packages/build/src/core/build.ts b/packages/build/src/core/build.ts
index 8f438479f1..63d1a4ad8f 100644
--- a/packages/build/src/core/build.ts
+++ b/packages/build/src/core/build.ts
@@ -449,6 +449,11 @@ const initAndRunBuild = async function ({
edgeFunctionsBootstrapURL,
eventHandlers,
}) {
+ const pluginsEnv = {
+ ...childEnv,
+ ...getBlobsEnvironmentContext({ api, deployId: deployId, siteId: siteInfo?.id, token }),
+ }
+
const { pluginsOptions: pluginsOptionsA, timers: timersA } = await getPluginsOptions({
pluginsOptions,
netlifyConfig,
@@ -469,13 +474,9 @@ const initAndRunBuild = async function ({
integrations,
context,
systemLog,
+ pluginsEnv,
})
- const pluginsEnv = {
- ...childEnv,
- ...getBlobsEnvironmentContext({ api, deployId: deployId, siteId: siteInfo?.id, token }),
- }
-
if (pluginsOptionsA?.length) {
const buildPlugins = {}
for (const plugin of pluginsOptionsA) {
diff --git a/packages/build/src/core/lingering.js b/packages/build/src/core/lingering.js
index ef6d18507f..a436a7d061 100644
--- a/packages/build/src/core/lingering.js
+++ b/packages/build/src/core/lingering.js
@@ -66,6 +66,7 @@ const IGNORED_COMMANDS = [
// Internal buildbot commands
'[build]',
+ 'sleep 5',
/buildbot.*\[node]/,
// buildbot's main Bash script
'/opt/build-bin/build',
diff --git a/packages/build/src/install/missing.js b/packages/build/src/install/missing.js
index 8df8d5eb29..45c60b810b 100644
--- a/packages/build/src/install/missing.js
+++ b/packages/build/src/install/missing.js
@@ -34,6 +34,8 @@ export const installIntegrationPlugins = async function ({
logs,
context,
testOpts,
+ pluginsEnv,
+ buildDir,
}) {
const integrationsToBuild = integrations.filter(
(integration) => typeof integration.dev !== 'undefined' && context === 'dev',
@@ -46,7 +48,11 @@ export const installIntegrationPlugins = async function ({
)
}
const packages = (
- await Promise.all(integrations.map((integration) => getIntegrationPackage({ integration, context, testOpts })))
+ await Promise.all(
+ integrations.map((integration) =>
+ getIntegrationPackage({ integration, context, testOpts, buildDir, pluginsEnv }),
+ ),
+ )
).filter(Boolean)
logInstallIntegrations(
logs,
@@ -64,7 +70,13 @@ export const installIntegrationPlugins = async function ({
await addExactDependencies({ packageRoot: autoPluginsDir, isLocal: mode !== 'buildbot', packages })
}
-const getIntegrationPackage = async function ({ integration: { version, dev }, context, testOpts = {} }) {
+const getIntegrationPackage = async function ({
+ integration: { version, dev },
+ context,
+ testOpts = {},
+ buildDir,
+ pluginsEnv,
+}) {
if (typeof version !== 'undefined') {
return `${version}/packages/buildhooks.tgz`
}
@@ -72,9 +84,10 @@ const getIntegrationPackage = async function ({ integration: { version, dev }, c
if (typeof dev !== 'undefined' && context === 'dev') {
const { path } = dev
- const integrationDir = testOpts.cwd ? resolve(testOpts.cwd, path) : resolve(path)
+ const integrationDir = testOpts.cwd ? resolve(testOpts.cwd, path) : resolve(buildDir, path)
+
try {
- const res = await execa('npm', ['run', 'build'], { cwd: integrationDir })
+ const res = await execa('npm', ['run', 'build'], { cwd: integrationDir, env: pluginsEnv })
// This is horrible and hacky, but `npm run build` will
// return status code 0 even if it fails
@@ -82,7 +95,7 @@ const getIntegrationPackage = async function ({ integration: { version, dev }, c
throw new Error(res.stdout)
}
} catch (e) {
- throw new Error(`Failed to build integration`)
+ throw new Error(`Failed to build integration. Error:\n\n${e.stack}`)
}
return undefined
diff --git a/packages/build/src/log/messages/core_steps.js b/packages/build/src/log/messages/core_steps.js
index 6fce440850..e5587e288a 100644
--- a/packages/build/src/log/messages/core_steps.js
+++ b/packages/build/src/log/messages/core_steps.js
@@ -15,6 +15,13 @@ const logBundleResultFunctions = ({ functions, headerMessage, logs, error }) =>
logArray(logs, functionNames)
}
+/**
+ * Logs the result of bundling functions
+ *
+ * @param {object} options
+ * @param {any} options.logs
+ * @param {import("@netlify/zip-it-and-ship-it").FunctionResult[]} options.results
+ */
export const logBundleResults = ({ logs, results = [] }) => {
const resultsWithErrors = results.filter(({ bundlerErrors }) => bundlerErrors && bundlerErrors.length !== 0)
const resultsWithWarnings = results.filter(
diff --git a/packages/build/src/plugins/ipc.js b/packages/build/src/plugins/ipc.js
index 19509b4d49..0dbf6955f2 100644
--- a/packages/build/src/plugins/ipc.js
+++ b/packages/build/src/plugins/ipc.js
@@ -39,8 +39,8 @@ export const getEventFromChild = async function (childProcess, callId) {
throw getChildExitError('Could not receive event from child process because it already exited.')
}
- const messagePromise = pEvent(childProcess, 'message', { filter: ([actualCallId]) => actualCallId === callId })
- const errorPromise = pEvent(childProcess, 'message', { filter: ([actualCallId]) => actualCallId === 'error' })
+ const messagePromise = pEvent(childProcess, 'message', { filter: (data) => data?.[0] === callId })
+ const errorPromise = pEvent(childProcess, 'message', { filter: (data) => data?.[0] === 'error' })
const exitPromise = pEvent(childProcess, 'exit', { multiArgs: true })
try {
return await Promise.race([getMessage(messagePromise), getError(errorPromise), getExit(exitPromise)])
diff --git a/packages/build/src/plugins/options.ts b/packages/build/src/plugins/options.ts
index f895006514..e9347f7bba 100644
--- a/packages/build/src/plugins/options.ts
+++ b/packages/build/src/plugins/options.ts
@@ -32,6 +32,7 @@ const tGetPluginsOptions = async function ({
integrations,
context,
systemLog,
+ pluginsEnv,
}) {
const pluginsOptionsA = await resolvePluginsPath({
pluginsOptions,
@@ -51,6 +52,7 @@ const tGetPluginsOptions = async function ({
integrations,
context,
systemLog,
+ pluginsEnv,
})
const pluginsOptionsB = await Promise.all(
pluginsOptionsA.map((pluginOptions) => loadPluginFiles({ pluginOptions, debug })),
diff --git a/packages/build/src/plugins/resolve.js b/packages/build/src/plugins/resolve.js
index c39a9aef7e..7ff2dc9dcc 100644
--- a/packages/build/src/plugins/resolve.js
+++ b/packages/build/src/plugins/resolve.js
@@ -33,6 +33,7 @@ export const resolvePluginsPath = async function ({
integrations,
context,
systemLog,
+ pluginsEnv,
}) {
const autoPluginsDir = getAutoPluginsDir(buildDir, packagePath)
const pluginsOptionsA = await Promise.all(
@@ -77,6 +78,7 @@ export const resolvePluginsPath = async function ({
buildDir,
context,
testOpts,
+ pluginsEnv,
})
return [...pluginsOptionsE, ...integrationPluginOptions]
@@ -164,9 +166,27 @@ const handleMissingPlugins = async function ({ pluginsOptions, autoPluginsDir, m
return Promise.all(pluginsOptions.map((pluginOptions) => resolveMissingPluginPath({ pluginOptions, autoPluginsDir })))
}
-const handleIntegrations = async function ({ integrations, autoPluginsDir, mode, logs, buildDir, context, testOpts }) {
+const handleIntegrations = async function ({
+ integrations,
+ autoPluginsDir,
+ mode,
+ logs,
+ buildDir,
+ context,
+ testOpts,
+ pluginsEnv,
+}) {
const toInstall = integrations.filter((integration) => integration.has_build)
- await installIntegrationPlugins({ integrations: toInstall, autoPluginsDir, mode, logs, context, testOpts })
+ await installIntegrationPlugins({
+ integrations: toInstall,
+ autoPluginsDir,
+ mode,
+ logs,
+ context,
+ testOpts,
+ buildDir,
+ pluginsEnv,
+ })
if (toInstall.length === 0) {
return []
@@ -188,7 +208,7 @@ const handleIntegrations = async function ({ integrations, autoPluginsDir, mode,
const resolveIntegration = async function ({ integration, autoPluginsDir, buildDir, context, testOpts }) {
if (typeof integration.dev !== 'undefined' && context === 'dev') {
const { path } = integration.dev
- const integrationDir = testOpts.cwd ? resolve(testOpts.cwd, path) : resolve(path)
+ const integrationDir = testOpts.cwd ? resolve(testOpts.cwd, path) : resolve(buildDir, path)
const pluginPath = await resolvePath(`${integrationDir}/.ntli/build`, buildDir)
return { pluginPath, packageName: `${integration.slug}`, isIntegration: true, integration, loadedFrom: 'local' }
diff --git a/packages/build/src/plugins/spawn.ts b/packages/build/src/plugins/spawn.ts
index 75073ad482..7e67a30a52 100644
--- a/packages/build/src/plugins/spawn.ts
+++ b/packages/build/src/plugins/spawn.ts
@@ -1,10 +1,11 @@
import { createRequire } from 'module'
+import { platform } from 'os'
import { fileURLToPath, pathToFileURL } from 'url'
import { promisify } from 'util'
import { trace } from '@opentelemetry/api'
import { ExecaChildProcess, execaNode } from 'execa'
-import { gte } from 'semver'
+import { gte, satisfies } from 'semver'
import { FeatureFlags } from '../core/feature_flags.js'
import { addErrorInfo } from '../error/info.js'
@@ -217,5 +218,17 @@ const stopPlugin = async function ({
})
childProcess.disconnect()
}
- childProcess.kill()
+
+ // On Windows with Node 21+, there's a bug where attempting to kill a child process
+ // results in an EPERM error. Ignore the error in that case.
+ // See: https://github.com/nodejs/node/issues/51766
+ // We also disable execa's `forceKillAfterTimeout` in this case
+ // which can cause unhandled rejection.
+ try {
+ childProcess.kill('SIGTERM', {
+ forceKillAfterTimeout: platform() === 'win32' && satisfies(process.version, '>=21') ? false : undefined,
+ })
+ } catch {
+ // no-op
+ }
}
diff --git a/packages/build/src/plugins_core/blobs_upload/index.ts b/packages/build/src/plugins_core/blobs_upload/index.ts
index b7d76a52a7..7ee7596146 100644
--- a/packages/build/src/plugins_core/blobs_upload/index.ts
+++ b/packages/build/src/plugins_core/blobs_upload/index.ts
@@ -72,7 +72,8 @@ const coreStep: CoreStepFunction = async function ({
systemLog(`Uploading blob ${key}`)
const { data, metadata } = await getFileWithMetadata(key, contentPath, metadataPath)
- await blobStore.set(key, data, { metadata })
+ const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.length)
+ await blobStore.set(key, arrayBuffer, { metadata })
},
{ concurrency: 10 },
)
diff --git a/packages/build/src/plugins_core/dev_blobs_upload/index.ts b/packages/build/src/plugins_core/dev_blobs_upload/index.ts
index 525a8066b2..2b7a645067 100644
--- a/packages/build/src/plugins_core/dev_blobs_upload/index.ts
+++ b/packages/build/src/plugins_core/dev_blobs_upload/index.ts
@@ -77,7 +77,8 @@ const coreStep: CoreStepFunction = async function ({
log(logs, `- Uploading blob ${key}`, { indent: true })
}
const { data, metadata } = await getFileWithMetadata(key, contentPath, metadataPath)
- await blobStore.set(key, data, { metadata })
+ const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.length)
+ await blobStore.set(key, arrayBuffer, { metadata })
},
{ concurrency: 10 },
)
diff --git a/packages/build/src/plugins_core/functions/index.ts b/packages/build/src/plugins_core/functions/index.ts
index 29beaec90f..a5819f8179 100644
--- a/packages/build/src/plugins_core/functions/index.ts
+++ b/packages/build/src/plugins_core/functions/index.ts
@@ -1,6 +1,6 @@
import { resolve } from 'path'
-import { NodeBundlerName, RUNTIME, zipFunctions } from '@netlify/zip-it-and-ship-it'
+import { NodeBundlerName, RUNTIME, zipFunctions, FunctionResult } from '@netlify/zip-it-and-ship-it'
import { pathExists } from 'path-exists'
import { addErrorInfo } from '../../error/info.js'
@@ -13,7 +13,7 @@ import { getUserAndInternalFunctions, validateFunctionsSrc } from './utils.js'
import { getZisiParameters } from './zisi.js'
// Get a list of all unique bundlers in this run
-const getBundlers = (results: Awaited> = []) =>
+const getBundlers = (results: FunctionResult[] = []) =>
// using a Set to filter duplicates
new Set(
results
@@ -38,7 +38,7 @@ const eventTriggeredFunctions = new Set([
'identity-login',
])
-const validateCustomRoutes = function (functions: Awaited>) {
+const validateCustomRoutes = function (functions: FunctionResult[]) {
for (const { routes, name, schedule } of functions) {
if (!routes || routes.length === 0) continue
@@ -61,6 +61,7 @@ const validateCustomRoutes = function (functions: Awaited {
const zisiParameters = getZisiParameters({
+ branch,
buildDir,
childEnv,
featureFlags,
@@ -118,6 +120,7 @@ const coreStep = async function ({
FUNCTIONS_DIST: relativeFunctionsDist,
},
buildDir,
+ branch,
packagePath,
logs,
netlifyConfig,
@@ -166,6 +169,7 @@ const coreStep = async function ({
}
const { bundlers } = await zipFunctionsAndLogResults({
+ branch,
buildDir,
childEnv,
featureFlags,
@@ -237,7 +241,7 @@ export const bundleFunctions = {
// `zip-it-and-ship-it` methods. Therefore, we need to use an intermediary
// function and export them so tests can use it.
export const zipItAndShipIt = {
- async zipFunctions(...args: Parameters) {
+ async zipFunctions(...args: Parameters): Promise {
return await zipFunctions(...args)
},
}
diff --git a/packages/build/src/plugins_core/functions/zisi.ts b/packages/build/src/plugins_core/functions/zisi.ts
index 41651e3cd3..d6204b3094 100644
--- a/packages/build/src/plugins_core/functions/zisi.ts
+++ b/packages/build/src/plugins_core/functions/zisi.ts
@@ -9,6 +9,7 @@ import type { FeatureFlags } from '../../core/feature_flags.js'
import { getZisiFeatureFlags } from './feature_flags.js'
type GetZisiParametersType = {
+ branch?: string
buildDir: string
childEnv: Record
featureFlags: FeatureFlags
@@ -40,6 +41,7 @@ const getLambdaNodeVersion = (childEnv: Record, userNodeVersion:
}
export const getZisiParameters = ({
+ branch,
buildDir,
childEnv,
featureFlags,
@@ -65,6 +67,7 @@ export const getZisiParameters = ({
return {
basePath: buildDir,
+ branch,
config,
manifest,
featureFlags: zisiFeatureFlags,
diff --git a/packages/build/src/plugins_core/pre_dev_cleanup/index.ts b/packages/build/src/plugins_core/pre_dev_cleanup/index.ts
index c4a41a9c1c..95c50eefa1 100644
--- a/packages/build/src/plugins_core/pre_dev_cleanup/index.ts
+++ b/packages/build/src/plugins_core/pre_dev_cleanup/index.ts
@@ -3,7 +3,6 @@ import { resolve } from 'node:path'
import { listFrameworks } from '@netlify/framework-info'
-import { log } from '../../log/logger.js'
import { CoreStep, CoreStepCondition, CoreStepFunction, CoreStepFunctionArgs } from '../types.js'
const dirExists = async (path: string): Promise => {
@@ -37,7 +36,7 @@ const coreStep: CoreStepFunction = async (input) => {
for (const dir of dirs) {
await rm(resolve(input.buildDir, dir), { recursive: true, force: true })
}
- log(input.logs, `Cleaned up ${dirs.join(', ')}.`)
+ input.systemLog(input.logs, `Cleaned up ${dirs.join(', ')}.`)
return {}
}
diff --git a/packages/build/src/steps/plugin.js b/packages/build/src/steps/plugin.js
index 58053fbf23..c4ef936492 100644
--- a/packages/build/src/steps/plugin.js
+++ b/packages/build/src/steps/plugin.js
@@ -108,7 +108,9 @@ export const firePluginStep = async function ({
})
return { newError }
} finally {
- await unpipePluginOutput(childProcess, logs, listeners, standardStreams)
+ if (!isTrustedPlugin(pluginPackageJson?.name) || listeners) {
+ await unpipePluginOutput(childProcess, logs, listeners, standardStreams)
+ }
logStepCompleted(logs, verbose)
}
}
diff --git a/packages/build/tests/functions/fixtures/v2/netlify/functions/test.mjs b/packages/build/tests/functions/fixtures/v2/netlify/functions/test.mjs
new file mode 100644
index 0000000000..11d0241321
--- /dev/null
+++ b/packages/build/tests/functions/fixtures/v2/netlify/functions/test.mjs
@@ -0,0 +1,3 @@
+export default async () => new Response("Hello")
+
+export const config = { path: "/hello" }
diff --git a/packages/build/tests/functions/tests.js b/packages/build/tests/functions/tests.js
index 3d7d505bf9..a23893c647 100644
--- a/packages/build/tests/functions/tests.js
+++ b/packages/build/tests/functions/tests.js
@@ -1,9 +1,9 @@
import { readdir, readFile, rm, stat, writeFile } from 'fs/promises'
-import { resolve } from 'path'
+import { join, resolve } from 'path'
import { version as nodeVersion } from 'process'
import { fileURLToPath } from 'url'
-import { Fixture, normalizeOutput, removeDir, getTempName } from '@netlify/testing'
+import { Fixture, normalizeOutput, removeDir, getTempName, unzipFile } from '@netlify/testing'
import test from 'ava'
import { pathExists } from 'path-exists'
import semver from 'semver'
@@ -204,3 +204,39 @@ if (semver.gte(nodeVersion, '16.9.0')) {
t.true(app2FunctionsDist.includes('worker.zip'))
})
}
+
+test('Functions: creates metadata file', async (t) => {
+ const fixture = await new Fixture('./fixtures/v2').withCopyRoot({ git: false })
+ const build = await fixture
+ .withFlags({
+ branch: 'my-branch',
+ cwd: fixture.repositoryRoot,
+ featureFlags: { zisi_add_metadata_file: true },
+ })
+ .runWithBuildAndIntrospect()
+
+ t.true(build.success)
+
+ const functionsDistPath = resolve(fixture.repositoryRoot, '.netlify/functions')
+ const functionsDistFiles = await readdir(functionsDistPath)
+
+ t.true(functionsDistFiles.includes('manifest.json'))
+ t.true(functionsDistFiles.includes('test.zip'))
+
+ const unzipPath = join(functionsDistPath, `.netlify-test-${Date.now()}`)
+
+ await unzipFile(join(functionsDistPath, 'test.zip'), unzipPath)
+
+ const functionFiles = await readdir(unzipPath)
+
+ t.true(functionFiles.includes('___netlify-bootstrap.mjs'))
+ t.true(functionFiles.includes('___netlify-entry-point.mjs'))
+ t.true(functionFiles.includes('___netlify-metadata.json'))
+ t.true(functionFiles.includes('test.mjs'))
+
+ const metadata = JSON.parse(await readFile(join(unzipPath, '___netlify-metadata.json'), 'utf8'))
+
+ t.is(semver.valid(metadata.bootstrap_version), metadata.bootstrap_version)
+ t.is(metadata.branch, 'my-branch')
+ t.is(metadata.version, 1)
+})
diff --git a/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/.gitkeep b/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/.gitkeep
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/index.js b/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/index.js
index e69de29bb2..8c1f901340 100644
--- a/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/index.js
+++ b/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/index.js
@@ -0,0 +1,3 @@
+export const onPreBuild = function () {
+ console.log("Hello world");
+}
diff --git a/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/manifest.yml b/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/manifest.yml
new file mode 100644
index 0000000000..bbc1fee978
--- /dev/null
+++ b/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/manifest.yml
@@ -0,0 +1,2 @@
+name: abc-integration
+inputs: []
diff --git a/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/package.json b/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/package.json
new file mode 100644
index 0000000000..507b311db0
--- /dev/null
+++ b/packages/build/tests/install/fixtures/local_missing_integration/integration/.ntli/build/package.json
@@ -0,0 +1,7 @@
+{
+ "main": "index.js",
+ "type": "module",
+ "version": "0.0.0",
+ "name": "abc-integration",
+ "dependencies": {}
+}
diff --git a/packages/build/tests/install/fixtures/local_missing_integration/integration/manifest.yml b/packages/build/tests/install/fixtures/local_missing_integration/integration/manifest.yml
index a3512f0259..bbc1fee978 100644
--- a/packages/build/tests/install/fixtures/local_missing_integration/integration/manifest.yml
+++ b/packages/build/tests/install/fixtures/local_missing_integration/integration/manifest.yml
@@ -1,2 +1,2 @@
-name: test
+name: abc-integration
inputs: []
diff --git a/packages/build/tests/install/fixtures/local_missing_integration/integration/package.json b/packages/build/tests/install/fixtures/local_missing_integration/integration/package.json
index 8521f0f9aa..26fafe3703 100644
--- a/packages/build/tests/install/fixtures/local_missing_integration/integration/package.json
+++ b/packages/build/tests/install/fixtures/local_missing_integration/integration/package.json
@@ -1,5 +1,5 @@
{
- "name": "plugin_deps_plugin",
+ "name": "abc-integration",
"version": "0.0.1",
"type": "module",
"scripts": {
diff --git a/packages/build/tests/install/fixtures/local_missing_integration/netlify.toml b/packages/build/tests/install/fixtures/local_missing_integration/netlify.toml
index c7204d2e8c..45a6d67bac 100644
--- a/packages/build/tests/install/fixtures/local_missing_integration/netlify.toml
+++ b/packages/build/tests/install/fixtures/local_missing_integration/netlify.toml
@@ -1,5 +1,5 @@
[[integrations]]
-name = "abc-integration"
+name = "test"
[integrations.dev]
path = "./integration"
diff --git a/packages/build/tests/install/snapshots/tests.js.md b/packages/build/tests/install/snapshots/tests.js.md
index 9b23f807b5..5f3ce01703 100644
--- a/packages/build/tests/install/snapshots/tests.js.md
+++ b/packages/build/tests/install/snapshots/tests.js.md
@@ -1051,7 +1051,6 @@ Generated by [AVA](https://avajs.dev).
debug: true␊
repositoryRoot: packages/build/tests/install/fixtures/local_missing_integration␊
testOpts:␊
- cwd: ./tests/install/fixtures/local_missing_integration/␊
pluginsListUrl: test␊
silentLingeringProcesses: true␊
␊
@@ -1070,10 +1069,18 @@ Generated by [AVA](https://avajs.dev).
dev␊
␊
> Building integrations␊
- - abc-integration from ./integration␊
+ - test from ./integration␊
␊
> Loading integrations␊
- - abc-integration␊
+ - test␊
+ ␊
+ test (onPreBuild event) ␊
+ ────────────────────────────────────────────────────────────────␊
+ ␊
+ Hello world␊
+ ␊
+ (test onPreBuild completed in 1ms)␊
+ Build step duration: test onPreBuild completed in 1ms␊
␊
Netlify Build Complete ␊
────────────────────────────────────────────────────────────────␊
diff --git a/packages/build/tests/install/snapshots/tests.js.snap b/packages/build/tests/install/snapshots/tests.js.snap
index 8828ee6096..490600bcfb 100644
Binary files a/packages/build/tests/install/snapshots/tests.js.snap and b/packages/build/tests/install/snapshots/tests.js.snap differ
diff --git a/packages/build/tests/install/tests.js b/packages/build/tests/install/tests.js
index 343da99ad0..1a81eb2ac4 100644
--- a/packages/build/tests/install/tests.js
+++ b/packages/build/tests/install/tests.js
@@ -154,14 +154,7 @@ test('Install local plugin dependencies: missing plugin in netlify.toml', async
})
test('In integration dev mode, install local plugins and install the integration when forcing build', async (t) => {
- const output = await new Fixture('./fixtures/local_missing_integration')
- .withFlags({
- context: 'dev',
- testOpts: {
- cwd: './tests/install/fixtures/local_missing_integration/',
- },
- })
- .runWithBuild()
+ const output = await new Fixture('./fixtures/local_missing_integration').withFlags({ context: 'dev' }).runWithBuild()
t.snapshot(normalizeOutput(output))
})
diff --git a/packages/build/tests/plugins/fixtures/process_send_object/manifest.yml b/packages/build/tests/plugins/fixtures/process_send_object/manifest.yml
new file mode 100644
index 0000000000..a3512f0259
--- /dev/null
+++ b/packages/build/tests/plugins/fixtures/process_send_object/manifest.yml
@@ -0,0 +1,2 @@
+name: test
+inputs: []
diff --git a/packages/build/tests/plugins/fixtures/process_send_object/netlify.toml b/packages/build/tests/plugins/fixtures/process_send_object/netlify.toml
new file mode 100644
index 0000000000..4b06556c85
--- /dev/null
+++ b/packages/build/tests/plugins/fixtures/process_send_object/netlify.toml
@@ -0,0 +1,2 @@
+[[plugins]]
+package = "./plugin.js"
diff --git a/packages/build/tests/plugins/fixtures/process_send_object/plugin.js b/packages/build/tests/plugins/fixtures/process_send_object/plugin.js
new file mode 100644
index 0000000000..9a99d0ee52
--- /dev/null
+++ b/packages/build/tests/plugins/fixtures/process_send_object/plugin.js
@@ -0,0 +1,5 @@
+import process from 'process';
+
+export const onPreBuild = function () {
+ process.send({});
+}
diff --git a/packages/build/tests/plugins/tests.js b/packages/build/tests/plugins/tests.js
index fdb4d7ffaf..0ddd21e14d 100644
--- a/packages/build/tests/plugins/tests.js
+++ b/packages/build/tests/plugins/tests.js
@@ -401,3 +401,8 @@ test('Plugins have a pre-populated Blobs context', async (t) => {
t.is(netlifyConfig.build.command, `echo ""Hello there""`)
})
+
+test('Plugins can respond anything to parent process', async (t) => {
+ const build = await new Fixture('./fixtures/process_send_object').runBuildBinary()
+ t.true(build.exitCode === 0)
+})
diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md
index 6bee6a4aaa..a216de5f51 100644
--- a/packages/config/CHANGELOG.md
+++ b/packages/config/CHANGELOG.md
@@ -76,6 +76,13 @@
* dependencies
* netlify bumped from ^13.1.8 to ^13.1.9
+## [20.19.1](https://github.com/netlify/build/compare/config-v20.19.0...config-v20.19.1) (2024-11-15)
+
+
+### Bug Fixes
+
+* environment variable build context filtering fix ([#5887](https://github.com/netlify/build/issues/5887)) ([c0ab547](https://github.com/netlify/build/commit/c0ab5479bd533315d39983e50a9c8a1bee5966ff))
+
## [20.19.0](https://github.com/netlify/build/compare/config-v20.18.0...config-v20.19.0) (2024-08-21)
diff --git a/packages/config/package.json b/packages/config/package.json
index 92e9d3c8f5..571897ba0c 100644
--- a/packages/config/package.json
+++ b/packages/config/package.json
@@ -1,6 +1,6 @@
{
"name": "@netlify/config",
- "version": "20.19.0",
+ "version": "20.19.1",
"description": "Netlify config module",
"type": "module",
"exports": "./lib/index.js",
diff --git a/packages/config/src/api/site_info.ts b/packages/config/src/api/site_info.ts
index 699219768d..4ac6db793e 100644
--- a/packages/config/src/api/site_info.ts
+++ b/packages/config/src/api/site_info.ts
@@ -35,53 +35,18 @@ export const getSiteInfo = async function ({
context,
offline = false,
testOpts = {},
- featureFlags = {},
siteFeatureFlagPrefix,
}: GetSiteInfoOpts) {
const { env: testEnv = false } = testOpts
- const useV2Endpoint = !!accountId && featureFlags.cli_integration_installations_meta
-
- if (useV2Endpoint) {
- if (api === undefined || mode === 'buildbot' || testEnv) {
- const siteInfo: { id?: string; account_id?: string } = {}
-
- if (siteId !== undefined) siteInfo.id = siteId
- if (accountId !== undefined) siteInfo.account_id = accountId
-
- const integrations =
- mode === 'buildbot' && !offline
- ? await getIntegrations({ siteId, testOpts, offline, useV2Endpoint, accountId })
- : []
-
- return { siteInfo, accounts: [], addons: [], integrations }
- }
-
- const promises = [
- getSite(api, siteId, siteFeatureFlagPrefix),
- getAccounts(api),
- getAddons(api, siteId),
- getIntegrations({ siteId, testOpts, offline, useV2Endpoint, accountId }),
- ]
-
- const [siteInfo, accounts, addons, integrations] = await Promise.all(promises)
-
- if (siteInfo.use_envelope) {
- const envelope = await getEnvelope({ api, accountId: siteInfo.account_slug, siteId, context })
-
- siteInfo.build_settings.env = envelope
- }
-
- return { siteInfo, accounts, addons, integrations }
- }
-
if (api === undefined || mode === 'buildbot' || testEnv) {
const siteInfo: { id?: string; account_id?: string } = {}
if (siteId !== undefined) siteInfo.id = siteId
if (accountId !== undefined) siteInfo.account_id = accountId
- const integrations = mode === 'buildbot' && !offline ? await getIntegrations({ siteId, testOpts, offline }) : []
+ const integrations =
+ mode === 'buildbot' && !offline ? await getIntegrations({ siteId, testOpts, offline, accountId }) : []
return { siteInfo, accounts: [], addons: [], integrations }
}
@@ -90,7 +55,7 @@ export const getSiteInfo = async function ({
getSite(api, siteId, siteFeatureFlagPrefix),
getAccounts(api),
getAddons(api, siteId),
- getIntegrations({ siteId, testOpts, offline }),
+ getIntegrations({ siteId, testOpts, offline, accountId }),
]
const [siteInfo, accounts, addons, integrations] = await Promise.all(promises)
@@ -144,7 +109,6 @@ type GetIntegrationsOpts = {
accountId?: string
testOpts: TestOptions
offline: boolean
- useV2Endpoint?: boolean
}
const getIntegrations = async function ({
@@ -152,7 +116,6 @@ const getIntegrations = async function ({
accountId,
testOpts,
offline,
- useV2Endpoint,
}: GetIntegrationsOpts): Promise {
if (!siteId || offline) {
return []
@@ -162,8 +125,8 @@ const getIntegrations = async function ({
const baseUrl = new URL(host ? `http://${host}` : `https://api.netlifysdk.com`)
- // use future state feature flag
- const url = useV2Endpoint
+ // if accountId isn't present, use safe v1 endpoint
+ const url = accountId
? `${baseUrl}team/${accountId}/integrations/installations/meta/${siteId}`
: `${baseUrl}site/${siteId}/integrations/safe`
diff --git a/packages/config/src/case.js b/packages/config/src/case.ts
similarity index 68%
rename from packages/config/src/case.js
rename to packages/config/src/case.ts
index e8755ed876..0bbdad2180 100644
--- a/packages/config/src/case.js
+++ b/packages/config/src/case.ts
@@ -1,10 +1,18 @@
// Some properties can be optionally capitalized. We normalize them to lowercase
-export const normalizeConfigCase = function ({ Build, build = Build, ...config }) {
+export const normalizeConfigCase = function ({
+ Build,
+ build = Build,
+ ...config
+}: {
+ Build: Record
+ build: Record
+ [key: string]: unknown
+}): Record {
const buildA = normalizeBuildCase(build)
return { ...config, build: buildA }
}
-const normalizeBuildCase = function ({
+const normalizeBuildCase = ({
Base,
base = Base,
Command,
@@ -22,7 +30,7 @@ const normalizeBuildCase = function ({
Publish,
publish = Publish,
...build
-} = {}) {
+}: Record = {}): Record => {
return {
...build,
base,
diff --git a/packages/config/src/env/envelope.ts b/packages/config/src/env/envelope.ts
index d75e0786ee..3f4d88e1e3 100644
--- a/packages/config/src/env/envelope.ts
+++ b/packages/config/src/env/envelope.ts
@@ -17,10 +17,10 @@ export const getEnvelope = async function ({
try {
const environmentVariables = await (api as any).getEnvVars({ accountId, siteId, context_name: context })
- const sortedEnvVarsFromDevContext = environmentVariables
+ const sortedEnvVarsFromContext = environmentVariables
.sort((left, right) => (left.key.toLowerCase() < right.key.toLowerCase() ? -1 : 1))
.reduce((acc, cur) => {
- const envVar = cur.values.find((val) => ['dev', 'all'].includes(val.context))
+ const envVar = cur.values.find((val) => ['all', context].includes(val.context))
if (envVar && envVar.value) {
return {
...acc,
@@ -29,7 +29,7 @@ export const getEnvelope = async function ({
}
return acc
}, {})
- return sortedEnvVarsFromDevContext
+ return sortedEnvVarsFromContext
} catch {
return {}
}
diff --git a/packages/config/src/env/main.ts b/packages/config/src/env/main.ts
index ea73911eaf..5ce066ba93 100644
--- a/packages/config/src/env/main.ts
+++ b/packages/config/src/env/main.ts
@@ -181,7 +181,7 @@ const getAccountEnv = async function ({
context?: string
}) {
if (siteInfo.use_envelope) {
- const envelope = await getEnvelope({ api, accountId: siteInfo.account_slug, context })
+ const envelope = await getEnvelope({ api, accountId: siteInfo.account_slug, siteId: siteInfo.site_id, context })
return envelope
}
const { site_env: siteEnv = {} } = accounts.find(({ slug }) => slug === siteInfo.account_slug) || {}
diff --git a/packages/config/tests/api/tests.js b/packages/config/tests/api/tests.js
index 8910aea2a8..897c9cc9ce 100644
--- a/packages/config/tests/api/tests.js
+++ b/packages/config/tests/api/tests.js
@@ -20,7 +20,7 @@ const SITE_INTEGRATIONS_RESPONSE = {
response: [
{
slug: 'test',
- version: 'so-cool',
+ version: 'so-cool-v1',
has_build: true,
},
],
@@ -31,7 +31,7 @@ const TEAM_INSTALLATIONS_META_RESPONSE = {
response: [
{
slug: 'test',
- version: 'so-cool',
+ version: 'so-cool-v2',
has_build: true,
},
],
@@ -42,6 +42,11 @@ const SITE_INTEGRATIONS_EMPTY_RESPONSE = {
response: [],
}
+const TEAM_INSTALLATIONS_META_EMPTY_RESPONSE = {
+ path: '/team/account1/integrations/installations/meta/test',
+ response: [],
+}
+
const SITE_INFO_BUILD_SETTINGS = {
path: SITE_INFO_PATH,
response: {
@@ -222,7 +227,7 @@ test('Build settings are not used in CI', async (t) => {
t.snapshot(normalizeOutput(output))
})
-test('Integrations are returned if feature flag is true', async (t) => {
+test('Integrations are returned from getSiteInfo from v1 safe API when there is not accountID', async (t) => {
const { output } = await new Fixture('./fixtures/base')
.withFlags({
token: 'test',
@@ -235,7 +240,7 @@ test('Integrations are returned if feature flag is true', async (t) => {
t.assert(config.integrations)
t.assert(config.integrations.length === 1)
t.assert(config.integrations[0].slug === 'test')
- t.assert(config.integrations[0].version === 'so-cool')
+ t.assert(config.integrations[0].version === 'so-cool-v1')
t.assert(config.integrations[0].has_build === true)
})
@@ -244,8 +249,9 @@ test('Integration specified in config is also returned if integration is availab
.withFlags({
token: 'test',
siteId: 'test',
+ accountId: 'account1',
})
- .runConfigServer([SITE_INFO_DATA, SITE_INTEGRATIONS_RESPONSE, FETCH_INTEGRATIONS_RESPONSE])
+ .runConfigServer([SITE_INFO_DATA, TEAM_INSTALLATIONS_META_RESPONSE, FETCH_INTEGRATIONS_RESPONSE])
const config = JSON.parse(output)
@@ -262,8 +268,9 @@ test('Integration specified in config is not returned if integration is not avai
.withFlags({
token: 'test',
siteId: 'test',
+ accountId: 'account1',
})
- .runConfigServer([SITE_INFO_DATA, SITE_INTEGRATIONS_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
+ .runConfigServer([SITE_INFO_DATA, TEAM_INSTALLATIONS_META_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
const config = JSON.parse(output)
@@ -278,8 +285,9 @@ test('In integration dev mode, integration specified in config is returned even
token: 'test',
siteId: 'test',
context: 'dev',
+ accountId: 'account1',
})
- .runConfigServer([SITE_INFO_DATA, SITE_INTEGRATIONS_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
+ .runConfigServer([SITE_INFO_DATA, TEAM_INSTALLATIONS_META_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
const config = JSON.parse(output)
@@ -297,8 +305,9 @@ test('In integration dev mode, integration specified in config is returned even
token: 'test',
siteId: 'test',
context: 'dev',
+ accountId: 'account1',
})
- .runConfigServer([SITE_INFO_DATA, SITE_INTEGRATIONS_EMPTY_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
+ .runConfigServer([SITE_INFO_DATA, TEAM_INSTALLATIONS_META_EMPTY_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
const config = JSON.parse(output)
@@ -309,8 +318,8 @@ test('In integration dev mode, integration specified in config is returned even
t.assert(config.integrations[0].version === undefined)
})
-test('In integration dev mode, integration specified in config is returned and build is forced by config', async (t) => {
- const { output } = await new Fixture('./fixtures/dev_integration_with_force_build')
+test('In integration dev mode, integration specified in config is returned even if integration is not enabled on site and accountId not present', async (t) => {
+ const { output } = await new Fixture('./fixtures/dev_integration')
.withFlags({
token: 'test',
siteId: 'test',
@@ -323,76 +332,52 @@ test('In integration dev mode, integration specified in config is returned and b
t.assert(config.integrations)
t.assert(config.integrations.length === 1)
t.assert(config.integrations[0].slug === 'abc-integration')
- t.assert(config.integrations[0].has_build === true)
+ t.assert(config.integrations[0].has_build === false)
t.assert(config.integrations[0].version === undefined)
})
-test('Integrations are not returned if offline', async (t) => {
- const { output } = await new Fixture('./fixtures/base')
+test('In integration dev mode, integration specified in config is returned and build is forced by config', async (t) => {
+ const { output } = await new Fixture('./fixtures/dev_integration_with_force_build')
.withFlags({
- offline: true,
+ token: 'test',
siteId: 'test',
- mode: 'buildbot',
+ context: 'dev',
+ accountId: 'account1',
})
- .runConfigServer([SITE_INTEGRATIONS_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
+ .runConfigServer([SITE_INFO_DATA, TEAM_INSTALLATIONS_META_EMPTY_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
const config = JSON.parse(output)
t.assert(config.integrations)
- t.assert(config.integrations.length === 0)
+ t.assert(config.integrations.length === 1)
+ t.assert(config.integrations[0].slug === 'abc-integration')
+ t.assert(config.integrations[0].has_build === true)
+ t.assert(config.integrations[0].version === undefined)
})
-test('Integrations and account id are returned if feature flag is false and mode is buildbot', async (t) => {
+test('Integrations are not returned if offline', async (t) => {
const { output } = await new Fixture('./fixtures/base')
.withFlags({
+ offline: true,
siteId: 'test',
mode: 'buildbot',
accountId: 'account1',
- token: 'test',
})
- .runConfigServer([SITE_INFO_DATA, SITE_INTEGRATIONS_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
+ .runConfigServer([TEAM_INSTALLATIONS_META_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
const config = JSON.parse(output)
t.assert(config.integrations)
- t.is(config.integrations.length, 1)
- t.is(config.integrations[0].slug, 'test')
- t.is(config.integrations[0].version, 'so-cool')
- t.is(config.integrations[0].has_build, true)
-
- // account id is also available
- t.assert(config.siteInfo)
- t.is(config.siteInfo.account_id, 'account1')
-})
-
-test('Integrations are returned if feature flag is false and mode is dev', async (t) => {
- const { output } = await new Fixture('./fixtures/base')
- .withFlags({
- siteId: 'test',
- mode: 'dev',
- token: 'test',
- })
- .runConfigServer([SITE_INFO_DATA, SITE_INTEGRATIONS_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
-
- const config = JSON.parse(output)
-
- t.assert(config.integrations)
- t.assert(config.integrations.length === 1)
- t.assert(config.integrations[0].slug === 'test')
- t.assert(config.integrations[0].version === 'so-cool')
- t.assert(config.integrations[0].has_build === true)
+ t.assert(config.integrations.length === 0)
})
-test('Integrations and account id are returned if flag is true for site and mode is buildbot', async (t) => {
+test('Integrations and account id are returned if mode is buildbot', async (t) => {
const { output } = await new Fixture('./fixtures/base')
.withFlags({
siteId: 'test',
mode: 'buildbot',
- token: 'test',
accountId: 'account1',
- featureFlags: {
- cli_integration_installations_meta: true,
- },
+ token: 'test',
})
.runConfigServer([SITE_INFO_DATA, TEAM_INSTALLATIONS_META_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
@@ -401,7 +386,7 @@ test('Integrations and account id are returned if flag is true for site and mode
t.assert(config.integrations)
t.is(config.integrations.length, 1)
t.is(config.integrations[0].slug, 'test')
- t.is(config.integrations[0].version, 'so-cool')
+ t.is(config.integrations[0].version, 'so-cool-v2')
t.is(config.integrations[0].has_build, true)
// account id is also available
@@ -409,16 +394,13 @@ test('Integrations and account id are returned if flag is true for site and mode
t.is(config.siteInfo.account_id, 'account1')
})
-test('Integrations are returned if flag is true for site and mode is dev', async (t) => {
+test('Integrations are returned if accountId is present and mode is dev', async (t) => {
const { output } = await new Fixture('./fixtures/base')
.withFlags({
siteId: 'test',
mode: 'dev',
token: 'test',
accountId: 'account1',
- featureFlags: {
- cli_integration_installations_meta: true,
- },
})
.runConfigServer([SITE_INFO_DATA, TEAM_INSTALLATIONS_META_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE])
@@ -427,7 +409,7 @@ test('Integrations are returned if flag is true for site and mode is dev', async
t.assert(config.integrations)
t.assert(config.integrations.length === 1)
t.assert(config.integrations[0].slug === 'test')
- t.assert(config.integrations[0].version === 'so-cool')
+ t.assert(config.integrations[0].version === 'so-cool-v2')
t.assert(config.integrations[0].has_build === true)
})
diff --git a/packages/config/tests/env/tests.js b/packages/config/tests/env/tests.js
index 0d82d7f9d9..a8d6463132 100644
--- a/packages/config/tests/env/tests.js
+++ b/packages/config/tests/env/tests.js
@@ -310,7 +310,7 @@ test('Sets environment variables when configured to use Envelope', async (t) =>
t.deepEqual(env.URL.sources, ['general'])
t.is(env.URL.value, 'test')
t.is(env.SHARED_ENV_VAR.value, 'ENVELOPE_TEAM_ALL')
- t.is(env.SITE_ENV_VAR.value, 'ENVELOPE_SITE_DEV')
+ t.is(env.SITE_ENV_VAR.value, 'ENVELOPE_SITE_PROD')
t.is(env.MONGO_ENV_VAR, undefined)
})
diff --git a/packages/edge-bundler/node/bridge.ts b/packages/edge-bundler/node/bridge.ts
index 8405c60978..a597bd3275 100644
--- a/packages/edge-bundler/node/bridge.ts
+++ b/packages/edge-bundler/node/bridge.ts
@@ -13,11 +13,10 @@ import { getBinaryExtension } from './platform.js'
const DENO_VERSION_FILE = 'version.txt'
-// When updating DENO_VERSION_RANGE, ensure that the deno version installed in the
-// build-image/buildbot does satisfy this range!
-// We're pinning the range because of an issue with v1.45.0 of the Deno CLI:
-// https://linear.app/netlify/issue/FRP-775/deno-cli-v1450-causing-issues
-const DENO_VERSION_RANGE = '1.37.0 - 1.44.4'
+// When updating DENO_VERSION_RANGE, ensure that the deno version
+// on the netlify/buildbot build image satisfies this range!
+// https://github.com/netlify/buildbot/blob/f9c03c9dcb091d6570e9d0778381560d469e78ad/build-image/noble/Dockerfile#L410
+const DENO_VERSION_RANGE = '1.39.0 - 1.46.3'
type OnBeforeDownloadHook = () => void | Promise
type OnAfterDownloadHook = (error?: Error) => void | Promise
diff --git a/packages/edge-bundler/node/server/util.ts b/packages/edge-bundler/node/server/util.ts
index 058018456a..e3c416c634 100644
--- a/packages/edge-bundler/node/server/util.ts
+++ b/packages/edge-bundler/node/server/util.ts
@@ -1,6 +1,9 @@
+import { platform } from 'os'
+
import { ExecaChildProcess } from 'execa'
import fetch from 'node-fetch'
import waitFor from 'p-wait-for'
+import { satisfies } from 'semver'
// 1 second
const SERVER_KILL_TIMEOUT = 1e3
@@ -43,9 +46,19 @@ const killProcess = (ps: ExecaChildProcess) => {
ps.on('close', resolve)
ps.on('error', reject)
- ps.kill('SIGTERM', {
- forceKillAfterTimeout: SERVER_KILL_TIMEOUT,
- })
+ // On Windows with Node 21+, there's a bug where attempting to kill a child process
+ // results in an EPERM error. Ignore the error in that case.
+ // See: https://github.com/nodejs/node/issues/51766
+ // We also disable execa's `forceKillAfterTimeout` in this case
+ // which can cause unhandled rejection.
+ try {
+ ps.kill('SIGTERM', {
+ forceKillAfterTimeout:
+ platform() === 'win32' && satisfies(process.version, '>=21') ? false : SERVER_KILL_TIMEOUT,
+ })
+ } catch {
+ // no-op
+ }
})
}
diff --git a/packages/functions-utils/CHANGELOG.md b/packages/functions-utils/CHANGELOG.md
index 0201f9f226..36edc74196 100644
--- a/packages/functions-utils/CHANGELOG.md
+++ b/packages/functions-utils/CHANGELOG.md
@@ -1,5 +1,95 @@
# Changelog
+## [5.2.93](https://github.com/netlify/build/compare/functions-utils-v5.2.92...functions-utils-v5.2.93) (2024-10-25)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.41.0 to 9.41.1
+
+## [5.2.92](https://github.com/netlify/build/compare/functions-utils-v5.2.91...functions-utils-v5.2.92) (2024-10-21)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.40.2 to 9.41.0
+
+## [5.2.91](https://github.com/netlify/build/compare/functions-utils-v5.2.90...functions-utils-v5.2.91) (2024-10-11)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.40.1 to 9.40.2
+
+## [5.2.90](https://github.com/netlify/build/compare/functions-utils-v5.2.89...functions-utils-v5.2.90) (2024-10-11)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.40.0 to 9.40.1
+
+## [5.2.89](https://github.com/netlify/build/compare/functions-utils-v5.2.88...functions-utils-v5.2.89) (2024-10-10)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.39.7 to 9.40.0
+
+## [5.2.88](https://github.com/netlify/build/compare/functions-utils-v5.2.87...functions-utils-v5.2.88) (2024-10-01)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.39.6 to 9.39.7
+
+## [5.2.87](https://github.com/netlify/build/compare/functions-utils-v5.2.86...functions-utils-v5.2.87) (2024-10-01)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.39.5 to 9.39.6
+
+## [5.2.86](https://github.com/netlify/build/compare/functions-utils-v5.2.85...functions-utils-v5.2.86) (2024-09-26)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.39.4 to 9.39.5
+
+## [5.2.85](https://github.com/netlify/build/compare/functions-utils-v5.2.84...functions-utils-v5.2.85) (2024-09-24)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.39.3 to 9.39.4
+
+## [5.2.84](https://github.com/netlify/build/compare/functions-utils-v5.2.83...functions-utils-v5.2.84) (2024-09-23)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @netlify/zip-it-and-ship-it bumped from 9.39.2 to 9.39.3
+
## [5.2.83](https://github.com/netlify/build/compare/functions-utils-v5.2.82...functions-utils-v5.2.83) (2024-09-20)
diff --git a/packages/functions-utils/package.json b/packages/functions-utils/package.json
index 6b8c5a83c5..9f108be7d0 100644
--- a/packages/functions-utils/package.json
+++ b/packages/functions-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@netlify/functions-utils",
- "version": "5.2.83",
+ "version": "5.2.93",
"description": "Utility for adding Functions files in Netlify Build",
"type": "module",
"exports": "./lib/main.js",
@@ -50,7 +50,7 @@
},
"license": "MIT",
"dependencies": {
- "@netlify/zip-it-and-ship-it": "9.39.2",
+ "@netlify/zip-it-and-ship-it": "9.41.1",
"cpy": "^9.0.0",
"path-exists": "^5.0.0"
},
diff --git a/packages/js-client/package.json b/packages/js-client/package.json
index 65c132544f..a2e876746f 100644
--- a/packages/js-client/package.json
+++ b/packages/js-client/package.json
@@ -41,7 +41,7 @@
"node client"
],
"dependencies": {
- "@netlify/open-api": "^2.34.0",
+ "@netlify/open-api": "^2.35.0",
"lodash-es": "^4.17.21",
"micro-api-client": "^3.3.0",
"node-fetch": "^3.0.0",
diff --git a/packages/testing/src/fs.ts b/packages/testing/src/fs.ts
new file mode 100644
index 0000000000..93d06f1838
--- /dev/null
+++ b/packages/testing/src/fs.ts
@@ -0,0 +1,14 @@
+import { mkdir } from 'fs/promises'
+import { platform } from 'process'
+
+import { execa } from 'execa'
+
+export const unzipFile = async function (path: string, dest: string): Promise {
+ await mkdir(dest, { recursive: true })
+
+ if (platform === 'win32') {
+ await execa('tar', ['-xf', path, '-C', dest])
+ } else {
+ await execa('unzip', ['-o', path, '-d', dest])
+ }
+}
diff --git a/packages/testing/src/index.ts b/packages/testing/src/index.ts
index ebb6511823..4b25185540 100644
--- a/packages/testing/src/index.ts
+++ b/packages/testing/src/index.ts
@@ -1,5 +1,6 @@
export * from './dir.js'
export * from './fixture.js'
+export * from './fs.js'
export * from './normalize.js'
export * from './server.js'
export * from './tcp_server.js'
diff --git a/packages/zip-it-and-ship-it/CHANGELOG.md b/packages/zip-it-and-ship-it/CHANGELOG.md
index ed3a81abdd..a1074ce18a 100644
--- a/packages/zip-it-and-ship-it/CHANGELOG.md
+++ b/packages/zip-it-and-ship-it/CHANGELOG.md
@@ -7,6 +7,81 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
+## [9.41.1](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.41.0...zip-it-and-ship-it-v9.41.1) (2024-10-25)
+
+
+### Bug Fixes
+
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.31.0 ([#5898](https://github.com/netlify/build/issues/5898)) ([a849029](https://github.com/netlify/build/commit/a849029e10547c717c2291c5f6cd56f8ba72e5d3))
+
+## [9.41.0](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.40.2...zip-it-and-ship-it-v9.41.0) (2024-10-21)
+
+
+### Features
+
+* add `bootstrapVersion` field to manifest ([#5890](https://github.com/netlify/build/issues/5890)) ([46de664](https://github.com/netlify/build/commit/46de664d2815c430f7d1b45c8bd92d42a5cc4502))
+
+## [9.40.2](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.40.1...zip-it-and-ship-it-v9.40.2) (2024-10-11)
+
+
+### Bug Fixes
+
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.30.1 ([#5882](https://github.com/netlify/build/issues/5882)) ([bdb19d8](https://github.com/netlify/build/commit/bdb19d8dd26b8b8eeff37311680e3b329e177f14))
+
+## [9.40.1](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.40.0...zip-it-and-ship-it-v9.40.1) (2024-10-11)
+
+
+### Bug Fixes
+
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.29.1 ([#5879](https://github.com/netlify/build/issues/5879)) ([959d7a5](https://github.com/netlify/build/commit/959d7a55c7a395a739f92bee541d762d1629e322))
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.30.0 ([#5881](https://github.com/netlify/build/issues/5881)) ([cc6443c](https://github.com/netlify/build/commit/cc6443c323e61b36dba35d2c983641276ecd28c1))
+
+## [9.40.0](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.39.7...zip-it-and-ship-it-v9.40.0) (2024-10-10)
+
+
+### Features
+
+* write metadata file ([#5875](https://github.com/netlify/build/issues/5875)) ([7dfac4c](https://github.com/netlify/build/commit/7dfac4cc38058e135c4952d53ef6b63c8034f962))
+
+## [9.39.7](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.39.6...zip-it-and-ship-it-v9.39.7) (2024-10-01)
+
+
+### Bug Fixes
+
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.28.1 ([#5867](https://github.com/netlify/build/issues/5867)) ([3a78085](https://github.com/netlify/build/commit/3a780856002ac59df7f6da50b3d521ca14c09f7c))
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.29.0 ([#5869](https://github.com/netlify/build/issues/5869)) ([d4966fb](https://github.com/netlify/build/commit/d4966fbd18ba01d2721334ddcecc0f188bb3bb3c))
+
+## [9.39.6](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.39.5...zip-it-and-ship-it-v9.39.6) (2024-10-01)
+
+
+### Bug Fixes
+
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.27.0 ([#5864](https://github.com/netlify/build/issues/5864)) ([dd86809](https://github.com/netlify/build/commit/dd8680932f0c02aec5dd6fba0ac43af3347eb213))
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.28.0 ([#5865](https://github.com/netlify/build/issues/5865)) ([672281e](https://github.com/netlify/build/commit/672281ec6a943e911f86ec5f67db3b2a9c4c84a5))
+
+## [9.39.5](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.39.4...zip-it-and-ship-it-v9.39.5) (2024-09-26)
+
+
+### Bug Fixes
+
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.26.1 ([#5854](https://github.com/netlify/build/issues/5854)) ([c5e7f26](https://github.com/netlify/build/commit/c5e7f26141b9237e75a52b0ed9ecc7ccb5e504a4))
+
+## [9.39.4](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.39.3...zip-it-and-ship-it-v9.39.4) (2024-09-24)
+
+
+### Bug Fixes
+
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.24.1 ([#5847](https://github.com/netlify/build/issues/5847)) ([501b555](https://github.com/netlify/build/commit/501b55544e2c9a01f0e5b7474217d445bda7754d))
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.25.0 ([#5849](https://github.com/netlify/build/issues/5849)) ([3998337](https://github.com/netlify/build/commit/3998337b86c7f1538d112c6dbb8b861531070862))
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.26.0 ([#5850](https://github.com/netlify/build/issues/5850)) ([efd600c](https://github.com/netlify/build/commit/efd600c8411cab0a9a9a8ccbbc50942724bd1cd5))
+
+## [9.39.3](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.39.2...zip-it-and-ship-it-v9.39.3) (2024-09-23)
+
+
+### Bug Fixes
+
+* **deps:** update dependency @netlify/serverless-functions-api to ^1.24.0 ([#5843](https://github.com/netlify/build/issues/5843)) ([7c88c52](https://github.com/netlify/build/commit/7c88c52732cf2916247ba5bbe8875eabcb709bfd))
+
## [9.39.2](https://github.com/netlify/build/compare/zip-it-and-ship-it-v9.39.1...zip-it-and-ship-it-v9.39.2) (2024-09-20)
diff --git a/packages/zip-it-and-ship-it/package.json b/packages/zip-it-and-ship-it/package.json
index 135737e74b..d7240c135e 100644
--- a/packages/zip-it-and-ship-it/package.json
+++ b/packages/zip-it-and-ship-it/package.json
@@ -1,6 +1,6 @@
{
"name": "@netlify/zip-it-and-ship-it",
- "version": "9.39.2",
+ "version": "9.41.1",
"description": "Zip it and ship it",
"main": "./dist/main.js",
"type": "module",
@@ -42,9 +42,9 @@
},
"dependencies": {
"@babel/parser": "^7.22.5",
- "@babel/types": "7.25.6",
+ "@babel/types": "7.26.3",
"@netlify/binary-info": "^1.0.0",
- "@netlify/serverless-functions-api": "^1.24.0",
+ "@netlify/serverless-functions-api": "^1.31.1",
"@vercel/nft": "^0.27.1",
"archiver": "^7.0.0",
"common-path-prefix": "^3.0.0",
diff --git a/packages/zip-it-and-ship-it/src/feature_flags.ts b/packages/zip-it-and-ship-it/src/feature_flags.ts
index c8198d07e1..9f1a39ab25 100644
--- a/packages/zip-it-and-ship-it/src/feature_flags.ts
+++ b/packages/zip-it-and-ship-it/src/feature_flags.ts
@@ -33,8 +33,8 @@ export const defaultFlags = {
// Adds the `___netlify-telemetry.mjs` file to the function bundle.
zisi_add_instrumentation_loader: true,
- // Adds a `___netlify-bootstrap-version` file to the function bundle.
- zisi_add_version_file: false,
+ // Adds a `___netlify-metadata.json` file to the function bundle.
+ zisi_add_metadata_file: false,
} as const
export type FeatureFlags = Partial>
diff --git a/packages/zip-it-and-ship-it/src/manifest.ts b/packages/zip-it-and-ship-it/src/manifest.ts
index e6d54ce851..41a4481235 100644
--- a/packages/zip-it-and-ship-it/src/manifest.ts
+++ b/packages/zip-it-and-ship-it/src/manifest.ts
@@ -9,20 +9,20 @@ import type { ExtendedRoute, Route } from './utils/routes.js'
interface ManifestFunction {
buildData?: Record
+ bundler?: string
+ displayName?: string
+ excludedRoutes?: Route[]
+ generator?: string
invocationMode?: InvocationMode
mainFile: string
name: string
path: string
+ priority?: number
routes?: ExtendedRoute[]
- excludedRoutes?: Route[]
runtime: string
runtimeVersion?: string
schedule?: string
- displayName?: string
- bundler?: string
- generator?: string
timeout?: number
- priority?: number
trafficRules?: TrafficRules
}
@@ -51,6 +51,7 @@ export const createManifest = async ({ functions, path }: { functions: FunctionR
}
const formatFunctionForManifest = ({
+ bootstrapVersion,
bundler,
displayName,
excludedRoutes,
@@ -74,7 +75,7 @@ const formatFunctionForManifest = ({
generator,
timeout,
invocationMode,
- buildData: { runtimeAPIVersion },
+ buildData: { bootstrapVersion, runtimeAPIVersion },
mainFile,
name,
priority,
diff --git a/packages/zip-it-and-ship-it/src/runtimes/node/index.ts b/packages/zip-it-and-ship-it/src/runtimes/node/index.ts
index 3ed6e10eb8..f488d55d55 100644
--- a/packages/zip-it-and-ship-it/src/runtimes/node/index.ts
+++ b/packages/zip-it-and-ship-it/src/runtimes/node/index.ts
@@ -37,6 +37,7 @@ const getSrcFilesWithBundler: GetSrcFilesFunction = async (parameters) => {
const zipFunction: ZipFunction = async function ({
archiveFormat,
basePath,
+ branch,
cache,
config = {},
destFolder,
@@ -109,10 +110,11 @@ const zipFunction: ZipFunction = async function ({
createPluginsModulesPathAliases(srcFiles, pluginsModulesPath, aliases, finalBasePath)
const generator = mergedConfig?.generator || getInternalValue(isInternal)
- const zipPath = await zipNodeJs({
+ const zipResult = await zipNodeJs({
aliases,
archiveFormat,
basePath: finalBasePath,
+ branch,
cache,
destFolder,
extension,
@@ -150,11 +152,12 @@ const zipFunction: ZipFunction = async function ({
const trafficRules = mergedConfig?.rateLimit ? getTrafficRulesConfig(mergedConfig.rateLimit) : undefined
return {
+ bootstrapVersion: zipResult.bootstrapVersion,
bundler: bundlerName,
bundlerWarnings,
config: mergedConfig,
displayName: mergedConfig?.name,
- entryFilename: zipPath.entryFilename,
+ entryFilename: zipResult.entryFilename,
generator,
timeout: mergedConfig?.timeout,
inputs,
@@ -163,7 +166,7 @@ const zipFunction: ZipFunction = async function ({
invocationMode,
outputModuleFormat,
nativeNodeModules,
- path: zipPath.path,
+ path: zipResult.path,
priority,
trafficRules,
runtimeVersion:
diff --git a/packages/zip-it-and-ship-it/src/runtimes/node/utils/entry_file.ts b/packages/zip-it-and-ship-it/src/runtimes/node/utils/entry_file.ts
index 5e1cf7ab17..837cbe8def 100644
--- a/packages/zip-it-and-ship-it/src/runtimes/node/utils/entry_file.ts
+++ b/packages/zip-it-and-ship-it/src/runtimes/node/utils/entry_file.ts
@@ -18,6 +18,7 @@ import { normalizeFilePath } from './normalize_path.js'
export const ENTRY_FILE_NAME = '___netlify-entry-point'
export const BOOTSTRAP_FILE_NAME = '___netlify-bootstrap.mjs'
export const BOOTSTRAP_VERSION_FILE_NAME = '___netlify-bootstrap-version'
+export const METADATA_FILE_NAME = '___netlify-metadata.json'
export const TELEMETRY_FILE_NAME = '___netlify-telemetry.mjs'
const require = createRequire(import.meta.url)
diff --git a/packages/zip-it-and-ship-it/src/runtimes/node/utils/metadata_file.ts b/packages/zip-it-and-ship-it/src/runtimes/node/utils/metadata_file.ts
new file mode 100644
index 0000000000..ad4edf327c
--- /dev/null
+++ b/packages/zip-it-and-ship-it/src/runtimes/node/utils/metadata_file.ts
@@ -0,0 +1,11 @@
+export interface MetadataFile {
+ bootstrap_version?: string
+ branch?: string
+ version: number
+}
+
+export const getMetadataFile = (bootstrapVersion?: string, branch?: string): MetadataFile => ({
+ bootstrap_version: bootstrapVersion,
+ branch,
+ version: 1,
+})
diff --git a/packages/zip-it-and-ship-it/src/runtimes/node/utils/node_runtime.ts b/packages/zip-it-and-ship-it/src/runtimes/node/utils/node_runtime.ts
index 8b399ace61..194320bac4 100644
--- a/packages/zip-it-and-ship-it/src/runtimes/node/utils/node_runtime.ts
+++ b/packages/zip-it-and-ship-it/src/runtimes/node/utils/node_runtime.ts
@@ -5,6 +5,7 @@ const validRuntimeMap = {
16: 'nodejs16.x',
18: 'nodejs18.x',
20: 'nodejs20.x',
+ 22: 'nodejs22.x',
} as const
const minimumV2Version = 18
diff --git a/packages/zip-it-and-ship-it/src/runtimes/node/utils/zip.ts b/packages/zip-it-and-ship-it/src/runtimes/node/utils/zip.ts
index 657e0583e5..b079430083 100644
--- a/packages/zip-it-and-ship-it/src/runtimes/node/utils/zip.ts
+++ b/packages/zip-it-and-ship-it/src/runtimes/node/utils/zip.ts
@@ -23,13 +23,14 @@ import { cachedLstat, mkdirAndWriteFile } from '../../../utils/fs.js'
import {
BOOTSTRAP_FILE_NAME,
- BOOTSTRAP_VERSION_FILE_NAME,
+ METADATA_FILE_NAME,
conflictsWithEntryFile,
EntryFile,
getEntryFile,
getTelemetryFile,
isNamedLikeEntryFile,
} from './entry_file.js'
+import { getMetadataFile } from './metadata_file.js'
import { ModuleFormat } from './module_format.js'
import { normalizeFilePath } from './normalize_path.js'
import { getPackageJsonIfAvailable } from './package_json.js'
@@ -44,6 +45,7 @@ const DEFAULT_USER_SUBDIRECTORY = 'src'
interface ZipNodeParameters {
aliases?: Map
basePath: string
+ branch?: string
cache: RuntimeCache
destFolder: string
extension: string
@@ -186,6 +188,7 @@ const createDirectory = async function ({
const createZipArchive = async function ({
aliases = new Map(),
basePath,
+ branch,
cache,
destFolder,
extension,
@@ -226,6 +229,7 @@ const createZipArchive = async function ({
const userNamespace = hasEntryFileConflict ? DEFAULT_USER_SUBDIRECTORY : ''
let entryFilename = `${basename(filename, extname(filename))}.js`
+ let bootstrapVersion: string | undefined
if (needsEntryFile) {
const entryFile = getEntryFile({
@@ -251,12 +255,13 @@ const createZipArchive = async function ({
if (runtimeAPIVersion === 2) {
const bootstrapPath = addBootstrapFile(srcFiles, aliases)
- if (featureFlags.zisi_add_version_file === true) {
- const { version: bootstrapVersion } = await getPackageJsonIfAvailable(bootstrapPath)
+ if (featureFlags.zisi_add_metadata_file === true) {
+ const { version } = await getPackageJsonIfAvailable(bootstrapPath)
+ const payload = JSON.stringify(getMetadataFile(version, branch))
- if (bootstrapVersion) {
- addZipContent(archive, bootstrapVersion, BOOTSTRAP_VERSION_FILE_NAME)
- }
+ bootstrapVersion = version
+
+ addZipContent(archive, payload, METADATA_FILE_NAME)
}
}
@@ -279,16 +284,19 @@ const createZipArchive = async function ({
await endZip(archive, output)
- return { path: destPath, entryFilename }
+ return { path: destPath, entryFilename, bootstrapVersion }
+}
+
+interface ZipNodeJsResult {
+ bootstrapVersion?: string
+ entryFilename: string
+ path: string
}
export const zipNodeJs = function ({
archiveFormat,
...options
-}: ZipNodeParameters & { archiveFormat: ArchiveFormat }): Promise<{
- path: string
- entryFilename: string
-}> {
+}: ZipNodeParameters & { archiveFormat: ArchiveFormat }): Promise {
if (archiveFormat === ARCHIVE_FORMAT.ZIP) {
return createZipArchive(options)
}
diff --git a/packages/zip-it-and-ship-it/src/runtimes/runtime.ts b/packages/zip-it-and-ship-it/src/runtimes/runtime.ts
index e527c97adc..2361eb5b55 100644
--- a/packages/zip-it-and-ship-it/src/runtimes/runtime.ts
+++ b/packages/zip-it-and-ship-it/src/runtimes/runtime.ts
@@ -66,6 +66,7 @@ export type ZipFunction = (
args: {
archiveFormat: ArchiveFormat
basePath?: string
+ branch?: string
cache: RuntimeCache
config: FunctionConfig
destFolder: string
diff --git a/packages/zip-it-and-ship-it/src/utils/format_result.ts b/packages/zip-it-and-ship-it/src/utils/format_result.ts
index e5e1d30417..3e2cbb3564 100644
--- a/packages/zip-it-and-ship-it/src/utils/format_result.ts
+++ b/packages/zip-it-and-ship-it/src/utils/format_result.ts
@@ -5,6 +5,7 @@ import { removeUndefined } from './remove_undefined.js'
import type { ExtendedRoute, Route } from './routes.js'
export type FunctionResult = Omit & {
+ bootstrapVersion?: string
routes?: ExtendedRoute[]
excludedRoutes?: Route[]
runtime: RuntimeName
diff --git a/packages/zip-it-and-ship-it/src/zip.ts b/packages/zip-it-and-ship-it/src/zip.ts
index c805e2a934..995ccaa2e0 100644
--- a/packages/zip-it-and-ship-it/src/zip.ts
+++ b/packages/zip-it-and-ship-it/src/zip.ts
@@ -13,7 +13,7 @@ import { getFunctionsFromPaths } from './runtimes/index.js'
import { MODULE_FORMAT } from './runtimes/node/utils/module_format.js'
import { addArchiveSize } from './utils/archive_size.js'
import { RuntimeCache } from './utils/cache.js'
-import { formatZipResult } from './utils/format_result.js'
+import { formatZipResult, FunctionResult } from './utils/format_result.js'
import { listFunctionsDirectories, resolveFunctionsDirectories } from './utils/fs.js'
import { getLogger, LogFunction } from './utils/logger.js'
import { nonNullable } from './utils/non_nullable.js'
@@ -21,6 +21,7 @@ import { nonNullable } from './utils/non_nullable.js'
export interface ZipFunctionOptions {
archiveFormat?: ArchiveFormat
basePath?: string
+ branch?: string
config?: Config
featureFlags?: FeatureFlags
repositoryRoot?: string
@@ -53,6 +54,7 @@ export const zipFunctions = async function (
{
archiveFormat = ARCHIVE_FORMAT.ZIP,
basePath,
+ branch,
config = {},
configFileDirectories,
featureFlags: inputFeatureFlags,
@@ -63,7 +65,7 @@ export const zipFunctions = async function (
debug,
internalSrcFolder,
}: ZipFunctionsOptions = {},
-) {
+): Promise {
validateArchiveFormat(archiveFormat)
const logger = getLogger(systemLog, debug)
@@ -94,6 +96,7 @@ export const zipFunctions = async function (
const zipResult = await func.runtime.zipFunction({
archiveFormat,
basePath,
+ branch,
cache,
config: func.config,
destFolder,
@@ -145,7 +148,7 @@ export const zipFunction = async function (
debug,
internalSrcFolder,
}: ZipFunctionOptions = {},
-) {
+): Promise {
validateArchiveFormat(archiveFormat)
const logger = getLogger(systemLog, debug)
diff --git a/packages/zip-it-and-ship-it/tests/v2api.test.ts b/packages/zip-it-and-ship-it/tests/v2api.test.ts
index 1cddf65d41..a79c6348ac 100644
--- a/packages/zip-it-and-ship-it/tests/v2api.test.ts
+++ b/packages/zip-it-and-ship-it/tests/v2api.test.ts
@@ -709,22 +709,79 @@ describe.runIf(semver.gte(nodeVersion, '18.13.0'))('V2 functions API', () => {
expect(files[0].runtimeVersion).toBe('nodejs20.x')
})
- test('Adds a file with the bootstrap version to the ZIP archive', async () => {
- const fixtureName = 'v2-api'
- const { files } = await zipFixture(fixtureName, {
+ describe('Adds a file with metadata', () => {
+ test('Without a branch', async () => {
+ const fixtureName = 'v2-api'
+ const { files } = await zipFixture(fixtureName, {
+ fixtureDir: FIXTURES_ESM_DIR,
+ opts: {
+ featureFlags: {
+ zisi_add_metadata_file: true,
+ },
+ },
+ })
+ const [unzippedFunction] = await unzipFiles(files)
+ const bootstrapPath = getBootstrapPath()
+ const bootstrapPackageJson = await readFile(resolve(bootstrapPath, '..', '..', 'package.json'), 'utf8')
+ const { version: bootstrapVersion } = JSON.parse(bootstrapPackageJson)
+ const versionFileContents = await readFile(join(unzippedFunction.unzipPath, '___netlify-metadata.json'), 'utf8')
+
+ expect(JSON.parse(versionFileContents)).toEqual({ bootstrap_version: bootstrapVersion, version: 1 })
+ })
+
+ test('With a branch', async () => {
+ const fixtureName = 'v2-api'
+ const { files } = await zipFixture(fixtureName, {
+ fixtureDir: FIXTURES_ESM_DIR,
+ opts: {
+ branch: 'main',
+ featureFlags: {
+ zisi_add_metadata_file: true,
+ },
+ },
+ })
+ const [unzippedFunction] = await unzipFiles(files)
+ const bootstrapPath = getBootstrapPath()
+ const bootstrapPackageJson = await readFile(resolve(bootstrapPath, '..', '..', 'package.json'), 'utf8')
+ const { version: bootstrapVersion } = JSON.parse(bootstrapPackageJson)
+ const versionFileContents = await readFile(join(unzippedFunction.unzipPath, '___netlify-metadata.json'), 'utf8')
+
+ expect(JSON.parse(versionFileContents)).toEqual({
+ bootstrap_version: bootstrapVersion,
+ branch: 'main',
+ version: 1,
+ })
+ })
+ })
+
+ test('Adds a `buildData` object to each function entry in the manifest file', async () => {
+ const bootstrapPath = getBootstrapPath()
+ const bootstrapPackageJson = await readFile(resolve(bootstrapPath, '..', '..', 'package.json'), 'utf8')
+ const { version: bootstrapVersion } = JSON.parse(bootstrapPackageJson)
+
+ const { path: tmpDir } = await getTmpDir({ prefix: 'zip-it-test' })
+ const manifestPath = join(tmpDir, 'manifest.json')
+
+ const { files } = await zipFixture('v2-api', {
fixtureDir: FIXTURES_ESM_DIR,
opts: {
featureFlags: {
- zisi_add_version_file: true,
+ zisi_add_metadata_file: true,
},
+ manifest: manifestPath,
},
})
- const [unzippedFunction] = await unzipFiles(files)
- const bootstrapPath = getBootstrapPath()
- const bootstrapPackageJson = await readFile(resolve(bootstrapPath, '..', '..', 'package.json'), 'utf8')
- const { version: bootstrapVersion } = JSON.parse(bootstrapPackageJson)
- const versionFileContents = await readFile(join(unzippedFunction.unzipPath, '___netlify-bootstrap-version'), 'utf8')
- expect(versionFileContents).toBe(bootstrapVersion)
+ expect(files.length).toBe(1)
+ expect(files[0].name).toBe('function')
+ expect(files[0].bootstrapVersion).toBe(bootstrapVersion)
+ expect(files[0].runtimeAPIVersion).toBe(2)
+
+ const manifestString = await readFile(manifestPath, { encoding: 'utf8' })
+ const manifest = JSON.parse(manifestString)
+
+ expect(manifest.functions.length).toBe(1)
+ expect(manifest.functions[0].name).toBe('function')
+ expect(manifest.functions[0].buildData).toEqual({ bootstrapVersion, runtimeAPIVersion: 2 })
})
})