diff --git a/package-lock.json b/package-lock.json index 26c30c3419..84a3cf6c82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8401,6 +8401,10 @@ "resolved": "packages/config", "link": true }, + "node_modules/@netlify/config-schema": { + "resolved": "packages/config-schema", + "link": true + }, "node_modules/@netlify/edge-bundler": { "resolved": "packages/edge-bundler", "link": true @@ -32022,6 +32026,16 @@ "node": "^14.16.0 || >=16.0.0" } }, + "packages/config-schema": { + "name": "@netlify/config-schema", + "license": "MIT", + "devDependencies": { + "ajv": "^8.12.0", + "fast-glob": "^3.2.12", + "toml": "^3.0.0", + "vitest": "^0.30.1" + } + }, "packages/config/node_modules/@types/node": { "version": "14.18.63", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", diff --git a/packages/config-schema/README.md b/packages/config-schema/README.md new file mode 100644 index 0000000000..fefee8261c --- /dev/null +++ b/packages/config-schema/README.md @@ -0,0 +1,3 @@ +# JSON schema for netlify.toml + +This package includes the JSON schema file for `netlify.toml` diff --git a/packages/config-schema/package.json b/packages/config-schema/package.json new file mode 100644 index 0000000000..a6053993fb --- /dev/null +++ b/packages/config-schema/package.json @@ -0,0 +1,33 @@ +{ + "name": "@netlify/config-schema", + "private": true, + "description": "JSON schema for netlify.toml", + "type": "module", + "author": "Netlify Inc.", + "scripts": { + "build": "echo 'done'", + "test": "vitest run", + "test:dev": "vitest", + "test:ci": "vitest run --reporter=default" + }, + "keywords": [ + "netlify", + "netlify.toml" + ], + "homepage": "https://github.com/netlify/build", + "repository": { + "type": "git", + "url": "https://github.com/netlify/build.git", + "directory": "packages/config-schema" + }, + "bugs": { + "url": "https://github.com/netlify/build/issues" + }, + "license": "MIT", + "devDependencies": { + "ajv": "^8.12.0", + "fast-glob": "^3.2.12", + "toml": "^3.0.0", + "vitest": "^0.30.1" + } +} diff --git a/packages/config-schema/src/schema/netlify-toml.schema.json b/packages/config-schema/src/schema/netlify-toml.schema.json new file mode 100644 index 0000000000..cdaa8ab6ab --- /dev/null +++ b/packages/config-schema/src/schema/netlify-toml.schema.json @@ -0,0 +1,729 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Netlify TOML config schema", + "description": "Config file for Netlify", + "type": "object", + "definitions": { + "BasePath": { + "title": "Base path", + "description": "Directory to change to before starting a build. This is where we will look for dependency management files such as `package.json` or `.nvmrc`. If not set, defaults to the root directory.", + "type": "string", + "default": "project/" + }, + "EdgeFunctionPath": { + "title": "Edge functions directory", + "description": "Custom path to your edge functions directory. If not set, defaults to `netlify/edge-functions/`.", + "type": "string", + "default": "netlify/edge-functions/" + }, + "PublishPath": { + "title": "Publish path", + "description": "Directory that contains the deploy-ready HTML files and assets generated by the build. This is relative to the root directory or relative to the base directory if one has been set.", + "type": "string", + "default": "build/" + }, + "BuildCommand": { "title": "Build command", "description": "Default build command.", "type": "string" }, + "Environment": { + "title": "Environment Variables", + "description": "Define build environment variables. Variables set here override those set with the Netlify UI, CLI, or API. Be mindful when using this option and avoid committing sensitive values to public source repositories.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "Processing": { + "title": "Post processing", + "description": "Manage post processing settings for your builds. These values will override values set in the Netlify UI.\n\nNote that `skip_processing` must be set to `false` for any other settings to take effect.", + "type": "object", + "additionalProperties": false, + "properties": { + "skip_processing": { + "title": "Skip Processing", + "description": "Skip all post processing and ignore all other settings.", + "type": "boolean", + "default": false + }, + "css": { + "title": "CSS", + "description": "Processing settings for CSS files.", + "type": "object", + "additionalProperties": false, + "properties": { + "bundle": { + "title": "Bundle CSS", + "description": "Concatenate consecutive CSS files together to reduce HTTP requests.", + "type": "boolean", + "default": true + }, + "minify": { + "title": "Minify CSS", + "description": "Run CSS through a minifier to reduce file size.", + "type": "boolean", + "default": true + } + } + }, + "js": { + "title": "JS", + "description": "Processing settings for JavaScript files.", + "type": "object", + "additionalProperties": false, + "properties": { + "bundle": { + "title": "Bundle JS", + "description": "Concatenate consecutive JS files together to reduce HTTP requests.", + "type": "boolean", + "default": true + }, + "minify": { + "title": "Minify JS", + "description": "Run JS through a minifier to reduce file size.", + "type": "boolean", + "default": true + } + } + }, + "html": { + "title": "HTML", + "description": "Processing settings for HTML files.", + "type": "object", + "additionalProperties": false, + "properties": { + "pretty_urls": { + "title": "Pretty URLs", + "description": "Rewrite link URLs to pretty URLs. For example, we will rewrite `/about.html` to `/about` and `/about/index.html` to `/about/`.", + "type": "boolean" + } + } + }, + "images": { + "title": "Images", + "description": "Processing settings for image files.", + "type": "object", + "additionalProperties": false, + "properties": { + "compress": { + "title": "Compress Images", + "description": "Run all images through lossless image compression.", + "type": "boolean" + } + } + } + } + }, + "BuildOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "base": { "$ref": "#/definitions/BasePath" }, + "command": { "$ref": "#/definitions/BuildCommand" }, + "edge_functions": { "$ref": "#/definitions/EdgeFunctionPath" }, + "environment": { "$ref": "#/definitions/Environment" }, + "functions": { + "title": "[DEPRECATED] Directory", + "description": " [DEPRECATED] Use `[functions] directory=\"\"`", + "type": "string", + "default": "netlify/functions", + "deprecated": true + }, + "processing": { "$ref": "#/definitions/Processing" }, + "publish": { "$ref": "#/definitions/PublishPath" } + }, + "title": "Build settings", + "description": "Settings under [build] are global and are applied to all deploy contexts unless they are overridden by settings for more specific deploy contexts.", + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/configure-builds/file-based-configuration/#build-settings" + } + } + }, + "Plugins": { + "type": "array", + "default": [{ "package": "" }], + "items": { + "title": "Build Plugins", + "description": "Netlify Build Plugins extend the functionality of the build process.", + "type": "object", + "properties": { + "package": { + "type": "string", + "description": "Package name of the build plugin." + }, + "inputs": { + "type": "object", + "description": "Additional inputs for configuring the build plugin." + } + }, + "additionalProperties": false, + "required": ["package"] + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/configure-builds/file-based-configuration/#build-plugins" + } + } + }, + "ProductionContext": { + "title": "Production context", + "description": "All deploys from your site’s production branch will inherit these settings. You can define environment variables here but we recommend using the Netlify UI for sensitive values to keep them out of your source repository.", + "type": "object", + "additionalProperties": false, + "properties": { + "base": { "$ref": "#/definitions/BasePath" }, + "command": { "$ref": "#/definitions/BuildCommand" }, + "edge_functions": { "$ref": "#/definitions/EdgeFunctionPath" }, + "environment": { "$ref": "#/definitions/Environment" }, + "plugins": { "$ref": "#/definitions/Plugins" }, + "processing": { "$ref": "#/definitions/Processing" }, + "publish": { "$ref": "#/definitions/PublishPath" } + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/configure-builds/file-based-configuration/#deploy-contexts" + } + } + }, + "DeployPreviewContext": { + "title": "Deploy Preview context", + "description": "All deploys generated from a pull/merge request will inherit these settings.", + "type": "object", + "additionalProperties": false, + "properties": { + "base": { "$ref": "#/definitions/BasePath" }, + "command": { "$ref": "#/definitions/BuildCommand" }, + "edge_functions": { "$ref": "#/definitions/EdgeFunctionPath" }, + "environment": { "$ref": "#/definitions/Environment" }, + "plugins": { "$ref": "#/definitions/Plugins" }, + "processing": { "$ref": "#/definitions/Processing" }, + "publish": { "$ref": "#/definitions/PublishPath" } + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/configure-builds/file-based-configuration/#deploy-contexts" + } + } + }, + "BranchDeployContext": { + "title": "Branch Deploy context", + "description": "All deploys generated from a branch that is not your production branch will inherit these settings.", + "type": "object", + "additionalProperties": false, + "properties": { + "base": { "$ref": "#/definitions/BasePath" }, + "command": { "$ref": "#/definitions/BuildCommand" }, + "edge_functions": { "$ref": "#/definitions/EdgeFunctionPath" }, + "environment": { "$ref": "#/definitions/Environment" }, + "plugins": { "$ref": "#/definitions/Plugins" }, + "processing": { "$ref": "#/definitions/Processing" }, + "publish": { "$ref": "#/definitions/PublishPath" } + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/configure-builds/file-based-configuration/#deploy-contexts" + } + } + }, + "SpecificBranchContext": { + "title": "Specific branch context", + "description": "All deploys from this specific branch will inherit these settings and they'll take precedence over all other context settings for this branch — including settings for Deploy Preview and branch deploy contexts. To set a specific context for a branch with special characters, enclose the branch name with quotes.", + "type": "object", + "additionalProperties": false, + "properties": { + "base": { "$ref": "#/definitions/BasePath" }, + "command": { "$ref": "#/definitions/BuildCommand" }, + "edge_functions": { "$ref": "#/definitions/EdgeFunctionPath" }, + "environment": { "$ref": "#/definitions/Environment" }, + "plugins": { "$ref": "#/definitions/Plugins" }, + "processing": { "$ref": "#/definitions/Processing" }, + "publish": { "$ref": "#/definitions/PublishPath" } + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/configure-builds/file-based-configuration/#deploy-contexts" + } + } + }, + "FunctionConfigNodeBundler": { + "title": "Node bundler", + "description": "Function bundling method used in `@netlify/zip-it-and-ship-it`.", + "type": "string", + "enum": ["zisi", "esbuild", "nft", "none"], + "x-taplo": { + "docs": { + "enumValues": [ + "Default function bundling method for JavaScript functions.", + "Method that leverages `esbuild` to bundle functions, resulting in shorter bundling times and smaller artifacts. TypeScript functions use `esbuild` by default.", + "Method that leverages `nft` to bundle functions, resulting in shorter bundling times and smaller artifacts. ESM functions use `nft` by default.", + "Disables bundling completely and expects the function to be already bundled and usable." + ] + }, + "links": { + "key": "https://github.com/netlify/zip-it-and-ship-it" + } + } + }, + "FunctionConfigExternalNodeModules": { + "title": "External node modules", + "description": "List of Node.js modules that are copied to the bundled artifact without adjusting their source or references during the bundling process; only applies when `node_bundler` is set to `esbuild`. This property helps handle dependencies that can't be inlined, such as modules with native add-ons.", + "type": "array", + "items": { + "type": "string" + } + }, + "FunctionConfigIgnoredNodeModules": { + "title": "Ignored node modules", + "description": "List of Node.js modules that are ignored during bundling.", + "type": "array", + "items": { + "type": "string" + } + }, + "FunctionConfigIncludedFiles": { + "title": "Included files", + "description": "List of additional paths to include in the function bundle. Although our build system includes statically referenced files (like `require(\"./some-file.js\")`) by default, `included_files` lets you specify additional files or directories and reference them dynamically in function code. You can use `*` to match any character or prefix an entry with `!` to exclude files. Paths are relative to the base directory.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "properties": { + "build": { + "$ref": "#/definitions/BuildOptions" + }, + "plugins": { + "$ref": "#/definitions/Plugins" + }, + "context": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/SpecificBranchContext" + }, + "properties": { + "production": { + "$ref": "#/definitions/ProductionContext" + }, + "deploy-preview": { + "$ref": "#/definitions/DeployPreviewContext" + }, + "branch-deploy": { + "$ref": "#/definitions/BranchDeployContext" + } + } + }, + "redirects": { + "type": "array", + "default": [{ "from": "/old-path", "to": "/new-path" }], + "items": { + "title": "Redirects", + "description": "Declare global redirects for all builds. For each redirect you want to declare, add an entry with the [[redirects]] heading. If you need context-specific redirect rules, use a `_headers` or `_redirects` file instead.", + "type": "object", + "default": { "from": "/old-path", "to": "/new-path" }, + "properties": { + "from": { + "title": "From", + "description": "Path you want to redirect.", + "type": "string", + "default": "/old-path" + }, + "to": { + "title": "To", + "description": "URL or path you want to redirect to.", + "type": "string", + "default": "/new-path" + }, + "status": { + "title": "HTTP status code", + "description": "HTTP status code you want to use in that redirect; 301 by default.", + "type": "integer", + "default": 301, + "minimum": 100, + "exclusiveMaximum": 600 + }, + "force": { + "title": "Force", + "description": "Whether to override any existing content in the path or not; false by default.", + "type": "boolean", + "default": true, + "x-taplo": { + "docs": { + "main": "Whether to override any existing content in the path or not; false by default. Visit the [shadowing](https://docs.netlify.com/routing/redirects/rewrites-proxies/#shadowing) instructions for more details." + }, + "links": { + "key": "https://docs.netlify.com/routing/redirects/rewrites-proxies/#shadowing" + } + } + }, + "query": { + "title": "Query string parameters", + "description": "Query string parameters REQUIRED to match the redirect.", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "x-taplo": { + "docs": { + "main": "Query string parameters REQUIRED to match the redirect. Visit the [query parameters](https://docs.netlify.com/routing/redirects/redirect-options/#query-parameters) instructions for more details." + }, + "links": { + "key": "https://docs.netlify.com/routing/redirects/redirect-options/#query-parameters" + } + } + }, + "conditions": { + "title": "Conditions", + "description": "Redirect based on conditions including browser language, geolocation, identity role, or cookie presence.", + "type": "object", + "properties": { + "Cookie": { + "title": "Cookie presence", + "description": "Cookie-based redirects allow you to send visitors content based on whether a specific HTTP cookie exists in the request or not, regardless of its value. This condition checks the cookie name in a case-insensitive way.", + "type": "array", + "items": { + "type": "string" + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/routing/redirects/redirect-options/#redirect-by-cookie-presence" + } + } + }, + "Country": { + "title": "Country", + "description": "The Country attribute accepts ISO 3166-1 alpha-2 country codes.", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-zA-Z]{2}$" + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/routing/redirects/redirect-options/#redirect-by-country-or-language" + } + } + }, + "Language": { + "title": "Language", + "description": "The Language attribute accepts standard browser language identification codes and locale codes that combine language and country.", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-zA-Z]{2}(-[a-zA-Z]{2})?$" + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/routing/redirects/redirect-options/#redirect-by-country-or-language" + } + } + }, + "Role": { + "title": "Role", + "description": "Role-based redirects let you restrict access to certain paths of your application to logged-in visitors with certain roles, as authorized by Netlify Identity or any authentication provider that supports JSON Web Tokens (JWT).", + "type": "array", + "items": { + "type": "string" + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/routing/redirects/redirect-options/#redirect-by-role" + } + } + } + } + }, + "headers": { + "title": "Request headers", + "description": "Additional request headers to send in proxy redirects.", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "x-taplo": { + "docs": { + "main": "Additional request headers to send in [proxy redirects](https://docs.netlify.com/routing/redirects/rewrites-proxies/#custom-headers-in-proxy-redirects)." + }, + "links": { + "key": "https://docs.netlify.com/routing/redirects/rewrites-proxies/#custom-headers-in-proxy-redirects" + } + } + }, + "signed": { + "title": "Signed", + "description": "Name of the environment variable that contains the token to use for signed proxy redirects.", + "type": "string", + "x-taplo": { + "docs": { + "main": "Name of the environment variable that contains the token to use for [signed proxy redirects](https://docs.netlify.com/routing/redirects/rewrites-proxies/#signed-proxy-redirects)." + }, + "links": { + "key": "https://docs.netlify.com/routing/redirects/rewrites-proxies/#signed-proxy-redirects" + } + } + } + }, + "additionalProperties": false, + "required": ["from"], + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/routing/redirects/#syntax-for-the-netlify-configuration-file" + } + } + } + }, + "headers": { + "type": "array", + "items": { + "title": "Headers", + "description": "Define custom headers for specific paths.", + "type": "object", + "properties": { + "for": { + "title": "For", + "description": "Define which paths this specific headers block will cover.", + "type": "string" + }, + "values": { + "title": "Values", + "description": "Define the actual headers.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": ["for"], + "additionalProperties": false + }, + "x-taplo": { + "docs": { + "main": "Additional request headers to send in [proxy redirects](https://docs.netlify.com/routing/redirects/rewrites-proxies/#custom-headers-in-proxy-redirects)." + }, + "links": { + "key": "https://docs.netlify.com/routing/redirects/rewrites-proxies/#custom-headers-in-proxy-redirects" + } + } + }, + "functions": { + "title": "Functions", + "description": "Although there are default settings for Netlify Functions to help you get started, you can use this section for optional, custom configuration.", + "type": "object", + "additionalProperties": { + "description": "Set these options for specific functions by filtering them by function name. You can use glob patterns in your filter. If a function matches several configuration blocks containing one of these properties, the values are concatenated.", + "type": "object", + "properties": { + "node_bundler": { "$ref": "#/definitions/FunctionConfigNodeBundler" }, + "external_node_modules": { "$ref": "#/definitions/FunctionConfigExternalNodeModules" }, + "ignored_node_modules": { "$ref": "#/definitions/FunctionConfigIgnoredNodeModules" }, + "included_files": { "$ref": "#/definitions/FunctionConfigIncludedFiles" }, + "schedule": { + "title": "Schedule", + "description": "Define the cron expression for a scheduled function.", + "type": "string", + "default": "0 0 * * *", + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/functions/scheduled-functions/" + } + } + } + }, + "additionalProperties": false + }, + "properties": { + "deno_import_map": { + "title": "Import maps", + "description": "Relative path to an import map file to map module URLs to names.", + "type": "string" + }, + "directory": { + "title": "Directory", + "description": "Custom path to your functions. The default location is `YOUR_BASE_DIRECTORY/netlify/functions`.", + "type": "string", + "default": "netlify/functions" + }, + "external_node_modules": { "$ref": "#/definitions/FunctionConfigExternalNodeModules" }, + "ignored_node_modules": { "$ref": "#/definitions/FunctionConfigIgnoredNodeModules" }, + "included_files": { "$ref": "#/definitions/FunctionConfigIncludedFiles" }, + "node_bundler": { "$ref": "#/definitions/FunctionConfigNodeBundler" } + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/configure-builds/file-based-configuration/#functions" + } + } + }, + "edge_functions": { + "type": "array", + "default": [{ "path": "/path", "function": "function-name" }], + "items": { + "title": "Edge Functions", + "description": "While you can declare edge functions inline, you can use this section for more advanced edge function configurations.", + "type": "object", + "properties": { + "cache": { + "type": "string", + "description": "Should the response of the edge function be cached.", + "enum": ["off", "manual"], + "x-taplo": { + "docs": { + "enumValues": [ + "Default, edge function response is not cached.", + "Edge function response is cached in the Netlify CDN based on the specified caching headers." + ] + } + } + }, + "function": { + "type": "string", + "description": "Name of the function." + }, + "path": { + "type": "string", + "description": "Path pattern to associate with this edge function.", + "pattern": "^/.*" + } + }, + "additionalProperties": false, + "required": ["path", "function"] + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/edge-functions/declarations/" + } + } + }, + "dev": { + "title": "Netlify Dev", + "description": "Netlify Dev enables a local development environment without any additional setup and this includes using the functions directory setting to scaffold and serve your functions locally. You can use the `[dev]` section to configure this local development environment. \n\nNote that `[dev]` doesn't run in the Bash shell, so you won't be able to use Bash-compatible syntax with the command.", + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "title": "Dev command", + "description": "Command that starts your development server or runs a command such as a compiler watch in the background. If no `targetPort` is specified, it runs the command in the background in addition to the static file server.", + "type": "string" + }, + "port": { + "title": "Port", + "description": "Port that Netlify Dev is accessible from in the browser.", + "type": "number", + "default": 8888 + }, + "staticServerPort": { + "title": "Static server port", + "description": "Port for the static file server. This value will only take effect if `framework` is set to `#static` or `#auto` and no framework is detected.", + "type": "number" + }, + "targetPort": { + "title": "Target port", + "description": "Port for your application server, framework, or site generator. If provided, the CLI will wait until the provided `targetPort` is reachable and then proxy requests to it. If you specify values for both `command` and `targetPort`, `framework` must be `#custom`.", + "type": "number" + }, + "functionsPort": { + "title": "Functions port", + "description": "Port where Netlify Dev serves functions.", + "type": "number" + }, + "publish": { + "title": "Publish path", + "description": "Path to your static content folder.", + "type": "string" + }, + "jwtRolePath": { + "title": "JWT Role Path", + "description": "Object path that points to role values for JWT-based redirects.", + "type": "string" + }, + "jwtSecret": { + "title": "JWT Secret", + "description": "Secret used to verify tokens for JWT-based redirects.", + "type": "string" + }, + "envFiles": { + "title": "Dotenv files", + "description": "List of dotenv files to consider loading. Defaults to `.env.development.local, .env.local, .env.development, .env` in this order.", + "type": "array", + "default": [".env.development.local", ".env.local", ".env.development", ".env"], + "items": { + "title": "Filename", + "description": "The name of the dotenv file.", + "type": "string" + } + }, + "autoLaunch": { + "title": "Automatically launch browser", + "description": "Boolean value that determines whether Netlify Dev launches the local server address in your browser.", + "type": "boolean" + }, + "framework": { + "title": "Framework", + "description": "Setting to use if a project is detected incorrectly, flagged by multiple detectors, or requires a `command` and `targetPort`. Possible values are `#auto`, `#static`, `#custom` or a framework name.", + "type": "string", + "default": "#auto" + }, + "https": { + "title": "HTTPS", + "description": "Specify an SSL/TLS certificate and key file for the Netlify Dev local server. By default, Netlify Dev starts an HTTP server, but you can configure a certificate and key file if you require HTTPS.", + "type": "object", + "properties": { + "certFile": { + "title": "Certificate file", + "description": "Path to the certificate file.", + "type": "string" + }, + "keyFile": { + "title": "Private key file", + "description": "Path to the private key file.", + "type": "string" + } + }, + "required": ["certFile", "keyFile"], + "additionalProperties": false + } + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/configure-builds/file-based-configuration/#netlify-dev" + } + } + }, + "template": { + "title": "Template configuration", + "description": "", + "type": "object", + "additionalProperties": false, + "properties": { + "incoming-hooks": { + "title": "Incoming hooks", + "description": "A list of incoming hooks for the user’s site. These hooks allow third party services, such as Contentful and DatoCMS, to control when the site is deployed.", + "type": "array", + "default": [""], + "items": { + "title": "Integrations", + "description": "The name of an integration.", + "type": "string" + } + }, + "environment": { + "title": "Environment Variables", + "description": "A list of required environment variables. This allows users to configure specific configuration options upon deployment. It also enables customization without having to change the code of the base template.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/site-deploys/create-deploys/#template-configuration" + } + } + } + }, + "x-taplo": { + "links": { + "key": "https://docs.netlify.com/configure-builds/file-based-configuration/" + } + } +} diff --git a/packages/config-schema/tests/fixtures/build-errors/snapshot.snap b/packages/config-schema/tests/fixtures/build-errors/snapshot.snap new file mode 100644 index 0000000000..b5ff5e9d59 --- /dev/null +++ b/packages/config-schema/tests/fixtures/build-errors/snapshot.snap @@ -0,0 +1,59 @@ +{ + "errors": [ + { + "instancePath": "/build", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "abcd", + }, + "schemaPath": "#/additionalProperties", + }, + { + "instancePath": "/build/base", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/BasePath/type", + }, + { + "instancePath": "/build/command", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/BuildCommand/type", + }, + { + "instancePath": "/build/edge_functions", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/EdgeFunctionPath/type", + }, + { + "instancePath": "/build/environment/FOO", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/Environment/additionalProperties/type", + }, + { + "instancePath": "/build/publish", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/PublishPath/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/build-errors/test.toml b/packages/config-schema/tests/fixtures/build-errors/test.toml new file mode 100644 index 0000000000..33a139c303 --- /dev/null +++ b/packages/config-schema/tests/fixtures/build-errors/test.toml @@ -0,0 +1,9 @@ +[build] +base = 1 +publish = 1 +command = 1 +edge_functions = 1 +abcd = "string" + +[build.environment] +FOO = 1 diff --git a/packages/config-schema/tests/fixtures/build-errors2/snapshot.snap b/packages/config-schema/tests/fixtures/build-errors2/snapshot.snap new file mode 100644 index 0000000000..8af91d6f9a --- /dev/null +++ b/packages/config-schema/tests/fixtures/build-errors2/snapshot.snap @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "instancePath": "/build/environment", + "keyword": "type", + "message": "must be object", + "params": { + "type": "object", + }, + "schemaPath": "#/definitions/Environment/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/build-errors2/test.toml b/packages/config-schema/tests/fixtures/build-errors2/test.toml new file mode 100644 index 0000000000..09289d2ecc --- /dev/null +++ b/packages/config-schema/tests/fixtures/build-errors2/test.toml @@ -0,0 +1,2 @@ +[build] +environment = 1 diff --git a/packages/config-schema/tests/fixtures/build-pass/snapshot.snap b/packages/config-schema/tests/fixtures/build-pass/snapshot.snap new file mode 100644 index 0000000000..5a4012064f --- /dev/null +++ b/packages/config-schema/tests/fixtures/build-pass/snapshot.snap @@ -0,0 +1,4 @@ +{ + "errors": undefined, + "valid": true, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/build-pass/test.toml b/packages/config-schema/tests/fixtures/build-pass/test.toml new file mode 100644 index 0000000000..ed5741d272 --- /dev/null +++ b/packages/config-schema/tests/fixtures/build-pass/test.toml @@ -0,0 +1,9 @@ +[build] +base = "static/" +publish = "build-output/" +command = "echo 'hello'" +edge_functions = "edge-functions-path/" +functions = "folder" + +[build.environment] +FOO = "FOO" diff --git a/packages/config-schema/tests/fixtures/context-errors/snapshot.snap b/packages/config-schema/tests/fixtures/context-errors/snapshot.snap new file mode 100644 index 0000000000..1c312bc0ed --- /dev/null +++ b/packages/config-schema/tests/fixtures/context-errors/snapshot.snap @@ -0,0 +1,95 @@ +{ + "errors": [ + { + "instancePath": "/context/deploy-preview", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "other", + }, + "schemaPath": "#/additionalProperties", + }, + { + "instancePath": "/context/deploy-preview/environment/ACCESS_TOKEN", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/Environment/additionalProperties/type", + }, + { + "instancePath": "/context/dev", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "this", + }, + "schemaPath": "#/additionalProperties", + }, + { + "instancePath": "/context/feature", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "non", + }, + "schemaPath": "#/additionalProperties", + }, + { + "instancePath": "/context/production", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "any", + }, + "schemaPath": "#/additionalProperties", + }, + { + "instancePath": "/context/production/base", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/BasePath/type", + }, + { + "instancePath": "/context/production/command", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/BuildCommand/type", + }, + { + "instancePath": "/context/production/edge_functions", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/EdgeFunctionPath/type", + }, + { + "instancePath": "/context/production/plugins", + "keyword": "type", + "message": "must be array", + "params": { + "type": "array", + }, + "schemaPath": "#/definitions/Plugins/type", + }, + { + "instancePath": "/context/production/publish", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/PublishPath/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/context-errors/test.toml b/packages/config-schema/tests/fixtures/context-errors/test.toml new file mode 100644 index 0000000000..c105c970ad --- /dev/null +++ b/packages/config-schema/tests/fixtures/context-errors/test.toml @@ -0,0 +1,21 @@ +[context.production] +base = 1 +command = 1 +publish = 1 +edge_functions = 1 +[context.production.any] +ACCESS_TOKEN = "super secret" +[context.production.plugins] +package = "@netlify/plugin-sitemap" + +[context.deploy-preview.other] +ACCESS_TOKEN = "not so secret" + +[context.deploy-preview.environment] +ACCESS_TOKEN = 1 + +[context.dev.this] +NODE_ENV = "development" + +[context.feature.non] +command = "make feature" diff --git a/packages/config-schema/tests/fixtures/context-pass/snapshot.snap b/packages/config-schema/tests/fixtures/context-pass/snapshot.snap new file mode 100644 index 0000000000..5a4012064f --- /dev/null +++ b/packages/config-schema/tests/fixtures/context-pass/snapshot.snap @@ -0,0 +1,4 @@ +{ + "errors": undefined, + "valid": true, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/context-pass/test.toml b/packages/config-schema/tests/fixtures/context-pass/test.toml new file mode 100644 index 0000000000..bba187eebd --- /dev/null +++ b/packages/config-schema/tests/fixtures/context-pass/test.toml @@ -0,0 +1,21 @@ +[context.production] +command = "make production" +[context.production.environment] +ACCESS_TOKEN = "super secret" +[[context.production.plugins]] +package = "@netlify/plugin-sitemap" + +[context.deploy-preview.environment] +ACCESS_TOKEN = "not so secret" + +[context.branch-deploy] +command = "make staging" + +[context.dev.environment] +NODE_ENV = "development" + +[context.feature] +command = "make feature" + +[context."features/branch"] +command = "gulp" diff --git a/packages/config-schema/tests/fixtures/dev-errors/snapshot.snap b/packages/config-schema/tests/fixtures/dev-errors/snapshot.snap new file mode 100644 index 0000000000..a617b2923d --- /dev/null +++ b/packages/config-schema/tests/fixtures/dev-errors/snapshot.snap @@ -0,0 +1,140 @@ +{ + "errors": [ + { + "instancePath": "/dev", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "other", + }, + "schemaPath": "#/properties/dev/additionalProperties", + }, + { + "instancePath": "/dev/autoLaunch", + "keyword": "type", + "message": "must be boolean", + "params": { + "type": "boolean", + }, + "schemaPath": "#/properties/dev/properties/autoLaunch/type", + }, + { + "instancePath": "/dev/command", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/dev/properties/command/type", + }, + { + "instancePath": "/dev/envFiles", + "keyword": "type", + "message": "must be array", + "params": { + "type": "array", + }, + "schemaPath": "#/properties/dev/properties/envFiles/type", + }, + { + "instancePath": "/dev/framework", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/dev/properties/framework/type", + }, + { + "instancePath": "/dev/functionsPort", + "keyword": "type", + "message": "must be number", + "params": { + "type": "number", + }, + "schemaPath": "#/properties/dev/properties/functionsPort/type", + }, + { + "instancePath": "/dev/https", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "nay", + }, + "schemaPath": "#/properties/dev/properties/https/additionalProperties", + }, + { + "instancePath": "/dev/https/certFile", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/dev/properties/https/properties/certFile/type", + }, + { + "instancePath": "/dev/https/keyFile", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/dev/properties/https/properties/keyFile/type", + }, + { + "instancePath": "/dev/jwtRolePath", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/dev/properties/jwtRolePath/type", + }, + { + "instancePath": "/dev/jwtSecret", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/dev/properties/jwtSecret/type", + }, + { + "instancePath": "/dev/port", + "keyword": "type", + "message": "must be number", + "params": { + "type": "number", + }, + "schemaPath": "#/properties/dev/properties/port/type", + }, + { + "instancePath": "/dev/publish", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/dev/properties/publish/type", + }, + { + "instancePath": "/dev/staticServerPort", + "keyword": "type", + "message": "must be number", + "params": { + "type": "number", + }, + "schemaPath": "#/properties/dev/properties/staticServerPort/type", + }, + { + "instancePath": "/dev/targetPort", + "keyword": "type", + "message": "must be number", + "params": { + "type": "number", + }, + "schemaPath": "#/properties/dev/properties/targetPort/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/dev-errors/test.toml b/packages/config-schema/tests/fixtures/dev-errors/test.toml new file mode 100644 index 0000000000..8d74daba3f --- /dev/null +++ b/packages/config-schema/tests/fixtures/dev-errors/test.toml @@ -0,0 +1,17 @@ +[dev] +command = 1 +port = "string" +targetPort = "8000" +functionsPort = "8000" +staticServerPort = "5858" +publish = 1 +jwtRolePath = 1 +jwtSecret = 1 +autoLaunch = 1 +framework = 1 +other = 1 +envFiles = 1 +[dev.https] +certFile = 1 +keyFile = 1 +nay = 1 diff --git a/packages/config-schema/tests/fixtures/dev-errors2/snapshot.snap b/packages/config-schema/tests/fixtures/dev-errors2/snapshot.snap new file mode 100644 index 0000000000..5c6afd8580 --- /dev/null +++ b/packages/config-schema/tests/fixtures/dev-errors2/snapshot.snap @@ -0,0 +1,23 @@ +{ + "errors": [ + { + "instancePath": "/dev/envFiles/0", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/dev/properties/envFiles/items/type", + }, + { + "instancePath": "/dev/https", + "keyword": "required", + "message": "must have required property 'keyFile'", + "params": { + "missingProperty": "keyFile", + }, + "schemaPath": "#/properties/dev/properties/https/required", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/dev-errors2/test.toml b/packages/config-schema/tests/fixtures/dev-errors2/test.toml new file mode 100644 index 0000000000..4ff89a5f62 --- /dev/null +++ b/packages/config-schema/tests/fixtures/dev-errors2/test.toml @@ -0,0 +1,4 @@ +[dev] +envFiles = [1] +[dev.https] +certFile = "cert.pem" diff --git a/packages/config-schema/tests/fixtures/dev-pass/snapshot.snap b/packages/config-schema/tests/fixtures/dev-pass/snapshot.snap new file mode 100644 index 0000000000..5a4012064f --- /dev/null +++ b/packages/config-schema/tests/fixtures/dev-pass/snapshot.snap @@ -0,0 +1,4 @@ +{ + "errors": undefined, + "valid": true, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/dev-pass/test.toml b/packages/config-schema/tests/fixtures/dev-pass/test.toml new file mode 100644 index 0000000000..770c34a0a0 --- /dev/null +++ b/packages/config-schema/tests/fixtures/dev-pass/test.toml @@ -0,0 +1,15 @@ +[dev] +command = "yarn start" +port = 8888 +targetPort = 3000 +functionsPort = 3000 +staticServerPort = 5858 +publish = "dist" +jwtRolePath = "app_metadata.authorization.roles" +jwtSecret = "MY_JWT_SECRET_VALUE" +autoLaunch = true +framework = "#custom" +envFiles = [".env", ".env.local"] +[dev.https] +certFile = "cert.pem" +keyFile = "key.pem" diff --git a/packages/config-schema/tests/fixtures/edge-functions-errors/snapshot.snap b/packages/config-schema/tests/fixtures/edge-functions-errors/snapshot.snap new file mode 100644 index 0000000000..ec4967ecd4 --- /dev/null +++ b/packages/config-schema/tests/fixtures/edge-functions-errors/snapshot.snap @@ -0,0 +1,92 @@ +{ + "errors": [ + { + "instancePath": "/edge_functions/0/cache", + "keyword": "enum", + "message": "must be equal to one of the allowed values", + "params": { + "allowedValues": [ + "off", + "manual", + ], + }, + "schemaPath": "#/properties/edge_functions/items/properties/cache/enum", + }, + { + "instancePath": "/edge_functions/0/function", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/edge_functions/items/properties/function/type", + }, + { + "instancePath": "/edge_functions/0/path", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/edge_functions/items/properties/path/type", + }, + { + "instancePath": "/edge_functions/1", + "keyword": "required", + "message": "must have required property 'path'", + "params": { + "missingProperty": "path", + }, + "schemaPath": "#/properties/edge_functions/items/required", + }, + { + "instancePath": "/edge_functions/1", + "keyword": "required", + "message": "must have required property 'function'", + "params": { + "missingProperty": "function", + }, + "schemaPath": "#/properties/edge_functions/items/required", + }, + { + "instancePath": "/edge_functions/1", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "other", + }, + "schemaPath": "#/properties/edge_functions/items/additionalProperties", + }, + { + "instancePath": "/edge_functions/1/cache", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/edge_functions/items/properties/cache/type", + }, + { + "instancePath": "/edge_functions/1/cache", + "keyword": "enum", + "message": "must be equal to one of the allowed values", + "params": { + "allowedValues": [ + "off", + "manual", + ], + }, + "schemaPath": "#/properties/edge_functions/items/properties/cache/enum", + }, + { + "instancePath": "/edge_functions/2/path", + "keyword": "pattern", + "message": "must match pattern \"^/.*\"", + "params": { + "pattern": "^/.*", + }, + "schemaPath": "#/properties/edge_functions/items/properties/path/pattern", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/edge-functions-errors/test.toml b/packages/config-schema/tests/fixtures/edge-functions-errors/test.toml new file mode 100644 index 0000000000..20ac36e674 --- /dev/null +++ b/packages/config-schema/tests/fixtures/edge-functions-errors/test.toml @@ -0,0 +1,12 @@ +[[edge_functions]] +path = 1 +function = 1 +cache = "notvalid" + +[[edge_functions]] +other = 1 +cache = 1 + +[[edge_functions]] +path = "noslash" +function = "foo" diff --git a/packages/config-schema/tests/fixtures/edge-functions-no-table/snapshot.snap b/packages/config-schema/tests/fixtures/edge-functions-no-table/snapshot.snap new file mode 100644 index 0000000000..f4853b0c4d --- /dev/null +++ b/packages/config-schema/tests/fixtures/edge-functions-no-table/snapshot.snap @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "instancePath": "/edge_functions", + "keyword": "type", + "message": "must be array", + "params": { + "type": "array", + }, + "schemaPath": "#/properties/edge_functions/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/edge-functions-no-table/test.toml b/packages/config-schema/tests/fixtures/edge-functions-no-table/test.toml new file mode 100644 index 0000000000..53f4cb5950 --- /dev/null +++ b/packages/config-schema/tests/fixtures/edge-functions-no-table/test.toml @@ -0,0 +1,3 @@ +[edge_functions] +path = "/admin" +function = "auth" diff --git a/packages/config-schema/tests/fixtures/edge-functions-pass/snapshot.snap b/packages/config-schema/tests/fixtures/edge-functions-pass/snapshot.snap new file mode 100644 index 0000000000..5a4012064f --- /dev/null +++ b/packages/config-schema/tests/fixtures/edge-functions-pass/snapshot.snap @@ -0,0 +1,4 @@ +{ + "errors": undefined, + "valid": true, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/edge-functions-pass/test.toml b/packages/config-schema/tests/fixtures/edge-functions-pass/test.toml new file mode 100644 index 0000000000..64f4576196 --- /dev/null +++ b/packages/config-schema/tests/fixtures/edge-functions-pass/test.toml @@ -0,0 +1,17 @@ +[[edge_functions]] +path = "/admin" +function = "auth" + +[[edge_functions]] +path = "/*" +cache = "off" +function = "injector" + +[[edge_functions]] +path = "/caching" +cache = "manual" +function = "injector" + +[[edge_functions]] +path = "/" +function = "injector" diff --git a/packages/config-schema/tests/fixtures/functions-errors/snapshot.snap b/packages/config-schema/tests/fixtures/functions-errors/snapshot.snap new file mode 100644 index 0000000000..e1873cf500 --- /dev/null +++ b/packages/config-schema/tests/fixtures/functions-errors/snapshot.snap @@ -0,0 +1,109 @@ +{ + "errors": [ + { + "instancePath": "/functions/api_*", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "nay", + }, + "schemaPath": "#/properties/functions/additionalProperties/additionalProperties", + }, + { + "instancePath": "/functions/api_*/external_node_modules/0", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/FunctionConfigExternalNodeModules/items/type", + }, + { + "instancePath": "/functions/api_*/included_files/0", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/FunctionConfigIncludedFiles/items/type", + }, + { + "instancePath": "/functions/api_*/schedule", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/functions/additionalProperties/properties/schedule/type", + }, + { + "instancePath": "/functions/deno_import_map", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/functions/properties/deno_import_map/type", + }, + { + "instancePath": "/functions/directory", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/functions/properties/directory/type", + }, + { + "instancePath": "/functions/external_node_modules", + "keyword": "type", + "message": "must be array", + "params": { + "type": "array", + }, + "schemaPath": "#/definitions/FunctionConfigExternalNodeModules/type", + }, + { + "instancePath": "/functions/included_files", + "keyword": "type", + "message": "must be array", + "params": { + "type": "array", + }, + "schemaPath": "#/definitions/FunctionConfigIncludedFiles/type", + }, + { + "instancePath": "/functions/node_bundler", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/definitions/FunctionConfigNodeBundler/type", + }, + { + "instancePath": "/functions/node_bundler", + "keyword": "enum", + "message": "must be equal to one of the allowed values", + "params": { + "allowedValues": [ + "zisi", + "esbuild", + "nft", + "none", + ], + }, + "schemaPath": "#/definitions/FunctionConfigNodeBundler/enum", + }, + { + "instancePath": "/functions/other", + "keyword": "type", + "message": "must be object", + "params": { + "type": "object", + }, + "schemaPath": "#/properties/functions/additionalProperties/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/functions-errors/test.toml b/packages/config-schema/tests/fixtures/functions-errors/test.toml new file mode 100644 index 0000000000..0bc14fd3b0 --- /dev/null +++ b/packages/config-schema/tests/fixtures/functions-errors/test.toml @@ -0,0 +1,13 @@ +[functions] +directory = 1 +node_bundler = 1 +external_node_modules = 1 +included_files = 1 +deno_import_map = 1 +other = 1 + +[functions."api_*"] +external_node_modules = [1] +included_files = [1] +nay = 1 +schedule = 1 diff --git a/packages/config-schema/tests/fixtures/functions-pass/snapshot.snap b/packages/config-schema/tests/fixtures/functions-pass/snapshot.snap new file mode 100644 index 0000000000..5a4012064f --- /dev/null +++ b/packages/config-schema/tests/fixtures/functions-pass/snapshot.snap @@ -0,0 +1,4 @@ +{ + "errors": undefined, + "valid": true, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/functions-pass/test.toml b/packages/config-schema/tests/fixtures/functions-pass/test.toml new file mode 100644 index 0000000000..7d4c36c025 --- /dev/null +++ b/packages/config-schema/tests/fixtures/functions-pass/test.toml @@ -0,0 +1,17 @@ +[functions] +directory = "myfunctions/" +node_bundler = "esbuild" +external_node_modules = ["package-1"] +deno_import_map = "string" +included_files = ["files/*.md"] + +[functions."api_*"] +external_node_modules = ["package-2"] +included_files = ["!files/post-1.md"] + +[functions.api_payment] +external_node_modules = ["package-3", "package-4"] +included_files = ["!files/post-2.md", "package.json", "images/**"] + +[functions.scheduled] +schedule = "0 0 * * *" diff --git a/packages/config-schema/tests/fixtures/headers-errors/snapshot.snap b/packages/config-schema/tests/fixtures/headers-errors/snapshot.snap new file mode 100644 index 0000000000..09f88ce8a9 --- /dev/null +++ b/packages/config-schema/tests/fixtures/headers-errors/snapshot.snap @@ -0,0 +1,41 @@ +{ + "errors": [ + { + "instancePath": "/headers/0", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "other", + }, + "schemaPath": "#/properties/headers/items/additionalProperties", + }, + { + "instancePath": "/headers/0/for", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/headers/items/properties/for/type", + }, + { + "instancePath": "/headers/0/values/X-Frame-Options", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/headers/items/properties/values/additionalProperties/type", + }, + { + "instancePath": "/headers/1", + "keyword": "required", + "message": "must have required property 'for'", + "params": { + "missingProperty": "for", + }, + "schemaPath": "#/properties/headers/items/required", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/headers-errors/test.toml b/packages/config-schema/tests/fixtures/headers-errors/test.toml new file mode 100644 index 0000000000..4a63711694 --- /dev/null +++ b/packages/config-schema/tests/fixtures/headers-errors/test.toml @@ -0,0 +1,9 @@ +[[headers]] +for = 1 +other = "string" +[headers.values] +X-Frame-Options = 1 + +[[headers]] +[headers.values] +X-Frame-Options = "foo" diff --git a/packages/config-schema/tests/fixtures/headers-no-table/snapshot.snap b/packages/config-schema/tests/fixtures/headers-no-table/snapshot.snap new file mode 100644 index 0000000000..44096512bc --- /dev/null +++ b/packages/config-schema/tests/fixtures/headers-no-table/snapshot.snap @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "instancePath": "/headers", + "keyword": "type", + "message": "must be array", + "params": { + "type": "array", + }, + "schemaPath": "#/properties/headers/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/headers-no-table/test.toml b/packages/config-schema/tests/fixtures/headers-no-table/test.toml new file mode 100644 index 0000000000..175ef1b7c0 --- /dev/null +++ b/packages/config-schema/tests/fixtures/headers-no-table/test.toml @@ -0,0 +1,2 @@ +[headers] +for = "/*" diff --git a/packages/config-schema/tests/fixtures/headers-pass/snapshot.snap b/packages/config-schema/tests/fixtures/headers-pass/snapshot.snap new file mode 100644 index 0000000000..5a4012064f --- /dev/null +++ b/packages/config-schema/tests/fixtures/headers-pass/snapshot.snap @@ -0,0 +1,4 @@ +{ + "errors": undefined, + "valid": true, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/headers-pass/test.toml b/packages/config-schema/tests/fixtures/headers-pass/test.toml new file mode 100644 index 0000000000..bcec301495 --- /dev/null +++ b/packages/config-schema/tests/fixtures/headers-pass/test.toml @@ -0,0 +1,13 @@ +[[headers]] +for = "/*" +[headers.values] +X-Frame-Options = "DENY" +X-XSS-Protection = "1; mode=block" + +cache-control = ''' + max-age=0, + no-cache, + no-store, + must-revalidate''' + +Basic-Auth = "someuser:somepassword anotheruser:anotherpassword" diff --git a/packages/config-schema/tests/fixtures/plugins-errors/snapshot.snap b/packages/config-schema/tests/fixtures/plugins-errors/snapshot.snap new file mode 100644 index 0000000000..f9b960ec5c --- /dev/null +++ b/packages/config-schema/tests/fixtures/plugins-errors/snapshot.snap @@ -0,0 +1,32 @@ +{ + "errors": [ + { + "instancePath": "/plugins/0", + "keyword": "required", + "message": "must have required property 'package'", + "params": { + "missingProperty": "package", + }, + "schemaPath": "#/definitions/Plugins/items/required", + }, + { + "instancePath": "/plugins/0", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "other", + }, + "schemaPath": "#/definitions/Plugins/items/additionalProperties", + }, + { + "instancePath": "/plugins/0", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "more", + }, + "schemaPath": "#/definitions/Plugins/items/additionalProperties", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/plugins-errors/test.toml b/packages/config-schema/tests/fixtures/plugins-errors/test.toml new file mode 100644 index 0000000000..a42e16be36 --- /dev/null +++ b/packages/config-schema/tests/fixtures/plugins-errors/test.toml @@ -0,0 +1,6 @@ +[[plugins]] +other = "my-npm-package" + +[plugins.more] +breeds = ["pomeranian", "chihuahua", "bulldog"] +any = "thing" diff --git a/packages/config-schema/tests/fixtures/plugins-no-table/snapshot.snap b/packages/config-schema/tests/fixtures/plugins-no-table/snapshot.snap new file mode 100644 index 0000000000..f4dbf370f7 --- /dev/null +++ b/packages/config-schema/tests/fixtures/plugins-no-table/snapshot.snap @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "instancePath": "/plugins", + "keyword": "type", + "message": "must be array", + "params": { + "type": "array", + }, + "schemaPath": "#/definitions/Plugins/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/plugins-no-table/test.toml b/packages/config-schema/tests/fixtures/plugins-no-table/test.toml new file mode 100644 index 0000000000..8361f2d530 --- /dev/null +++ b/packages/config-schema/tests/fixtures/plugins-no-table/test.toml @@ -0,0 +1,3 @@ +[plugins] +package = "my-npm-package" + diff --git a/packages/config-schema/tests/fixtures/plugins-pass/snapshot.snap b/packages/config-schema/tests/fixtures/plugins-pass/snapshot.snap new file mode 100644 index 0000000000..5a4012064f --- /dev/null +++ b/packages/config-schema/tests/fixtures/plugins-pass/snapshot.snap @@ -0,0 +1,4 @@ +{ + "errors": undefined, + "valid": true, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/plugins-pass/test.toml b/packages/config-schema/tests/fixtures/plugins-pass/test.toml new file mode 100644 index 0000000000..3784c42dda --- /dev/null +++ b/packages/config-schema/tests/fixtures/plugins-pass/test.toml @@ -0,0 +1,17 @@ +[[plugins]] +package = "my-npm-package" + +[[plugins]] +package = "@scope/my-npm-package" + +[[plugins]] +package = "./my-local-package" + +inputs = { any = "thing" } + +[[plugins]] +package = "/my-local-package" + +[plugins.inputs] +breeds = ["pomeranian", "chihuahua", "bulldog"] +any = "thing" diff --git a/packages/config-schema/tests/fixtures/redirects-errors/snapshot.snap b/packages/config-schema/tests/fixtures/redirects-errors/snapshot.snap new file mode 100644 index 0000000000..c66481f674 --- /dev/null +++ b/packages/config-schema/tests/fixtures/redirects-errors/snapshot.snap @@ -0,0 +1,160 @@ +{ + "errors": [ + { + "instancePath": "/redirects/0/conditions/Cookie/0", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/redirects/items/properties/conditions/properties/Cookie/items/type", + }, + { + "instancePath": "/redirects/0/conditions/Country/0", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/redirects/items/properties/conditions/properties/Country/items/type", + }, + { + "instancePath": "/redirects/0/conditions/Language/0", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/redirects/items/properties/conditions/properties/Language/items/type", + }, + { + "instancePath": "/redirects/0/conditions/Role/0", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/redirects/items/properties/conditions/properties/Role/items/type", + }, + { + "instancePath": "/redirects/0/force", + "keyword": "type", + "message": "must be boolean", + "params": { + "type": "boolean", + }, + "schemaPath": "#/properties/redirects/items/properties/force/type", + }, + { + "instancePath": "/redirects/0/from", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/redirects/items/properties/from/type", + }, + { + "instancePath": "/redirects/0/headers/X-From", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/redirects/items/properties/headers/additionalProperties/type", + }, + { + "instancePath": "/redirects/0/query/path", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/redirects/items/properties/query/additionalProperties/type", + }, + { + "instancePath": "/redirects/0/signed", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/redirects/items/properties/signed/type", + }, + { + "instancePath": "/redirects/0/status", + "keyword": "exclusiveMaximum", + "message": "must be < 600", + "params": { + "comparison": "<", + "limit": 600, + }, + "schemaPath": "#/properties/redirects/items/properties/status/exclusiveMaximum", + }, + { + "instancePath": "/redirects/0/to", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/redirects/items/properties/to/type", + }, + { + "instancePath": "/redirects/1/conditions/Country/0", + "keyword": "pattern", + "message": "must match pattern \"^[a-zA-Z]{2}$\"", + "params": { + "pattern": "^[a-zA-Z]{2}$", + }, + "schemaPath": "#/properties/redirects/items/properties/conditions/properties/Country/items/pattern", + }, + { + "instancePath": "/redirects/1/conditions/Language/0", + "keyword": "pattern", + "message": "must match pattern \"^[a-zA-Z]{2}(-[a-zA-Z]{2})?$\"", + "params": { + "pattern": "^[a-zA-Z]{2}(-[a-zA-Z]{2})?$", + }, + "schemaPath": "#/properties/redirects/items/properties/conditions/properties/Language/items/pattern", + }, + { + "instancePath": "/redirects/1/query", + "keyword": "type", + "message": "must be object", + "params": { + "type": "object", + }, + "schemaPath": "#/properties/redirects/items/properties/query/type", + }, + { + "instancePath": "/redirects/1/status", + "keyword": "minimum", + "message": "must be >= 100", + "params": { + "comparison": ">=", + "limit": 100, + }, + "schemaPath": "#/properties/redirects/items/properties/status/minimum", + }, + { + "instancePath": "/redirects/2/conditions", + "keyword": "type", + "message": "must be object", + "params": { + "type": "object", + }, + "schemaPath": "#/properties/redirects/items/properties/conditions/type", + }, + { + "instancePath": "/redirects/3", + "keyword": "required", + "message": "must have required property 'from'", + "params": { + "missingProperty": "from", + }, + "schemaPath": "#/properties/redirects/items/required", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/redirects-errors/test.toml b/packages/config-schema/tests/fixtures/redirects-errors/test.toml new file mode 100644 index 0000000000..3b14579ea8 --- /dev/null +++ b/packages/config-schema/tests/fixtures/redirects-errors/test.toml @@ -0,0 +1,22 @@ +[[redirects]] +from = 1 +to = 1 +status = 600 +force = 1 +query = { path = 1 } +conditions = { Cookie = [1], Language = [1], Country = [1], Role = [1] } +headers = { X-From = 1 } +signed = 1 + +[[redirects]] +from = "/search" +conditions = { Language = ["bert"], Country = ["Norway"] } +status = 99 +query = 1 + +[[redirects]] +from = "/search" +conditions = 1 + +[[redirects]] +status = 222 diff --git a/packages/config-schema/tests/fixtures/redirects-no-table/snapshot.snap b/packages/config-schema/tests/fixtures/redirects-no-table/snapshot.snap new file mode 100644 index 0000000000..6462b6c3dc --- /dev/null +++ b/packages/config-schema/tests/fixtures/redirects-no-table/snapshot.snap @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "instancePath": "/redirects", + "keyword": "type", + "message": "must be array", + "params": { + "type": "array", + }, + "schemaPath": "#/properties/redirects/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/redirects-no-table/test.toml b/packages/config-schema/tests/fixtures/redirects-no-table/test.toml new file mode 100644 index 0000000000..7363772389 --- /dev/null +++ b/packages/config-schema/tests/fixtures/redirects-no-table/test.toml @@ -0,0 +1,7 @@ +[redirects] +from = "/old-path" +to = "/new-path" +status = 301 +force = false +query = { path = ":path" } +conditions = { Language = ["en"], Country = ["US"], Role = ["admin"] } diff --git a/packages/config-schema/tests/fixtures/redirects-pass/snapshot.snap b/packages/config-schema/tests/fixtures/redirects-pass/snapshot.snap new file mode 100644 index 0000000000..5a4012064f --- /dev/null +++ b/packages/config-schema/tests/fixtures/redirects-pass/snapshot.snap @@ -0,0 +1,4 @@ +{ + "errors": undefined, + "valid": true, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/redirects-pass/test.toml b/packages/config-schema/tests/fixtures/redirects-pass/test.toml new file mode 100644 index 0000000000..681b3261e8 --- /dev/null +++ b/packages/config-schema/tests/fixtures/redirects-pass/test.toml @@ -0,0 +1,24 @@ +[[redirects]] +from = "/old-path" +to = "/new-path" +status = 301 +force = false +query = { path = ":path" } +conditions = { Language = ["en"], Country = ["US"], Role = ["admin"] } + +[[redirects]] +from = "/search" +to = "https://api.mysearch.com" +status = 200 +force = true +headers = { X-From = "Netlify" } +signed = "API_SIGNATURE_TOKEN" + +[[redirects]] +from = "/search" +status = 100 +conditions = { Language = ["en-US"] } + +[[redirects]] +from = "/search" +status = 599 diff --git a/packages/config-schema/tests/fixtures/templates-errors/snapshot.snap b/packages/config-schema/tests/fixtures/templates-errors/snapshot.snap new file mode 100644 index 0000000000..7e34e9146f --- /dev/null +++ b/packages/config-schema/tests/fixtures/templates-errors/snapshot.snap @@ -0,0 +1,32 @@ +{ + "errors": [ + { + "instancePath": "/template", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "other", + }, + "schemaPath": "#/properties/template/additionalProperties", + }, + { + "instancePath": "/template/environment", + "keyword": "type", + "message": "must be object", + "params": { + "type": "object", + }, + "schemaPath": "#/properties/template/properties/environment/type", + }, + { + "instancePath": "/template/incoming-hooks", + "keyword": "type", + "message": "must be array", + "params": { + "type": "array", + }, + "schemaPath": "#/properties/template/properties/incoming-hooks/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/templates-errors/test.toml b/packages/config-schema/tests/fixtures/templates-errors/test.toml new file mode 100644 index 0000000000..d38bde5a67 --- /dev/null +++ b/packages/config-schema/tests/fixtures/templates-errors/test.toml @@ -0,0 +1,4 @@ +[template] +incoming-hooks = 1 +other = 1 +environment = 1 diff --git a/packages/config-schema/tests/fixtures/templates-errors2/snapshot.snap b/packages/config-schema/tests/fixtures/templates-errors2/snapshot.snap new file mode 100644 index 0000000000..a8457c980a --- /dev/null +++ b/packages/config-schema/tests/fixtures/templates-errors2/snapshot.snap @@ -0,0 +1,23 @@ +{ + "errors": [ + { + "instancePath": "/template/environment/ASDFG", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/template/properties/environment/additionalProperties/type", + }, + { + "instancePath": "/template/incoming-hooks/0", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string", + }, + "schemaPath": "#/properties/template/properties/incoming-hooks/items/type", + }, + ], + "valid": false, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/templates-errors2/test.toml b/packages/config-schema/tests/fixtures/templates-errors2/test.toml new file mode 100644 index 0000000000..677c78f9a4 --- /dev/null +++ b/packages/config-schema/tests/fixtures/templates-errors2/test.toml @@ -0,0 +1,5 @@ +[template] +incoming-hooks = [1] + +[template.environment] +ASDFG = 1 diff --git a/packages/config-schema/tests/fixtures/templates-pass/snapshot.snap b/packages/config-schema/tests/fixtures/templates-pass/snapshot.snap new file mode 100644 index 0000000000..5a4012064f --- /dev/null +++ b/packages/config-schema/tests/fixtures/templates-pass/snapshot.snap @@ -0,0 +1,4 @@ +{ + "errors": undefined, + "valid": true, +} \ No newline at end of file diff --git a/packages/config-schema/tests/fixtures/templates-pass/test.toml b/packages/config-schema/tests/fixtures/templates-pass/test.toml new file mode 100644 index 0000000000..4a7295f44c --- /dev/null +++ b/packages/config-schema/tests/fixtures/templates-pass/test.toml @@ -0,0 +1,5 @@ +[template] +incoming-hooks = ["Contentful"] + +[template.environment] +ASDFG = "STRING" diff --git a/packages/config-schema/tests/schema-tests.ts b/packages/config-schema/tests/schema-tests.ts new file mode 100644 index 0000000000..279db1395a --- /dev/null +++ b/packages/config-schema/tests/schema-tests.ts @@ -0,0 +1,34 @@ +import { readFile } from 'fs/promises' +import { basename, dirname, join } from 'path' +import { fileURLToPath } from 'url' + +import Ajv, { ErrorObject } from 'ajv' +import glob from 'fast-glob' +import toml from 'toml' +import { describe, expect, test } from 'vitest' + +const ajv = new Ajv({ keywords: ['x-taplo'], allErrors: true }) +const currentDir = dirname(fileURLToPath(import.meta.url)) +const schemaPath = join(currentDir, '../src/schema/netlify-toml.schema.json') +const schema = JSON.parse(await readFile(schemaPath, 'utf-8')) +const validate = ajv.compile(schema) + +const fixtures = await glob('fixtures/*/test.toml', { absolute: true, cwd: currentDir }) + +const sortErrors = (a: ErrorObject, b: ErrorObject): number => { + if (a.instancePath === b.instancePath) return 0 + + return a.instancePath < b.instancePath ? -1 : 1 +} + +describe('JSON config schema', () => { + fixtures.forEach((fixturePath) => { + const fixture = dirname(fixturePath) + test(basename(fixture), async () => { + const tomlConfig = toml.parse(await readFile(join(fixture, 'test.toml'), 'utf-8')) + const snapshot = join(fixture, 'snapshot.snap') + + expect({ valid: validate(tomlConfig), errors: validate.errors?.sort(sortErrors) }).toMatchFileSnapshot(snapshot) + }) + }) +}) diff --git a/packages/config-schema/vitest.config.ts b/packages/config-schema/vitest.config.ts new file mode 100644 index 0000000000..d11a2715e7 --- /dev/null +++ b/packages/config-schema/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['tests/schema-tests.ts'], + }, +})