style: formatting
This commit is contained in:
Родитель
dfcfc908b1
Коммит
2ba3b51fbc
74
docs/ENV.md
74
docs/ENV.md
|
@ -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))) {
|
||||
|
|
|
@ -290,4 +290,4 @@ declare interface CoreToolsZipInfo {
|
|||
sha2: string;
|
||||
}
|
||||
|
||||
declare type NpmPackageManager = 'npm' | 'yarn' | 'pnpm';
|
||||
declare type NpmPackageManager = "npm" | "yarn" | "pnpm";
|
||||
|
|
Загрузка…
Ссылка в новой задаче