зеркало из
1
0
Форкнуть 0
This commit is contained in:
sinedied 2022-05-05 12:00:39 +02:00 коммит произвёл Yohan Lasorsa
Родитель dfcfc908b1
Коммит 2ba3b51fbc
43 изменённых файлов: 434 добавлений и 435 удалений

Просмотреть файл

@ -1,5 +1,5 @@
| Environment Variable | Purpose | Read Only? | Default Value | | Environment Variable | Purpose | Read Only? | Default Value |
| ----------------------------- | ---------------------------------------------------------------------------------- | ---------- | ------------- | | ----------------------------- | --------------------------------------------------------------------------------------------------- | ---------- | ------------- |
| **General Settings** | | | | | **General Settings** | | | |
| SWA_CLI_VERSION | CLI version | Yes | | | SWA_CLI_VERSION | CLI version | Yes | |
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` | | SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |

Просмотреть файл

@ -29,6 +29,7 @@ Build the website for production - by default this creates the static content in
$ cd docs/www $ cd docs/www
$ npm run build $ npm run build
``` ```
You can preview the local build version using this command: You can preview the local build version using this command:
```bash ```bash
@ -55,6 +56,7 @@ Next use `npm deploy` or `yarn deploy` to push the built site to the GitHub page
$ cd docs/www $ cd docs/www
$ npm deploy $ npm deploy
``` ```
Before you do that, make sure the `docusaurus.config.js` is setup correctly using [this guidance](https://docusaurus.io/docs/deployment#github-pages-overview). Look for the _`// Please change this to your repo`_ comments and update those to reflect the local repo. Before you do that, make sure the `docusaurus.config.js` is setup correctly using [this guidance](https://docusaurus.io/docs/deployment#github-pages-overview). Look for the _`// Please change this to your repo`_ comments and update those to reflect the local repo.
Note that when using `npm run deploy` from the commandline (e.g., for initial testing), you need to set the `GIT_USER` and `GIT_PASS` environment variables to Note that when using `npm run deploy` from the commandline (e.g., for initial testing), you need to set the `GIT_USER` and `GIT_PASS` environment variables to
@ -64,52 +66,55 @@ Note that when using `npm run deploy` from the commandline (e.g., for initial te
// TODO: Fill in details for deploying same source to SWA // TODO: Fill in details for deploying same source to SWA
// Site: https://azurestaticwebapps.dev/ // Site: https://azurestaticwebapps.dev/
## 6. Content Structure ## 6. Content Structure
> Docusaurus has this default structure for content: > Docusaurus has this default structure for content:
* `blog/` - posts with index page, tags, RSS feed. - `blog/` - posts with index page, tags, RSS feed.
* `docs/` - tutorials with sidebar, prev/next navigation - `docs/` - tutorials with sidebar, prev/next navigation
* `src/pages` - standalone pages (map directly to routes) - `src/pages` - standalone pages (map directly to routes)
* `static/` - static assets (served as is) - `static/` - static assets (served as is)
> These are the main configuration files: > These are the main configuration files:
* `docusaurus.config.js` - all site configuration
* `package.json` - NPM dependencies for build - `docusaurus.config.js` - all site configuration
* `sidebars.js` - explicitly specify sidebar contents - `package.json` - NPM dependencies for build
- `sidebars.js` - explicitly specify sidebar contents
> These are the key files to customize this site: > These are the key files to customize this site:
1. `docusaurus.config.js`
1. `docusaurus.config.js`
- update contents of navbar - update contents of navbar
- update contents of footer - update contents of footer
- identify and customize plugins - identify and customize plugins
- activate or deactivate default features (e.g., blog) - activate or deactivate default features (e.g., blog)
- activate and customize banner (top of landing page) - activate and customize banner (top of landing page)
2. `src/components/HomepageFeatures/index.js` 2. `src/components/HomepageFeatures/index.js`
- customize landing page body (features grid) - customize landing page body (features grid)
2. `src/components/index.js` 3. `src/components/index.js`
- customize landing page structure (layout) - customize landing page structure (layout)
4. `src/css/custom.css` 4. `src/css/custom.css`
- customize theme palette (dark, light) - customize theme palette (dark, light)
- implement sitewide css changes - implement sitewide css changes
> Guidelines for adding new content > Guidelines for adding new content
- Blog feature is **deactivated**. (Update `docusaurus.config.js` to reactivate)
- Have **step-by-step** instructions? - Blog feature is **deactivated**. (Update `docusaurus.config.js` to reactivate)
- Have **step-by-step** instructions?
- Add file under `docs/`X/Y for tutorial X with steps Y - Add file under `docs/`X/Y for tutorial X with steps Y
- Create `docs/`X/_category_.json to define placement, metadata - Create `docs/`X/_category_.json to define placement, metadata
- Have **single-page** documents that standalone? - Have **single-page** documents that standalone?
- Add file as `src/pages/`X.md - Add file as `src/pages/`X.md
- Choose X to reflect the desired _route_ name for this page - Choose X to reflect the desired _route_ name for this page
## 6. Color Palette ## 6. Color Palette
> Old Color Palette (v0.8.3) > Old Color Palette (v0.8.3)
* #ff9cfd
* #F131F8 - #ff9cfd
* #b319a6 - #F131F8
> New Color Palette (GA) - #b319a6
* A100 (light) = #FF80AB > New Color Palette (GA)
* A200 (medium) = #FF4081 - A100 (light) = #FF80AB
* A400 (dark) = #F50057 - A200 (medium) = #FF4081
- A400 (dark) = #F50057

Просмотреть файл

@ -13,4 +13,4 @@ You can use the following options to override default values for any `swa` comma
| `--print-config` | Print all resolved options | `false` | `--print-config` or `--print-config=true` | | `--print-config` | Print all resolved options | `false` | `--print-config` or `--print-config=true` |
| `--swa-config-location` | The directory where the `staticwebapp.config.json` file is located | `./` | `--swa-config-location=./app` | | `--swa-config-location` | The directory where the `staticwebapp.config.json` file is located | `./` | `--swa-config-location=./app` |
| |

Просмотреть файл

@ -4,7 +4,6 @@ sidebar_position: 3
# `swa start` # `swa start`
If you need to override the default values for the `swa start` subcommand, you can provide the following options: If you need to override the default values for the `swa start` subcommand, you can provide the following options:
| Option | Description | Default | Example | | Option | Description | Default | Example |

Просмотреть файл

@ -4,18 +4,15 @@ sidebar_position: 4
# `swa deploy` # `swa deploy`
If you need to override the default values for the `swa deploy` subcommand, you can provide the following options: If you need to override the default values for the `swa deploy` subcommand, you can provide the following options:
| Option | Description | Default | Example | | Option | Description | Default | Example |
|:--|:--|:--|:--| | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | :-------- | :---------------------------------------- |
| `--api-location`| The folder containing the source code of the API application |`./api` | `--api-location="./api"`| | `--api-location` | The folder containing the source code of the API application | `./api` | `--api-location="./api"` |
|`--deployment-token` | The secret toekn used to authenticate with the Static Web Apps| | `--deployment-token="123"` | | `--deployment-token` | The secret toekn used to authenticate with the Static Web Apps | | `--deployment-token="123"` |
| `--dry-run` |Simulate a deploy process without actually running it | `false`| `--dry-run`| | `--dry-run` | Simulate a deploy process without actually running it | `false` | `--dry-run` |
| `--print-token`|print the deployment token | `false` | `--print-token`| | `--print-token` | print the deployment token | `false` | `--print-token` |
| `--env`| the type of deployment environment where to deploy the project | `preview`| `--env="production"` or `--env="preview"`| | `--env` | the type of deployment environment where to deploy the project | `preview` | `--env="production"` or `--env="preview"` |
|`--print-token` |Print the deployment token. Usefull when using `--deployment-token` on CI/CD <br/> Note: this command does not run the deployment process. | `false` | `--print-token`| | `--print-token` | Print the deployment token. Usefull when using `--deployment-token` on CI/CD <br/> Note: this command does not run the deployment process. | `false` | `--print-token` |
The deploy command does also support the same options as the `swa login` command. The deploy command does also support the same options as the `swa login` command.

Просмотреть файл

@ -4,7 +4,6 @@ sidebar_position: 4
# `swa-cli.config.json` # `swa-cli.config.json`
The CLI can also load options from a `swa-cli.config.json` file: The CLI can also load options from a `swa-cli.config.json` file:
```json ```json

Просмотреть файл

@ -3,12 +3,10 @@ sidebar_position: 4
title: env vars title: env vars
--- ---
# Environment Variables # Environment Variables
| Environment Variable | Purpose | Read Only? | Default Value | | Environment Variable | Purpose | Read Only? | Default Value |
| ----------------------------- | ---------------------------------------------------------------------------------- | ---------- | ------------- | | ----------------------------- | --------------------------------------------------------------------------------------------------- | ---------- | ------------- |
| **General Settings** | | | | | **General Settings** | | | |
| SWA_CLI_VERSION | CLI version | Yes | | | SWA_CLI_VERSION | CLI version | Yes | |
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` | | SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |

Просмотреть файл

@ -10,14 +10,14 @@ Please review the [Contributor Guidelines](https://github.com/Azure/static-web-a
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. As a contributor, this is what you need to know: This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. As a contributor, this is what you need to know:
- [Signing the CLA](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#signing-the-cla) - [Signing the CLA](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#signing-the-cla)
- [Code of Conduct](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#code-of-conduct) - [Code of Conduct](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#code-of-conduct)
- [Got a Question or Problem?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#got-a-question-or-problem) - [Got a Question or Problem?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#got-a-question-or-problem)
- [Found an Bug?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#found-an-bug) - [Found an Bug?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#found-an-bug)
- [Missing a Feature?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#missing-a-feature) - [Missing a Feature?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#missing-a-feature)
- [Submission Guidelines](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#submission-guidelines) - [Submission Guidelines](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#submission-guidelines)
- [Coding Rules](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#coding-rules) - [Coding Rules](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#coding-rules)
- [Commit Message Guidelines](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#commit-message-guidelines) - [Commit Message Guidelines](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#commit-message-guidelines)
> 🚧 | `TODO:` Update ga links to main. > 🚧 | `TODO:` Update ga links to main.

Просмотреть файл

@ -16,10 +16,10 @@ Thanks go to these wonderful people ([emoji key](https://allcontributors.org/doc
> 🚧 | TODO: Automate bot > 🚧 | TODO: Automate bot
This project follows the [all-contributors][ac] specification. This project follows the [all-contributors][ac] specification.
* Contributions of any kind welcome!
* Automatically updated by [the @all-contributors bot 🤖][acbot]. - Contributions of any kind welcome!
- Automatically updated by [the @all-contributors bot 🤖][acbot].
[acek]: https://allcontributors.org/docs/en/emoji-key [acek]: https://allcontributors.org/docs/en/emoji-key
[ac]: https://github.com/all-contributors/all-contributors [ac]: https://github.com/all-contributors/all-contributors

Просмотреть файл

@ -8,14 +8,13 @@ The **Static Web Apps (SWA) CLI** is an open-source commandline tool used for si
[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/azure/static-web-apps-cli/issues) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/azure/static-web-apps-cli/issues)
## What is Static Web Apps? ## What is Static Web Apps?
Azure Static Web Apps is a turnkey service for modern full-stack applications with pre-built or pre-rendered front-ends, and serverless backends. It became [generally available in May 2021](https://azure.microsoft.com/en-us/updates/azure-static-web-apps-is-now-generally-available/?WT.mc_id=30daysofswa-61155-cxall) and: Azure Static Web Apps is a turnkey service for modern full-stack applications with pre-built or pre-rendered front-ends, and serverless backends. It became [generally available in May 2021](https://azure.microsoft.com/en-us/updates/azure-static-web-apps-is-now-generally-available/?WT.mc_id=30daysofswa-61155-cxall) and:
* works with [your favorite front-end frameworks and static site generators](https://docs.microsoft.com/en-us/azure/static-web-apps/front-end-frameworks) - works with [your favorite front-end frameworks and static site generators](https://docs.microsoft.com/en-us/azure/static-web-apps/front-end-frameworks)
* has [quickstart options](https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started?tabs=vanilla-javascript) for IDE, command line, and browser (via Azure Portal). - has [quickstart options](https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started?tabs=vanilla-javascript) for IDE, command line, and browser (via Azure Portal).
* can automate workflows for code repositories in [GitHub](https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started?tabs=vanilla-javascript), [GitLab](https://docs.microsoft.com/en-us/azure/static-web-apps/gitlab?tabs=vanilla-javascript) and [Bit Bucket](https://docs.microsoft.com/en-us/azure/static-web-apps/bitbucket?tabs=vanilla-javascript). - can automate workflows for code repositories in [GitHub](https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started?tabs=vanilla-javascript), [GitLab](https://docs.microsoft.com/en-us/azure/static-web-apps/gitlab?tabs=vanilla-javascript) and [Bit Bucket](https://docs.microsoft.com/en-us/azure/static-web-apps/bitbucket?tabs=vanilla-javascript).
Visit the [Azure Static Web Apps Documentation](https://docs.microsoft.com/en-us/azure/static-web-apps/) for more information. Visit the [Azure Static Web Apps Documentation](https://docs.microsoft.com/en-us/azure/static-web-apps/) for more information.
@ -29,7 +28,6 @@ The Static Web Apps CLI (aka **SWA CLI**) is an [open-source](https://github.com
- Emulate Static Web Apps configuration (including routing & role-based auth) - Emulate Static Web Apps configuration (including routing & role-based auth)
- Deploy your app to Azure Static Web Apps (for a unified develop-deploy workflow) - Deploy your app to Azure Static Web Apps (for a unified develop-deploy workflow)
## SWA CLI Components ## SWA CLI Components
At a high level, the architecture looks something like this: At a high level, the architecture looks something like this:
@ -46,8 +44,7 @@ The key components of the SWA CLI are:
- The **Static content server**. Serves app's static content locally for testing, validation. - The **Static content server**. Serves app's static content locally for testing, validation.
- The **Serverless API server**. Served by Azure Functions Core Tools, for local API testing. - The **Serverless API server**. Served by Azure Functions Core Tools, for local API testing.
> **```🚨 CAVEAT: STATIC WEB APPS CLI IS CURRENTLY IN PREVIEW. 🚨```** <br/> It _emulates_ key capabilities of the Azure Static Web Apps service, so differences from actual behavior are to be expected. Deploy and test your app in Azure for final validation. > **`🚨 CAVEAT: STATIC WEB APPS CLI IS CURRENTLY IN PREVIEW. 🚨`** <br/> It _emulates_ key capabilities of the Azure Static Web Apps service, so differences from actual behavior are to be expected. Deploy and test your app in Azure for final validation.
## Contribute to SWA CLI ## Contribute to SWA CLI
@ -55,18 +52,18 @@ Static Web Apps CLI preview release v0.8.3 was releaseed in **April 2022**. The
This is an open-source project made for the benefit of our developer community. Your feedback and contributions are key to its success. Here are some ways to help: This is an open-source project made for the benefit of our developer community. Your feedback and contributions are key to its success. Here are some ways to help:
* Discovered buggy or unusual behavior? [Send us a bug report](https://github.com/Azure/static-web-apps-cli/issues/new?assignees=&labels=&template=bug_report.md&title=) - Discovered buggy or unusual behavior? [Send us a bug report](https://github.com/Azure/static-web-apps-cli/issues/new?assignees=&labels=&template=bug_report.md&title=)
* Have a feature request? [Send us a Feature Request](https://github.com/Azure/static-web-apps-cli/issues/new?assignees=&labels=&template=feature_request.md&title=) - Have a feature request? [Send us a Feature Request](https://github.com/Azure/static-web-apps-cli/issues/new?assignees=&labels=&template=feature_request.md&title=)
* Found a security vulnerability? [Report Security Issues](https://github.com/Azure/static-web-apps-cli/security/policy) - Found a security vulnerability? [Report Security Issues](https://github.com/Azure/static-web-apps-cli/security/policy)
* Have other questions or comments? [Post to our Discussions board](https://github.com/Azure/static-web-apps-cli/discussions) - Have other questions or comments? [Post to our Discussions board](https://github.com/Azure/static-web-apps-cli/discussions)
* Posting questions to Stack Overflow? [Post to the swa-cli tag](https://stackoverflow.com/questions/tagged/swa-cli) - Posting questions to Stack Overflow? [Post to the swa-cli tag](https://stackoverflow.com/questions/tagged/swa-cli)
You can also contribute directly to the project by: You can also contribute directly to the project by:
* Fixing bugs identified in issues
* Writing or improving the documentation - Fixing bugs identified in issues
* Extending or improving CLI capabilities - Writing or improving the documentation
- Extending or improving CLI capabilities
To get started, read our [Contributor Guide](/docs/contribute/intro) To get started, read our [Contributor Guide](/docs/contribute/intro)
Thank you for your continued support! ♥️ Thank you for your continued support! ♥️

Просмотреть файл

@ -22,7 +22,6 @@ npm install -g @azure/static-web-apps-cli
npm install -D @azure/static-web-apps-cli npm install -D @azure/static-web-apps-cli
``` ```
## Install with yarn ## Install with yarn
**See: **[@azure/static-web-apps-cli](hhttps://yarnpkg.com/package/@azure/static-web-apps-cli). **See: **[@azure/static-web-apps-cli](hhttps://yarnpkg.com/package/@azure/static-web-apps-cli).
@ -32,6 +31,7 @@ npm install -D @azure/static-web-apps-cli
```bash ```bash
yarn add global @azure/static-web-apps-cli yarn add global @azure/static-web-apps-cli
``` ```
2. The recommended approach is to install this locally, as a devDependency, using: 2. The recommended approach is to install this locally, as a devDependency, using:
```bash ```bash
@ -64,4 +64,3 @@ Or use this command to start the emulator on your local device:
```bash ```bash
$ npx @azure/static-web-apps-cli start $ npx @azure/static-web-apps-cli start
``` ```

Просмотреть файл

@ -5,10 +5,9 @@ sidebar_position: 3
# 2. Start The Emulator # 2. Start The Emulator
The SWA Emulator is run by using the `swa start` command. The SWA Emulator is run by using the `swa start` command.
* It runs on `http://localhost:4280` by default.
* Read the [docs](/docs/cli/swa-start) for more command details.
- It runs on `http://localhost:4280` by default.
- Read the [docs](/docs/cli/swa-start) for more command details.
## 2.1 Serve from current folder ## 2.1 Serve from current folder
@ -32,8 +31,8 @@ When developing your SWA front-end, you may want to use the front-end framework'
SWA CLI can reverse proxy requests to that dev server, allowing you to retain the above benefits during local development of your SWA. You can achieve this in two steps: SWA CLI can reverse proxy requests to that dev server, allowing you to retain the above benefits during local development of your SWA. You can achieve this in two steps:
1. Start the local dev server as usual. _Note the URL (localhost:port) the dev server runs on._ 1. Start the local dev server as usual. _Note the URL (localhost:port) the dev server runs on._
2. Start the SWA CLI in a new terminal _with dev server URL specified._ 2. Start the SWA CLI in a new terminal _with dev server URL specified._
```bash ```bash
swa start <dev-server-url> swa start <dev-server-url>
@ -70,7 +69,6 @@ swa start http://localhost:4200 --run "./startup.sh"
Then access the application with the emulated services from `http://localhost:4280` Then access the application with the emulated services from `http://localhost:4280`
## 2.5 Default Dev Server ports ## 2.5 Default Dev Server ports
Here are some of the default ports used by dev servers in popular front-end frameworks and static site generators today. Here are some of the default ports used by dev servers in popular front-end frameworks and static site generators today.

Просмотреть файл

@ -4,16 +4,16 @@ sidebar_position: 4
# 4. Start the API Server # 4. Start the API Server
## 4.1 Azure Functions Core Tools ## 4.1 Azure Functions Core Tools
While not mandatory, your application may choose to take advantage of [serverless API support with Azure Functions](https://docs.microsoft.com/en-us/azure/static-web-apps/apis). While not mandatory, your application may choose to take advantage of [serverless API support with Azure Functions](https://docs.microsoft.com/en-us/azure/static-web-apps/apis).
Azure Functions service has its own [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) CLI to support local development. Read the [API Reference](https://docs.microsoft.com/en-us/azure/azure-functions/functions-core-tools-reference?tabs=v2) to learn about its `func` command and options. Azure Functions service has its own [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) CLI to support local development. Read the [API Reference](https://docs.microsoft.com/en-us/azure/azure-functions/functions-core-tools-reference?tabs=v2) to learn about its `func` command and options.
By using the SWA CLI, we can streamline the local development workflow (across Azure Static Web Apps and Azure Functions) even further. The SWA CLI can: By using the SWA CLI, we can streamline the local development workflow (across Azure Static Web Apps and Azure Functions) even further. The SWA CLI can:
* verify that [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) is installed in your local development environment.
* download or update the [right version](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local#install-the-azure-functions-core-tools) for you if needed. - verify that [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) is installed in your local development environment.
* give you flexibility to have the CLI manage the API server launch if needed. - download or update the [right version](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local#install-the-azure-functions-core-tools) for you if needed.
- give you flexibility to have the CLI manage the API server launch if needed.
Let's explore these options. Let's explore these options.
@ -24,9 +24,11 @@ You might want to run the Azure Functions Core Tools (API server) separately, to
To use the SWA emulator services alongside the API server: To use the SWA emulator services alongside the API server:
1. Start API server first using Azure Functions Core Tools CLI (below) or the [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions). _Note the URL for the local API server, once it is running_. 1. Start API server first using Azure Functions Core Tools CLI (below) or the [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions). _Note the URL for the local API server, once it is running_.
```bash ```bash
func host start func host start
``` ```
2. Start SWA CLI in a separate terminal and use the `--api-location` option to pass it the relevant local API Server URI. _For example:_ 2. Start SWA CLI in a separate terminal and use the `--api-location` option to pass it the relevant local API Server URI. _For example:_
```bash ```bash
@ -48,4 +50,3 @@ swa start ./my-dist --api-location ./api
```bash ```bash
swa start http://localhost:3000 --api-location ./api swa start http://localhost:3000 --api-location ./api
``` ```

Просмотреть файл

@ -4,7 +4,6 @@ sidebar_position: 5
# 5. Deploy SWA to Azure # 5. Deploy SWA to Azure
The CLI can also be used to deploy an app to Azure Static Web Apps using the command: `swa deploy`. Here are some common use cases: The CLI can also be used to deploy an app to Azure Static Web Apps using the command: `swa deploy`. Here are some common use cases:
1. Deploy a front-end app without an API 1. Deploy a front-end app without an API

Просмотреть файл

@ -6,7 +6,6 @@ sidebar_position: 5
Azure Static Web Apps can be configured with an optional `staticwebapp.config.json` file. For more information, see [Configure Static Web Apps documentation](https://docs.microsoft.com/azure/static-web-apps/configuration). Azure Static Web Apps can be configured with an optional `staticwebapp.config.json` file. For more information, see [Configure Static Web Apps documentation](https://docs.microsoft.com/azure/static-web-apps/configuration).
## 6.1 Default File Location ## 6.1 Default File Location
If you are serving static files from a folder, the CLI will search this folder for `staticwebapp.config.json`. If you are serving static files from a folder, the CLI will search this folder for `staticwebapp.config.json`.
@ -23,7 +22,6 @@ If you are using a front-end dev server, the CLI will search the current directo
swa start http://localhost:3000 swa start http://localhost:3000
``` ```
## 6.2 Specify File Location ## 6.2 Specify File Location
To control where the CLI searches for `staticwebapp.config.json`, use `--swa-config-location`. To control where the CLI searches for `staticwebapp.config.json`, use `--swa-config-location`.

Просмотреть файл

@ -19,7 +19,7 @@
"watch": "tsc --watch", "watch": "tsc --watch",
"copy-assets": "node ./scripts/copy-assets.js", "copy-assets": "node ./scripts/copy-assets.js",
"prepare": "npm run build", "prepare": "npm run build",
"format": "prettier --write 'src/**/*.ts' '**/*.md' 'scripts/**/*.js' 'cypress/**/*.js' 'schema/**/*.json'" "format": "prettier --write 'src/**/*.ts' *.md'' 'docs/**/*.md' 'scripts/**/*.js' 'cypress/**/*.js' 'schema/**/*.json'"
}, },
"bin": { "bin": {
"swa": "dist/cli/bin.js" "swa": "dist/cli/bin.js"

Просмотреть файл

@ -24,6 +24,7 @@ The CLI emulates commonly used capabilities of the Azure Static Web Apps cloud s
### Using `npm` or `yarn`: ### Using `npm` or `yarn`:
- Install the cli - Install the cli
```bash ```bash
npm install -g @azure/static-web-apps-cli npm install -g @azure/static-web-apps-cli
``` ```
@ -49,11 +50,13 @@ See all available [options](#cli-options).
### Using `npx`: ### Using `npx`:
- Open a SWA app folder at the root (outside any /api or /app folders): - Open a SWA app folder at the root (outside any /api or /app folders):
```bash ```bash
cd my-awesome-swa-app cd my-awesome-swa-app
``` ```
- Create a configuration for your project: - Create a configuration for your project:
```bash ```bash
npx @azure/static-web-apps-cli init npx @azure/static-web-apps-cli init
``` ```

Просмотреть файл

@ -236,8 +236,7 @@
"type": "object" "type": "object"
}, },
"loginCommandParameters": { "loginCommandParameters": {
"properties": { "properties": {},
},
"type": "object" "type": "object"
}, },
"deployCommandParameters": { "deployCommandParameters": {

Просмотреть файл

@ -23,7 +23,7 @@ describe("swa build", () => {
it("should run npm install before build command", async () => { it("should run npm install before build command", async () => {
const execSyncMock = jest.requireMock("child_process").execSync; const execSyncMock = jest.requireMock("child_process").execSync;
mockFs({ 'package.json': {} }); mockFs({ "package.json": {} });
await build({ ...DEFAULT_CONFIG, appBuildCommand: "npm run something" }); await build({ ...DEFAULT_CONFIG, appBuildCommand: "npm run something" });
expect(execSyncMock.mock.calls[0][0]).toBe("npm install"); expect(execSyncMock.mock.calls[0][0]).toBe("npm install");
@ -32,12 +32,12 @@ describe("swa build", () => {
it("should run command in package.json path", async () => { it("should run command in package.json path", async () => {
const execSyncMock = jest.requireMock("child_process").execSync; const execSyncMock = jest.requireMock("child_process").execSync;
mockFs({ 'app/package.json': {} }); mockFs({ "app/package.json": {} });
await build({ await build({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG,
outputLocation: "app/dist", outputLocation: "app/dist",
appBuildCommand: "npm run something" appBuildCommand: "npm run something",
}); });
expect(execSyncMock.mock.calls[0][1].cwd).toBe("app"); expect(execSyncMock.mock.calls[0][1].cwd).toBe("app");
}); });
@ -46,27 +46,27 @@ describe("swa build", () => {
const execSyncMock = jest.requireMock("child_process").execSync; const execSyncMock = jest.requireMock("child_process").execSync;
mockFs(); mockFs();
await build({ ...DEFAULT_CONFIG, apiLocation: 'api/', apiBuildCommand: "npm run something" }); await build({ ...DEFAULT_CONFIG, apiLocation: "api/", apiBuildCommand: "npm run something" });
expect(execSyncMock.mock.calls[0][0]).toBe("npm run something"); expect(execSyncMock.mock.calls[0][0]).toBe("npm run something");
}); });
it("should run npm install before build command", async () => { it("should run npm install before build command", async () => {
const execSyncMock = jest.requireMock("child_process").execSync; const execSyncMock = jest.requireMock("child_process").execSync;
mockFs({ 'api/package.json': {} }); mockFs({ "api/package.json": {} });
await build({ ...DEFAULT_CONFIG, apiLocation: 'api/', apiBuildCommand: "npm run something" }); await build({ ...DEFAULT_CONFIG, apiLocation: "api/", apiBuildCommand: "npm run something" });
expect(execSyncMock.mock.calls[0][0]).toBe("npm install"); expect(execSyncMock.mock.calls[0][0]).toBe("npm install");
expect(execSyncMock.mock.calls[1][0]).toBe("npm run something"); expect(execSyncMock.mock.calls[1][0]).toBe("npm run something");
}); });
it("should run command in package.json path", async () => { it("should run command in package.json path", async () => {
const execSyncMock = jest.requireMock("child_process").execSync; const execSyncMock = jest.requireMock("child_process").execSync;
mockFs({ 'api/package.json': {} }); mockFs({ "api/package.json": {} });
await build({ await build({
...DEFAULT_CONFIG, ...DEFAULT_CONFIG,
apiLocation: 'api', apiLocation: "api",
apiBuildCommand: "npm run something" apiBuildCommand: "npm run something",
}); });
expect(execSyncMock.mock.calls[0][1].cwd).toBe("api"); expect(execSyncMock.mock.calls[0][1].cwd).toBe("api");
}); });

Просмотреть файл

@ -4,7 +4,16 @@ import { Command } from "commander";
import { DEFAULT_CONFIG } from "../../config"; import { DEFAULT_CONFIG } from "../../config";
import { detectProjectFolders, generateConfiguration } from "../../core/frameworks"; import { detectProjectFolders, generateConfiguration } from "../../core/frameworks";
import { import {
configureOptions, findUpPackageJsonDir, isUserOption, isUserOrConfigOption, logger, matchLoadedConfigName, pathExists, readWorkflowFile, runCommand, swaCliConfigFilename, configureOptions,
findUpPackageJsonDir,
isUserOption,
isUserOrConfigOption,
logger,
matchLoadedConfigName,
pathExists,
readWorkflowFile,
runCommand,
swaCliConfigFilename,
} from "../../core/utils"; } from "../../core/utils";
export default function registerCommand(program: Command) { export default function registerCommand(program: Command) {
@ -21,7 +30,7 @@ export default function registerCommand(program: Command) {
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => { .action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "build"); const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "build");
if (positionalArg && !matchLoadedConfigName(positionalArg)) { if (positionalArg && !matchLoadedConfigName(positionalArg)) {
if (isUserOption('appLocation')) { if (isUserOption("appLocation")) {
logger.error(`swa build <appLocation> cannot be when with --app-location option is also set.`); logger.error(`swa build <appLocation> cannot be when with --app-location option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true); logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
} }
@ -45,7 +54,7 @@ export async function build(options: SWACLIConfig) {
outputLocation: options.outputLocation, outputLocation: options.outputLocation,
appBuildCommand: options.appBuildCommand, appBuildCommand: options.appBuildCommand,
apiBuildCommand: options.apiBuildCommand, apiBuildCommand: options.apiBuildCommand,
} },
}); });
let appLocation = options.appLocation ?? workflowConfig?.appLocation; let appLocation = options.appLocation ?? workflowConfig?.appLocation;
@ -61,7 +70,7 @@ export async function build(options: SWACLIConfig) {
} }
if (options.auto) { if (options.auto) {
logger.log('Detecting build configuration...'); logger.log("Detecting build configuration...");
const detectedFolders = await detectProjectFolders(appLocation); const detectedFolders = await detectProjectFolders(appLocation);
if (detectedFolders.app.length === 0 && detectedFolders.api.length === 0) { if (detectedFolders.app.length === 0 && detectedFolders.api.length === 0) {
@ -89,21 +98,21 @@ export async function build(options: SWACLIConfig) {
if (!appBuildCommand && !apiBuildCommand) { if (!appBuildCommand && !apiBuildCommand) {
if (!hasBuildOptionsDefined(options)) { if (!hasBuildOptionsDefined(options)) {
logger.warn('No build options were defined.'); logger.warn("No build options were defined.");
logger.warn('If your app needs a build step, run "swa init" to set your project configuration'); logger.warn('If your app needs a build step, run "swa init" to set your project configuration');
logger.warn(`or use option flags to set your build commands and paths.\n`); logger.warn(`or use option flags to set your build commands and paths.\n`);
} }
logger.log('Nothing to build.'); logger.log("Nothing to build.");
return; return;
} }
logger.log(`Build configuration:`); logger.log(`Build configuration:`);
logger.log(`- App location: ${chalk.green(appLocation || '')}`); logger.log(`- App location: ${chalk.green(appLocation || "")}`);
logger.log(`- API location: ${chalk.green(apiLocation || '')}`); logger.log(`- API location: ${chalk.green(apiLocation || "")}`);
logger.log(`- Output location: ${chalk.green(outputLocation || '')}`); logger.log(`- Output location: ${chalk.green(outputLocation || "")}`);
logger.log(`- App build command: ${chalk.green(appBuildCommand || '')}`); logger.log(`- App build command: ${chalk.green(appBuildCommand || "")}`);
logger.log(`- API build command: ${chalk.green(apiBuildCommand || '')}`); logger.log(`- API build command: ${chalk.green(apiBuildCommand || "")}`);
if (appBuildCommand) { if (appBuildCommand) {
const packageJsonPath = await findUpPackageJsonDir(appLocation!, outputLocation!); const packageJsonPath = await findUpPackageJsonDir(appLocation!, outputLocation!);
@ -118,7 +127,7 @@ export async function build(options: SWACLIConfig) {
if (apiBuildCommand) { if (apiBuildCommand) {
// For now, only look up in the api location as there's no equivalent to outputLocation for api // For now, only look up in the api location as there's no equivalent to outputLocation for api
const packageJsonPath = await findUpPackageJsonDir(apiLocation!, '.'); const packageJsonPath = await findUpPackageJsonDir(apiLocation!, ".");
if (packageJsonPath) { if (packageJsonPath) {
logger.log(`Found package.json in ${packageJsonPath}`); logger.log(`Found package.json in ${packageJsonPath}`);
await installNpmDependencies(packageJsonPath); await installNpmDependencies(packageJsonPath);
@ -133,7 +142,7 @@ function hasBuildOptionsDefined(options: SWACLIConfig): boolean {
if (options.appBuildCommand || options.apiBuildCommand) { if (options.appBuildCommand || options.apiBuildCommand) {
return true; return true;
} }
return isUserOrConfigOption('appBuildCommand') || isUserOrConfigOption('apiBuildCommand'); return isUserOrConfigOption("appBuildCommand") || isUserOrConfigOption("apiBuildCommand");
} }
function showAutoErrorMessageAndExit() { function showAutoErrorMessageAndExit() {
@ -142,19 +151,19 @@ function showAutoErrorMessageAndExit() {
} }
async function detectPackageManager(basePath: string): Promise<NpmPackageManager> { async function detectPackageManager(basePath: string): Promise<NpmPackageManager> {
const hasYarnLock = await pathExists(path.join(basePath, 'yarn.lock')); const hasYarnLock = await pathExists(path.join(basePath, "yarn.lock"));
const hasNpmLock = await pathExists(path.join(basePath, 'package-lock.json')); const hasNpmLock = await pathExists(path.join(basePath, "package-lock.json"));
const hasPnpmLock = await pathExists(path.join(basePath, 'pnpm-lock.yaml')); const hasPnpmLock = await pathExists(path.join(basePath, "pnpm-lock.yaml"));
if (hasPnpmLock && !hasNpmLock && !hasYarnLock) { if (hasPnpmLock && !hasNpmLock && !hasYarnLock) {
return 'pnpm'; return "pnpm";
} }
if (hasYarnLock && !hasNpmLock) { if (hasYarnLock && !hasNpmLock) {
return 'yarn'; return "yarn";
} }
return 'npm'; return "npm";
} }
async function installNpmDependencies(packageJsonPath: string): Promise<void> { async function installNpmDependencies(packageJsonPath: string): Promise<void> {

Просмотреть файл

@ -43,7 +43,7 @@ export default function registerCommand(program: Command) {
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => { .action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "deploy"); const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "deploy");
if (positionalArg && !matchLoadedConfigName(positionalArg)) { if (positionalArg && !matchLoadedConfigName(positionalArg)) {
if (isUserOption('outputLocation')) { if (isUserOption("outputLocation")) {
logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`); logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true); logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
} }
@ -125,7 +125,9 @@ export async function deploy(options: SWACLIConfig) {
const apiFolder = await findApiFolderInPath(appLocation); const apiFolder = await findApiFolderInPath(appLocation);
if (apiFolder) { if (apiFolder) {
logger.warn( logger.warn(
`An API folder was found at ".${path.sep + path.basename(apiFolder)}" but the --api-location option was not provided. The API will not be deployed.\n` `An API folder was found at ".${
path.sep + path.basename(apiFolder)
}" but the --api-location option was not provided. The API will not be deployed.\n`
); );
} }
} }
@ -356,5 +358,5 @@ export async function deploy(options: SWACLIConfig) {
async function findApiFolderInPath(appPath: string): Promise<string | undefined> { async function findApiFolderInPath(appPath: string): Promise<string | undefined> {
const entries = await fs.promises.readdir(appPath, { withFileTypes: true }); const entries = await fs.promises.readdir(appPath, { withFileTypes: true });
return entries.find(entry => entry.name.toLowerCase() === 'api' && entry.isDirectory())?.name; return entries.find((entry) => entry.name.toLowerCase() === "api" && entry.isDirectory())?.name;
} }

Просмотреть файл

@ -24,7 +24,7 @@ export default function registerCommand(program: Command) {
.action(async (configName: string | undefined, _options: SWACLIConfig, command: Command) => { .action(async (configName: string | undefined, _options: SWACLIConfig, command: Command) => {
const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init", false); const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init", false);
if (configName) { if (configName) {
if (isUserOption('configName')) { if (isUserOption("configName")) {
logger.error(`swa init <configName> cannot be used when --config-name option is also set.`); logger.error(`swa init <configName> cannot be used when --config-name option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true); logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
} }
@ -71,19 +71,19 @@ export async function init(options: SWACLIConfig, showHints: boolean = true) {
name: "app", name: "app",
message: "Which app folder do you want to use?", message: "Which app folder do you want to use?",
choices: detectedFolders.app.map((folder) => ({ title: folder.rootPath, value: folder })), choices: detectedFolders.app.map((folder) => ({ title: folder.rootPath, value: folder })),
initial: 0 initial: 0,
}); });
// Workaround for bug https://github.com/terkelg/prompts/issues/205 // Workaround for bug https://github.com/terkelg/prompts/issues/205
app = typeof response.app === 'number' ? detectedFolders.app[response.app] : response.app app = typeof response.app === "number" ? detectedFolders.app[response.app] : response.app;
} }
// Check if we can find api folders under selected app folder, and filter selection if we found some // Check if we can find api folders under selected app folder, and filter selection if we found some
if (app !== undefined) { if (app !== undefined) {
const childApiFolders = detectedFolders.api.filter(folder => isDescendantPath(folder.rootPath, app!.rootPath)); const childApiFolders = detectedFolders.api.filter((folder) => isDescendantPath(folder.rootPath, app!.rootPath));
if (childApiFolders.length > 0) { if (childApiFolders.length > 0) {
logger.silly(`Found (${childApiFolders.length}) api folders under the app folder`); logger.silly(`Found (${childApiFolders.length}) api folders under the app folder`);
logger.silly(`- ${childApiFolders.map(f => `${f.rootPath} (${f.frameworks.map(fr => fr.name).join(', ')})`).join("\n- ")}`); logger.silly(`- ${childApiFolders.map((f) => `${f.rootPath} (${f.frameworks.map((fr) => fr.name).join(", ")})`).join("\n- ")}`);
detectedFolders.api = childApiFolders; detectedFolders.api = childApiFolders;
} }
} }
@ -96,11 +96,11 @@ export async function init(options: SWACLIConfig, showHints: boolean = true) {
name: "api", name: "api",
message: "Which api folder do you want to use?", message: "Which api folder do you want to use?",
choices: detectedFolders.api.map((folder) => ({ title: folder.rootPath, value: folder })), choices: detectedFolders.api.map((folder) => ({ title: folder.rootPath, value: folder })),
initial: 0 initial: 0,
}); });
// Workaround for bug https://github.com/terkelg/prompts/issues/205 // Workaround for bug https://github.com/terkelg/prompts/issues/205
api = typeof response.api === 'number' ? detectedFolders.api[response.api] : response.api api = typeof response.api === "number" ? detectedFolders.api[response.api] : response.api;
} else { } else {
api = detectedFolders.api[0]; api = detectedFolders.api[0];
} }
@ -117,10 +117,10 @@ export async function init(options: SWACLIConfig, showHints: boolean = true) {
printFrameworkConfig(projectConfig); printFrameworkConfig(projectConfig);
const { confirmSettings } = await promptOrUseDefault(disablePrompts, { const { confirmSettings } = await promptOrUseDefault(disablePrompts, {
type: 'confirm', type: "confirm",
name: 'confirmSettings', name: "confirmSettings",
message: 'Are these settings correct?', message: "Are these settings correct?",
initial: true initial: true,
}); });
if (!confirmSettings) { if (!confirmSettings) {
// Ask for each settings // Ask for each settings
@ -231,15 +231,15 @@ async function promptConfigSettings(disablePrompts: boolean, detectedConfig: Fra
} }
function printFrameworkConfig(config: FrameworkConfig) { function printFrameworkConfig(config: FrameworkConfig) {
logger.log(chalk.bold('\nDetected configuration for your app:')); logger.log(chalk.bold("\nDetected configuration for your app:"));
logger.log(`- Framework(s): ${chalk.green(config.name ?? 'none')}`); logger.log(`- Framework(s): ${chalk.green(config.name ?? "none")}`);
logger.log(`- App location: ${chalk.green(config.appLocation)}`); logger.log(`- App location: ${chalk.green(config.appLocation)}`);
logger.log(`- Output location: ${chalk.green(config.outputLocation)}`); logger.log(`- Output location: ${chalk.green(config.outputLocation)}`);
logger.log(`- API location: ${chalk.green(config.apiLocation ?? '')}`); logger.log(`- API location: ${chalk.green(config.apiLocation ?? "")}`);
logger.log(`- App build command: ${chalk.green(config.appBuildCommand ?? '')}`); logger.log(`- App build command: ${chalk.green(config.appBuildCommand ?? "")}`);
logger.log(`- API build command: ${chalk.green(config.apiBuildCommand ?? '')}`); logger.log(`- API build command: ${chalk.green(config.apiBuildCommand ?? "")}`);
logger.log(`- Dev command: ${chalk.green(config.devServerCommand ?? '')}`); logger.log(`- Dev command: ${chalk.green(config.devServerCommand ?? "")}`);
logger.log(`- Dev server URL: ${chalk.green(config.devServerUrl ?? '')}\n`); logger.log(`- Dev server URL: ${chalk.green(config.devServerUrl ?? "")}\n`);
} }
// function isEmptyFolder(path: string) { // function isEmptyFolder(path: string) {

Просмотреть файл

@ -4,7 +4,7 @@ import concurrently, { CloseEvent } from "concurrently";
import { CommandInfo } from "concurrently/dist/src/command"; import { CommandInfo } from "concurrently/dist/src/command";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import { execSync } from 'child_process'; import { execSync } from "child_process";
import { DEFAULT_CONFIG } from "../../config"; import { DEFAULT_CONFIG } from "../../config";
import { import {
askNewPort, askNewPort,
@ -41,7 +41,7 @@ export default function registerCommand(program: Command) {
.option<number>("--api-port <apiPort>", "the API server port passed to `func start`", parsePort, DEFAULT_CONFIG.apiPort) .option<number>("--api-port <apiPort>", "the API server port passed to `func start`", parsePort, DEFAULT_CONFIG.apiPort)
.option("--host <host>", "the host address to use for the CLI dev server", DEFAULT_CONFIG.host) .option("--host <host>", "the host address to use for the CLI dev server", DEFAULT_CONFIG.host)
.option<number>("--port <port>", "the port value to use for the CLI dev server", parsePort, DEFAULT_CONFIG.port) .option<number>("--port <port>", "the port value to use for the CLI dev server", parsePort, DEFAULT_CONFIG.port)
.option("--run-build", "run \"swa build\" before starting the emulator", false) .option("--run-build", 'run "swa build" before starting the emulator', false)
.option("--ssl", "serve the front-end application and API over HTTPS", DEFAULT_CONFIG.ssl) .option("--ssl", "serve the front-end application and API over HTTPS", DEFAULT_CONFIG.ssl)
.option("--ssl-cert <sslCertLocation>", "the SSL certificate (.crt) to use when enabling HTTPS", DEFAULT_CONFIG.sslCert) .option("--ssl-cert <sslCertLocation>", "the SSL certificate (.crt) to use when enabling HTTPS", DEFAULT_CONFIG.sslCert)
@ -66,13 +66,13 @@ export default function registerCommand(program: Command) {
// If it's not the config name, it's either output location or dev server url // If it's not the config name, it's either output location or dev server url
const isUrl = isHttpUrl(positionalArg); const isUrl = isHttpUrl(positionalArg);
if (isUrl) { if (isUrl) {
if (isUserOption('devServerUrl')) { if (isUserOption("devServerUrl")) {
logger.error(`swa deploy <devServerUrl> cannot be used when --dev-server-url option is also set.`); logger.error(`swa deploy <devServerUrl> cannot be used when --dev-server-url option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true); logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
} }
options.devServerUrl = positionalArg; options.devServerUrl = positionalArg;
} else { } else {
if (isUserOption('outputLocation')) { if (isUserOption("outputLocation")) {
logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`); logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true); logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
} }
@ -147,7 +147,7 @@ export async function start(options: SWACLIConfig) {
let resolvedPortNumber = await isAcceptingTcpConnections({ host, port }); let resolvedPortNumber = await isAcceptingTcpConnections({ host, port });
if (resolvedPortNumber === 0) { if (resolvedPortNumber === 0) {
logger.warn(`Port ${port} is already taken!`); logger.warn(`Port ${port} is already taken!`);
resolvedPortNumber = await askNewPort() resolvedPortNumber = await askNewPort();
} else { } else {
logger.silly(`Port ${port} is available. Use it.`); logger.silly(`Port ${port} is available. Use it.`);
} }
@ -347,17 +347,15 @@ export async function start(options: SWACLIConfig) {
if (startupCommand) { if (startupCommand) {
let startupPath = userWorkflowConfig?.appLocation; let startupPath = userWorkflowConfig?.appLocation;
concurrentlyCommands.push( concurrentlyCommands.push({ command: `cd "${startupPath}" && ${startupCommand}`, name: "run", env, prefixColor: "gray.dim" });
{ command: `cd "${startupPath}" && ${startupCommand}`, name: "run", env, prefixColor: "gray.dim" }
);
} }
if (runBuild) { if (runBuild) {
// run swa build // run swa build
execSync("swa build", { execSync("swa build", {
stdio: 'inherit', stdio: "inherit",
// Set CI to avoid extra NPM logs and potentially unwanted interactive modes // Set CI to avoid extra NPM logs and potentially unwanted interactive modes
env: { ...process.env, CI: "1" } env: { ...process.env, CI: "1" },
}); });
} }

Просмотреть файл

@ -51,12 +51,15 @@ export async function run(argv?: string[]) {
const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init"); const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init");
swaMagic(options); swaMagic(options);
}) })
.addHelpText("after", ` .addHelpText(
"after",
`
Type "swa" to get started and deploy your project. Type "swa" to get started and deploy your project.
Documentation: Documentation:
https://aka.ms/swa/cli-local-development https://aka.ms/swa/cli-local-development
`); `
);
// Register commands // Register commands
registerLogin(program); registerLogin(program);
@ -74,7 +77,7 @@ export async function swaMagic(_options: SWACLIConfig) {
try { try {
const hasLoadedConfig = getCurrentSwaCliConfigFromFile(); const hasLoadedConfig = getCurrentSwaCliConfigFromFile();
if (!hasLoadedConfig) { if (!hasLoadedConfig) {
runCommand("swa init") runCommand("swa init");
} }
runCommand("swa build"); runCommand("swa build");
@ -82,7 +85,7 @@ export async function swaMagic(_options: SWACLIConfig) {
type: "confirm", type: "confirm",
name: "deploy", name: "deploy",
message: "Do you want to deploy your app now?", message: "Do you want to deploy your app now?",
initial: true initial: true,
}); });
if (!response.deploy) { if (!response.deploy) {
logger.log(`\nWhen you'll be ready to deploy your app, just use ${chalk.cyan("swa")} again.`); logger.log(`\nWhen you'll be ready to deploy your app, just use ${chalk.cyan("swa")} again.`);

Просмотреть файл

@ -26,7 +26,7 @@ const {
SWA_CLI_APP_NAME, SWA_CLI_APP_NAME,
SWA_CLI_DEPLOY_DRY_RUN, SWA_CLI_DEPLOY_DRY_RUN,
SWA_CLI_DEPLOY_ENV, SWA_CLI_DEPLOY_ENV,
SWA_CLI_LOGIN_CLEAR_CREDENTIALS SWA_CLI_LOGIN_CLEAR_CREDENTIALS,
} = swaCLIEnv(); } = swaCLIEnv();
export const DEFAULT_CONFIG: SWACLIConfig = { export const DEFAULT_CONFIG: SWACLIConfig = {

Просмотреть файл

@ -3,9 +3,9 @@ import { DEFAULT_CONFIG } from "../config";
import { address, isHttpUrl } from "./utils/net"; import { address, isHttpUrl } from "./utils/net";
export const STATIC_SITE_CLIENT_RELEASE_METADATA_URL = "https://swalocaldeploy.azureedge.net/downloads/versions.json"; export const STATIC_SITE_CLIENT_RELEASE_METADATA_URL = "https://swalocaldeploy.azureedge.net/downloads/versions.json";
export const SWA_COMMANDS = ['login', 'init', 'start', 'deploy', 'build'] as const; export const SWA_COMMANDS = ["login", "init", "start", "deploy", "build"] as const;
// Type cannot be in swa.d.ts as it's inferred from SWA_COMMANDS // Type cannot be in swa.d.ts as it's inferred from SWA_COMMANDS
export type SWACommand = (typeof SWA_COMMANDS)[number]; export type SWACommand = typeof SWA_COMMANDS[number];
export const SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB = 20; // 20kb export const SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB = 20; // 20kb

Просмотреть файл

@ -146,7 +146,9 @@ export function getPlatform(): "win-x64" | "osx-x64" | "linux-x64" | null {
export async function fetchClientVersionDefinition(releaseVersion: string): Promise<StaticSiteClientReleaseMetadata | undefined> { export async function fetchClientVersionDefinition(releaseVersion: string): Promise<StaticSiteClientReleaseMetadata | undefined> {
logger.silly(`Fetching release metadata for version: ${releaseVersion}. Please wait...`); logger.silly(`Fetching release metadata for version: ${releaseVersion}. Please wait...`);
const remoteVersionDefinitions = await fetch(STATIC_SITE_CLIENT_RELEASE_METADATA_URL).then((res) => res.json()) as StaticSiteClientReleaseMetadata[]; const remoteVersionDefinitions = (await fetch(STATIC_SITE_CLIENT_RELEASE_METADATA_URL).then((res) =>
res.json()
)) as StaticSiteClientReleaseMetadata[];
if (Array.isArray(remoteVersionDefinitions) && remoteVersionDefinitions.length) { if (Array.isArray(remoteVersionDefinitions) && remoteVersionDefinitions.length) {
const releaseMetadata = remoteVersionDefinitions.find((versionDefinition) => versionDefinition?.version === releaseVersion); const releaseMetadata = remoteVersionDefinitions.find((versionDefinition) => versionDefinition?.version === releaseVersion);

Просмотреть файл

@ -10,7 +10,7 @@ const packageJsonFile = "package.json";
export async function generateConfiguration(app?: DetectedFolder, api?: DetectedFolder): Promise<FrameworkConfig> { export async function generateConfiguration(app?: DetectedFolder, api?: DetectedFolder): Promise<FrameworkConfig> {
let config: FrameworkConfig = { let config: FrameworkConfig = {
appLocation: DEFAULT_CONFIG.appLocation!, appLocation: DEFAULT_CONFIG.appLocation!,
outputLocation: DEFAULT_CONFIG.outputLocation! outputLocation: DEFAULT_CONFIG.outputLocation!,
}; };
if (!app && !api) { if (!app && !api) {
@ -18,11 +18,11 @@ export async function generateConfiguration(app?: DetectedFolder, api?: Detected
return config; return config;
} }
let name = ''; let name = "";
if (app) { if (app) {
name += `${app.frameworks.map(f => f.name).join(', ')}`; name += `${app.frameworks.map((f) => f.name).join(", ")}`;
app.frameworks.forEach(f => config = { ...config, ...f.config }); app.frameworks.forEach((f) => (config = { ...config, ...f.config }));
config.appLocation = await computePath(app.rootPath, config.appLocation); config.appLocation = await computePath(app.rootPath, config.appLocation);
config.appLocation = removeTrailingPathSep(config.appLocation); config.appLocation = removeTrailingPathSep(config.appLocation);
config.outputLocation = await computePath(config.appLocation, config.outputLocation); config.outputLocation = await computePath(config.appLocation, config.outputLocation);
@ -31,9 +31,9 @@ export async function generateConfiguration(app?: DetectedFolder, api?: Detected
} }
if (api) { if (api) {
name += (name ? ', with ' : 'No app frameworks detected, '); name += name ? ", with " : "No app frameworks detected, ";
name += `API: ${api.frameworks.map(f => f.name).join(', ')}`; name += `API: ${api.frameworks.map((f) => f.name).join(", ")}`;
api.frameworks.forEach(f => config = { ...config, ...f.config }); api.frameworks.forEach((f) => (config = { ...config, ...f.config }));
const computedApiLocation = await computePath(api.rootPath, config.apiLocation); const computedApiLocation = await computePath(api.rootPath, config.apiLocation);
if (computedApiLocation !== api.rootPath) { if (computedApiLocation !== api.rootPath) {
@ -71,7 +71,7 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
return basePath; return basePath;
} }
if (!additionalPath.startsWith('{')) { if (!additionalPath.startsWith("{")) {
return path.join(basePath, additionalPath); return path.join(basePath, additionalPath);
} }
@ -93,8 +93,7 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
throw new Error(`Invalid JSON file: ${file}`); throw new Error(`Invalid JSON file: ${file}`);
} }
const evaluateExpression = (json: JsonData, expr: string) => const evaluateExpression = (json: JsonData, expr: string) => Function(`"use strict";return data => (${expr})`)()(json);
Function(`"use strict";return data => (${expr})`)()(json);
try { try {
const result = evaluateExpression(json, expression); const result = evaluateExpression(json, expression);
@ -110,7 +109,7 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
return basePath; return basePath;
} }
export async function detectProjectFolders(projectPath: string = '.'): Promise<DetectionResult> { export async function detectProjectFolders(projectPath: string = "."): Promise<DetectionResult> {
const projectFiles = await getFiles(projectPath); const projectFiles = await getFiles(projectPath);
const apiFrameworks = await detectApiFrameworks(projectFiles); const apiFrameworks = await detectApiFrameworks(projectFiles);
const appFrameworks = await detectAppFrameworks(projectFiles); const appFrameworks = await detectAppFrameworks(projectFiles);
@ -152,15 +151,9 @@ export async function detectFrameworks(projectFiles: string[], frameworks: Frame
// Parent files are implicit for child frameworks // Parent files are implicit for child frameworks
if (framework.parent) { if (framework.parent) {
const parent = frameworksById[framework.parent]; const parent = frameworksById[framework.parent];
files.push( files.push(...(parent.files ?? []), ...(parent.packages ? [packageJsonFile] : []));
...(parent.files ?? []),
...(parent.packages ? [packageJsonFile] : [])
);
} }
files.push( files.push(...(framework.files ?? []), ...(framework.packages ? [packageJsonFile] : []));
...(framework.files ?? []),
...(framework.packages ? [packageJsonFile] : [])
);
const rootPaths = await findRootPathsForFiles(files, projectFiles); const rootPaths = await findRootPathsForFiles(files, projectFiles);
if (rootPaths !== undefined) { if (rootPaths !== undefined) {
@ -170,7 +163,7 @@ export async function detectFrameworks(projectFiles: string[], frameworks: Frame
detectedFrameworks = await asyncFilter( detectedFrameworks = await asyncFilter(
detectedFrameworks, detectedFrameworks,
async framework => await matchPackages(framework) && await matchContains(framework, projectFiles) async (framework) => (await matchPackages(framework)) && (await matchContains(framework, projectFiles))
); );
let detectedFolders = await aggregateFolders(detectedFrameworks); let detectedFolders = await aggregateFolders(detectedFrameworks);
detectedFolders = filterDescendantFolders(detectedFolders); detectedFolders = filterDescendantFolders(detectedFolders);
@ -182,14 +175,14 @@ export async function detectFrameworks(projectFiles: string[], frameworks: Frame
async function detectApiFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> { async function detectApiFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> {
const detectedApiFolders: DetectedFolder[] = await detectFrameworks(projectFiles, apiFrameworks); const detectedApiFolders: DetectedFolder[] = await detectFrameworks(projectFiles, apiFrameworks);
logger.silly(formatDetectedFolders(detectedApiFolders, 'api')); logger.silly(formatDetectedFolders(detectedApiFolders, "api"));
return detectedApiFolders; return detectedApiFolders;
} }
async function detectAppFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> { async function detectAppFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> {
const detectedAppFolders: DetectedFolder[] = await detectFrameworks(projectFiles, appFrameworks); const detectedAppFolders: DetectedFolder[] = await detectFrameworks(projectFiles, appFrameworks);
logger.silly(formatDetectedFolders(detectedAppFolders, 'app')); logger.silly(formatDetectedFolders(detectedAppFolders, "app"));
return detectedAppFolders; return detectedAppFolders;
} }
@ -212,7 +205,7 @@ async function matchPackages(framework: DetectedFramework): Promise<boolean> {
return true; return true;
} }
const rootPathsMatches: string[] | undefined = await asyncFilter(framework.rootPaths, async rootPath => { const rootPathsMatches: string[] | undefined = await asyncFilter(framework.rootPaths, async (rootPath) => {
const packageJsonPath = path.join(rootPath, packageJsonFile); const packageJsonPath = path.join(rootPath, packageJsonFile);
const packageJson = await safeReadJson(packageJsonPath); const packageJson = await safeReadJson(packageJsonPath);
if (!packageJson) { if (!packageJson) {
@ -222,10 +215,7 @@ async function matchPackages(framework: DetectedFramework): Promise<boolean> {
const dependencies = Object.keys(packageJson.dependencies ?? {}); const dependencies = Object.keys(packageJson.dependencies ?? {});
const devDependencies = Object.keys(packageJson.devDependencies ?? {}); const devDependencies = Object.keys(packageJson.devDependencies ?? {});
return framework.packages!.some(packageName => return framework.packages!.some((packageName) => dependencies.includes(packageName) || devDependencies.includes(packageName));
dependencies.includes(packageName) ||
devDependencies.includes(packageName)
);
}); });
framework.rootPaths = rootPathsMatches; framework.rootPaths = rootPathsMatches;
@ -237,7 +227,7 @@ async function matchContains(framework: DetectedFramework, files: string[]): Pro
return true; return true;
} }
const rootPathsMatches: string[] | undefined = await asyncFilter(framework.rootPaths, async rootPath => { const rootPathsMatches: string[] | undefined = await asyncFilter(framework.rootPaths, async (rootPath) => {
const currentFiles = filesFromRootPath(rootPath, files); const currentFiles = filesFromRootPath(rootPath, files);
return asyncEvery(Object.entries(framework.contains!), async ([filename, stringToFind]) => { return asyncEvery(Object.entries(framework.contains!), async ([filename, stringToFind]) => {
const file = findFile(filename, currentFiles); const file = findFile(filename, currentFiles);
@ -262,8 +252,8 @@ function filterDescendantFolders(folders: DetectedFolder[]): DetectedFolder[] {
// Find all folders that are descendants of other folders // Find all folders that are descendants of other folders
const descendantPaths: Set<string> = new Set(); const descendantPaths: Set<string> = new Set();
for (const folder of folders) { for (const folder of folders) {
const descendantsFolders = folders.filter(f => isDescendantPath(f.rootPath, folder.rootPath)); const descendantsFolders = folders.filter((f) => isDescendantPath(f.rootPath, folder.rootPath));
descendantsFolders.forEach(f => descendantPaths.add(f.rootPath)); descendantsFolders.forEach((f) => descendantPaths.add(f.rootPath));
} }
if (descendantPaths.size === 0) { if (descendantPaths.size === 0) {
@ -274,15 +264,15 @@ function filterDescendantFolders(folders: DetectedFolder[]): DetectedFolder[] {
logger.silly(`- ${Array.from(descendantPaths).join("\n- ")}`); logger.silly(`- ${Array.from(descendantPaths).join("\n- ")}`);
// Only keep folders that are not descendants // Only keep folders that are not descendants
return folders.filter(f => !descendantPaths.has(f.rootPath)); return folders.filter((f) => !descendantPaths.has(f.rootPath));
} }
function filterPreemptedFrameworks(detectedFolders: DetectedFolder[]): void { function filterPreemptedFrameworks(detectedFolders: DetectedFolder[]): void {
for (const folder of detectedFolders) { for (const folder of detectedFolders) {
const overridenFrameworkIds: Set<string> = new Set(); const overridenFrameworkIds: Set<string> = new Set();
folder.frameworks.forEach(f => { folder.frameworks.forEach((f) => {
if (f.overrides) { if (f.overrides) {
f.overrides.forEach(id => overridenFrameworkIds.add(id)); f.overrides.forEach((id) => overridenFrameworkIds.add(id));
} }
}); });
@ -298,7 +288,7 @@ function filterPreemptedFrameworks(detectedFolders: DetectedFolder[]): void {
logger.silly(`Found frameworks to override in path ${folder.rootPath}: ${Array.from(overridenFrameworkIds).join(",")}`); logger.silly(`Found frameworks to override in path ${folder.rootPath}: ${Array.from(overridenFrameworkIds).join(",")}`);
folder.frameworks = folder.frameworks.filter(f => !overridenFrameworkIds.has(f.id)); folder.frameworks = folder.frameworks.filter((f) => !overridenFrameworkIds.has(f.id));
} }
} }
@ -312,7 +302,7 @@ function orderFrameworksByParent(detectedFolders: DetectedFolder[]): void {
for (const framework of frameworks) { for (const framework of frameworks) {
if (framework.parent && frameworkIndexById[framework.parent] === undefined) { if (framework.parent && frameworkIndexById[framework.parent] === undefined) {
// Whoops, parent must be placed before this framework! // Whoops, parent must be placed before this framework!
const parentIndex = frameworks.findIndex(f => f.id === framework.parent); const parentIndex = frameworks.findIndex((f) => f.id === framework.parent);
if (parentIndex === -1) { if (parentIndex === -1) {
// Lonely childs should not be a thing // Lonely childs should not be a thing
logger.silly(`Framework ${framework.id} has parent ${framework.parent} but it's not detected`); logger.silly(`Framework ${framework.id} has parent ${framework.parent} but it's not detected`);
@ -330,7 +320,7 @@ function orderFrameworksByParent(detectedFolders: DetectedFolder[]): void {
function findAllFiles(fileglob: string, files: string[]): string[] { function findAllFiles(fileglob: string, files: string[]): string[] {
const { regex } = globrex(`?(*/)${fileglob}`, { extended: true, flags: "i" } as globrex.Options); const { regex } = globrex(`?(*/)${fileglob}`, { extended: true, flags: "i" } as globrex.Options);
return files.filter(file => regex.test(file)); return files.filter((file) => regex.test(file));
} }
function findFile(fileglob: string, files: string[]): string | undefined { function findFile(fileglob: string, files: string[]): string | undefined {
@ -338,34 +328,34 @@ function findFile(fileglob: string, files: string[]): string | undefined {
} }
function filesFromRootPath(rootPath: string, files: string[]): string[] { function filesFromRootPath(rootPath: string, files: string[]): string[] {
return files.filter(file => file.startsWith(rootPath)); return files.filter((file) => file.startsWith(rootPath));
} }
function findRootPathsForFiles(fileglobs: string[], files: string[]): string[] | undefined { function findRootPathsForFiles(fileglobs: string[], files: string[]): string[] | undefined {
const foundFiles = fileglobs.map(fileglob => findAllFiles(fileglob, files)); const foundFiles = fileglobs.map((fileglob) => findAllFiles(fileglob, files));
// Get possible root path from first glob matches // Get possible root path from first glob matches
// Note: currently it doesn't work if globs include subfolders // Note: currently it doesn't work if globs include subfolders
// TODO: find common path denominator based on lowest dirname ancestor to try to find common root // TODO: find common path denominator based on lowest dirname ancestor to try to find common root
const uniqueRootPaths = new Set(foundFiles[0].map(file => path.dirname(file))); const uniqueRootPaths = new Set(foundFiles[0].map((file) => path.dirname(file)));
const otherFoundFiles = foundFiles.slice(1); const otherFoundFiles = foundFiles.slice(1);
const possibleRootPaths = [...uniqueRootPaths].filter( const possibleRootPaths = [...uniqueRootPaths].filter((p) => otherFoundFiles.every((files) => files.some((file) => path.dirname(file) === p)));
p => otherFoundFiles.every(files => files.some(file => path.dirname(file) === p))
);
return possibleRootPaths.length > 0 ? possibleRootPaths : undefined; return possibleRootPaths.length > 0 ? possibleRootPaths : undefined;
} }
async function getFiles(rootPath: string): Promise<string[]> { async function getFiles(rootPath: string): Promise<string[]> {
const entries = await fs.readdir(rootPath, { withFileTypes: true }); const entries = await fs.readdir(rootPath, { withFileTypes: true });
const files = await Promise.all(entries.map(async (entry): Promise<string[]> => { const files = await Promise.all(
entries.map(async (entry): Promise<string[]> => {
// Ignore dot files and node_modules // Ignore dot files and node_modules
if (entry.name.startsWith(".") || entry.name.includes("node_modules")) { if (entry.name.startsWith(".") || entry.name.includes("node_modules")) {
return []; return [];
} }
const entryPath = path.join(rootPath, entry.name); const entryPath = path.join(rootPath, entry.name);
return entry.isDirectory() ? [entryPath, ...(await getFiles(entryPath))] : [entryPath]; return entry.isDirectory() ? [entryPath, ...(await getFiles(entryPath))] : [entryPath];
})); })
);
return files.flat(); return files.flat();
} }
@ -382,9 +372,9 @@ async function asyncEvery<T>(array: T[], predicate: (item: T) => Promise<boolean
export function printSupportedFrameworks(showList = false): void { export function printSupportedFrameworks(showList = false): void {
if (showList) { if (showList) {
logger.info(`Supported api frameworks: ${apiFrameworks.length}`); logger.info(`Supported api frameworks: ${apiFrameworks.length}`);
logger.info(`- ${apiFrameworks.map(f => f.name).join("- \n")}`); logger.info(`- ${apiFrameworks.map((f) => f.name).join("- \n")}`);
logger.info(`Supported app frameworks: ${apiFrameworks.length}`); logger.info(`Supported app frameworks: ${apiFrameworks.length}`);
logger.info(`- ${appFrameworks.map(f => f.name).join("- \n")}`); logger.info(`- ${appFrameworks.map((f) => f.name).join("- \n")}`);
} else { } else {
logger.info(`Supported frameworks:`); logger.info(`Supported frameworks:`);
logger.info(`- api: ${apiFrameworks.length}`); logger.info(`- api: ${apiFrameworks.length}`);
@ -393,6 +383,8 @@ export function printSupportedFrameworks(showList = false): void {
} }
export function formatDetectedFolders(folders: DetectedFolder[], type: string): string { export function formatDetectedFolders(folders: DetectedFolder[], type: string): string {
return `Detected ${type} folders (${folders.length}):\n` + return (
`- ${folders.map(f => `${f.rootPath} (${f.frameworks.map(fr => fr.name).join(', ')})`).join("\n- ")}`; `Detected ${type} folders (${folders.length}):\n` +
`- ${folders.map((f) => `${f.rootPath} (${f.frameworks.map((fr) => fr.name).join(", ")})`).join("\n- ")}`
);
} }

Просмотреть файл

@ -5,7 +5,7 @@ export const apiFrameworks: FrameworkDefinition[] = [
files: ["package.json", "host.json"], files: ["package.json", "host.json"],
config: { config: {
apiBuildCommand: "npm run build --if-present", apiBuildCommand: "npm run build --if-present",
} },
}, },
{ {
id: "typescript", id: "typescript",
@ -24,7 +24,7 @@ export const apiFrameworks: FrameworkDefinition[] = [
files: ["*.?(csproj|fsproj)", "host.json"], files: ["*.?(csproj|fsproj)", "host.json"],
config: { config: {
apiBuildCommand: "dotnet build", apiBuildCommand: "dotnet build",
} },
}, },
{ {
id: "python", id: "python",
@ -32,8 +32,8 @@ export const apiFrameworks: FrameworkDefinition[] = [
files: ["?(requirements.txt|pyproject.toml|runtime.txt|setup.py)", "host.json"], files: ["?(requirements.txt|pyproject.toml|runtime.txt|setup.py)", "host.json"],
config: { config: {
// Nothing to setup, but we need to know the apiLocation (rootPath) // Nothing to setup, but we need to know the apiLocation (rootPath)
} },
} },
]; ];
export const appFrameworks: FrameworkDefinition[] = [ export const appFrameworks: FrameworkDefinition[] = [
@ -43,7 +43,7 @@ export const appFrameworks: FrameworkDefinition[] = [
config: { config: {
outputLocation: "./", outputLocation: "./",
}, },
files: ["@(index.htm|default.htm)?(l)"] files: ["@(index.htm|default.htm)?(l)"],
}, },
{ {
id: "angular", id: "angular",
@ -64,7 +64,7 @@ export const appFrameworks: FrameworkDefinition[] = [
packages: ["@scullyio/scully"], packages: ["@scullyio/scully"],
config: { config: {
// Same as base angular // Same as base angular
} },
}, },
{ {
id: "react", id: "react",
@ -75,7 +75,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm start", devServerCommand: "npm start",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "build", outputLocation: "build",
} },
}, },
{ {
id: "nextjs", id: "nextjs",
@ -86,8 +86,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm dev", devServerCommand: "npm dev",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "./" outputLocation: "./",
} },
}, },
{ {
id: "gatsby", id: "gatsby",
@ -99,8 +99,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm start", devServerCommand: "npm start",
devServerUrl: "http://localhost:8000", devServerUrl: "http://localhost:8000",
outputLocation: "public" outputLocation: "public",
} },
}, },
{ {
id: "docusaurus", id: "docusaurus",
@ -109,7 +109,7 @@ export const appFrameworks: FrameworkDefinition[] = [
packages: ["@docusaurus/core"], packages: ["@docusaurus/core"],
config: { config: {
// Same as base react // Same as base react
} },
}, },
{ {
id: "react-static", id: "react-static",
@ -117,8 +117,8 @@ export const appFrameworks: FrameworkDefinition[] = [
parent: "react", parent: "react",
packages: ["react-static"], packages: ["react-static"],
config: { config: {
outputLocation: "dist" outputLocation: "dist",
} },
}, },
{ {
id: "preact", id: "preact",
@ -128,8 +128,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "build" outputLocation: "build",
} },
}, },
{ {
id: "vue", id: "vue",
@ -140,7 +140,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run serve", devServerCommand: "npm run serve",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "dist", outputLocation: "dist",
} },
}, },
{ {
id: "vite", id: "vite",
@ -150,7 +150,7 @@ export const appFrameworks: FrameworkDefinition[] = [
config: { config: {
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
} },
}, },
{ {
id: "nuxtjs", id: "nuxtjs",
@ -163,7 +163,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "dist", outputLocation: "dist",
} },
}, },
{ {
id: "vuepress", id: "vuepress",
@ -174,8 +174,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "src/.vuepress/dist" outputLocation: "src/.vuepress/dist",
} },
}, },
{ {
id: "vitepress", id: "vitepress",
@ -187,7 +187,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run docs:dev", devServerCommand: "npm run docs:dev",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "docs/.vitepress/dist", outputLocation: "docs/.vitepress/dist",
} },
}, },
{ {
id: "svelte", id: "svelte",
@ -197,8 +197,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "public" outputLocation: "public",
} },
}, },
{ {
id: "svelte-kit", id: "svelte-kit",
@ -209,8 +209,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "build" outputLocation: "build",
} },
}, },
{ {
id: "sapper", id: "sapper",
@ -221,8 +221,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run export", appBuildCommand: "npm run export",
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "__sapper__/export" outputLocation: "__sapper__/export",
} },
}, },
{ {
id: "riot", id: "riot",
@ -232,8 +232,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm start", devServerCommand: "npm start",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "dist" outputLocation: "dist",
} },
}, },
{ {
id: "stencil", id: "stencil",
@ -244,8 +244,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm start", devServerCommand: "npm start",
devServerUrl: "http://localhost:3333", devServerUrl: "http://localhost:3333",
outputLocation: "www" outputLocation: "www",
} },
}, },
{ {
id: "aurelia", id: "aurelia",
@ -255,8 +255,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm start", devServerCommand: "npm start",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "dist" outputLocation: "dist",
} },
}, },
{ {
id: "ember", id: "ember",
@ -266,8 +266,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build", appBuildCommand: "npm run build",
devServerCommand: "npm start", devServerCommand: "npm start",
devServerUrl: "http://localhost:4200", devServerUrl: "http://localhost:4200",
outputLocation: "dist" outputLocation: "dist",
} },
}, },
{ {
id: "elm", id: "elm",
@ -277,8 +277,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "elm make src/Main.elm --optimize", appBuildCommand: "elm make src/Main.elm --optimize",
devServerCommand: "elm reactor", devServerCommand: "elm reactor",
devServerUrl: "http://localhost:8000", devServerUrl: "http://localhost:8000",
outputLocation: "./" outputLocation: "./",
} },
}, },
{ {
id: "polymer", id: "polymer",
@ -289,9 +289,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "polymer build --preset es6-bundled", appBuildCommand: "polymer build --preset es6-bundled",
devServerCommand: "polymer serve --open", devServerCommand: "polymer serve --open",
devServerUrl: "http://localhost:8081", devServerUrl: "http://localhost:8081",
outputLocation: "build/es6-bundled" outputLocation: "build/es6-bundled",
},
}
}, },
{ {
id: "lit", id: "lit",
@ -301,8 +300,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build --if-present", appBuildCommand: "npm run build --if-present",
devServerCommand: "npm start", devServerCommand: "npm start",
devServerUrl: "http://localhost:8081", devServerUrl: "http://localhost:8081",
outputLocation: "./" outputLocation: "./",
} },
}, },
{ {
id: "hugo", id: "hugo",
@ -315,8 +314,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "hugo -D", appBuildCommand: "hugo -D",
devServerCommand: "hugo server -D", devServerCommand: "hugo server -D",
devServerUrl: "http://localhost:1313", devServerUrl: "http://localhost:1313",
outputLocation: "public" outputLocation: "public",
} },
}, },
{ {
id: "flutter", id: "flutter",
@ -327,7 +326,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "flutter run --web-port 8080", devServerCommand: "flutter run --web-port 8080",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "build/web", outputLocation: "build/web",
} },
}, },
{ {
id: "jekyll", id: "jekyll",
@ -338,7 +337,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "bundle exec jekyll serve --livereload", devServerCommand: "bundle exec jekyll serve --livereload",
devServerUrl: "http://localhost:4000", devServerUrl: "http://localhost:4000",
outputLocation: "_site", outputLocation: "_site",
} },
}, },
{ {
id: "slate", id: "slate",
@ -350,7 +349,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "./slate.sh serve", devServerCommand: "./slate.sh serve",
devServerUrl: "http://localhost:4567", devServerUrl: "http://localhost:4567",
outputLocation: "build", outputLocation: "build",
} },
}, },
{ {
id: "mkdocs", id: "mkdocs",
@ -361,7 +360,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "mkdocs serve", devServerCommand: "mkdocs serve",
devServerUrl: "http://localhost:8000", devServerUrl: "http://localhost:8000",
outputLocation: "site", outputLocation: "site",
} },
}, },
{ {
id: "eleventy", id: "eleventy",
@ -372,7 +371,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "eleventy --serve", devServerCommand: "eleventy --serve",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "_site", outputLocation: "_site",
} },
}, },
{ {
id: "astro", id: "astro",
@ -385,7 +384,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "_site", outputLocation: "_site",
} },
}, },
{ {
id: "pelican", id: "pelican",
@ -396,7 +395,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "make devserver", devServerCommand: "make devserver",
devServerUrl: "http://localhost:8000", devServerUrl: "http://localhost:8000",
outputLocation: "output", outputLocation: "output",
} },
}, },
{ {
id: "hexo", id: "hexo",
@ -407,21 +406,21 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run server", devServerCommand: "npm run server",
devServerUrl: "http://localhost:4000", devServerUrl: "http://localhost:4000",
outputLocation: "public", outputLocation: "public",
} },
}, },
{ {
id: "blazor-wasm", id: "blazor-wasm",
name: "Blazor WASM", name: "Blazor WASM",
files: ["*.csproj", "App.razor", "wwwroot", "Program.cs"], files: ["*.csproj", "App.razor", "wwwroot", "Program.cs"],
contains: { contains: {
"Program.cs": "WebAssemblyHostBuilder.CreateDefault" "Program.cs": "WebAssemblyHostBuilder.CreateDefault",
}, },
config: { config: {
appBuildCommand: "dotnet build", appBuildCommand: "dotnet build",
devServerCommand: "dotnet watch run", devServerCommand: "dotnet watch run",
devServerUrl: "http://localhost:8000", devServerUrl: "http://localhost:8000",
outputLocation: "output", outputLocation: "output",
} },
}, },
{ {
id: "gridsome", id: "gridsome",
@ -432,7 +431,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run develop", devServerCommand: "npm run develop",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "dist", outputLocation: "dist",
} },
}, },
{ {
id: "solid", id: "solid",
@ -443,7 +442,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "dist", outputLocation: "dist",
} },
}, },
{ {
id: "remix", id: "remix",
@ -455,7 +454,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run dev", devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "public/build", outputLocation: "public/build",
} },
}, },
{ {
id: "metalsmith", id: "metalsmith",
@ -466,7 +465,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run serve", devServerCommand: "npm run serve",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "build", outputLocation: "build",
} },
}, },
{ {
id: "brunch", id: "brunch",
@ -477,7 +476,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm start", devServerCommand: "npm start",
devServerUrl: "http://localhost:3333", devServerUrl: "http://localhost:3333",
outputLocation: "public", outputLocation: "public",
} },
}, },
{ {
id: "wintersmith", id: "wintersmith",
@ -488,7 +487,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "wintersmith preview", devServerCommand: "wintersmith preview",
devServerUrl: "http://localhost:8080", devServerUrl: "http://localhost:8080",
outputLocation: "build", outputLocation: "build",
} },
}, },
{ {
id: "middleman", id: "middleman",
@ -499,7 +498,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "bundle exec middleman server", devServerCommand: "bundle exec middleman server",
devServerUrl: "http://localhost:4567", devServerUrl: "http://localhost:4567",
outputLocation: "build", outputLocation: "build",
} },
}, },
{ {
id: "mdbook", id: "mdbook",
@ -510,7 +509,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "mdbook serve", devServerCommand: "mdbook serve",
devServerUrl: "http://localhost:3000", devServerUrl: "http://localhost:3000",
outputLocation: "book", outputLocation: "book",
} },
}, },
{ {
id: "zola", id: "zola",
@ -524,7 +523,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "zola serve", devServerCommand: "zola serve",
devServerUrl: "http://localhost:1111", devServerUrl: "http://localhost:1111",
outputLocation: "public", outputLocation: "public",
} },
}, },
{ {
id: "lektor", id: "lektor",
@ -535,6 +534,6 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "lektor server", devServerCommand: "lektor server",
devServerUrl: "http://localhost:5000", devServerUrl: "http://localhost:5000",
outputLocation: "dist", outputLocation: "dist",
} },
}, },
]; ];

Просмотреть файл

@ -1,2 +1,2 @@
export * from './frameworks'; export * from "./frameworks";
export * from './detect'; export * from "./detect";

Просмотреть файл

@ -46,10 +46,7 @@ export function matchLoadedConfigName(name: string) {
* @param configFilePath The path to the `swa-cli.config.json` file. * @param configFilePath The path to the `swa-cli.config.json` file.
* @returns An object with the `{@link SWACLIOptions}` config or an empty object if the config file, or the config entry were not found. * @returns An object with the `{@link SWACLIOptions}` config or an empty object if the config file, or the config entry were not found.
*/ */
export async function getConfigFileOptions( export async function getConfigFileOptions(configName: string | undefined, configFilePath: string): Promise<SWACLIConfig> {
configName: string | undefined,
configFilePath: string
): Promise<SWACLIConfig> {
logger.silly(`Getting config file options from ${configFilePath}...`); logger.silly(`Getting config file options from ${configFilePath}...`);
configFilePath = path.resolve(configFilePath); configFilePath = path.resolve(configFilePath);

Просмотреть файл

@ -1,15 +1,15 @@
import process from 'process'; import process from "process";
import { execSync } from 'child_process'; import { execSync } from "child_process";
export function runCommand(command: string, cwd?: string) { export function runCommand(command: string, cwd?: string) {
execSync(command, { execSync(command, {
stdio: 'inherit', stdio: "inherit",
cwd: cwd ?? process.cwd(), cwd: cwd ?? process.cwd(),
// Set CI to avoid extra NPM logs and potentially unwanted interactive modes // Set CI to avoid extra NPM logs and potentially unwanted interactive modes
env: { env: {
...process.env, ...process.env,
// Internal flag to avoid duplicating user messages // Internal flag to avoid duplicating user messages
SWA_CLI_INTERNAL_COMMAND: "1", SWA_CLI_INTERNAL_COMMAND: "1",
} },
}); });
} }

Просмотреть файл

@ -12,12 +12,14 @@ describe("safeReadJson()", () => {
}); });
it("should ignore JSON comments and parse JSON anyway", async () => { it("should ignore JSON comments and parse JSON anyway", async () => {
mockFs({ "test.json": `{ mockFs({
"test.json": `{
// this is a /* tricky comment // this is a /* tricky comment
"hello": "world", "hello": "world",
/* and this should be removed too // */ /* and this should be removed too // */
"but": "not // this or /* this */ or /* this" "but": "not // this or /* this */ or /* this"
}`}); }`,
});
const json = await safeReadJson("test.json"); const json = await safeReadJson("test.json");
expect(json && json.hello).toBe("world"); expect(json && json.hello).toBe("world");
}); });
@ -62,27 +64,26 @@ describe("findUpPackageJsonDir()", () => {
it("should return undefined if package.json is not found", async () => { it("should return undefined if package.json is not found", async () => {
mockFs(); mockFs();
expect(await findUpPackageJsonDir('app', 'dist')).toBe(undefined); expect(await findUpPackageJsonDir("app", "dist")).toBe(undefined);
}); });
it("should return undefined if package.json is not found in path range", async () => { it("should return undefined if package.json is not found in path range", async () => {
mockFs({ 'package.json': '{}' }); mockFs({ "package.json": "{}" });
expect(await findUpPackageJsonDir('app', 'dist')).toBe(undefined); expect(await findUpPackageJsonDir("app", "dist")).toBe(undefined);
}); });
it("should return base path", async () => { it("should return base path", async () => {
mockFs({ 'app/package.json': '{}' }); mockFs({ "app/package.json": "{}" });
expect(await findUpPackageJsonDir('app/', 'dist')).toBe('app'); expect(await findUpPackageJsonDir("app/", "dist")).toBe("app");
}); });
it("should return start path", async () => { it("should return start path", async () => {
mockFs({ 'app/dist/package.json': '{}' }); mockFs({ "app/dist/package.json": "{}" });
expect(await findUpPackageJsonDir('app', 'dist/')).toBe('app/dist'); expect(await findUpPackageJsonDir("app", "dist/")).toBe("app/dist");
}); });
it("should return the correct path", async () => { it("should return the correct path", async () => {
mockFs({ 'app/toto/package.json': '{}' }); mockFs({ "app/toto/package.json": "{}" });
expect(await findUpPackageJsonDir('app', 'toto/dist')).toBe('app/toto'); expect(await findUpPackageJsonDir("app", "toto/dist")).toBe("app/toto");
}); });
}); });

Просмотреть файл

@ -5,7 +5,7 @@ import { stripJsonComments } from "./strings";
export async function safeReadJson(path: string): Promise<JsonData | undefined> { export async function safeReadJson(path: string): Promise<JsonData | undefined> {
try { try {
let contents = await fs.readFile(path, 'utf8'); let contents = await fs.readFile(path, "utf8");
contents = stripJsonComments(contents); contents = stripJsonComments(contents);
return JSON.parse(contents) as JsonData; return JSON.parse(contents) as JsonData;
} catch (error) { } catch (error) {
@ -20,7 +20,7 @@ export async function safeReadFile(path?: string): Promise<string | undefined> {
} }
try { try {
return await fs.readFile(path, 'utf8'); return await fs.readFile(path, "utf8");
} catch (error) { } catch (error) {
logger.warn(`Failed to read file at: ${path}`); logger.warn(`Failed to read file at: ${path}`);
return undefined; return undefined;
@ -44,19 +44,19 @@ export async function findUpPackageJsonDir(rootPath: string, startPath: string):
return undefined; return undefined;
} }
rootPath = (rootPath === '.' || rootPath === `.${path.sep}`) ? '' : rootPath; rootPath = rootPath === "." || rootPath === `.${path.sep}` ? "" : rootPath;
startPath = path.join(rootPath, startPath); startPath = path.join(rootPath, startPath);
const rootPathLength = rootPath.split(/[/\\]/).filter(c => c).length; const rootPathLength = rootPath.split(/[/\\]/).filter((c) => c).length;
const find = async (components: string[]): Promise<string | undefined> => { const find = async (components: string[]): Promise<string | undefined> => {
if (components.length === 0 || components.length < rootPathLength) { if (components.length === 0 || components.length < rootPathLength) {
return undefined; return undefined;
} }
const dir = path.join(...components); const dir = path.join(...components);
const packageFile = path.join(dir, 'package.json'); const packageFile = path.join(dir, "package.json");
return (await pathExists(packageFile)) ? dir : find(components.slice(0, -1)); return (await pathExists(packageFile)) ? dir : find(components.slice(0, -1));
}; };
const components = startPath.split(/[/\\]/).filter(c => c); const components = startPath.split(/[/\\]/).filter((c) => c);
return find(components); return find(components);
} }

Просмотреть файл

@ -24,7 +24,9 @@ export async function configureOptions(
// Clean up subcommands overrides before merging // Clean up subcommands overrides before merging
// to avoid confusing the user when printing options // to avoid confusing the user when printing options
SWA_COMMANDS.forEach((command) => { delete configFileOptions[command]; }); SWA_COMMANDS.forEach((command) => {
delete configFileOptions[command];
});
configFileDefinedOptions = { ...configFileOptions, ...configFileCommandSpecificOptions }; configFileDefinedOptions = { ...configFileOptions, ...configFileCommandSpecificOptions };
options = { options = {

Просмотреть файл

@ -14,20 +14,24 @@ describe("dasherize()", () => {
describe("stripJsonComments()", () => { describe("stripJsonComments()", () => {
it("should leave valid JSON untouched", () => { it("should leave valid JSON untouched", () => {
expect(stripJsonComments(`{ expect(
stripJsonComments(`{
"hello": "world" "hello": "world"
}`)).toBe(`{ }`)
).toBe(`{
"hello": "world" "hello": "world"
}`); }`);
}); });
it("should strip JSON comments", () => { it("should strip JSON comments", () => {
expect(stripJsonComments(`{ expect(
stripJsonComments(`{
// this is a /* tricky comment // this is a /* tricky comment
"hello": "world", "hello": "world",
/* and this should be removed too // */ /* and this should be removed too // */
"but": "not // this or /* this */ or /* this" "but": "not // this or /* this */ or /* this"
}`)).toBe(`{ }`)
).toBe(`{
"hello": "world", "hello": "world",
@ -38,24 +42,24 @@ describe("stripJsonComments()", () => {
describe("removeTrailingPathSep()", () => { describe("removeTrailingPathSep()", () => {
it("should leave path untouched", () => { it("should leave path untouched", () => {
expect(removeTrailingPathSep('./dir')).toBe('./dir'); expect(removeTrailingPathSep("./dir")).toBe("./dir");
}); });
it("should remove trailing slash", () => { it("should remove trailing slash", () => {
expect(removeTrailingPathSep('./')).toBe('.'); expect(removeTrailingPathSep("./")).toBe(".");
}); });
it("should remove trailing anti-slash", () => { it("should remove trailing anti-slash", () => {
expect(removeTrailingPathSep('dir\\')).toBe('dir'); expect(removeTrailingPathSep("dir\\")).toBe("dir");
}); });
}); });
describe("hasSpaces()", () => { describe("hasSpaces()", () => {
it("should return false", () => { it("should return false", () => {
expect(hasSpaces('./dir')).toBe(false); expect(hasSpaces("./dir")).toBe(false);
}); });
it("should return true", () => { it("should return true", () => {
expect(hasSpaces('c:\\my documents\\')).toBe(true); expect(hasSpaces("c:\\my documents\\")).toBe(true);
}); });
}); });

Просмотреть файл

@ -14,12 +14,12 @@ export function stripJsonComments(json: string) {
// in-between slash-star comments including newlines in a non-greedy way // in-between slash-star comments including newlines in a non-greedy way
// Only the captured group is removed, ignoring comments inside strings as a result // Only the captured group is removed, ignoring comments inside strings as a result
/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/gm, /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/gm,
(match, group) => group ? "" : match (match, group) => (group ? "" : match)
); );
} }
export function removeTrailingPathSep(filePath: string): string { export function removeTrailingPathSep(filePath: string): string {
return filePath.replace(/[\\/]+$/, ''); return filePath.replace(/[\\/]+$/, "");
} }
export const hasSpaces = (str: string): boolean => str.indexOf(' ') !== -1; export const hasSpaces = (str: string): boolean => str.indexOf(" ") !== -1;

Просмотреть файл

@ -64,9 +64,7 @@ export async function findSWAConfigFile(folder: string): Promise<{ filepath: str
const fileSize = (await stat(file.filepath)).size; const fileSize = (await stat(file.filepath)).size;
const fileSizeInKb = Math.round(fileSize / 1024); const fileSizeInKb = Math.round(fileSize / 1024);
if (fileSizeInKb > SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB) { if (fileSizeInKb > SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB) {
logger.warn( logger.warn(`WARNING: ${SWA_CONFIG_FILENAME} is ${fileSizeInKb} bytes. The maximum size is ${SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB} bytes.`);
`WARNING: ${SWA_CONFIG_FILENAME} is ${fileSizeInKb} bytes. The maximum size is ${SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB} bytes.`
);
} }
logger.silly(`Content parsed successfully`); logger.silly(`Content parsed successfully`);

Просмотреть файл

@ -95,7 +95,7 @@ export function isFunctionRequest(req: http.IncomingMessage, rewritePath?: strin
export async function validateFunctionTriggers(url: string) { export async function validateFunctionTriggers(url: string) {
try { try {
const functionsResponse = await fetch(`${url}/admin/functions`); const functionsResponse = await fetch(`${url}/admin/functions`);
const functions = await functionsResponse.json() as Array<{config: {bindings: string[]}}>; const functions = (await functionsResponse.json()) as Array<{ config: { bindings: string[] } }>;
const triggers = functions.map((f: any) => f.config.bindings.find((b: any) => /trigger$/i.test(b.type))).map((b: any) => b.type); const triggers = functions.map((f: any) => f.config.bindings.find((b: any) => /trigger$/i.test(b.type))).map((b: any) => b.type);
if (triggers.some((t: string) => !/^httptrigger$/i.test(t))) { if (triggers.some((t: string) => !/^httptrigger$/i.test(t))) {

2
src/swa.d.ts поставляемый
Просмотреть файл

@ -290,4 +290,4 @@ declare interface CoreToolsZipInfo {
sha2: string; sha2: string;
} }
declare type NpmPackageManager = 'npm' | 'yarn' | 'pnpm'; declare type NpmPackageManager = "npm" | "yarn" | "pnpm";