зеркало из
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,37 +1,37 @@
| Environment Variable | Purpose | Read Only? | Default Value |
| ----------------------------- | ---------------------------------------------------------------------------------- | ---------- | ------------- |
| **General Settings** | | | |
| SWA_CLI_VERSION | CLI version | Yes | |
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |
| DEBUG | General purpose environment variable used to enable verbose logs for commong tools | | |
| **Emulator Settings** | | | |
| SWA_CLI_API_LOCATION | Folder containing the source code of the API application | | `./api` |
| SWA_CLI_APP_LOCATION | Folder containing the source code of the front-end application | | `./` |
| SWA_CLI_OUTPUT_LOCATION | Folder containing the front-end public files | | `./` |
| SWA_CLI_HOST | Host address to use for the CLI dev server | | `localhost` |
| SWA_CLI_PORT | Host port to use for the CLI dev server | | `4280` |
| SWA_CLI_API_PORT | API server port to use | | `7071` |
| SWA_CLI_APP_SSL | Use HTTPS to serve the front-end application and API (`true` or `false`) | | `false` |
| SWA_CLI_APP_SSL_KEY | SSL key (.key) to use when enabling HTTPS | | |
| SWA_CLI_APP_SSL_CERT | SSL certificate (.crt) to use when enabling HTTPS | | |
| SWA_CLI_STARTUP_COMMAND | Run a custom shell command or script file at startup | | |
| SWA_CLI_OPEN_BROWSER | Automatically open the CLI dev server in the default browser (`true` or `false`) | | `false` |
| SWA_CLI_SERVER_TIMEOUT | The time to wait (in seconds) when connecting to a front-end application's dev server or api server | | `60` |
| **Deploy settings** | | | |
| SWA_CLI_APP_NAME | Project name | | |
| SWA_CLI_DEPLOYMENT_TOKEN | Secret token used to authenticate with the Static Web Apps | | |
| SWA_CLI_DEPLOY_DRY_RUN | Simulate a deploy process without actually running it (`true` or `false`) | | `false` |
| SWA_CLI_DEPLOY_BINARY_VERSION | Deployment binary version to use | | `stable` |
| SWA_CLI_DEPLOY_BINARY | Absoluate path to the deploy binary | Yes | |
| SWA_CLI_DEPLOY_ENV | the type of deployment environment where to deploy the project | | `preview` |
| AZURE_REGION_LOCATION | Azure region where to deploy the project | | `West US 2` |
| AZURE_RESOURCE_GROUP | Azure resource group | | |
| AZURE_SUBSCRIPTION_ID | Azure subscription ID | | |
| **Runtime settings** | | | |
| SWA_RUNTIME_CONFIG | Absolute path to `staticwebapp.config.json` | | |
| SWA_RUNTIME_CONFIG_LOCATION | Folder containing the file `staticwebapp.config.json` | | |
| SWA_RUNTIME_WORKFLOW_LOCATION | Absolute path to `.github/workflows/azure-static-web-apps-xyz.yml` | Yes | |
| **Azure Identity** | | | |
| AZURE_CLIENT_ID | Azure Active Directory client ID | | |
| AZURE_CLIENT_SECRET | Azure Active Directory secret | | |
| AZURE_TENANT_ID | Azure Active Directory tenant ID | | |
| Environment Variable | Purpose | Read Only? | Default Value |
| ----------------------------- | --------------------------------------------------------------------------------------------------- | ---------- | ------------- |
| **General Settings** | | | |
| SWA_CLI_VERSION | CLI version | Yes | |
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |
| DEBUG | General purpose environment variable used to enable verbose logs for commong tools | | |
| **Emulator Settings** | | | |
| SWA_CLI_API_LOCATION | Folder containing the source code of the API application | | `./api` |
| SWA_CLI_APP_LOCATION | Folder containing the source code of the front-end application | | `./` |
| SWA_CLI_OUTPUT_LOCATION | Folder containing the front-end public files | | `./` |
| SWA_CLI_HOST | Host address to use for the CLI dev server | | `localhost` |
| SWA_CLI_PORT | Host port to use for the CLI dev server | | `4280` |
| SWA_CLI_API_PORT | API server port to use | | `7071` |
| SWA_CLI_APP_SSL | Use HTTPS to serve the front-end application and API (`true` or `false`) | | `false` |
| SWA_CLI_APP_SSL_KEY | SSL key (.key) to use when enabling HTTPS | | |
| SWA_CLI_APP_SSL_CERT | SSL certificate (.crt) to use when enabling HTTPS | | |
| SWA_CLI_STARTUP_COMMAND | Run a custom shell command or script file at startup | | |
| SWA_CLI_OPEN_BROWSER | Automatically open the CLI dev server in the default browser (`true` or `false`) | | `false` |
| SWA_CLI_SERVER_TIMEOUT | The time to wait (in seconds) when connecting to a front-end application's dev server or api server | | `60` |
| **Deploy settings** | | | |
| SWA_CLI_APP_NAME | Project name | | |
| SWA_CLI_DEPLOYMENT_TOKEN | Secret token used to authenticate with the Static Web Apps | | |
| SWA_CLI_DEPLOY_DRY_RUN | Simulate a deploy process without actually running it (`true` or `false`) | | `false` |
| SWA_CLI_DEPLOY_BINARY_VERSION | Deployment binary version to use | | `stable` |
| SWA_CLI_DEPLOY_BINARY | Absoluate path to the deploy binary | Yes | |
| SWA_CLI_DEPLOY_ENV | the type of deployment environment where to deploy the project | | `preview` |
| AZURE_REGION_LOCATION | Azure region where to deploy the project | | `West US 2` |
| AZURE_RESOURCE_GROUP | Azure resource group | | |
| AZURE_SUBSCRIPTION_ID | Azure subscription ID | | |
| **Runtime settings** | | | |
| SWA_RUNTIME_CONFIG | Absolute path to `staticwebapp.config.json` | | |
| SWA_RUNTIME_CONFIG_LOCATION | Folder containing the file `staticwebapp.config.json` | | |
| SWA_RUNTIME_WORKFLOW_LOCATION | Absolute path to `.github/workflows/azure-static-web-apps-xyz.yml` | Yes | |
| **Azure Identity** | | | |
| AZURE_CLIENT_ID | Azure Active Directory client ID | | |
| AZURE_CLIENT_SECRET | Azure Active Directory secret | | |
| AZURE_TENANT_ID | Azure Active Directory tenant ID | | |

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

@ -1,13 +1,13 @@
# Website Development
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. This README documents the process for creating and updating the site for **this** project.
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. This README documents the process for creating and updating the site for **this** project.
---
## 1. Site Creation
The documentation source was scaffolded in the `docs/www` directory using the default _classic_ theme.
The documentation source was scaffolded in the `docs/www` directory using the default _classic_ theme.
```bash
$ npx create-docusaurus@latest docs/www classic
```
@ -29,7 +29,8 @@ Build the website for production - by default this creates the static content in
$ cd docs/www
$ npm run build
```
You can preview the local build version using this command:
You can preview the local build version using this command:
```bash
$ npm run serve
@ -42,11 +43,11 @@ Docusaurus provides [this guidance](https://docusaurus.io/docs/deployment#github
First, update the `docusaurus.config.js` file with the settings for the project repository name, owner and branch. Here is the default configuration used for this project:
```javascript
url: 'https://azure.github.io',
url: 'https://azure.github.io',
baseUrl: '/static-web-apps-cli/',
organizationName: 'azure',
projectName: 'static-web-apps-cli',
deploymentBranch: `gh-pages`,
organizationName: 'azure',
projectName: 'static-web-apps-cli',
deploymentBranch: `gh-pages`,
```
Next use `npm deploy` or `yarn deploy` to push the built site to the GitHub pages endpoint. This will setup the deployment branch (`gh-pages`) if it was not previously created.
@ -55,61 +56,65 @@ Next use `npm deploy` or `yarn deploy` to push the built site to the GitHub page
$ cd docs/www
$ 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.
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
## 5. Deploy: Azure Static Web Apps
// TODO: Fill in details for deploying same source to SWA
// Site: https://azurestaticwebapps.dev/
## 6. Content Structure
## 6. Content Structure
> Docusaurus has this default structure for content:
* `blog/` - posts with index page, tags, RSS feed.
* `docs/` - tutorials with sidebar, prev/next navigation
* `src/pages` - standalone pages (map directly to routes)
* `static/` - static assets (served as is)
- `blog/` - posts with index page, tags, RSS feed.
- `docs/` - tutorials with sidebar, prev/next navigation
- `src/pages` - standalone pages (map directly to routes)
- `static/` - static assets (served as is)
> These are the main configuration files:
* `docusaurus.config.js` - all site configuration
* `package.json` - NPM dependencies for build
* `sidebars.js` - explicitly specify sidebar contents
- `docusaurus.config.js` - all site configuration
- `package.json` - NPM dependencies for build
- `sidebars.js` - explicitly specify sidebar contents
> These are the key files to customize this site:
1. `docusaurus.config.js`
1. `docusaurus.config.js`
- update contents of navbar
- update contents of footer
- identify and customize plugins
- activate or deactivate default features (e.g., blog)
- 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)
2. `src/components/index.js`
3. `src/components/index.js`
- customize landing page structure (layout)
4. `src/css/custom.css`
4. `src/css/custom.css`
- customize theme palette (dark, light)
- implement sitewide css changes
> Guidelines for adding new content
- 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
- Create `docs/`X/_category_.json to define placement, metadata
- Have **single-page** documents that standalone?
- Add file as `src/pages/`X.md
- Choose X to reflect the desired _route_ name for this page
- 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
- Create `docs/`X/_category_.json to define placement, metadata
- Have **single-page** documents that standalone?
- Add file as `src/pages/`X.md
- Choose X to reflect the desired _route_ name for this page
## 6. Color Palette
> Old Color Palette (v0.8.3)
* #ff9cfd
* #F131F8
* #b319a6
> New Color Palette (GA)
* A100 (light) = #FF80AB
* A200 (medium) = #FF4081
* A400 (dark) = #F50057
- #ff9cfd
- #F131F8
- #b319a6
> New Color Palette (GA)
- A100 (light) = #FF80AB
- 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` |
| `--swa-config-location` | The directory where the `staticwebapp.config.json` file is located | `./` | `--swa-config-location=./app` |
|
|

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

@ -2,4 +2,4 @@
sidebar_position: 2
---
# `swa login`
# `swa login`

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

@ -4,7 +4,6 @@ sidebar_position: 3
# `swa start`
If you need to override the default values for the `swa start` subcommand, you can provide the following options:
| Option | Description | Default | Example |
@ -20,4 +19,4 @@ If you need to override the default values for the `swa start` subcommand, you c
| `--run` | Run a custon shell command or file at startup | | `--run="cd app & npm start"` |
| `--devserver-timeout` | The time (in milliseconds) to wait when connecting to a front-end application's dev server | `30000` | `--devserver-timeout=60000` |
| `--func-args` | Pass additional arguments to the `func start` command | | `--func-args="--javascript"` |
| `--open` | Automatically open the CLI dev server in the default browser. | `false` | `--open` or `--open=true` |
| `--open` | Automatically open the CLI dev server in the default browser. | `false` | `--open` or `--open=true` |

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

@ -4,18 +4,15 @@ sidebar_position: 4
# `swa deploy`
If you need to override the default values for the `swa deploy` subcommand, you can provide the following options:
| Option | Description | Default | Example |
|:--|:--|:--|:--|
| `--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"` |
| `--dry-run` |Simulate a deploy process without actually running it | `false`| `--dry-run`|
| `--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"`|
|`--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`|
| Option | Description | Default | Example |
| :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | :-------- | :---------------------------------------- |
| `--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"` |
| `--dry-run` | Simulate a deploy process without actually running it | `false` | `--dry-run` |
| `--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"` |
| `--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.

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

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

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

@ -3,44 +3,42 @@ sidebar_position: 4
title: env vars
---
# Environment Variables
| Environment Variable | Purpose | Read Only? | Default Value |
| ----------------------------- | ---------------------------------------------------------------------------------- | ---------- | ------------- |
| **General Settings** | | | |
| SWA_CLI_VERSION | CLI version | Yes | |
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |
| DEBUG | General purpose environment variable used to enable verbose logs for commong tools | | |
| **Emulator Settings** | | | |
| SWA_CLI_API_LOCATION | Folder containing the source code of the API application | | `./api` |
| SWA_CLI_APP_LOCATION | Folder containing the source code of the front-end application | | `./` |
| SWA_CLI_OUTPUT_LOCATION | Folder containing the front-end public files | | `./` |
| SWA_CLI_HOST | Host address to use for the CLI dev server | | `localhost` |
| SWA_CLI_PORT | Host port to use for the CLI dev server | | `4280` |
| SWA_CLI_API_PORT | API server port to use | | `7071` |
| SWA_CLI_APP_SSL | Use HTTPS to serve the front-end application and API (`true` or `false`) | | `false` |
| SWA_CLI_APP_SSL_KEY | SSL key (.key) to use when enabling HTTPS | | |
| SWA_CLI_APP_SSL_CERT | SSL certificate (.crt) to use when enabling HTTPS | | |
| SWA_CLI_STARTUP_COMMAND | Run a custom shell command or script file at startup | | |
| SWA_CLI_OPEN_BROWSER | Automatically open the CLI dev server in the default browser (`true` or `false`) | | `false` |
| SWA_CLI_SERVER_TIMEOUT | The time to wait (in seconds) when connecting to a front-end application's dev server or api server | | `60` |
| **Deploy settings** | | | |
| SWA_CLI_APP_NAME | Project name | | |
| SWA_CLI_DEPLOYMENT_TOKEN | Secret token used to authenticate with the Static Web Apps | | |
| SWA_CLI_DEPLOY_DRY_RUN | Simulate a deploy process without actually running it (`true` or `false`) | | `false` |
| SWA_CLI_DEPLOY_BINARY_VERSION | Deployment binary version to use | | `stable` |
| SWA_CLI_DEPLOY_BINARY | Absoluate path to the deploy binary | Yes | |
| SWA_CLI_DEPLOY_ENV | the type of deployment environment where to deploy the project | | `preview` |
| AZURE_REGION_LOCATION | Azure region where to deploy the project | | `West US 2` |
| AZURE_RESOURCE_GROUP | Azure resource group | | |
| AZURE_SUBSCRIPTION_ID | Azure subscription ID | | |
| **Runtime settings** | | | |
| SWA_RUNTIME_CONFIG | Absolute path to `staticwebapp.config.json` | | |
| SWA_RUNTIME_CONFIG_LOCATION | Folder containing the file `staticwebapp.config.json` | | |
| SWA_RUNTIME_WORKFLOW_LOCATION | Absolute path to `.github/workflows/azure-static-web-apps-xyz.yml` | Yes | |
| **Azure Identity** | | | |
| AZURE_CLIENT_ID | Azure Active Directory client ID | | |
| AZURE_CLIENT_SECRET | Azure Active Directory secret | | |
| AZURE_TENANT_ID | Azure Active Directory tenant ID | | |
| Environment Variable | Purpose | Read Only? | Default Value |
| ----------------------------- | --------------------------------------------------------------------------------------------------- | ---------- | ------------- |
| **General Settings** | | | |
| SWA_CLI_VERSION | CLI version | Yes | |
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |
| DEBUG | General purpose environment variable used to enable verbose logs for commong tools | | |
| **Emulator Settings** | | | |
| SWA_CLI_API_LOCATION | Folder containing the source code of the API application | | `./api` |
| SWA_CLI_APP_LOCATION | Folder containing the source code of the front-end application | | `./` |
| SWA_CLI_OUTPUT_LOCATION | Folder containing the front-end public files | | `./` |
| SWA_CLI_HOST | Host address to use for the CLI dev server | | `localhost` |
| SWA_CLI_PORT | Host port to use for the CLI dev server | | `4280` |
| SWA_CLI_API_PORT | API server port to use | | `7071` |
| SWA_CLI_APP_SSL | Use HTTPS to serve the front-end application and API (`true` or `false`) | | `false` |
| SWA_CLI_APP_SSL_KEY | SSL key (.key) to use when enabling HTTPS | | |
| SWA_CLI_APP_SSL_CERT | SSL certificate (.crt) to use when enabling HTTPS | | |
| SWA_CLI_STARTUP_COMMAND | Run a custom shell command or script file at startup | | |
| SWA_CLI_OPEN_BROWSER | Automatically open the CLI dev server in the default browser (`true` or `false`) | | `false` |
| SWA_CLI_SERVER_TIMEOUT | The time to wait (in seconds) when connecting to a front-end application's dev server or api server | | `60` |
| **Deploy settings** | | | |
| SWA_CLI_APP_NAME | Project name | | |
| SWA_CLI_DEPLOYMENT_TOKEN | Secret token used to authenticate with the Static Web Apps | | |
| SWA_CLI_DEPLOY_DRY_RUN | Simulate a deploy process without actually running it (`true` or `false`) | | `false` |
| SWA_CLI_DEPLOY_BINARY_VERSION | Deployment binary version to use | | `stable` |
| SWA_CLI_DEPLOY_BINARY | Absoluate path to the deploy binary | Yes | |
| SWA_CLI_DEPLOY_ENV | the type of deployment environment where to deploy the project | | `preview` |
| AZURE_REGION_LOCATION | Azure region where to deploy the project | | `West US 2` |
| AZURE_RESOURCE_GROUP | Azure resource group | | |
| AZURE_SUBSCRIPTION_ID | Azure subscription ID | | |
| **Runtime settings** | | | |
| SWA_RUNTIME_CONFIG | Absolute path to `staticwebapp.config.json` | | |
| SWA_RUNTIME_CONFIG_LOCATION | Folder containing the file `staticwebapp.config.json` | | |
| SWA_RUNTIME_WORKFLOW_LOCATION | Absolute path to `.github/workflows/azure-static-web-apps-xyz.yml` | Yes | |
| **Azure Identity** | | | |
| AZURE_CLIENT_ID | Azure Active Directory client ID | | |
| AZURE_CLIENT_SECRET | Azure Active Directory secret | | |
| AZURE_TENANT_ID | Azure Active Directory tenant ID | | |

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

@ -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:
- [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)
- [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)
- [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)
- [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)
- [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)
- [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)
- [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)
- [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)
> 🚧 | `TODO:` Update ga links to main.

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

@ -1,6 +1,6 @@
---
id: contributors
title: Contributors 🌟
title: Contributors 🌟
sidebar_position: 2
---
@ -14,13 +14,13 @@ Thanks go to these wonderful people ([emoji key](https://allcontributors.org/doc
## About: All-Contributors
> 🚧 | TODO: Automate bot
> 🚧 | TODO: Automate bot
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
[ac]: https://github.com/all-contributors/all-contributors
[acbot]: https://allcontributors.org/docs/en/bot/overview
[acbot]: https://allcontributors.org/docs/en/bot/overview

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

@ -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)
## 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:
* 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).
* 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).
- 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).
- 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.
@ -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)
- Deploy your app to Azure Static Web Apps (for a unified develop-deploy workflow)
## SWA CLI Components
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 **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
@ -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:
* 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=)
* 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)
* Posting questions to Stack Overflow? [Post to the swa-cli tag](https://stackoverflow.com/questions/tagged/swa-cli)
- 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=)
- 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)
- 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:
* Fixing bugs identified in issues
* Writing or improving the documentation
* Extending or improving CLI capabilities
- Fixing bugs identified in issues
- Writing or improving the documentation
- Extending or improving CLI capabilities
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
```
## Install with yarn
**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
yarn add global @azure/static-web-apps-cli
```
2. The recommended approach is to install this locally, as a devDependency, using:
```bash
@ -40,7 +40,7 @@ yarn add -D @azure/static-web-apps-cli
## Validate the install
Installing the package should make the **swa** command available on your development machine. To validate, try using the command with an appropriate option.
Installing the package should make the **swa** command available on your development machine. To validate, try using the command with an appropriate option.
For instance, check the installed version.
@ -51,7 +51,7 @@ $ swa --version
## Run using npx
The [npx](https://docs.npmjs.com/cli/v7/commands/npx) command (aka "npm exec") lets you run an arbitrary command from a local or remote npm package. If the command was not installed globally on the device, this installs it for you in a central cache - making it a useful option if you want to use different versions of the same command on the local device.
The [npx](https://docs.npmjs.com/cli/v7/commands/npx) command (aka "npm exec") lets you run an arbitrary command from a local or remote npm package. If the command was not installed globally on the device, this installs it for you in a central cache - making it a useful option if you want to use different versions of the same command on the local device.
We can now run any Static Web Apps CLI commands directly using npx - for example: check the version as follows:
@ -64,4 +64,3 @@ Or use this command to start the emulator on your local device:
```bash
$ npx @azure/static-web-apps-cli start
```

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

@ -4,11 +4,10 @@ sidebar_position: 3
# 2. Start The Emulator
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.
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.
## 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:
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._
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._
```bash
swa start <dev-server-url>
@ -70,10 +69,9 @@ swa start http://localhost:4200 --run "./startup.sh"
Then access the application with the emulated services from `http://localhost:4280`
## 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.
| Tool | Port | Command |
| ---------------------------------------------------------------------------------- | ---- | --------------------------------- |

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

@ -4,16 +4,16 @@ sidebar_position: 4
# 4. Start the API Server
## 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.
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.
* give you flexibility to have the CLI manage the API server launch 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.
- 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.
@ -24,10 +24,12 @@ You might want to run the Azure Functions Core Tools (API server) separately, to
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_.
```bash
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
swa start ./my-dist --api-location http://localhost:7071
@ -43,9 +45,8 @@ This assumes you have previously created (and tested) an Azure Functions App pro
swa start ./my-dist --api-location ./api
```
2. Combine the launch with usage of a running dev server
2. Combine the launch with usage of a running dev server
```bash
swa start http://localhost:3000 --api-location ./api
```

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

@ -4,7 +4,6 @@ sidebar_position: 5
# 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:
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).
## 6.1 Default File Location
If you are serving static files from a folder, the CLI will search this folder for `staticwebapp.config.json`.
@ -23,8 +22,7 @@ If you are using a front-end dev server, the CLI will search the current directo
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`.
@ -34,4 +32,4 @@ swa start ./my-dist --swa-config-location ./my-app-source
# front-end dev server
swa start http://localhost:3000 --swa-config-location ./my-app-source
```
```

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

@ -19,7 +19,7 @@
"watch": "tsc --watch",
"copy-assets": "node ./scripts/copy-assets.js",
"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": {
"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`:
- Install the cli
```bash
npm install -g @azure/static-web-apps-cli
```
@ -49,11 +50,13 @@ See all available [options](#cli-options).
### Using `npx`:
- Open a SWA app folder at the root (outside any /api or /app folders):
```bash
cd my-awesome-swa-app
```
- Create a configuration for your project:
```bash
npx @azure/static-web-apps-cli init
```

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

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

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

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

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

@ -4,7 +4,16 @@ import { Command } from "commander";
import { DEFAULT_CONFIG } from "../../config";
import { detectProjectFolders, generateConfiguration } from "../../core/frameworks";
import {
configureOptions, findUpPackageJsonDir, isUserOption, isUserOrConfigOption, logger, matchLoadedConfigName, pathExists, readWorkflowFile, runCommand, swaCliConfigFilename,
configureOptions,
findUpPackageJsonDir,
isUserOption,
isUserOrConfigOption,
logger,
matchLoadedConfigName,
pathExists,
readWorkflowFile,
runCommand,
swaCliConfigFilename,
} from "../../core/utils";
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) => {
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "build");
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(`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,
appBuildCommand: options.appBuildCommand,
apiBuildCommand: options.apiBuildCommand,
}
},
});
let appLocation = options.appLocation ?? workflowConfig?.appLocation;
@ -61,7 +70,7 @@ export async function build(options: SWACLIConfig) {
}
if (options.auto) {
logger.log('Detecting build configuration...');
logger.log("Detecting build configuration...");
const detectedFolders = await detectProjectFolders(appLocation);
if (detectedFolders.app.length === 0 && detectedFolders.api.length === 0) {
@ -86,25 +95,25 @@ export async function build(options: SWACLIConfig) {
return;
}
}
if (!appBuildCommand && !apiBuildCommand) {
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(`or use option flags to set your build commands and paths.\n`);
}
logger.log('Nothing to build.');
logger.log("Nothing to build.");
return;
}
logger.log(`Build configuration:`);
logger.log(`- App location: ${chalk.green(appLocation || '')}`);
logger.log(`- API location: ${chalk.green(apiLocation || '')}`);
logger.log(`- Output location: ${chalk.green(outputLocation || '')}`);
logger.log(`- App build command: ${chalk.green(appBuildCommand || '')}`);
logger.log(`- API build command: ${chalk.green(apiBuildCommand || '')}`);
logger.log(`- App location: ${chalk.green(appLocation || "")}`);
logger.log(`- API location: ${chalk.green(apiLocation || "")}`);
logger.log(`- Output location: ${chalk.green(outputLocation || "")}`);
logger.log(`- App build command: ${chalk.green(appBuildCommand || "")}`);
logger.log(`- API build command: ${chalk.green(apiBuildCommand || "")}`);
if (appBuildCommand) {
const packageJsonPath = await findUpPackageJsonDir(appLocation!, outputLocation!);
if (packageJsonPath) {
@ -118,7 +127,7 @@ export async function build(options: SWACLIConfig) {
if (apiBuildCommand) {
// 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) {
logger.log(`Found package.json in ${packageJsonPath}`);
await installNpmDependencies(packageJsonPath);
@ -133,7 +142,7 @@ function hasBuildOptionsDefined(options: SWACLIConfig): boolean {
if (options.appBuildCommand || options.apiBuildCommand) {
return true;
}
return isUserOrConfigOption('appBuildCommand') || isUserOrConfigOption('apiBuildCommand');
return isUserOrConfigOption("appBuildCommand") || isUserOrConfigOption("apiBuildCommand");
}
function showAutoErrorMessageAndExit() {
@ -142,19 +151,19 @@ function showAutoErrorMessageAndExit() {
}
async function detectPackageManager(basePath: string): Promise<NpmPackageManager> {
const hasYarnLock = await pathExists(path.join(basePath, 'yarn.lock'));
const hasNpmLock = await pathExists(path.join(basePath, 'package-lock.json'));
const hasPnpmLock = await pathExists(path.join(basePath, 'pnpm-lock.yaml'));
const hasYarnLock = await pathExists(path.join(basePath, "yarn.lock"));
const hasNpmLock = await pathExists(path.join(basePath, "package-lock.json"));
const hasPnpmLock = await pathExists(path.join(basePath, "pnpm-lock.yaml"));
if (hasPnpmLock && !hasNpmLock && !hasYarnLock) {
return 'pnpm';
return "pnpm";
}
if (hasYarnLock && !hasNpmLock) {
return 'yarn';
return "yarn";
}
return 'npm';
return "npm";
}
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) => {
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "deploy");
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(`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);
if (apiFolder) {
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`
);
}
}
@ -355,6 +357,6 @@ export async function deploy(options: SWACLIConfig) {
}
async function findApiFolderInPath(appPath: string): Promise<string | undefined> {
const entries = await fs.promises.readdir(appPath, { withFileTypes: true });
return entries.find(entry => entry.name.toLowerCase() === 'api' && entry.isDirectory())?.name;
const entries = await fs.promises.readdir(appPath, { withFileTypes: true });
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) => {
const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init", false);
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(`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",
message: "Which app folder do you want to use?",
choices: detectedFolders.app.map((folder) => ({ title: folder.rootPath, value: folder })),
initial: 0
initial: 0,
});
// 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
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) {
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;
}
}
@ -96,11 +96,11 @@ export async function init(options: SWACLIConfig, showHints: boolean = true) {
name: "api",
message: "Which api folder do you want to use?",
choices: detectedFolders.api.map((folder) => ({ title: folder.rootPath, value: folder })),
initial: 0
initial: 0,
});
// 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 {
api = detectedFolders.api[0];
}
@ -117,10 +117,10 @@ export async function init(options: SWACLIConfig, showHints: boolean = true) {
printFrameworkConfig(projectConfig);
const { confirmSettings } = await promptOrUseDefault(disablePrompts, {
type: 'confirm',
name: 'confirmSettings',
message: 'Are these settings correct?',
initial: true
type: "confirm",
name: "confirmSettings",
message: "Are these settings correct?",
initial: true,
});
if (!confirmSettings) {
// Ask for each settings
@ -231,15 +231,15 @@ async function promptConfigSettings(disablePrompts: boolean, detectedConfig: Fra
}
function printFrameworkConfig(config: FrameworkConfig) {
logger.log(chalk.bold('\nDetected configuration for your app:'));
logger.log(`- Framework(s): ${chalk.green(config.name ?? 'none')}`);
logger.log(chalk.bold("\nDetected configuration for your app:"));
logger.log(`- Framework(s): ${chalk.green(config.name ?? "none")}`);
logger.log(`- App location: ${chalk.green(config.appLocation)}`);
logger.log(`- Output location: ${chalk.green(config.outputLocation)}`);
logger.log(`- API location: ${chalk.green(config.apiLocation ?? '')}`);
logger.log(`- App build command: ${chalk.green(config.appBuildCommand ?? '')}`);
logger.log(`- API build command: ${chalk.green(config.apiBuildCommand ?? '')}`);
logger.log(`- Dev command: ${chalk.green(config.devServerCommand ?? '')}`);
logger.log(`- Dev server URL: ${chalk.green(config.devServerUrl ?? '')}\n`);
logger.log(`- API location: ${chalk.green(config.apiLocation ?? "")}`);
logger.log(`- App build command: ${chalk.green(config.appBuildCommand ?? "")}`);
logger.log(`- API build command: ${chalk.green(config.apiBuildCommand ?? "")}`);
logger.log(`- Dev command: ${chalk.green(config.devServerCommand ?? "")}`);
logger.log(`- Dev server URL: ${chalk.green(config.devServerUrl ?? "")}\n`);
}
// function isEmptyFolder(path: string) {

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

@ -21,7 +21,7 @@ export function addSharedLoginOptionsToCommand(command: Command) {
.option("--client-secret <clientSecret>", "Azure client secret", DEFAULT_CONFIG.clientSecret)
.option("--app-name <appName>", "Azure Static Web App application name", DEFAULT_CONFIG.appName)
.option("--clear-credentials", "clear persisted credentials before login", DEFAULT_CONFIG.clearCredentials)
.option("--use-keychain", "enable using the operating system native keychain for persistent credentials", DEFAULT_CONFIG.useKeychain)
// Note: Commander does not automatically recognize the --no-* option, so we have to explicitly use --no-use-keychain- instead
.option("--no-use-keychain", "disable using the operating system native keychain", !DEFAULT_CONFIG.useKeychain);

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

@ -4,7 +4,7 @@ import concurrently, { CloseEvent } from "concurrently";
import { CommandInfo } from "concurrently/dist/src/command";
import fs from "fs";
import path from "path";
import { execSync } from 'child_process';
import { execSync } from "child_process";
import { DEFAULT_CONFIG } from "../../config";
import {
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("--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("--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-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
const isUrl = isHttpUrl(positionalArg);
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(`You either have to use the positional argument or option, not both at the same time.`, true);
}
options.devServerUrl = positionalArg;
} else {
if (isUserOption('outputLocation')) {
if (isUserOption("outputLocation")) {
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);
}
@ -147,7 +147,7 @@ export async function start(options: SWACLIConfig) {
let resolvedPortNumber = await isAcceptingTcpConnections({ host, port });
if (resolvedPortNumber === 0) {
logger.warn(`Port ${port} is already taken!`);
resolvedPortNumber = await askNewPort()
resolvedPortNumber = await askNewPort();
} else {
logger.silly(`Port ${port} is available. Use it.`);
}
@ -163,7 +163,7 @@ export async function start(options: SWACLIConfig) {
// resolve the absolute path to the appLocation
appLocation = path.resolve(appLocation as string);
if (devServerUrl) {
logger.silly(`devServerUrl provided, we will try connect to dev server at ${outputLocation}`);
// TODO: properly refactor this after GA to send devServerUrl to the server
@ -346,18 +346,16 @@ export async function start(options: SWACLIConfig) {
// run an external script, if it's available
if (startupCommand) {
let startupPath = userWorkflowConfig?.appLocation;
concurrentlyCommands.push(
{ command: `cd "${startupPath}" && ${startupCommand}`, name: "run", env, prefixColor: "gray.dim" }
);
concurrentlyCommands.push({ command: `cd "${startupPath}" && ${startupCommand}`, name: "run", env, prefixColor: "gray.dim" });
}
if (runBuild) {
// run swa build
execSync("swa build", {
stdio: 'inherit',
stdio: "inherit",
// 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");
swaMagic(options);
})
.addHelpText("after", `
.addHelpText(
"after",
`
Type "swa" to get started and deploy your project.
Documentation:
https://aka.ms/swa/cli-local-development
`);
`
);
// Register commands
registerLogin(program);
@ -74,21 +77,21 @@ export async function swaMagic(_options: SWACLIConfig) {
try {
const hasLoadedConfig = getCurrentSwaCliConfigFromFile();
if (!hasLoadedConfig) {
runCommand("swa init")
runCommand("swa init");
}
runCommand("swa build");
const response = await promptOrUseDefault(false, {
type: "confirm",
name: "deploy",
message: "Do you want to deploy your app now?",
initial: true
initial: true,
});
if (!response.deploy) {
logger.log(`\nWhen you'll be ready to deploy your app, just use ${chalk.cyan("swa")} again.`);
return;
}
runCommand("swa deploy");
} catch (_) {
// Pokemon, go catch'em all!

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

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

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

@ -3,9 +3,9 @@ import { DEFAULT_CONFIG } from "../config";
import { address, isHttpUrl } from "./utils/net";
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
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

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

@ -146,7 +146,9 @@ export function getPlatform(): "win-x64" | "osx-x64" | "linux-x64" | null {
export async function fetchClientVersionDefinition(releaseVersion: string): Promise<StaticSiteClientReleaseMetadata | undefined> {
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) {
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> {
let config: FrameworkConfig = {
appLocation: DEFAULT_CONFIG.appLocation!,
outputLocation: DEFAULT_CONFIG.outputLocation!
outputLocation: DEFAULT_CONFIG.outputLocation!,
};
if (!app && !api) {
@ -18,11 +18,11 @@ export async function generateConfiguration(app?: DetectedFolder, api?: Detected
return config;
}
let name = '';
let name = "";
if (app) {
name += `${app.frameworks.map(f => f.name).join(', ')}`;
app.frameworks.forEach(f => config = { ...config, ...f.config });
name += `${app.frameworks.map((f) => f.name).join(", ")}`;
app.frameworks.forEach((f) => (config = { ...config, ...f.config }));
config.appLocation = await computePath(app.rootPath, config.appLocation);
config.appLocation = removeTrailingPathSep(config.appLocation);
config.outputLocation = await computePath(config.appLocation, config.outputLocation);
@ -31,9 +31,9 @@ export async function generateConfiguration(app?: DetectedFolder, api?: Detected
}
if (api) {
name += (name ? ', with ' : 'No app frameworks detected, ');
name += `API: ${api.frameworks.map(f => f.name).join(', ')}`;
api.frameworks.forEach(f => config = { ...config, ...f.config });
name += name ? ", with " : "No app frameworks detected, ";
name += `API: ${api.frameworks.map((f) => f.name).join(", ")}`;
api.frameworks.forEach((f) => (config = { ...config, ...f.config }));
const computedApiLocation = await computePath(api.rootPath, config.apiLocation);
if (computedApiLocation !== api.rootPath) {
@ -71,10 +71,10 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
return basePath;
}
if (!additionalPath.startsWith('{')) {
if (!additionalPath.startsWith("{")) {
return path.join(basePath, additionalPath);
}
// Matches {<filename>#<expression>}, the first group is the filename, the second the expression
const match = additionalPath.match(/^\{(.*?)#(.*?)\}$/);
const [, filename, expression] = match || [];
@ -93,9 +93,8 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
throw new Error(`Invalid JSON file: ${file}`);
}
const evaluateExpression = (json: JsonData, expr: string) =>
Function(`"use strict";return data => (${expr})`)()(json);
const evaluateExpression = (json: JsonData, expr: string) => Function(`"use strict";return data => (${expr})`)()(json);
try {
const result = evaluateExpression(json, expression);
if (result) {
@ -110,7 +109,7 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
return basePath;
}
export async function detectProjectFolders(projectPath: string = '.'): Promise<DetectionResult> {
export async function detectProjectFolders(projectPath: string = "."): Promise<DetectionResult> {
const projectFiles = await getFiles(projectPath);
const apiFrameworks = await detectApiFrameworks(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
if (framework.parent) {
const parent = frameworksById[framework.parent];
files.push(
...(parent.files ?? []),
...(parent.packages ? [packageJsonFile] : [])
);
files.push(...(parent.files ?? []), ...(parent.packages ? [packageJsonFile] : []));
}
files.push(
...(framework.files ?? []),
...(framework.packages ? [packageJsonFile] : [])
);
files.push(...(framework.files ?? []), ...(framework.packages ? [packageJsonFile] : []));
const rootPaths = await findRootPathsForFiles(files, projectFiles);
if (rootPaths !== undefined) {
@ -170,7 +163,7 @@ export async function detectFrameworks(projectFiles: string[], frameworks: Frame
detectedFrameworks = await asyncFilter(
detectedFrameworks,
async framework => await matchPackages(framework) && await matchContains(framework, projectFiles)
async (framework) => (await matchPackages(framework)) && (await matchContains(framework, projectFiles))
);
let detectedFolders = await aggregateFolders(detectedFrameworks);
detectedFolders = filterDescendantFolders(detectedFolders);
@ -182,14 +175,14 @@ export async function detectFrameworks(projectFiles: string[], frameworks: Frame
async function detectApiFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> {
const detectedApiFolders: DetectedFolder[] = await detectFrameworks(projectFiles, apiFrameworks);
logger.silly(formatDetectedFolders(detectedApiFolders, 'api'));
logger.silly(formatDetectedFolders(detectedApiFolders, "api"));
return detectedApiFolders;
}
async function detectAppFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> {
const detectedAppFolders: DetectedFolder[] = await detectFrameworks(projectFiles, appFrameworks);
logger.silly(formatDetectedFolders(detectedAppFolders, 'app'));
logger.silly(formatDetectedFolders(detectedAppFolders, "app"));
return detectedAppFolders;
}
@ -212,20 +205,17 @@ async function matchPackages(framework: DetectedFramework): Promise<boolean> {
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 packageJson = await safeReadJson(packageJsonPath);
if (!packageJson) {
return false;
}
const dependencies = Object.keys(packageJson.dependencies ?? {});
const devDependencies = Object.keys(packageJson.devDependencies ?? {});
return framework.packages!.some(packageName =>
dependencies.includes(packageName) ||
devDependencies.includes(packageName)
);
return framework.packages!.some((packageName) => dependencies.includes(packageName) || devDependencies.includes(packageName));
});
framework.rootPaths = rootPathsMatches;
@ -237,7 +227,7 @@ async function matchContains(framework: DetectedFramework, files: string[]): Pro
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);
return asyncEvery(Object.entries(framework.contains!), async ([filename, stringToFind]) => {
const file = findFile(filename, currentFiles);
@ -262,8 +252,8 @@ function filterDescendantFolders(folders: DetectedFolder[]): DetectedFolder[] {
// Find all folders that are descendants of other folders
const descendantPaths: Set<string> = new Set();
for (const folder of folders) {
const descendantsFolders = folders.filter(f => isDescendantPath(f.rootPath, folder.rootPath));
descendantsFolders.forEach(f => descendantPaths.add(f.rootPath));
const descendantsFolders = folders.filter((f) => isDescendantPath(f.rootPath, folder.rootPath));
descendantsFolders.forEach((f) => descendantPaths.add(f.rootPath));
}
if (descendantPaths.size === 0) {
@ -274,15 +264,15 @@ function filterDescendantFolders(folders: DetectedFolder[]): DetectedFolder[] {
logger.silly(`- ${Array.from(descendantPaths).join("\n- ")}`);
// 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 {
for (const folder of detectedFolders) {
const overridenFrameworkIds: Set<string> = new Set();
folder.frameworks.forEach(f => {
folder.frameworks.forEach((f) => {
if (f.overrides) {
f.overrides.forEach(id => overridenFrameworkIds.add(id));
f.overrides.forEach((id) => overridenFrameworkIds.add(id));
}
});
@ -291,14 +281,14 @@ function filterPreemptedFrameworks(detectedFolders: DetectedFolder[]): void {
if (folder.frameworks.length > 1) {
overridenFrameworkIds.add("static");
}
if (overridenFrameworkIds.size === 0) {
continue;
}
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) {
if (framework.parent && frameworkIndexById[framework.parent] === undefined) {
// 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) {
// Lonely childs should not be a thing
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[] {
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 {
@ -338,34 +328,34 @@ function findFile(fileglob: string, files: string[]): string | undefined {
}
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 {
const foundFiles = fileglobs.map(fileglob => findAllFiles(fileglob, files));
const foundFiles = fileglobs.map((fileglob) => findAllFiles(fileglob, files));
// Get possible root path from first glob matches
// 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
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 possibleRootPaths = [...uniqueRootPaths].filter(
p => otherFoundFiles.every(files => files.some(file => path.dirname(file) === p))
);
const possibleRootPaths = [...uniqueRootPaths].filter((p) => otherFoundFiles.every((files) => files.some((file) => path.dirname(file) === p)));
return possibleRootPaths.length > 0 ? possibleRootPaths : undefined;
}
async function getFiles(rootPath: string): Promise<string[]> {
const entries = await fs.readdir(rootPath, { withFileTypes: true });
const files = await Promise.all(entries.map(async (entry): Promise<string[]> => {
// Ignore dot files and node_modules
if (entry.name.startsWith(".") || entry.name.includes("node_modules")) {
return [];
}
const entryPath = path.join(rootPath, entry.name);
return entry.isDirectory() ? [entryPath, ...(await getFiles(entryPath))] : [entryPath];
}));
const files = await Promise.all(
entries.map(async (entry): Promise<string[]> => {
// Ignore dot files and node_modules
if (entry.name.startsWith(".") || entry.name.includes("node_modules")) {
return [];
}
const entryPath = path.join(rootPath, entry.name);
return entry.isDirectory() ? [entryPath, ...(await getFiles(entryPath))] : [entryPath];
})
);
return files.flat();
}
@ -382,9 +372,9 @@ async function asyncEvery<T>(array: T[], predicate: (item: T) => Promise<boolean
export function printSupportedFrameworks(showList = false): void {
if (showList) {
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(`- ${appFrameworks.map(f => f.name).join("- \n")}`);
logger.info(`- ${appFrameworks.map((f) => f.name).join("- \n")}`);
} else {
logger.info(`Supported frameworks:`);
logger.info(`- api: ${apiFrameworks.length}`);
@ -393,6 +383,8 @@ export function printSupportedFrameworks(showList = false): void {
}
export function formatDetectedFolders(folders: DetectedFolder[], type: string): string {
return `Detected ${type} folders (${folders.length}):\n` +
`- ${folders.map(f => `${f.rootPath} (${f.frameworks.map(fr => fr.name).join(', ')})`).join("\n- ")}`;
return (
`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"],
config: {
apiBuildCommand: "npm run build --if-present",
}
},
},
{
id: "typescript",
@ -24,7 +24,7 @@ export const apiFrameworks: FrameworkDefinition[] = [
files: ["*.?(csproj|fsproj)", "host.json"],
config: {
apiBuildCommand: "dotnet build",
}
},
},
{
id: "python",
@ -32,8 +32,8 @@ export const apiFrameworks: FrameworkDefinition[] = [
files: ["?(requirements.txt|pyproject.toml|runtime.txt|setup.py)", "host.json"],
config: {
// Nothing to setup, but we need to know the apiLocation (rootPath)
}
}
},
},
];
export const appFrameworks: FrameworkDefinition[] = [
@ -43,7 +43,7 @@ export const appFrameworks: FrameworkDefinition[] = [
config: {
outputLocation: "./",
},
files: ["@(index.htm|default.htm)?(l)"]
files: ["@(index.htm|default.htm)?(l)"],
},
{
id: "angular",
@ -64,7 +64,7 @@ export const appFrameworks: FrameworkDefinition[] = [
packages: ["@scullyio/scully"],
config: {
// Same as base angular
}
},
},
{
id: "react",
@ -75,7 +75,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm start",
devServerUrl: "http://localhost:3000",
outputLocation: "build",
}
},
},
{
id: "nextjs",
@ -86,8 +86,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm dev",
devServerUrl: "http://localhost:3000",
outputLocation: "./"
}
outputLocation: "./",
},
},
{
id: "gatsby",
@ -99,8 +99,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm start",
devServerUrl: "http://localhost:8000",
outputLocation: "public"
}
outputLocation: "public",
},
},
{
id: "docusaurus",
@ -109,7 +109,7 @@ export const appFrameworks: FrameworkDefinition[] = [
packages: ["@docusaurus/core"],
config: {
// Same as base react
}
},
},
{
id: "react-static",
@ -117,8 +117,8 @@ export const appFrameworks: FrameworkDefinition[] = [
parent: "react",
packages: ["react-static"],
config: {
outputLocation: "dist"
}
outputLocation: "dist",
},
},
{
id: "preact",
@ -128,8 +128,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080",
outputLocation: "build"
}
outputLocation: "build",
},
},
{
id: "vue",
@ -140,7 +140,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run serve",
devServerUrl: "http://localhost:8080",
outputLocation: "dist",
}
},
},
{
id: "vite",
@ -150,7 +150,7 @@ export const appFrameworks: FrameworkDefinition[] = [
config: {
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000",
}
},
},
{
id: "nuxtjs",
@ -163,7 +163,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000",
outputLocation: "dist",
}
},
},
{
id: "vuepress",
@ -174,8 +174,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080",
outputLocation: "src/.vuepress/dist"
}
outputLocation: "src/.vuepress/dist",
},
},
{
id: "vitepress",
@ -187,7 +187,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run docs:dev",
devServerUrl: "http://localhost:3000",
outputLocation: "docs/.vitepress/dist",
}
},
},
{
id: "svelte",
@ -197,8 +197,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080",
outputLocation: "public"
}
outputLocation: "public",
},
},
{
id: "svelte-kit",
@ -209,8 +209,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080",
outputLocation: "build"
}
outputLocation: "build",
},
},
{
id: "sapper",
@ -221,8 +221,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run export",
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000",
outputLocation: "__sapper__/export"
}
outputLocation: "__sapper__/export",
},
},
{
id: "riot",
@ -232,8 +232,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm start",
devServerUrl: "http://localhost:3000",
outputLocation: "dist"
}
outputLocation: "dist",
},
},
{
id: "stencil",
@ -244,8 +244,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm start",
devServerUrl: "http://localhost:3333",
outputLocation: "www"
}
outputLocation: "www",
},
},
{
id: "aurelia",
@ -255,8 +255,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm start",
devServerUrl: "http://localhost:8080",
outputLocation: "dist"
}
outputLocation: "dist",
},
},
{
id: "ember",
@ -266,8 +266,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build",
devServerCommand: "npm start",
devServerUrl: "http://localhost:4200",
outputLocation: "dist"
}
outputLocation: "dist",
},
},
{
id: "elm",
@ -277,8 +277,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "elm make src/Main.elm --optimize",
devServerCommand: "elm reactor",
devServerUrl: "http://localhost:8000",
outputLocation: "./"
}
outputLocation: "./",
},
},
{
id: "polymer",
@ -289,9 +289,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "polymer build --preset es6-bundled",
devServerCommand: "polymer serve --open",
devServerUrl: "http://localhost:8081",
outputLocation: "build/es6-bundled"
}
outputLocation: "build/es6-bundled",
},
},
{
id: "lit",
@ -301,8 +300,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "npm run build --if-present",
devServerCommand: "npm start",
devServerUrl: "http://localhost:8081",
outputLocation: "./"
}
outputLocation: "./",
},
},
{
id: "hugo",
@ -315,8 +314,8 @@ export const appFrameworks: FrameworkDefinition[] = [
appBuildCommand: "hugo -D",
devServerCommand: "hugo server -D",
devServerUrl: "http://localhost:1313",
outputLocation: "public"
}
outputLocation: "public",
},
},
{
id: "flutter",
@ -327,7 +326,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "flutter run --web-port 8080",
devServerUrl: "http://localhost:8080",
outputLocation: "build/web",
}
},
},
{
id: "jekyll",
@ -338,7 +337,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "bundle exec jekyll serve --livereload",
devServerUrl: "http://localhost:4000",
outputLocation: "_site",
}
},
},
{
id: "slate",
@ -350,7 +349,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "./slate.sh serve",
devServerUrl: "http://localhost:4567",
outputLocation: "build",
}
},
},
{
id: "mkdocs",
@ -361,7 +360,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "mkdocs serve",
devServerUrl: "http://localhost:8000",
outputLocation: "site",
}
},
},
{
id: "eleventy",
@ -372,7 +371,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "eleventy --serve",
devServerUrl: "http://localhost:8080",
outputLocation: "_site",
}
},
},
{
id: "astro",
@ -385,7 +384,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:8080",
outputLocation: "_site",
}
},
},
{
id: "pelican",
@ -396,7 +395,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "make devserver",
devServerUrl: "http://localhost:8000",
outputLocation: "output",
}
},
},
{
id: "hexo",
@ -407,21 +406,21 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run server",
devServerUrl: "http://localhost:4000",
outputLocation: "public",
}
},
},
{
id: "blazor-wasm",
name: "Blazor WASM",
files: ["*.csproj", "App.razor", "wwwroot", "Program.cs"],
contains: {
"Program.cs": "WebAssemblyHostBuilder.CreateDefault"
"Program.cs": "WebAssemblyHostBuilder.CreateDefault",
},
config: {
appBuildCommand: "dotnet build",
devServerCommand: "dotnet watch run",
devServerUrl: "http://localhost:8000",
outputLocation: "output",
}
},
},
{
id: "gridsome",
@ -432,7 +431,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run develop",
devServerUrl: "http://localhost:8080",
outputLocation: "dist",
}
},
},
{
id: "solid",
@ -443,7 +442,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000",
outputLocation: "dist",
}
},
},
{
id: "remix",
@ -455,7 +454,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run dev",
devServerUrl: "http://localhost:3000",
outputLocation: "public/build",
}
},
},
{
id: "metalsmith",
@ -466,7 +465,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm run serve",
devServerUrl: "http://localhost:3000",
outputLocation: "build",
}
},
},
{
id: "brunch",
@ -477,7 +476,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "npm start",
devServerUrl: "http://localhost:3333",
outputLocation: "public",
}
},
},
{
id: "wintersmith",
@ -488,7 +487,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "wintersmith preview",
devServerUrl: "http://localhost:8080",
outputLocation: "build",
}
},
},
{
id: "middleman",
@ -499,7 +498,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "bundle exec middleman server",
devServerUrl: "http://localhost:4567",
outputLocation: "build",
}
},
},
{
id: "mdbook",
@ -510,7 +509,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "mdbook serve",
devServerUrl: "http://localhost:3000",
outputLocation: "book",
}
},
},
{
id: "zola",
@ -524,7 +523,7 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "zola serve",
devServerUrl: "http://localhost:1111",
outputLocation: "public",
}
},
},
{
id: "lektor",
@ -535,6 +534,6 @@ export const appFrameworks: FrameworkDefinition[] = [
devServerCommand: "lektor server",
devServerUrl: "http://localhost:5000",
outputLocation: "dist",
}
},
},
];

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

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

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

@ -73,7 +73,7 @@ export class SWACLIPersistenceCachePlugin implements ICachePlugin {
logger.silly(`Machine ID: ${machineId ? "<hidden>" : "<empty>"}`);
const secretStorage = new SecretStorage(this.options, new NativeCredentialsStore(this.options), new CryptoService(machineId));
await secretStorage.deleteCredentials(machineId, Environment.AzureCloud.name);
logger.silly(`Credentials cache cleared`);
}

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

@ -46,10 +46,7 @@ export function matchLoadedConfigName(name: string) {
* @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.
*/
export async function getConfigFileOptions(
configName: string | undefined,
configFilePath: string
): Promise<SWACLIConfig> {
export async function getConfigFileOptions(configName: string | undefined, configFilePath: string): Promise<SWACLIConfig> {
logger.silly(`Getting config file options from ${configFilePath}...`);
configFilePath = path.resolve(configFilePath);

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

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

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

@ -12,12 +12,14 @@ describe("safeReadJson()", () => {
});
it("should ignore JSON comments and parse JSON anyway", async () => {
mockFs({ "test.json": `{
mockFs({
"test.json": `{
// this is a /* tricky comment
"hello": "world",
/* and this should be removed too // */
"but": "not // this or /* this */ or /* this"
}`});
}`,
});
const json = await safeReadJson("test.json");
expect(json && json.hello).toBe("world");
});
@ -62,27 +64,26 @@ describe("findUpPackageJsonDir()", () => {
it("should return undefined if package.json is not found", async () => {
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 () => {
mockFs({ 'package.json': '{}' });
expect(await findUpPackageJsonDir('app', 'dist')).toBe(undefined);
mockFs({ "package.json": "{}" });
expect(await findUpPackageJsonDir("app", "dist")).toBe(undefined);
});
it("should return base path", async () => {
mockFs({ 'app/package.json': '{}' });
expect(await findUpPackageJsonDir('app/', 'dist')).toBe('app');
mockFs({ "app/package.json": "{}" });
expect(await findUpPackageJsonDir("app/", "dist")).toBe("app");
});
it("should return start path", async () => {
mockFs({ 'app/dist/package.json': '{}' });
expect(await findUpPackageJsonDir('app', 'dist/')).toBe('app/dist');
mockFs({ "app/dist/package.json": "{}" });
expect(await findUpPackageJsonDir("app", "dist/")).toBe("app/dist");
});
it("should return the correct path", async () => {
mockFs({ 'app/toto/package.json': '{}' });
expect(await findUpPackageJsonDir('app', 'toto/dist')).toBe('app/toto');
mockFs({ "app/toto/package.json": "{}" });
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> {
try {
let contents = await fs.readFile(path, 'utf8');
let contents = await fs.readFile(path, "utf8");
contents = stripJsonComments(contents);
return JSON.parse(contents) as JsonData;
} catch (error) {
@ -20,7 +20,7 @@ export async function safeReadFile(path?: string): Promise<string | undefined> {
}
try {
return await fs.readFile(path, 'utf8');
return await fs.readFile(path, "utf8");
} catch (error) {
logger.warn(`Failed to read file at: ${path}`);
return undefined;
@ -44,19 +44,19 @@ export async function findUpPackageJsonDir(rootPath: string, startPath: string):
return undefined;
}
rootPath = (rootPath === '.' || rootPath === `.${path.sep}`) ? '' : rootPath;
rootPath = rootPath === "." || rootPath === `.${path.sep}` ? "" : rootPath;
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> => {
if (components.length === 0 || components.length < rootPathLength) {
return undefined;
}
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));
};
const components = startPath.split(/[/\\]/).filter(c => c);
const components = startPath.split(/[/\\]/).filter((c) => c);
return find(components);
}

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

@ -21,10 +21,12 @@ export async function configureOptions(
userDefinedOptions = getUserOptions(command);
const configFileOptions = loadConfigFile ? await getConfigFileOptions(options.configName || configName, options.config!) : {};
const configFileCommandSpecificOptions = commandName ? configFileOptions[commandName] || {} : {};
// Clean up subcommands overrides before merging
// 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 };
options = {

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

@ -14,20 +14,24 @@ describe("dasherize()", () => {
describe("stripJsonComments()", () => {
it("should leave valid JSON untouched", () => {
expect(stripJsonComments(`{
expect(
stripJsonComments(`{
"hello": "world"
}`)).toBe(`{
}`)
).toBe(`{
"hello": "world"
}`);
});
it("should strip JSON comments", () => {
expect(stripJsonComments(`{
expect(
stripJsonComments(`{
// this is a /* tricky comment
"hello": "world",
/* and this should be removed too // */
"but": "not // this or /* this */ or /* this"
}`)).toBe(`{
}`)
).toBe(`{
"hello": "world",
@ -38,24 +42,24 @@ describe("stripJsonComments()", () => {
describe("removeTrailingPathSep()", () => {
it("should leave path untouched", () => {
expect(removeTrailingPathSep('./dir')).toBe('./dir');
expect(removeTrailingPathSep("./dir")).toBe("./dir");
});
it("should remove trailing slash", () => {
expect(removeTrailingPathSep('./')).toBe('.');
expect(removeTrailingPathSep("./")).toBe(".");
});
it("should remove trailing anti-slash", () => {
expect(removeTrailingPathSep('dir\\')).toBe('dir');
expect(removeTrailingPathSep("dir\\")).toBe("dir");
});
});
describe("hasSpaces()", () => {
it("should return false", () => {
expect(hasSpaces('./dir')).toBe(false);
expect(hasSpaces("./dir")).toBe(false);
});
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
// Only the captured group is removed, ignoring comments inside strings as a result
/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/gm,
(match, group) => group ? "" : match
(match, group) => (group ? "" : match)
);
}
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 fileSizeInKb = Math.round(fileSize / 1024);
if (fileSizeInKb > SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB) {
logger.warn(
`WARNING: ${SWA_CONFIG_FILENAME} is ${fileSizeInKb} bytes. The maximum size is ${SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB} bytes.`
);
logger.warn(`WARNING: ${SWA_CONFIG_FILENAME} is ${fileSizeInKb} bytes. The maximum size is ${SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB} bytes.`);
}
logger.silly(`Content parsed successfully`);

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

@ -95,7 +95,7 @@ export function isFunctionRequest(req: http.IncomingMessage, rewritePath?: strin
export async function validateFunctionTriggers(url: string) {
try {
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);
if (triggers.some((t: string) => !/^httptrigger$/i.test(t))) {

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

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