style: formatting
This commit is contained in:
Родитель
dfcfc908b1
Коммит
2ba3b51fbc
|
@ -1,5 +1,5 @@
|
||||||
| Environment Variable | Purpose | Read Only? | Default Value |
|
| Environment Variable | Purpose | Read Only? | Default Value |
|
||||||
| ----------------------------- | ---------------------------------------------------------------------------------- | ---------- | ------------- |
|
| ----------------------------- | --------------------------------------------------------------------------------------------------- | ---------- | ------------- |
|
||||||
| **General Settings** | | | |
|
| **General Settings** | | | |
|
||||||
| SWA_CLI_VERSION | CLI version | Yes | |
|
| SWA_CLI_VERSION | CLI version | Yes | |
|
||||||
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |
|
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |
|
||||||
|
|
|
@ -29,6 +29,7 @@ Build the website for production - by default this creates the static content in
|
||||||
$ cd docs/www
|
$ cd docs/www
|
||||||
$ npm run build
|
$ npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
You can preview the local build version using this command:
|
You can preview the local build version using this command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -55,6 +56,7 @@ Next use `npm deploy` or `yarn deploy` to push the built site to the GitHub page
|
||||||
$ cd docs/www
|
$ cd docs/www
|
||||||
$ npm deploy
|
$ npm deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
Before you do that, make sure the `docusaurus.config.js` is setup correctly using [this guidance](https://docusaurus.io/docs/deployment#github-pages-overview). Look for the _`// Please change this to your repo`_ comments and update those to reflect the local repo.
|
Before you do that, make sure the `docusaurus.config.js` is setup correctly using [this guidance](https://docusaurus.io/docs/deployment#github-pages-overview). Look for the _`// Please change this to your repo`_ comments and update those to reflect the local repo.
|
||||||
|
|
||||||
Note that when using `npm run deploy` from the commandline (e.g., for initial testing), you need to set the `GIT_USER` and `GIT_PASS` environment variables to
|
Note that when using `npm run deploy` from the commandline (e.g., for initial testing), you need to set the `GIT_USER` and `GIT_PASS` environment variables to
|
||||||
|
@ -64,52 +66,55 @@ Note that when using `npm run deploy` from the commandline (e.g., for initial te
|
||||||
// TODO: Fill in details for deploying same source to SWA
|
// TODO: Fill in details for deploying same source to SWA
|
||||||
// Site: https://azurestaticwebapps.dev/
|
// Site: https://azurestaticwebapps.dev/
|
||||||
|
|
||||||
|
|
||||||
## 6. Content Structure
|
## 6. Content Structure
|
||||||
|
|
||||||
> Docusaurus has this default structure for content:
|
> Docusaurus has this default structure for content:
|
||||||
|
|
||||||
* `blog/` - posts with index page, tags, RSS feed.
|
- `blog/` - posts with index page, tags, RSS feed.
|
||||||
* `docs/` - tutorials with sidebar, prev/next navigation
|
- `docs/` - tutorials with sidebar, prev/next navigation
|
||||||
* `src/pages` - standalone pages (map directly to routes)
|
- `src/pages` - standalone pages (map directly to routes)
|
||||||
* `static/` - static assets (served as is)
|
- `static/` - static assets (served as is)
|
||||||
|
|
||||||
> These are the main configuration files:
|
> These are the main configuration files:
|
||||||
* `docusaurus.config.js` - all site configuration
|
|
||||||
* `package.json` - NPM dependencies for build
|
- `docusaurus.config.js` - all site configuration
|
||||||
* `sidebars.js` - explicitly specify sidebar contents
|
- `package.json` - NPM dependencies for build
|
||||||
|
- `sidebars.js` - explicitly specify sidebar contents
|
||||||
|
|
||||||
> These are the key files to customize this site:
|
> These are the key files to customize this site:
|
||||||
1. `docusaurus.config.js`
|
|
||||||
|
1. `docusaurus.config.js`
|
||||||
- update contents of navbar
|
- update contents of navbar
|
||||||
- update contents of footer
|
- update contents of footer
|
||||||
- identify and customize plugins
|
- identify and customize plugins
|
||||||
- activate or deactivate default features (e.g., blog)
|
- activate or deactivate default features (e.g., blog)
|
||||||
- activate and customize banner (top of landing page)
|
- activate and customize banner (top of landing page)
|
||||||
2. `src/components/HomepageFeatures/index.js`
|
2. `src/components/HomepageFeatures/index.js`
|
||||||
- customize landing page body (features grid)
|
- customize landing page body (features grid)
|
||||||
2. `src/components/index.js`
|
3. `src/components/index.js`
|
||||||
- customize landing page structure (layout)
|
- customize landing page structure (layout)
|
||||||
4. `src/css/custom.css`
|
4. `src/css/custom.css`
|
||||||
- customize theme palette (dark, light)
|
- customize theme palette (dark, light)
|
||||||
- implement sitewide css changes
|
- implement sitewide css changes
|
||||||
|
|
||||||
> Guidelines for adding new content
|
> Guidelines for adding new content
|
||||||
- Blog feature is **deactivated**. (Update `docusaurus.config.js` to reactivate)
|
|
||||||
- Have **step-by-step** instructions?
|
- Blog feature is **deactivated**. (Update `docusaurus.config.js` to reactivate)
|
||||||
|
- Have **step-by-step** instructions?
|
||||||
- Add file under `docs/`X/Y for tutorial X with steps Y
|
- Add file under `docs/`X/Y for tutorial X with steps Y
|
||||||
- Create `docs/`X/_category_.json to define placement, metadata
|
- Create `docs/`X/_category_.json to define placement, metadata
|
||||||
- Have **single-page** documents that standalone?
|
- Have **single-page** documents that standalone?
|
||||||
- Add file as `src/pages/`X.md
|
- Add file as `src/pages/`X.md
|
||||||
- Choose X to reflect the desired _route_ name for this page
|
- Choose X to reflect the desired _route_ name for this page
|
||||||
|
|
||||||
## 6. Color Palette
|
## 6. Color Palette
|
||||||
|
|
||||||
> Old Color Palette (v0.8.3)
|
> Old Color Palette (v0.8.3)
|
||||||
* #ff9cfd
|
|
||||||
* #F131F8
|
- #ff9cfd
|
||||||
* #b319a6
|
- #F131F8
|
||||||
> New Color Palette (GA)
|
- #b319a6
|
||||||
* A100 (light) = #FF80AB
|
> New Color Palette (GA)
|
||||||
* A200 (medium) = #FF4081
|
- A100 (light) = #FF80AB
|
||||||
* A400 (dark) = #F50057
|
- A200 (medium) = #FF4081
|
||||||
|
- A400 (dark) = #F50057
|
||||||
|
|
|
@ -13,4 +13,4 @@ You can use the following options to override default values for any `swa` comma
|
||||||
| `--print-config` | Print all resolved options | `false` | `--print-config` or `--print-config=true` |
|
| `--print-config` | Print all resolved options | `false` | `--print-config` or `--print-config=true` |
|
||||||
| `--swa-config-location` | The directory where the `staticwebapp.config.json` file is located | `./` | `--swa-config-location=./app` |
|
| `--swa-config-location` | The directory where the `staticwebapp.config.json` file is located | `./` | `--swa-config-location=./app` |
|
||||||
|
|
||||||
|
|
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ sidebar_position: 3
|
||||||
|
|
||||||
# `swa start`
|
# `swa start`
|
||||||
|
|
||||||
|
|
||||||
If you need to override the default values for the `swa start` subcommand, you can provide the following options:
|
If you need to override the default values for the `swa start` subcommand, you can provide the following options:
|
||||||
|
|
||||||
| Option | Description | Default | Example |
|
| Option | Description | Default | Example |
|
||||||
|
|
|
@ -4,18 +4,15 @@ sidebar_position: 4
|
||||||
|
|
||||||
# `swa deploy`
|
# `swa deploy`
|
||||||
|
|
||||||
|
|
||||||
If you need to override the default values for the `swa deploy` subcommand, you can provide the following options:
|
If you need to override the default values for the `swa deploy` subcommand, you can provide the following options:
|
||||||
|
|
||||||
| Option | Description | Default | Example |
|
| Option | Description | Default | Example |
|
||||||
|:--|:--|:--|:--|
|
| :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | :-------- | :---------------------------------------- |
|
||||||
| `--api-location`| The folder containing the source code of the API application |`./api` | `--api-location="./api"`|
|
| `--api-location` | The folder containing the source code of the API application | `./api` | `--api-location="./api"` |
|
||||||
|`--deployment-token` | The secret toekn used to authenticate with the Static Web Apps| | `--deployment-token="123"` |
|
| `--deployment-token` | The secret toekn used to authenticate with the Static Web Apps | | `--deployment-token="123"` |
|
||||||
| `--dry-run` |Simulate a deploy process without actually running it | `false`| `--dry-run`|
|
| `--dry-run` | Simulate a deploy process without actually running it | `false` | `--dry-run` |
|
||||||
| `--print-token`|print the deployment token | `false` | `--print-token`|
|
| `--print-token` | print the deployment token | `false` | `--print-token` |
|
||||||
| `--env`| the type of deployment environment where to deploy the project | `preview`| `--env="production"` or `--env="preview"`|
|
| `--env` | the type of deployment environment where to deploy the project | `preview` | `--env="production"` or `--env="preview"` |
|
||||||
|`--print-token` |Print the deployment token. Usefull when using `--deployment-token` on CI/CD <br/> Note: this command does not run the deployment process. | `false` | `--print-token`|
|
| `--print-token` | Print the deployment token. Usefull when using `--deployment-token` on CI/CD <br/> Note: this command does not run the deployment process. | `false` | `--print-token` |
|
||||||
|
|
||||||
The deploy command does also support the same options as the `swa login` command.
|
The deploy command does also support the same options as the `swa login` command.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ sidebar_position: 4
|
||||||
|
|
||||||
# `swa-cli.config.json`
|
# `swa-cli.config.json`
|
||||||
|
|
||||||
|
|
||||||
The CLI can also load options from a `swa-cli.config.json` file:
|
The CLI can also load options from a `swa-cli.config.json` file:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
|
@ -3,12 +3,10 @@ sidebar_position: 4
|
||||||
title: env vars
|
title: env vars
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
# Environment Variables
|
# Environment Variables
|
||||||
|
|
||||||
|
|
||||||
| Environment Variable | Purpose | Read Only? | Default Value |
|
| Environment Variable | Purpose | Read Only? | Default Value |
|
||||||
| ----------------------------- | ---------------------------------------------------------------------------------- | ---------- | ------------- |
|
| ----------------------------- | --------------------------------------------------------------------------------------------------- | ---------- | ------------- |
|
||||||
| **General Settings** | | | |
|
| **General Settings** | | | |
|
||||||
| SWA_CLI_VERSION | CLI version | Yes | |
|
| SWA_CLI_VERSION | CLI version | Yes | |
|
||||||
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |
|
| SWA_CLI_DEBUG | Enable verbose logs (`silly`, `silent`, `log`, `info` or `error`) | | `log` |
|
||||||
|
|
|
@ -10,14 +10,14 @@ Please review the [Contributor Guidelines](https://github.com/Azure/static-web-a
|
||||||
|
|
||||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. As a contributor, this is what you need to know:
|
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. As a contributor, this is what you need to know:
|
||||||
|
|
||||||
- [Signing the CLA](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#signing-the-cla)
|
- [Signing the CLA](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#signing-the-cla)
|
||||||
- [Code of Conduct](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#code-of-conduct)
|
- [Code of Conduct](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#code-of-conduct)
|
||||||
- [Got a Question or Problem?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#got-a-question-or-problem)
|
- [Got a Question or Problem?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#got-a-question-or-problem)
|
||||||
- [Found an Bug?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#found-an-bug)
|
- [Found an Bug?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#found-an-bug)
|
||||||
- [Missing a Feature?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#missing-a-feature)
|
- [Missing a Feature?](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#missing-a-feature)
|
||||||
- [Submission Guidelines](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#submission-guidelines)
|
- [Submission Guidelines](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#submission-guidelines)
|
||||||
- [Coding Rules](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#coding-rules)
|
- [Coding Rules](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#coding-rules)
|
||||||
- [Commit Message Guidelines](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#commit-message-guidelines)
|
- [Commit Message Guidelines](https://github.com/Azure/static-web-apps-cli/blob/ga/CONTRIBUTING.md#commit-message-guidelines)
|
||||||
|
|
||||||
> 🚧 | `TODO:` Update ga links to main.
|
> 🚧 | `TODO:` Update ga links to main.
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,10 @@ Thanks go to these wonderful people ([emoji key](https://allcontributors.org/doc
|
||||||
|
|
||||||
> 🚧 | TODO: Automate bot
|
> 🚧 | TODO: Automate bot
|
||||||
|
|
||||||
|
|
||||||
This project follows the [all-contributors][ac] specification.
|
This project follows the [all-contributors][ac] specification.
|
||||||
* Contributions of any kind welcome!
|
|
||||||
* Automatically updated by [the @all-contributors bot 🤖][acbot].
|
- Contributions of any kind welcome!
|
||||||
|
- Automatically updated by [the @all-contributors bot 🤖][acbot].
|
||||||
|
|
||||||
[acek]: https://allcontributors.org/docs/en/emoji-key
|
[acek]: https://allcontributors.org/docs/en/emoji-key
|
||||||
[ac]: https://github.com/all-contributors/all-contributors
|
[ac]: https://github.com/all-contributors/all-contributors
|
||||||
|
|
|
@ -8,14 +8,13 @@ The **Static Web Apps (SWA) CLI** is an open-source commandline tool used for si
|
||||||
|
|
||||||
[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/azure/static-web-apps-cli/issues)
|
[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/azure/static-web-apps-cli/issues)
|
||||||
|
|
||||||
|
|
||||||
## What is Static Web Apps?
|
## What is Static Web Apps?
|
||||||
|
|
||||||
Azure Static Web Apps is a turnkey service for modern full-stack applications with pre-built or pre-rendered front-ends, and serverless backends. It became [generally available in May 2021](https://azure.microsoft.com/en-us/updates/azure-static-web-apps-is-now-generally-available/?WT.mc_id=30daysofswa-61155-cxall) and:
|
Azure Static Web Apps is a turnkey service for modern full-stack applications with pre-built or pre-rendered front-ends, and serverless backends. It became [generally available in May 2021](https://azure.microsoft.com/en-us/updates/azure-static-web-apps-is-now-generally-available/?WT.mc_id=30daysofswa-61155-cxall) and:
|
||||||
|
|
||||||
* works with [your favorite front-end frameworks and static site generators](https://docs.microsoft.com/en-us/azure/static-web-apps/front-end-frameworks)
|
- works with [your favorite front-end frameworks and static site generators](https://docs.microsoft.com/en-us/azure/static-web-apps/front-end-frameworks)
|
||||||
* has [quickstart options](https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started?tabs=vanilla-javascript) for IDE, command line, and browser (via Azure Portal).
|
- has [quickstart options](https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started?tabs=vanilla-javascript) for IDE, command line, and browser (via Azure Portal).
|
||||||
* can automate workflows for code repositories in [GitHub](https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started?tabs=vanilla-javascript), [GitLab](https://docs.microsoft.com/en-us/azure/static-web-apps/gitlab?tabs=vanilla-javascript) and [Bit Bucket](https://docs.microsoft.com/en-us/azure/static-web-apps/bitbucket?tabs=vanilla-javascript).
|
- can automate workflows for code repositories in [GitHub](https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started?tabs=vanilla-javascript), [GitLab](https://docs.microsoft.com/en-us/azure/static-web-apps/gitlab?tabs=vanilla-javascript) and [Bit Bucket](https://docs.microsoft.com/en-us/azure/static-web-apps/bitbucket?tabs=vanilla-javascript).
|
||||||
|
|
||||||
Visit the [Azure Static Web Apps Documentation](https://docs.microsoft.com/en-us/azure/static-web-apps/) for more information.
|
Visit the [Azure Static Web Apps Documentation](https://docs.microsoft.com/en-us/azure/static-web-apps/) for more information.
|
||||||
|
|
||||||
|
@ -29,7 +28,6 @@ The Static Web Apps CLI (aka **SWA CLI**) is an [open-source](https://github.com
|
||||||
- Emulate Static Web Apps configuration (including routing & role-based auth)
|
- Emulate Static Web Apps configuration (including routing & role-based auth)
|
||||||
- Deploy your app to Azure Static Web Apps (for a unified develop-deploy workflow)
|
- Deploy your app to Azure Static Web Apps (for a unified develop-deploy workflow)
|
||||||
|
|
||||||
|
|
||||||
## SWA CLI Components
|
## SWA CLI Components
|
||||||
|
|
||||||
At a high level, the architecture looks something like this:
|
At a high level, the architecture looks something like this:
|
||||||
|
@ -46,8 +44,7 @@ The key components of the SWA CLI are:
|
||||||
- The **Static content server**. Serves app's static content locally for testing, validation.
|
- The **Static content server**. Serves app's static content locally for testing, validation.
|
||||||
- The **Serverless API server**. Served by Azure Functions Core Tools, for local API testing.
|
- The **Serverless API server**. Served by Azure Functions Core Tools, for local API testing.
|
||||||
|
|
||||||
> **```🚨 CAVEAT: STATIC WEB APPS CLI IS CURRENTLY IN PREVIEW. 🚨```** <br/> It _emulates_ key capabilities of the Azure Static Web Apps service, so differences from actual behavior are to be expected. Deploy and test your app in Azure for final validation.
|
> **`🚨 CAVEAT: STATIC WEB APPS CLI IS CURRENTLY IN PREVIEW. 🚨`** <br/> It _emulates_ key capabilities of the Azure Static Web Apps service, so differences from actual behavior are to be expected. Deploy and test your app in Azure for final validation.
|
||||||
|
|
||||||
|
|
||||||
## Contribute to SWA CLI
|
## Contribute to SWA CLI
|
||||||
|
|
||||||
|
@ -55,18 +52,18 @@ Static Web Apps CLI preview release v0.8.3 was releaseed in **April 2022**. The
|
||||||
|
|
||||||
This is an open-source project made for the benefit of our developer community. Your feedback and contributions are key to its success. Here are some ways to help:
|
This is an open-source project made for the benefit of our developer community. Your feedback and contributions are key to its success. Here are some ways to help:
|
||||||
|
|
||||||
* Discovered buggy or unusual behavior? [Send us a bug report](https://github.com/Azure/static-web-apps-cli/issues/new?assignees=&labels=&template=bug_report.md&title=)
|
- Discovered buggy or unusual behavior? [Send us a bug report](https://github.com/Azure/static-web-apps-cli/issues/new?assignees=&labels=&template=bug_report.md&title=)
|
||||||
* Have a feature request? [Send us a Feature Request](https://github.com/Azure/static-web-apps-cli/issues/new?assignees=&labels=&template=feature_request.md&title=)
|
- Have a feature request? [Send us a Feature Request](https://github.com/Azure/static-web-apps-cli/issues/new?assignees=&labels=&template=feature_request.md&title=)
|
||||||
* Found a security vulnerability? [Report Security Issues](https://github.com/Azure/static-web-apps-cli/security/policy)
|
- Found a security vulnerability? [Report Security Issues](https://github.com/Azure/static-web-apps-cli/security/policy)
|
||||||
* Have other questions or comments? [Post to our Discussions board](https://github.com/Azure/static-web-apps-cli/discussions)
|
- Have other questions or comments? [Post to our Discussions board](https://github.com/Azure/static-web-apps-cli/discussions)
|
||||||
* Posting questions to Stack Overflow? [Post to the swa-cli tag](https://stackoverflow.com/questions/tagged/swa-cli)
|
- Posting questions to Stack Overflow? [Post to the swa-cli tag](https://stackoverflow.com/questions/tagged/swa-cli)
|
||||||
|
|
||||||
You can also contribute directly to the project by:
|
You can also contribute directly to the project by:
|
||||||
* Fixing bugs identified in issues
|
|
||||||
* Writing or improving the documentation
|
- Fixing bugs identified in issues
|
||||||
* Extending or improving CLI capabilities
|
- Writing or improving the documentation
|
||||||
|
- Extending or improving CLI capabilities
|
||||||
|
|
||||||
To get started, read our [Contributor Guide](/docs/contribute/intro)
|
To get started, read our [Contributor Guide](/docs/contribute/intro)
|
||||||
|
|
||||||
|
|
||||||
Thank you for your continued support! ♥️
|
Thank you for your continued support! ♥️
|
|
@ -22,7 +22,6 @@ npm install -g @azure/static-web-apps-cli
|
||||||
npm install -D @azure/static-web-apps-cli
|
npm install -D @azure/static-web-apps-cli
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Install with yarn
|
## Install with yarn
|
||||||
|
|
||||||
**See: **[@azure/static-web-apps-cli](hhttps://yarnpkg.com/package/@azure/static-web-apps-cli).
|
**See: **[@azure/static-web-apps-cli](hhttps://yarnpkg.com/package/@azure/static-web-apps-cli).
|
||||||
|
@ -32,6 +31,7 @@ npm install -D @azure/static-web-apps-cli
|
||||||
```bash
|
```bash
|
||||||
yarn add global @azure/static-web-apps-cli
|
yarn add global @azure/static-web-apps-cli
|
||||||
```
|
```
|
||||||
|
|
||||||
2. The recommended approach is to install this locally, as a devDependency, using:
|
2. The recommended approach is to install this locally, as a devDependency, using:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -64,4 +64,3 @@ Or use this command to start the emulator on your local device:
|
||||||
```bash
|
```bash
|
||||||
$ npx @azure/static-web-apps-cli start
|
$ npx @azure/static-web-apps-cli start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,9 @@ sidebar_position: 3
|
||||||
# 2. Start The Emulator
|
# 2. Start The Emulator
|
||||||
|
|
||||||
The SWA Emulator is run by using the `swa start` command.
|
The SWA Emulator is run by using the `swa start` command.
|
||||||
* It runs on `http://localhost:4280` by default.
|
|
||||||
* Read the [docs](/docs/cli/swa-start) for more command details.
|
|
||||||
|
|
||||||
|
|
||||||
|
- It runs on `http://localhost:4280` by default.
|
||||||
|
- Read the [docs](/docs/cli/swa-start) for more command details.
|
||||||
|
|
||||||
## 2.1 Serve from current folder
|
## 2.1 Serve from current folder
|
||||||
|
|
||||||
|
@ -32,8 +31,8 @@ When developing your SWA front-end, you may want to use the front-end framework'
|
||||||
|
|
||||||
SWA CLI can reverse proxy requests to that dev server, allowing you to retain the above benefits during local development of your SWA. You can achieve this in two steps:
|
SWA CLI can reverse proxy requests to that dev server, allowing you to retain the above benefits during local development of your SWA. You can achieve this in two steps:
|
||||||
|
|
||||||
1. Start the local dev server as usual. _Note the URL (localhost:port) the dev server runs on._
|
1. Start the local dev server as usual. _Note the URL (localhost:port) the dev server runs on._
|
||||||
2. Start the SWA CLI in a new terminal _with dev server URL specified._
|
2. Start the SWA CLI in a new terminal _with dev server URL specified._
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
swa start <dev-server-url>
|
swa start <dev-server-url>
|
||||||
|
@ -70,7 +69,6 @@ swa start http://localhost:4200 --run "./startup.sh"
|
||||||
|
|
||||||
Then access the application with the emulated services from `http://localhost:4280`
|
Then access the application with the emulated services from `http://localhost:4280`
|
||||||
|
|
||||||
|
|
||||||
## 2.5 Default Dev Server ports
|
## 2.5 Default Dev Server ports
|
||||||
|
|
||||||
Here are some of the default ports used by dev servers in popular front-end frameworks and static site generators today.
|
Here are some of the default ports used by dev servers in popular front-end frameworks and static site generators today.
|
||||||
|
|
|
@ -4,16 +4,16 @@ sidebar_position: 4
|
||||||
|
|
||||||
# 4. Start the API Server
|
# 4. Start the API Server
|
||||||
|
|
||||||
|
|
||||||
## 4.1 Azure Functions Core Tools
|
## 4.1 Azure Functions Core Tools
|
||||||
|
|
||||||
While not mandatory, your application may choose to take advantage of [serverless API support with Azure Functions](https://docs.microsoft.com/en-us/azure/static-web-apps/apis).
|
While not mandatory, your application may choose to take advantage of [serverless API support with Azure Functions](https://docs.microsoft.com/en-us/azure/static-web-apps/apis).
|
||||||
Azure Functions service has its own [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) CLI to support local development. Read the [API Reference](https://docs.microsoft.com/en-us/azure/azure-functions/functions-core-tools-reference?tabs=v2) to learn about its `func` command and options.
|
Azure Functions service has its own [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) CLI to support local development. Read the [API Reference](https://docs.microsoft.com/en-us/azure/azure-functions/functions-core-tools-reference?tabs=v2) to learn about its `func` command and options.
|
||||||
|
|
||||||
By using the SWA CLI, we can streamline the local development workflow (across Azure Static Web Apps and Azure Functions) even further. The SWA CLI can:
|
By using the SWA CLI, we can streamline the local development workflow (across Azure Static Web Apps and Azure Functions) even further. The SWA CLI can:
|
||||||
* verify that [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) is installed in your local development environment.
|
|
||||||
* download or update the [right version](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local#install-the-azure-functions-core-tools) for you if needed.
|
- verify that [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) is installed in your local development environment.
|
||||||
* give you flexibility to have the CLI manage the API server launch if needed.
|
- download or update the [right version](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local#install-the-azure-functions-core-tools) for you if needed.
|
||||||
|
- give you flexibility to have the CLI manage the API server launch if needed.
|
||||||
|
|
||||||
Let's explore these options.
|
Let's explore these options.
|
||||||
|
|
||||||
|
@ -24,9 +24,11 @@ You might want to run the Azure Functions Core Tools (API server) separately, to
|
||||||
To use the SWA emulator services alongside the API server:
|
To use the SWA emulator services alongside the API server:
|
||||||
|
|
||||||
1. Start API server first using Azure Functions Core Tools CLI (below) or the [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions). _Note the URL for the local API server, once it is running_.
|
1. Start API server first using Azure Functions Core Tools CLI (below) or the [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions). _Note the URL for the local API server, once it is running_.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
func host start
|
func host start
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Start SWA CLI in a separate terminal and use the `--api-location` option to pass it the relevant local API Server URI. _For example:_
|
2. Start SWA CLI in a separate terminal and use the `--api-location` option to pass it the relevant local API Server URI. _For example:_
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -48,4 +50,3 @@ swa start ./my-dist --api-location ./api
|
||||||
```bash
|
```bash
|
||||||
swa start http://localhost:3000 --api-location ./api
|
swa start http://localhost:3000 --api-location ./api
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ sidebar_position: 5
|
||||||
|
|
||||||
# 5. Deploy SWA to Azure
|
# 5. Deploy SWA to Azure
|
||||||
|
|
||||||
|
|
||||||
The CLI can also be used to deploy an app to Azure Static Web Apps using the command: `swa deploy`. Here are some common use cases:
|
The CLI can also be used to deploy an app to Azure Static Web Apps using the command: `swa deploy`. Here are some common use cases:
|
||||||
|
|
||||||
1. Deploy a front-end app without an API
|
1. Deploy a front-end app without an API
|
||||||
|
|
|
@ -6,7 +6,6 @@ sidebar_position: 5
|
||||||
|
|
||||||
Azure Static Web Apps can be configured with an optional `staticwebapp.config.json` file. For more information, see [Configure Static Web Apps documentation](https://docs.microsoft.com/azure/static-web-apps/configuration).
|
Azure Static Web Apps can be configured with an optional `staticwebapp.config.json` file. For more information, see [Configure Static Web Apps documentation](https://docs.microsoft.com/azure/static-web-apps/configuration).
|
||||||
|
|
||||||
|
|
||||||
## 6.1 Default File Location
|
## 6.1 Default File Location
|
||||||
|
|
||||||
If you are serving static files from a folder, the CLI will search this folder for `staticwebapp.config.json`.
|
If you are serving static files from a folder, the CLI will search this folder for `staticwebapp.config.json`.
|
||||||
|
@ -23,7 +22,6 @@ If you are using a front-end dev server, the CLI will search the current directo
|
||||||
swa start http://localhost:3000
|
swa start http://localhost:3000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## 6.2 Specify File Location
|
## 6.2 Specify File Location
|
||||||
|
|
||||||
To control where the CLI searches for `staticwebapp.config.json`, use `--swa-config-location`.
|
To control where the CLI searches for `staticwebapp.config.json`, use `--swa-config-location`.
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
"watch": "tsc --watch",
|
"watch": "tsc --watch",
|
||||||
"copy-assets": "node ./scripts/copy-assets.js",
|
"copy-assets": "node ./scripts/copy-assets.js",
|
||||||
"prepare": "npm run build",
|
"prepare": "npm run build",
|
||||||
"format": "prettier --write 'src/**/*.ts' '**/*.md' 'scripts/**/*.js' 'cypress/**/*.js' 'schema/**/*.json'"
|
"format": "prettier --write 'src/**/*.ts' *.md'' 'docs/**/*.md' 'scripts/**/*.js' 'cypress/**/*.js' 'schema/**/*.json'"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"swa": "dist/cli/bin.js"
|
"swa": "dist/cli/bin.js"
|
||||||
|
|
|
@ -24,6 +24,7 @@ The CLI emulates commonly used capabilities of the Azure Static Web Apps cloud s
|
||||||
### Using `npm` or `yarn`:
|
### Using `npm` or `yarn`:
|
||||||
|
|
||||||
- Install the cli
|
- Install the cli
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -g @azure/static-web-apps-cli
|
npm install -g @azure/static-web-apps-cli
|
||||||
```
|
```
|
||||||
|
@ -49,11 +50,13 @@ See all available [options](#cli-options).
|
||||||
### Using `npx`:
|
### Using `npx`:
|
||||||
|
|
||||||
- Open a SWA app folder at the root (outside any /api or /app folders):
|
- Open a SWA app folder at the root (outside any /api or /app folders):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd my-awesome-swa-app
|
cd my-awesome-swa-app
|
||||||
```
|
```
|
||||||
|
|
||||||
- Create a configuration for your project:
|
- Create a configuration for your project:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx @azure/static-web-apps-cli init
|
npx @azure/static-web-apps-cli init
|
||||||
```
|
```
|
||||||
|
|
|
@ -236,8 +236,7 @@
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"loginCommandParameters": {
|
"loginCommandParameters": {
|
||||||
"properties": {
|
"properties": {},
|
||||||
},
|
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"deployCommandParameters": {
|
"deployCommandParameters": {
|
||||||
|
|
|
@ -23,7 +23,7 @@ describe("swa build", () => {
|
||||||
|
|
||||||
it("should run npm install before build command", async () => {
|
it("should run npm install before build command", async () => {
|
||||||
const execSyncMock = jest.requireMock("child_process").execSync;
|
const execSyncMock = jest.requireMock("child_process").execSync;
|
||||||
mockFs({ 'package.json': {} });
|
mockFs({ "package.json": {} });
|
||||||
|
|
||||||
await build({ ...DEFAULT_CONFIG, appBuildCommand: "npm run something" });
|
await build({ ...DEFAULT_CONFIG, appBuildCommand: "npm run something" });
|
||||||
expect(execSyncMock.mock.calls[0][0]).toBe("npm install");
|
expect(execSyncMock.mock.calls[0][0]).toBe("npm install");
|
||||||
|
@ -32,12 +32,12 @@ describe("swa build", () => {
|
||||||
|
|
||||||
it("should run command in package.json path", async () => {
|
it("should run command in package.json path", async () => {
|
||||||
const execSyncMock = jest.requireMock("child_process").execSync;
|
const execSyncMock = jest.requireMock("child_process").execSync;
|
||||||
mockFs({ 'app/package.json': {} });
|
mockFs({ "app/package.json": {} });
|
||||||
|
|
||||||
await build({
|
await build({
|
||||||
...DEFAULT_CONFIG,
|
...DEFAULT_CONFIG,
|
||||||
outputLocation: "app/dist",
|
outputLocation: "app/dist",
|
||||||
appBuildCommand: "npm run something"
|
appBuildCommand: "npm run something",
|
||||||
});
|
});
|
||||||
expect(execSyncMock.mock.calls[0][1].cwd).toBe("app");
|
expect(execSyncMock.mock.calls[0][1].cwd).toBe("app");
|
||||||
});
|
});
|
||||||
|
@ -46,27 +46,27 @@ describe("swa build", () => {
|
||||||
const execSyncMock = jest.requireMock("child_process").execSync;
|
const execSyncMock = jest.requireMock("child_process").execSync;
|
||||||
mockFs();
|
mockFs();
|
||||||
|
|
||||||
await build({ ...DEFAULT_CONFIG, apiLocation: 'api/', apiBuildCommand: "npm run something" });
|
await build({ ...DEFAULT_CONFIG, apiLocation: "api/", apiBuildCommand: "npm run something" });
|
||||||
expect(execSyncMock.mock.calls[0][0]).toBe("npm run something");
|
expect(execSyncMock.mock.calls[0][0]).toBe("npm run something");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should run npm install before build command", async () => {
|
it("should run npm install before build command", async () => {
|
||||||
const execSyncMock = jest.requireMock("child_process").execSync;
|
const execSyncMock = jest.requireMock("child_process").execSync;
|
||||||
mockFs({ 'api/package.json': {} });
|
mockFs({ "api/package.json": {} });
|
||||||
|
|
||||||
await build({ ...DEFAULT_CONFIG, apiLocation: 'api/', apiBuildCommand: "npm run something" });
|
await build({ ...DEFAULT_CONFIG, apiLocation: "api/", apiBuildCommand: "npm run something" });
|
||||||
expect(execSyncMock.mock.calls[0][0]).toBe("npm install");
|
expect(execSyncMock.mock.calls[0][0]).toBe("npm install");
|
||||||
expect(execSyncMock.mock.calls[1][0]).toBe("npm run something");
|
expect(execSyncMock.mock.calls[1][0]).toBe("npm run something");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should run command in package.json path", async () => {
|
it("should run command in package.json path", async () => {
|
||||||
const execSyncMock = jest.requireMock("child_process").execSync;
|
const execSyncMock = jest.requireMock("child_process").execSync;
|
||||||
mockFs({ 'api/package.json': {} });
|
mockFs({ "api/package.json": {} });
|
||||||
|
|
||||||
await build({
|
await build({
|
||||||
...DEFAULT_CONFIG,
|
...DEFAULT_CONFIG,
|
||||||
apiLocation: 'api',
|
apiLocation: "api",
|
||||||
apiBuildCommand: "npm run something"
|
apiBuildCommand: "npm run something",
|
||||||
});
|
});
|
||||||
expect(execSyncMock.mock.calls[0][1].cwd).toBe("api");
|
expect(execSyncMock.mock.calls[0][1].cwd).toBe("api");
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,16 @@ import { Command } from "commander";
|
||||||
import { DEFAULT_CONFIG } from "../../config";
|
import { DEFAULT_CONFIG } from "../../config";
|
||||||
import { detectProjectFolders, generateConfiguration } from "../../core/frameworks";
|
import { detectProjectFolders, generateConfiguration } from "../../core/frameworks";
|
||||||
import {
|
import {
|
||||||
configureOptions, findUpPackageJsonDir, isUserOption, isUserOrConfigOption, logger, matchLoadedConfigName, pathExists, readWorkflowFile, runCommand, swaCliConfigFilename,
|
configureOptions,
|
||||||
|
findUpPackageJsonDir,
|
||||||
|
isUserOption,
|
||||||
|
isUserOrConfigOption,
|
||||||
|
logger,
|
||||||
|
matchLoadedConfigName,
|
||||||
|
pathExists,
|
||||||
|
readWorkflowFile,
|
||||||
|
runCommand,
|
||||||
|
swaCliConfigFilename,
|
||||||
} from "../../core/utils";
|
} from "../../core/utils";
|
||||||
|
|
||||||
export default function registerCommand(program: Command) {
|
export default function registerCommand(program: Command) {
|
||||||
|
@ -21,7 +30,7 @@ export default function registerCommand(program: Command) {
|
||||||
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
|
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
|
||||||
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "build");
|
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "build");
|
||||||
if (positionalArg && !matchLoadedConfigName(positionalArg)) {
|
if (positionalArg && !matchLoadedConfigName(positionalArg)) {
|
||||||
if (isUserOption('appLocation')) {
|
if (isUserOption("appLocation")) {
|
||||||
logger.error(`swa build <appLocation> cannot be when with --app-location option is also set.`);
|
logger.error(`swa build <appLocation> cannot be when with --app-location option is also set.`);
|
||||||
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +54,7 @@ export async function build(options: SWACLIConfig) {
|
||||||
outputLocation: options.outputLocation,
|
outputLocation: options.outputLocation,
|
||||||
appBuildCommand: options.appBuildCommand,
|
appBuildCommand: options.appBuildCommand,
|
||||||
apiBuildCommand: options.apiBuildCommand,
|
apiBuildCommand: options.apiBuildCommand,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let appLocation = options.appLocation ?? workflowConfig?.appLocation;
|
let appLocation = options.appLocation ?? workflowConfig?.appLocation;
|
||||||
|
@ -61,7 +70,7 @@ export async function build(options: SWACLIConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.auto) {
|
if (options.auto) {
|
||||||
logger.log('Detecting build configuration...');
|
logger.log("Detecting build configuration...");
|
||||||
const detectedFolders = await detectProjectFolders(appLocation);
|
const detectedFolders = await detectProjectFolders(appLocation);
|
||||||
|
|
||||||
if (detectedFolders.app.length === 0 && detectedFolders.api.length === 0) {
|
if (detectedFolders.app.length === 0 && detectedFolders.api.length === 0) {
|
||||||
|
@ -89,21 +98,21 @@ export async function build(options: SWACLIConfig) {
|
||||||
|
|
||||||
if (!appBuildCommand && !apiBuildCommand) {
|
if (!appBuildCommand && !apiBuildCommand) {
|
||||||
if (!hasBuildOptionsDefined(options)) {
|
if (!hasBuildOptionsDefined(options)) {
|
||||||
logger.warn('No build options were defined.');
|
logger.warn("No build options were defined.");
|
||||||
logger.warn('If your app needs a build step, run "swa init" to set your project configuration');
|
logger.warn('If your app needs a build step, run "swa init" to set your project configuration');
|
||||||
logger.warn(`or use option flags to set your build commands and paths.\n`);
|
logger.warn(`or use option flags to set your build commands and paths.\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('Nothing to build.');
|
logger.log("Nothing to build.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(`Build configuration:`);
|
logger.log(`Build configuration:`);
|
||||||
logger.log(`- App location: ${chalk.green(appLocation || '')}`);
|
logger.log(`- App location: ${chalk.green(appLocation || "")}`);
|
||||||
logger.log(`- API location: ${chalk.green(apiLocation || '')}`);
|
logger.log(`- API location: ${chalk.green(apiLocation || "")}`);
|
||||||
logger.log(`- Output location: ${chalk.green(outputLocation || '')}`);
|
logger.log(`- Output location: ${chalk.green(outputLocation || "")}`);
|
||||||
logger.log(`- App build command: ${chalk.green(appBuildCommand || '')}`);
|
logger.log(`- App build command: ${chalk.green(appBuildCommand || "")}`);
|
||||||
logger.log(`- API build command: ${chalk.green(apiBuildCommand || '')}`);
|
logger.log(`- API build command: ${chalk.green(apiBuildCommand || "")}`);
|
||||||
|
|
||||||
if (appBuildCommand) {
|
if (appBuildCommand) {
|
||||||
const packageJsonPath = await findUpPackageJsonDir(appLocation!, outputLocation!);
|
const packageJsonPath = await findUpPackageJsonDir(appLocation!, outputLocation!);
|
||||||
|
@ -118,7 +127,7 @@ export async function build(options: SWACLIConfig) {
|
||||||
|
|
||||||
if (apiBuildCommand) {
|
if (apiBuildCommand) {
|
||||||
// For now, only look up in the api location as there's no equivalent to outputLocation for api
|
// For now, only look up in the api location as there's no equivalent to outputLocation for api
|
||||||
const packageJsonPath = await findUpPackageJsonDir(apiLocation!, '.');
|
const packageJsonPath = await findUpPackageJsonDir(apiLocation!, ".");
|
||||||
if (packageJsonPath) {
|
if (packageJsonPath) {
|
||||||
logger.log(`Found package.json in ${packageJsonPath}`);
|
logger.log(`Found package.json in ${packageJsonPath}`);
|
||||||
await installNpmDependencies(packageJsonPath);
|
await installNpmDependencies(packageJsonPath);
|
||||||
|
@ -133,7 +142,7 @@ function hasBuildOptionsDefined(options: SWACLIConfig): boolean {
|
||||||
if (options.appBuildCommand || options.apiBuildCommand) {
|
if (options.appBuildCommand || options.apiBuildCommand) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return isUserOrConfigOption('appBuildCommand') || isUserOrConfigOption('apiBuildCommand');
|
return isUserOrConfigOption("appBuildCommand") || isUserOrConfigOption("apiBuildCommand");
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAutoErrorMessageAndExit() {
|
function showAutoErrorMessageAndExit() {
|
||||||
|
@ -142,19 +151,19 @@ function showAutoErrorMessageAndExit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function detectPackageManager(basePath: string): Promise<NpmPackageManager> {
|
async function detectPackageManager(basePath: string): Promise<NpmPackageManager> {
|
||||||
const hasYarnLock = await pathExists(path.join(basePath, 'yarn.lock'));
|
const hasYarnLock = await pathExists(path.join(basePath, "yarn.lock"));
|
||||||
const hasNpmLock = await pathExists(path.join(basePath, 'package-lock.json'));
|
const hasNpmLock = await pathExists(path.join(basePath, "package-lock.json"));
|
||||||
const hasPnpmLock = await pathExists(path.join(basePath, 'pnpm-lock.yaml'));
|
const hasPnpmLock = await pathExists(path.join(basePath, "pnpm-lock.yaml"));
|
||||||
|
|
||||||
if (hasPnpmLock && !hasNpmLock && !hasYarnLock) {
|
if (hasPnpmLock && !hasNpmLock && !hasYarnLock) {
|
||||||
return 'pnpm';
|
return "pnpm";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasYarnLock && !hasNpmLock) {
|
if (hasYarnLock && !hasNpmLock) {
|
||||||
return 'yarn';
|
return "yarn";
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'npm';
|
return "npm";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installNpmDependencies(packageJsonPath: string): Promise<void> {
|
async function installNpmDependencies(packageJsonPath: string): Promise<void> {
|
||||||
|
|
|
@ -43,7 +43,7 @@ export default function registerCommand(program: Command) {
|
||||||
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
|
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
|
||||||
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "deploy");
|
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "deploy");
|
||||||
if (positionalArg && !matchLoadedConfigName(positionalArg)) {
|
if (positionalArg && !matchLoadedConfigName(positionalArg)) {
|
||||||
if (isUserOption('outputLocation')) {
|
if (isUserOption("outputLocation")) {
|
||||||
logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`);
|
logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`);
|
||||||
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,9 @@ export async function deploy(options: SWACLIConfig) {
|
||||||
const apiFolder = await findApiFolderInPath(appLocation);
|
const apiFolder = await findApiFolderInPath(appLocation);
|
||||||
if (apiFolder) {
|
if (apiFolder) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`An API folder was found at ".${path.sep + path.basename(apiFolder)}" but the --api-location option was not provided. The API will not be deployed.\n`
|
`An API folder was found at ".${
|
||||||
|
path.sep + path.basename(apiFolder)
|
||||||
|
}" but the --api-location option was not provided. The API will not be deployed.\n`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,5 +358,5 @@ export async function deploy(options: SWACLIConfig) {
|
||||||
|
|
||||||
async function findApiFolderInPath(appPath: string): Promise<string | undefined> {
|
async function findApiFolderInPath(appPath: string): Promise<string | undefined> {
|
||||||
const entries = await fs.promises.readdir(appPath, { withFileTypes: true });
|
const entries = await fs.promises.readdir(appPath, { withFileTypes: true });
|
||||||
return entries.find(entry => entry.name.toLowerCase() === 'api' && entry.isDirectory())?.name;
|
return entries.find((entry) => entry.name.toLowerCase() === "api" && entry.isDirectory())?.name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default function registerCommand(program: Command) {
|
||||||
.action(async (configName: string | undefined, _options: SWACLIConfig, command: Command) => {
|
.action(async (configName: string | undefined, _options: SWACLIConfig, command: Command) => {
|
||||||
const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init", false);
|
const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init", false);
|
||||||
if (configName) {
|
if (configName) {
|
||||||
if (isUserOption('configName')) {
|
if (isUserOption("configName")) {
|
||||||
logger.error(`swa init <configName> cannot be used when --config-name option is also set.`);
|
logger.error(`swa init <configName> cannot be used when --config-name option is also set.`);
|
||||||
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
||||||
}
|
}
|
||||||
|
@ -71,19 +71,19 @@ export async function init(options: SWACLIConfig, showHints: boolean = true) {
|
||||||
name: "app",
|
name: "app",
|
||||||
message: "Which app folder do you want to use?",
|
message: "Which app folder do you want to use?",
|
||||||
choices: detectedFolders.app.map((folder) => ({ title: folder.rootPath, value: folder })),
|
choices: detectedFolders.app.map((folder) => ({ title: folder.rootPath, value: folder })),
|
||||||
initial: 0
|
initial: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Workaround for bug https://github.com/terkelg/prompts/issues/205
|
// Workaround for bug https://github.com/terkelg/prompts/issues/205
|
||||||
app = typeof response.app === 'number' ? detectedFolders.app[response.app] : response.app
|
app = typeof response.app === "number" ? detectedFolders.app[response.app] : response.app;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we can find api folders under selected app folder, and filter selection if we found some
|
// Check if we can find api folders under selected app folder, and filter selection if we found some
|
||||||
if (app !== undefined) {
|
if (app !== undefined) {
|
||||||
const childApiFolders = detectedFolders.api.filter(folder => isDescendantPath(folder.rootPath, app!.rootPath));
|
const childApiFolders = detectedFolders.api.filter((folder) => isDescendantPath(folder.rootPath, app!.rootPath));
|
||||||
if (childApiFolders.length > 0) {
|
if (childApiFolders.length > 0) {
|
||||||
logger.silly(`Found (${childApiFolders.length}) api folders under the app folder`);
|
logger.silly(`Found (${childApiFolders.length}) api folders under the app folder`);
|
||||||
logger.silly(`- ${childApiFolders.map(f => `${f.rootPath} (${f.frameworks.map(fr => fr.name).join(', ')})`).join("\n- ")}`);
|
logger.silly(`- ${childApiFolders.map((f) => `${f.rootPath} (${f.frameworks.map((fr) => fr.name).join(", ")})`).join("\n- ")}`);
|
||||||
detectedFolders.api = childApiFolders;
|
detectedFolders.api = childApiFolders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,11 +96,11 @@ export async function init(options: SWACLIConfig, showHints: boolean = true) {
|
||||||
name: "api",
|
name: "api",
|
||||||
message: "Which api folder do you want to use?",
|
message: "Which api folder do you want to use?",
|
||||||
choices: detectedFolders.api.map((folder) => ({ title: folder.rootPath, value: folder })),
|
choices: detectedFolders.api.map((folder) => ({ title: folder.rootPath, value: folder })),
|
||||||
initial: 0
|
initial: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Workaround for bug https://github.com/terkelg/prompts/issues/205
|
// Workaround for bug https://github.com/terkelg/prompts/issues/205
|
||||||
api = typeof response.api === 'number' ? detectedFolders.api[response.api] : response.api
|
api = typeof response.api === "number" ? detectedFolders.api[response.api] : response.api;
|
||||||
} else {
|
} else {
|
||||||
api = detectedFolders.api[0];
|
api = detectedFolders.api[0];
|
||||||
}
|
}
|
||||||
|
@ -117,10 +117,10 @@ export async function init(options: SWACLIConfig, showHints: boolean = true) {
|
||||||
printFrameworkConfig(projectConfig);
|
printFrameworkConfig(projectConfig);
|
||||||
|
|
||||||
const { confirmSettings } = await promptOrUseDefault(disablePrompts, {
|
const { confirmSettings } = await promptOrUseDefault(disablePrompts, {
|
||||||
type: 'confirm',
|
type: "confirm",
|
||||||
name: 'confirmSettings',
|
name: "confirmSettings",
|
||||||
message: 'Are these settings correct?',
|
message: "Are these settings correct?",
|
||||||
initial: true
|
initial: true,
|
||||||
});
|
});
|
||||||
if (!confirmSettings) {
|
if (!confirmSettings) {
|
||||||
// Ask for each settings
|
// Ask for each settings
|
||||||
|
@ -231,15 +231,15 @@ async function promptConfigSettings(disablePrompts: boolean, detectedConfig: Fra
|
||||||
}
|
}
|
||||||
|
|
||||||
function printFrameworkConfig(config: FrameworkConfig) {
|
function printFrameworkConfig(config: FrameworkConfig) {
|
||||||
logger.log(chalk.bold('\nDetected configuration for your app:'));
|
logger.log(chalk.bold("\nDetected configuration for your app:"));
|
||||||
logger.log(`- Framework(s): ${chalk.green(config.name ?? 'none')}`);
|
logger.log(`- Framework(s): ${chalk.green(config.name ?? "none")}`);
|
||||||
logger.log(`- App location: ${chalk.green(config.appLocation)}`);
|
logger.log(`- App location: ${chalk.green(config.appLocation)}`);
|
||||||
logger.log(`- Output location: ${chalk.green(config.outputLocation)}`);
|
logger.log(`- Output location: ${chalk.green(config.outputLocation)}`);
|
||||||
logger.log(`- API location: ${chalk.green(config.apiLocation ?? '')}`);
|
logger.log(`- API location: ${chalk.green(config.apiLocation ?? "")}`);
|
||||||
logger.log(`- App build command: ${chalk.green(config.appBuildCommand ?? '')}`);
|
logger.log(`- App build command: ${chalk.green(config.appBuildCommand ?? "")}`);
|
||||||
logger.log(`- API build command: ${chalk.green(config.apiBuildCommand ?? '')}`);
|
logger.log(`- API build command: ${chalk.green(config.apiBuildCommand ?? "")}`);
|
||||||
logger.log(`- Dev command: ${chalk.green(config.devServerCommand ?? '')}`);
|
logger.log(`- Dev command: ${chalk.green(config.devServerCommand ?? "")}`);
|
||||||
logger.log(`- Dev server URL: ${chalk.green(config.devServerUrl ?? '')}\n`);
|
logger.log(`- Dev server URL: ${chalk.green(config.devServerUrl ?? "")}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function isEmptyFolder(path: string) {
|
// function isEmptyFolder(path: string) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import concurrently, { CloseEvent } from "concurrently";
|
||||||
import { CommandInfo } from "concurrently/dist/src/command";
|
import { CommandInfo } from "concurrently/dist/src/command";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from "child_process";
|
||||||
import { DEFAULT_CONFIG } from "../../config";
|
import { DEFAULT_CONFIG } from "../../config";
|
||||||
import {
|
import {
|
||||||
askNewPort,
|
askNewPort,
|
||||||
|
@ -41,7 +41,7 @@ export default function registerCommand(program: Command) {
|
||||||
.option<number>("--api-port <apiPort>", "the API server port passed to `func start`", parsePort, DEFAULT_CONFIG.apiPort)
|
.option<number>("--api-port <apiPort>", "the API server port passed to `func start`", parsePort, DEFAULT_CONFIG.apiPort)
|
||||||
.option("--host <host>", "the host address to use for the CLI dev server", DEFAULT_CONFIG.host)
|
.option("--host <host>", "the host address to use for the CLI dev server", DEFAULT_CONFIG.host)
|
||||||
.option<number>("--port <port>", "the port value to use for the CLI dev server", parsePort, DEFAULT_CONFIG.port)
|
.option<number>("--port <port>", "the port value to use for the CLI dev server", parsePort, DEFAULT_CONFIG.port)
|
||||||
.option("--run-build", "run \"swa build\" before starting the emulator", false)
|
.option("--run-build", 'run "swa build" before starting the emulator', false)
|
||||||
|
|
||||||
.option("--ssl", "serve the front-end application and API over HTTPS", DEFAULT_CONFIG.ssl)
|
.option("--ssl", "serve the front-end application and API over HTTPS", DEFAULT_CONFIG.ssl)
|
||||||
.option("--ssl-cert <sslCertLocation>", "the SSL certificate (.crt) to use when enabling HTTPS", DEFAULT_CONFIG.sslCert)
|
.option("--ssl-cert <sslCertLocation>", "the SSL certificate (.crt) to use when enabling HTTPS", DEFAULT_CONFIG.sslCert)
|
||||||
|
@ -66,13 +66,13 @@ export default function registerCommand(program: Command) {
|
||||||
// If it's not the config name, it's either output location or dev server url
|
// If it's not the config name, it's either output location or dev server url
|
||||||
const isUrl = isHttpUrl(positionalArg);
|
const isUrl = isHttpUrl(positionalArg);
|
||||||
if (isUrl) {
|
if (isUrl) {
|
||||||
if (isUserOption('devServerUrl')) {
|
if (isUserOption("devServerUrl")) {
|
||||||
logger.error(`swa deploy <devServerUrl> cannot be used when --dev-server-url option is also set.`);
|
logger.error(`swa deploy <devServerUrl> cannot be used when --dev-server-url option is also set.`);
|
||||||
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
||||||
}
|
}
|
||||||
options.devServerUrl = positionalArg;
|
options.devServerUrl = positionalArg;
|
||||||
} else {
|
} else {
|
||||||
if (isUserOption('outputLocation')) {
|
if (isUserOption("outputLocation")) {
|
||||||
logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`);
|
logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`);
|
||||||
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ export async function start(options: SWACLIConfig) {
|
||||||
let resolvedPortNumber = await isAcceptingTcpConnections({ host, port });
|
let resolvedPortNumber = await isAcceptingTcpConnections({ host, port });
|
||||||
if (resolvedPortNumber === 0) {
|
if (resolvedPortNumber === 0) {
|
||||||
logger.warn(`Port ${port} is already taken!`);
|
logger.warn(`Port ${port} is already taken!`);
|
||||||
resolvedPortNumber = await askNewPort()
|
resolvedPortNumber = await askNewPort();
|
||||||
} else {
|
} else {
|
||||||
logger.silly(`Port ${port} is available. Use it.`);
|
logger.silly(`Port ${port} is available. Use it.`);
|
||||||
}
|
}
|
||||||
|
@ -347,17 +347,15 @@ export async function start(options: SWACLIConfig) {
|
||||||
if (startupCommand) {
|
if (startupCommand) {
|
||||||
let startupPath = userWorkflowConfig?.appLocation;
|
let startupPath = userWorkflowConfig?.appLocation;
|
||||||
|
|
||||||
concurrentlyCommands.push(
|
concurrentlyCommands.push({ command: `cd "${startupPath}" && ${startupCommand}`, name: "run", env, prefixColor: "gray.dim" });
|
||||||
{ command: `cd "${startupPath}" && ${startupCommand}`, name: "run", env, prefixColor: "gray.dim" }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runBuild) {
|
if (runBuild) {
|
||||||
// run swa build
|
// run swa build
|
||||||
execSync("swa build", {
|
execSync("swa build", {
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
// Set CI to avoid extra NPM logs and potentially unwanted interactive modes
|
// Set CI to avoid extra NPM logs and potentially unwanted interactive modes
|
||||||
env: { ...process.env, CI: "1" }
|
env: { ...process.env, CI: "1" },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,12 +51,15 @@ export async function run(argv?: string[]) {
|
||||||
const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init");
|
const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init");
|
||||||
swaMagic(options);
|
swaMagic(options);
|
||||||
})
|
})
|
||||||
.addHelpText("after", `
|
.addHelpText(
|
||||||
|
"after",
|
||||||
|
`
|
||||||
Type "swa" to get started and deploy your project.
|
Type "swa" to get started and deploy your project.
|
||||||
|
|
||||||
Documentation:
|
Documentation:
|
||||||
https://aka.ms/swa/cli-local-development
|
https://aka.ms/swa/cli-local-development
|
||||||
`);
|
`
|
||||||
|
);
|
||||||
|
|
||||||
// Register commands
|
// Register commands
|
||||||
registerLogin(program);
|
registerLogin(program);
|
||||||
|
@ -74,7 +77,7 @@ export async function swaMagic(_options: SWACLIConfig) {
|
||||||
try {
|
try {
|
||||||
const hasLoadedConfig = getCurrentSwaCliConfigFromFile();
|
const hasLoadedConfig = getCurrentSwaCliConfigFromFile();
|
||||||
if (!hasLoadedConfig) {
|
if (!hasLoadedConfig) {
|
||||||
runCommand("swa init")
|
runCommand("swa init");
|
||||||
}
|
}
|
||||||
runCommand("swa build");
|
runCommand("swa build");
|
||||||
|
|
||||||
|
@ -82,7 +85,7 @@ export async function swaMagic(_options: SWACLIConfig) {
|
||||||
type: "confirm",
|
type: "confirm",
|
||||||
name: "deploy",
|
name: "deploy",
|
||||||
message: "Do you want to deploy your app now?",
|
message: "Do you want to deploy your app now?",
|
||||||
initial: true
|
initial: true,
|
||||||
});
|
});
|
||||||
if (!response.deploy) {
|
if (!response.deploy) {
|
||||||
logger.log(`\nWhen you'll be ready to deploy your app, just use ${chalk.cyan("swa")} again.`);
|
logger.log(`\nWhen you'll be ready to deploy your app, just use ${chalk.cyan("swa")} again.`);
|
||||||
|
|
|
@ -26,7 +26,7 @@ const {
|
||||||
SWA_CLI_APP_NAME,
|
SWA_CLI_APP_NAME,
|
||||||
SWA_CLI_DEPLOY_DRY_RUN,
|
SWA_CLI_DEPLOY_DRY_RUN,
|
||||||
SWA_CLI_DEPLOY_ENV,
|
SWA_CLI_DEPLOY_ENV,
|
||||||
SWA_CLI_LOGIN_CLEAR_CREDENTIALS
|
SWA_CLI_LOGIN_CLEAR_CREDENTIALS,
|
||||||
} = swaCLIEnv();
|
} = swaCLIEnv();
|
||||||
|
|
||||||
export const DEFAULT_CONFIG: SWACLIConfig = {
|
export const DEFAULT_CONFIG: SWACLIConfig = {
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { DEFAULT_CONFIG } from "../config";
|
||||||
import { address, isHttpUrl } from "./utils/net";
|
import { address, isHttpUrl } from "./utils/net";
|
||||||
|
|
||||||
export const STATIC_SITE_CLIENT_RELEASE_METADATA_URL = "https://swalocaldeploy.azureedge.net/downloads/versions.json";
|
export const STATIC_SITE_CLIENT_RELEASE_METADATA_URL = "https://swalocaldeploy.azureedge.net/downloads/versions.json";
|
||||||
export const SWA_COMMANDS = ['login', 'init', 'start', 'deploy', 'build'] as const;
|
export const SWA_COMMANDS = ["login", "init", "start", "deploy", "build"] as const;
|
||||||
// Type cannot be in swa.d.ts as it's inferred from SWA_COMMANDS
|
// Type cannot be in swa.d.ts as it's inferred from SWA_COMMANDS
|
||||||
export type SWACommand = (typeof SWA_COMMANDS)[number];
|
export type SWACommand = typeof SWA_COMMANDS[number];
|
||||||
|
|
||||||
export const SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB = 20; // 20kb
|
export const SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB = 20; // 20kb
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,9 @@ export function getPlatform(): "win-x64" | "osx-x64" | "linux-x64" | null {
|
||||||
export async function fetchClientVersionDefinition(releaseVersion: string): Promise<StaticSiteClientReleaseMetadata | undefined> {
|
export async function fetchClientVersionDefinition(releaseVersion: string): Promise<StaticSiteClientReleaseMetadata | undefined> {
|
||||||
logger.silly(`Fetching release metadata for version: ${releaseVersion}. Please wait...`);
|
logger.silly(`Fetching release metadata for version: ${releaseVersion}. Please wait...`);
|
||||||
|
|
||||||
const remoteVersionDefinitions = await fetch(STATIC_SITE_CLIENT_RELEASE_METADATA_URL).then((res) => res.json()) as StaticSiteClientReleaseMetadata[];
|
const remoteVersionDefinitions = (await fetch(STATIC_SITE_CLIENT_RELEASE_METADATA_URL).then((res) =>
|
||||||
|
res.json()
|
||||||
|
)) as StaticSiteClientReleaseMetadata[];
|
||||||
if (Array.isArray(remoteVersionDefinitions) && remoteVersionDefinitions.length) {
|
if (Array.isArray(remoteVersionDefinitions) && remoteVersionDefinitions.length) {
|
||||||
const releaseMetadata = remoteVersionDefinitions.find((versionDefinition) => versionDefinition?.version === releaseVersion);
|
const releaseMetadata = remoteVersionDefinitions.find((versionDefinition) => versionDefinition?.version === releaseVersion);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ const packageJsonFile = "package.json";
|
||||||
export async function generateConfiguration(app?: DetectedFolder, api?: DetectedFolder): Promise<FrameworkConfig> {
|
export async function generateConfiguration(app?: DetectedFolder, api?: DetectedFolder): Promise<FrameworkConfig> {
|
||||||
let config: FrameworkConfig = {
|
let config: FrameworkConfig = {
|
||||||
appLocation: DEFAULT_CONFIG.appLocation!,
|
appLocation: DEFAULT_CONFIG.appLocation!,
|
||||||
outputLocation: DEFAULT_CONFIG.outputLocation!
|
outputLocation: DEFAULT_CONFIG.outputLocation!,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!app && !api) {
|
if (!app && !api) {
|
||||||
|
@ -18,11 +18,11 @@ export async function generateConfiguration(app?: DetectedFolder, api?: Detected
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = '';
|
let name = "";
|
||||||
|
|
||||||
if (app) {
|
if (app) {
|
||||||
name += `${app.frameworks.map(f => f.name).join(', ')}`;
|
name += `${app.frameworks.map((f) => f.name).join(", ")}`;
|
||||||
app.frameworks.forEach(f => config = { ...config, ...f.config });
|
app.frameworks.forEach((f) => (config = { ...config, ...f.config }));
|
||||||
config.appLocation = await computePath(app.rootPath, config.appLocation);
|
config.appLocation = await computePath(app.rootPath, config.appLocation);
|
||||||
config.appLocation = removeTrailingPathSep(config.appLocation);
|
config.appLocation = removeTrailingPathSep(config.appLocation);
|
||||||
config.outputLocation = await computePath(config.appLocation, config.outputLocation);
|
config.outputLocation = await computePath(config.appLocation, config.outputLocation);
|
||||||
|
@ -31,9 +31,9 @@ export async function generateConfiguration(app?: DetectedFolder, api?: Detected
|
||||||
}
|
}
|
||||||
|
|
||||||
if (api) {
|
if (api) {
|
||||||
name += (name ? ', with ' : 'No app frameworks detected, ');
|
name += name ? ", with " : "No app frameworks detected, ";
|
||||||
name += `API: ${api.frameworks.map(f => f.name).join(', ')}`;
|
name += `API: ${api.frameworks.map((f) => f.name).join(", ")}`;
|
||||||
api.frameworks.forEach(f => config = { ...config, ...f.config });
|
api.frameworks.forEach((f) => (config = { ...config, ...f.config }));
|
||||||
|
|
||||||
const computedApiLocation = await computePath(api.rootPath, config.apiLocation);
|
const computedApiLocation = await computePath(api.rootPath, config.apiLocation);
|
||||||
if (computedApiLocation !== api.rootPath) {
|
if (computedApiLocation !== api.rootPath) {
|
||||||
|
@ -71,7 +71,7 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
|
||||||
return basePath;
|
return basePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!additionalPath.startsWith('{')) {
|
if (!additionalPath.startsWith("{")) {
|
||||||
return path.join(basePath, additionalPath);
|
return path.join(basePath, additionalPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,8 +93,7 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
|
||||||
throw new Error(`Invalid JSON file: ${file}`);
|
throw new Error(`Invalid JSON file: ${file}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const evaluateExpression = (json: JsonData, expr: string) =>
|
const evaluateExpression = (json: JsonData, expr: string) => Function(`"use strict";return data => (${expr})`)()(json);
|
||||||
Function(`"use strict";return data => (${expr})`)()(json);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = evaluateExpression(json, expression);
|
const result = evaluateExpression(json, expression);
|
||||||
|
@ -110,7 +109,7 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
|
||||||
return basePath;
|
return basePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function detectProjectFolders(projectPath: string = '.'): Promise<DetectionResult> {
|
export async function detectProjectFolders(projectPath: string = "."): Promise<DetectionResult> {
|
||||||
const projectFiles = await getFiles(projectPath);
|
const projectFiles = await getFiles(projectPath);
|
||||||
const apiFrameworks = await detectApiFrameworks(projectFiles);
|
const apiFrameworks = await detectApiFrameworks(projectFiles);
|
||||||
const appFrameworks = await detectAppFrameworks(projectFiles);
|
const appFrameworks = await detectAppFrameworks(projectFiles);
|
||||||
|
@ -152,15 +151,9 @@ export async function detectFrameworks(projectFiles: string[], frameworks: Frame
|
||||||
// Parent files are implicit for child frameworks
|
// Parent files are implicit for child frameworks
|
||||||
if (framework.parent) {
|
if (framework.parent) {
|
||||||
const parent = frameworksById[framework.parent];
|
const parent = frameworksById[framework.parent];
|
||||||
files.push(
|
files.push(...(parent.files ?? []), ...(parent.packages ? [packageJsonFile] : []));
|
||||||
...(parent.files ?? []),
|
|
||||||
...(parent.packages ? [packageJsonFile] : [])
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
files.push(
|
files.push(...(framework.files ?? []), ...(framework.packages ? [packageJsonFile] : []));
|
||||||
...(framework.files ?? []),
|
|
||||||
...(framework.packages ? [packageJsonFile] : [])
|
|
||||||
);
|
|
||||||
|
|
||||||
const rootPaths = await findRootPathsForFiles(files, projectFiles);
|
const rootPaths = await findRootPathsForFiles(files, projectFiles);
|
||||||
if (rootPaths !== undefined) {
|
if (rootPaths !== undefined) {
|
||||||
|
@ -170,7 +163,7 @@ export async function detectFrameworks(projectFiles: string[], frameworks: Frame
|
||||||
|
|
||||||
detectedFrameworks = await asyncFilter(
|
detectedFrameworks = await asyncFilter(
|
||||||
detectedFrameworks,
|
detectedFrameworks,
|
||||||
async framework => await matchPackages(framework) && await matchContains(framework, projectFiles)
|
async (framework) => (await matchPackages(framework)) && (await matchContains(framework, projectFiles))
|
||||||
);
|
);
|
||||||
let detectedFolders = await aggregateFolders(detectedFrameworks);
|
let detectedFolders = await aggregateFolders(detectedFrameworks);
|
||||||
detectedFolders = filterDescendantFolders(detectedFolders);
|
detectedFolders = filterDescendantFolders(detectedFolders);
|
||||||
|
@ -182,14 +175,14 @@ export async function detectFrameworks(projectFiles: string[], frameworks: Frame
|
||||||
|
|
||||||
async function detectApiFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> {
|
async function detectApiFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> {
|
||||||
const detectedApiFolders: DetectedFolder[] = await detectFrameworks(projectFiles, apiFrameworks);
|
const detectedApiFolders: DetectedFolder[] = await detectFrameworks(projectFiles, apiFrameworks);
|
||||||
logger.silly(formatDetectedFolders(detectedApiFolders, 'api'));
|
logger.silly(formatDetectedFolders(detectedApiFolders, "api"));
|
||||||
|
|
||||||
return detectedApiFolders;
|
return detectedApiFolders;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function detectAppFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> {
|
async function detectAppFrameworks(projectFiles: string[]): Promise<DetectedFolder[]> {
|
||||||
const detectedAppFolders: DetectedFolder[] = await detectFrameworks(projectFiles, appFrameworks);
|
const detectedAppFolders: DetectedFolder[] = await detectFrameworks(projectFiles, appFrameworks);
|
||||||
logger.silly(formatDetectedFolders(detectedAppFolders, 'app'));
|
logger.silly(formatDetectedFolders(detectedAppFolders, "app"));
|
||||||
|
|
||||||
return detectedAppFolders;
|
return detectedAppFolders;
|
||||||
}
|
}
|
||||||
|
@ -212,7 +205,7 @@ async function matchPackages(framework: DetectedFramework): Promise<boolean> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootPathsMatches: string[] | undefined = await asyncFilter(framework.rootPaths, async rootPath => {
|
const rootPathsMatches: string[] | undefined = await asyncFilter(framework.rootPaths, async (rootPath) => {
|
||||||
const packageJsonPath = path.join(rootPath, packageJsonFile);
|
const packageJsonPath = path.join(rootPath, packageJsonFile);
|
||||||
const packageJson = await safeReadJson(packageJsonPath);
|
const packageJson = await safeReadJson(packageJsonPath);
|
||||||
if (!packageJson) {
|
if (!packageJson) {
|
||||||
|
@ -222,10 +215,7 @@ async function matchPackages(framework: DetectedFramework): Promise<boolean> {
|
||||||
const dependencies = Object.keys(packageJson.dependencies ?? {});
|
const dependencies = Object.keys(packageJson.dependencies ?? {});
|
||||||
const devDependencies = Object.keys(packageJson.devDependencies ?? {});
|
const devDependencies = Object.keys(packageJson.devDependencies ?? {});
|
||||||
|
|
||||||
return framework.packages!.some(packageName =>
|
return framework.packages!.some((packageName) => dependencies.includes(packageName) || devDependencies.includes(packageName));
|
||||||
dependencies.includes(packageName) ||
|
|
||||||
devDependencies.includes(packageName)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
framework.rootPaths = rootPathsMatches;
|
framework.rootPaths = rootPathsMatches;
|
||||||
|
@ -237,7 +227,7 @@ async function matchContains(framework: DetectedFramework, files: string[]): Pro
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootPathsMatches: string[] | undefined = await asyncFilter(framework.rootPaths, async rootPath => {
|
const rootPathsMatches: string[] | undefined = await asyncFilter(framework.rootPaths, async (rootPath) => {
|
||||||
const currentFiles = filesFromRootPath(rootPath, files);
|
const currentFiles = filesFromRootPath(rootPath, files);
|
||||||
return asyncEvery(Object.entries(framework.contains!), async ([filename, stringToFind]) => {
|
return asyncEvery(Object.entries(framework.contains!), async ([filename, stringToFind]) => {
|
||||||
const file = findFile(filename, currentFiles);
|
const file = findFile(filename, currentFiles);
|
||||||
|
@ -262,8 +252,8 @@ function filterDescendantFolders(folders: DetectedFolder[]): DetectedFolder[] {
|
||||||
// Find all folders that are descendants of other folders
|
// Find all folders that are descendants of other folders
|
||||||
const descendantPaths: Set<string> = new Set();
|
const descendantPaths: Set<string> = new Set();
|
||||||
for (const folder of folders) {
|
for (const folder of folders) {
|
||||||
const descendantsFolders = folders.filter(f => isDescendantPath(f.rootPath, folder.rootPath));
|
const descendantsFolders = folders.filter((f) => isDescendantPath(f.rootPath, folder.rootPath));
|
||||||
descendantsFolders.forEach(f => descendantPaths.add(f.rootPath));
|
descendantsFolders.forEach((f) => descendantPaths.add(f.rootPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descendantPaths.size === 0) {
|
if (descendantPaths.size === 0) {
|
||||||
|
@ -274,15 +264,15 @@ function filterDescendantFolders(folders: DetectedFolder[]): DetectedFolder[] {
|
||||||
logger.silly(`- ${Array.from(descendantPaths).join("\n- ")}`);
|
logger.silly(`- ${Array.from(descendantPaths).join("\n- ")}`);
|
||||||
|
|
||||||
// Only keep folders that are not descendants
|
// Only keep folders that are not descendants
|
||||||
return folders.filter(f => !descendantPaths.has(f.rootPath));
|
return folders.filter((f) => !descendantPaths.has(f.rootPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterPreemptedFrameworks(detectedFolders: DetectedFolder[]): void {
|
function filterPreemptedFrameworks(detectedFolders: DetectedFolder[]): void {
|
||||||
for (const folder of detectedFolders) {
|
for (const folder of detectedFolders) {
|
||||||
const overridenFrameworkIds: Set<string> = new Set();
|
const overridenFrameworkIds: Set<string> = new Set();
|
||||||
folder.frameworks.forEach(f => {
|
folder.frameworks.forEach((f) => {
|
||||||
if (f.overrides) {
|
if (f.overrides) {
|
||||||
f.overrides.forEach(id => overridenFrameworkIds.add(id));
|
f.overrides.forEach((id) => overridenFrameworkIds.add(id));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -298,7 +288,7 @@ function filterPreemptedFrameworks(detectedFolders: DetectedFolder[]): void {
|
||||||
|
|
||||||
logger.silly(`Found frameworks to override in path ${folder.rootPath}: ${Array.from(overridenFrameworkIds).join(",")}`);
|
logger.silly(`Found frameworks to override in path ${folder.rootPath}: ${Array.from(overridenFrameworkIds).join(",")}`);
|
||||||
|
|
||||||
folder.frameworks = folder.frameworks.filter(f => !overridenFrameworkIds.has(f.id));
|
folder.frameworks = folder.frameworks.filter((f) => !overridenFrameworkIds.has(f.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +302,7 @@ function orderFrameworksByParent(detectedFolders: DetectedFolder[]): void {
|
||||||
for (const framework of frameworks) {
|
for (const framework of frameworks) {
|
||||||
if (framework.parent && frameworkIndexById[framework.parent] === undefined) {
|
if (framework.parent && frameworkIndexById[framework.parent] === undefined) {
|
||||||
// Whoops, parent must be placed before this framework!
|
// Whoops, parent must be placed before this framework!
|
||||||
const parentIndex = frameworks.findIndex(f => f.id === framework.parent);
|
const parentIndex = frameworks.findIndex((f) => f.id === framework.parent);
|
||||||
if (parentIndex === -1) {
|
if (parentIndex === -1) {
|
||||||
// Lonely childs should not be a thing
|
// Lonely childs should not be a thing
|
||||||
logger.silly(`Framework ${framework.id} has parent ${framework.parent} but it's not detected`);
|
logger.silly(`Framework ${framework.id} has parent ${framework.parent} but it's not detected`);
|
||||||
|
@ -330,7 +320,7 @@ function orderFrameworksByParent(detectedFolders: DetectedFolder[]): void {
|
||||||
|
|
||||||
function findAllFiles(fileglob: string, files: string[]): string[] {
|
function findAllFiles(fileglob: string, files: string[]): string[] {
|
||||||
const { regex } = globrex(`?(*/)${fileglob}`, { extended: true, flags: "i" } as globrex.Options);
|
const { regex } = globrex(`?(*/)${fileglob}`, { extended: true, flags: "i" } as globrex.Options);
|
||||||
return files.filter(file => regex.test(file));
|
return files.filter((file) => regex.test(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
function findFile(fileglob: string, files: string[]): string | undefined {
|
function findFile(fileglob: string, files: string[]): string | undefined {
|
||||||
|
@ -338,34 +328,34 @@ function findFile(fileglob: string, files: string[]): string | undefined {
|
||||||
}
|
}
|
||||||
|
|
||||||
function filesFromRootPath(rootPath: string, files: string[]): string[] {
|
function filesFromRootPath(rootPath: string, files: string[]): string[] {
|
||||||
return files.filter(file => file.startsWith(rootPath));
|
return files.filter((file) => file.startsWith(rootPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
function findRootPathsForFiles(fileglobs: string[], files: string[]): string[] | undefined {
|
function findRootPathsForFiles(fileglobs: string[], files: string[]): string[] | undefined {
|
||||||
const foundFiles = fileglobs.map(fileglob => findAllFiles(fileglob, files));
|
const foundFiles = fileglobs.map((fileglob) => findAllFiles(fileglob, files));
|
||||||
|
|
||||||
// Get possible root path from first glob matches
|
// Get possible root path from first glob matches
|
||||||
// Note: currently it doesn't work if globs include subfolders
|
// Note: currently it doesn't work if globs include subfolders
|
||||||
// TODO: find common path denominator based on lowest dirname ancestor to try to find common root
|
// TODO: find common path denominator based on lowest dirname ancestor to try to find common root
|
||||||
const uniqueRootPaths = new Set(foundFiles[0].map(file => path.dirname(file)));
|
const uniqueRootPaths = new Set(foundFiles[0].map((file) => path.dirname(file)));
|
||||||
const otherFoundFiles = foundFiles.slice(1);
|
const otherFoundFiles = foundFiles.slice(1);
|
||||||
const possibleRootPaths = [...uniqueRootPaths].filter(
|
const possibleRootPaths = [...uniqueRootPaths].filter((p) => otherFoundFiles.every((files) => files.some((file) => path.dirname(file) === p)));
|
||||||
p => otherFoundFiles.every(files => files.some(file => path.dirname(file) === p))
|
|
||||||
);
|
|
||||||
|
|
||||||
return possibleRootPaths.length > 0 ? possibleRootPaths : undefined;
|
return possibleRootPaths.length > 0 ? possibleRootPaths : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getFiles(rootPath: string): Promise<string[]> {
|
async function getFiles(rootPath: string): Promise<string[]> {
|
||||||
const entries = await fs.readdir(rootPath, { withFileTypes: true });
|
const entries = await fs.readdir(rootPath, { withFileTypes: true });
|
||||||
const files = await Promise.all(entries.map(async (entry): Promise<string[]> => {
|
const files = await Promise.all(
|
||||||
|
entries.map(async (entry): Promise<string[]> => {
|
||||||
// Ignore dot files and node_modules
|
// Ignore dot files and node_modules
|
||||||
if (entry.name.startsWith(".") || entry.name.includes("node_modules")) {
|
if (entry.name.startsWith(".") || entry.name.includes("node_modules")) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const entryPath = path.join(rootPath, entry.name);
|
const entryPath = path.join(rootPath, entry.name);
|
||||||
return entry.isDirectory() ? [entryPath, ...(await getFiles(entryPath))] : [entryPath];
|
return entry.isDirectory() ? [entryPath, ...(await getFiles(entryPath))] : [entryPath];
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
return files.flat();
|
return files.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,9 +372,9 @@ async function asyncEvery<T>(array: T[], predicate: (item: T) => Promise<boolean
|
||||||
export function printSupportedFrameworks(showList = false): void {
|
export function printSupportedFrameworks(showList = false): void {
|
||||||
if (showList) {
|
if (showList) {
|
||||||
logger.info(`Supported api frameworks: ${apiFrameworks.length}`);
|
logger.info(`Supported api frameworks: ${apiFrameworks.length}`);
|
||||||
logger.info(`- ${apiFrameworks.map(f => f.name).join("- \n")}`);
|
logger.info(`- ${apiFrameworks.map((f) => f.name).join("- \n")}`);
|
||||||
logger.info(`Supported app frameworks: ${apiFrameworks.length}`);
|
logger.info(`Supported app frameworks: ${apiFrameworks.length}`);
|
||||||
logger.info(`- ${appFrameworks.map(f => f.name).join("- \n")}`);
|
logger.info(`- ${appFrameworks.map((f) => f.name).join("- \n")}`);
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Supported frameworks:`);
|
logger.info(`Supported frameworks:`);
|
||||||
logger.info(`- api: ${apiFrameworks.length}`);
|
logger.info(`- api: ${apiFrameworks.length}`);
|
||||||
|
@ -393,6 +383,8 @@ export function printSupportedFrameworks(showList = false): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatDetectedFolders(folders: DetectedFolder[], type: string): string {
|
export function formatDetectedFolders(folders: DetectedFolder[], type: string): string {
|
||||||
return `Detected ${type} folders (${folders.length}):\n` +
|
return (
|
||||||
`- ${folders.map(f => `${f.rootPath} (${f.frameworks.map(fr => fr.name).join(', ')})`).join("\n- ")}`;
|
`Detected ${type} folders (${folders.length}):\n` +
|
||||||
|
`- ${folders.map((f) => `${f.rootPath} (${f.frameworks.map((fr) => fr.name).join(", ")})`).join("\n- ")}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ export const apiFrameworks: FrameworkDefinition[] = [
|
||||||
files: ["package.json", "host.json"],
|
files: ["package.json", "host.json"],
|
||||||
config: {
|
config: {
|
||||||
apiBuildCommand: "npm run build --if-present",
|
apiBuildCommand: "npm run build --if-present",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "typescript",
|
id: "typescript",
|
||||||
|
@ -24,7 +24,7 @@ export const apiFrameworks: FrameworkDefinition[] = [
|
||||||
files: ["*.?(csproj|fsproj)", "host.json"],
|
files: ["*.?(csproj|fsproj)", "host.json"],
|
||||||
config: {
|
config: {
|
||||||
apiBuildCommand: "dotnet build",
|
apiBuildCommand: "dotnet build",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "python",
|
id: "python",
|
||||||
|
@ -32,8 +32,8 @@ export const apiFrameworks: FrameworkDefinition[] = [
|
||||||
files: ["?(requirements.txt|pyproject.toml|runtime.txt|setup.py)", "host.json"],
|
files: ["?(requirements.txt|pyproject.toml|runtime.txt|setup.py)", "host.json"],
|
||||||
config: {
|
config: {
|
||||||
// Nothing to setup, but we need to know the apiLocation (rootPath)
|
// Nothing to setup, but we need to know the apiLocation (rootPath)
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const appFrameworks: FrameworkDefinition[] = [
|
export const appFrameworks: FrameworkDefinition[] = [
|
||||||
|
@ -43,7 +43,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
config: {
|
config: {
|
||||||
outputLocation: "./",
|
outputLocation: "./",
|
||||||
},
|
},
|
||||||
files: ["@(index.htm|default.htm)?(l)"]
|
files: ["@(index.htm|default.htm)?(l)"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "angular",
|
id: "angular",
|
||||||
|
@ -64,7 +64,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
packages: ["@scullyio/scully"],
|
packages: ["@scullyio/scully"],
|
||||||
config: {
|
config: {
|
||||||
// Same as base angular
|
// Same as base angular
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "react",
|
id: "react",
|
||||||
|
@ -75,7 +75,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm start",
|
devServerCommand: "npm start",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "build",
|
outputLocation: "build",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "nextjs",
|
id: "nextjs",
|
||||||
|
@ -86,8 +86,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm dev",
|
devServerCommand: "npm dev",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "./"
|
outputLocation: "./",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "gatsby",
|
id: "gatsby",
|
||||||
|
@ -99,8 +99,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm start",
|
devServerCommand: "npm start",
|
||||||
devServerUrl: "http://localhost:8000",
|
devServerUrl: "http://localhost:8000",
|
||||||
outputLocation: "public"
|
outputLocation: "public",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "docusaurus",
|
id: "docusaurus",
|
||||||
|
@ -109,7 +109,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
packages: ["@docusaurus/core"],
|
packages: ["@docusaurus/core"],
|
||||||
config: {
|
config: {
|
||||||
// Same as base react
|
// Same as base react
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "react-static",
|
id: "react-static",
|
||||||
|
@ -117,8 +117,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
parent: "react",
|
parent: "react",
|
||||||
packages: ["react-static"],
|
packages: ["react-static"],
|
||||||
config: {
|
config: {
|
||||||
outputLocation: "dist"
|
outputLocation: "dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "preact",
|
id: "preact",
|
||||||
|
@ -128,8 +128,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "build"
|
outputLocation: "build",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "vue",
|
id: "vue",
|
||||||
|
@ -140,7 +140,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm run serve",
|
devServerCommand: "npm run serve",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "dist",
|
outputLocation: "dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "vite",
|
id: "vite",
|
||||||
|
@ -150,7 +150,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
config: {
|
config: {
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "nuxtjs",
|
id: "nuxtjs",
|
||||||
|
@ -163,7 +163,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "dist",
|
outputLocation: "dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "vuepress",
|
id: "vuepress",
|
||||||
|
@ -174,8 +174,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "src/.vuepress/dist"
|
outputLocation: "src/.vuepress/dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "vitepress",
|
id: "vitepress",
|
||||||
|
@ -187,7 +187,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm run docs:dev",
|
devServerCommand: "npm run docs:dev",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "docs/.vitepress/dist",
|
outputLocation: "docs/.vitepress/dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "svelte",
|
id: "svelte",
|
||||||
|
@ -197,8 +197,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "public"
|
outputLocation: "public",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "svelte-kit",
|
id: "svelte-kit",
|
||||||
|
@ -209,8 +209,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "build"
|
outputLocation: "build",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "sapper",
|
id: "sapper",
|
||||||
|
@ -221,8 +221,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run export",
|
appBuildCommand: "npm run export",
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "__sapper__/export"
|
outputLocation: "__sapper__/export",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "riot",
|
id: "riot",
|
||||||
|
@ -232,8 +232,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm start",
|
devServerCommand: "npm start",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "dist"
|
outputLocation: "dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "stencil",
|
id: "stencil",
|
||||||
|
@ -244,8 +244,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm start",
|
devServerCommand: "npm start",
|
||||||
devServerUrl: "http://localhost:3333",
|
devServerUrl: "http://localhost:3333",
|
||||||
outputLocation: "www"
|
outputLocation: "www",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "aurelia",
|
id: "aurelia",
|
||||||
|
@ -255,8 +255,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm start",
|
devServerCommand: "npm start",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "dist"
|
outputLocation: "dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "ember",
|
id: "ember",
|
||||||
|
@ -266,8 +266,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build",
|
appBuildCommand: "npm run build",
|
||||||
devServerCommand: "npm start",
|
devServerCommand: "npm start",
|
||||||
devServerUrl: "http://localhost:4200",
|
devServerUrl: "http://localhost:4200",
|
||||||
outputLocation: "dist"
|
outputLocation: "dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "elm",
|
id: "elm",
|
||||||
|
@ -277,8 +277,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "elm make src/Main.elm --optimize",
|
appBuildCommand: "elm make src/Main.elm --optimize",
|
||||||
devServerCommand: "elm reactor",
|
devServerCommand: "elm reactor",
|
||||||
devServerUrl: "http://localhost:8000",
|
devServerUrl: "http://localhost:8000",
|
||||||
outputLocation: "./"
|
outputLocation: "./",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "polymer",
|
id: "polymer",
|
||||||
|
@ -289,9 +289,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "polymer build --preset es6-bundled",
|
appBuildCommand: "polymer build --preset es6-bundled",
|
||||||
devServerCommand: "polymer serve --open",
|
devServerCommand: "polymer serve --open",
|
||||||
devServerUrl: "http://localhost:8081",
|
devServerUrl: "http://localhost:8081",
|
||||||
outputLocation: "build/es6-bundled"
|
outputLocation: "build/es6-bundled",
|
||||||
|
},
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "lit",
|
id: "lit",
|
||||||
|
@ -301,8 +300,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "npm run build --if-present",
|
appBuildCommand: "npm run build --if-present",
|
||||||
devServerCommand: "npm start",
|
devServerCommand: "npm start",
|
||||||
devServerUrl: "http://localhost:8081",
|
devServerUrl: "http://localhost:8081",
|
||||||
outputLocation: "./"
|
outputLocation: "./",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "hugo",
|
id: "hugo",
|
||||||
|
@ -315,8 +314,8 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
appBuildCommand: "hugo -D",
|
appBuildCommand: "hugo -D",
|
||||||
devServerCommand: "hugo server -D",
|
devServerCommand: "hugo server -D",
|
||||||
devServerUrl: "http://localhost:1313",
|
devServerUrl: "http://localhost:1313",
|
||||||
outputLocation: "public"
|
outputLocation: "public",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "flutter",
|
id: "flutter",
|
||||||
|
@ -327,7 +326,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "flutter run --web-port 8080",
|
devServerCommand: "flutter run --web-port 8080",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "build/web",
|
outputLocation: "build/web",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "jekyll",
|
id: "jekyll",
|
||||||
|
@ -338,7 +337,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "bundle exec jekyll serve --livereload",
|
devServerCommand: "bundle exec jekyll serve --livereload",
|
||||||
devServerUrl: "http://localhost:4000",
|
devServerUrl: "http://localhost:4000",
|
||||||
outputLocation: "_site",
|
outputLocation: "_site",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "slate",
|
id: "slate",
|
||||||
|
@ -350,7 +349,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "./slate.sh serve",
|
devServerCommand: "./slate.sh serve",
|
||||||
devServerUrl: "http://localhost:4567",
|
devServerUrl: "http://localhost:4567",
|
||||||
outputLocation: "build",
|
outputLocation: "build",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "mkdocs",
|
id: "mkdocs",
|
||||||
|
@ -361,7 +360,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "mkdocs serve",
|
devServerCommand: "mkdocs serve",
|
||||||
devServerUrl: "http://localhost:8000",
|
devServerUrl: "http://localhost:8000",
|
||||||
outputLocation: "site",
|
outputLocation: "site",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "eleventy",
|
id: "eleventy",
|
||||||
|
@ -372,7 +371,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "eleventy --serve",
|
devServerCommand: "eleventy --serve",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "_site",
|
outputLocation: "_site",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "astro",
|
id: "astro",
|
||||||
|
@ -385,7 +384,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "_site",
|
outputLocation: "_site",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "pelican",
|
id: "pelican",
|
||||||
|
@ -396,7 +395,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "make devserver",
|
devServerCommand: "make devserver",
|
||||||
devServerUrl: "http://localhost:8000",
|
devServerUrl: "http://localhost:8000",
|
||||||
outputLocation: "output",
|
outputLocation: "output",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "hexo",
|
id: "hexo",
|
||||||
|
@ -407,21 +406,21 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm run server",
|
devServerCommand: "npm run server",
|
||||||
devServerUrl: "http://localhost:4000",
|
devServerUrl: "http://localhost:4000",
|
||||||
outputLocation: "public",
|
outputLocation: "public",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "blazor-wasm",
|
id: "blazor-wasm",
|
||||||
name: "Blazor WASM",
|
name: "Blazor WASM",
|
||||||
files: ["*.csproj", "App.razor", "wwwroot", "Program.cs"],
|
files: ["*.csproj", "App.razor", "wwwroot", "Program.cs"],
|
||||||
contains: {
|
contains: {
|
||||||
"Program.cs": "WebAssemblyHostBuilder.CreateDefault"
|
"Program.cs": "WebAssemblyHostBuilder.CreateDefault",
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
appBuildCommand: "dotnet build",
|
appBuildCommand: "dotnet build",
|
||||||
devServerCommand: "dotnet watch run",
|
devServerCommand: "dotnet watch run",
|
||||||
devServerUrl: "http://localhost:8000",
|
devServerUrl: "http://localhost:8000",
|
||||||
outputLocation: "output",
|
outputLocation: "output",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "gridsome",
|
id: "gridsome",
|
||||||
|
@ -432,7 +431,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm run develop",
|
devServerCommand: "npm run develop",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "dist",
|
outputLocation: "dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "solid",
|
id: "solid",
|
||||||
|
@ -443,7 +442,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "dist",
|
outputLocation: "dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "remix",
|
id: "remix",
|
||||||
|
@ -455,7 +454,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm run dev",
|
devServerCommand: "npm run dev",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "public/build",
|
outputLocation: "public/build",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "metalsmith",
|
id: "metalsmith",
|
||||||
|
@ -466,7 +465,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm run serve",
|
devServerCommand: "npm run serve",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "build",
|
outputLocation: "build",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "brunch",
|
id: "brunch",
|
||||||
|
@ -477,7 +476,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "npm start",
|
devServerCommand: "npm start",
|
||||||
devServerUrl: "http://localhost:3333",
|
devServerUrl: "http://localhost:3333",
|
||||||
outputLocation: "public",
|
outputLocation: "public",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "wintersmith",
|
id: "wintersmith",
|
||||||
|
@ -488,7 +487,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "wintersmith preview",
|
devServerCommand: "wintersmith preview",
|
||||||
devServerUrl: "http://localhost:8080",
|
devServerUrl: "http://localhost:8080",
|
||||||
outputLocation: "build",
|
outputLocation: "build",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "middleman",
|
id: "middleman",
|
||||||
|
@ -499,7 +498,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "bundle exec middleman server",
|
devServerCommand: "bundle exec middleman server",
|
||||||
devServerUrl: "http://localhost:4567",
|
devServerUrl: "http://localhost:4567",
|
||||||
outputLocation: "build",
|
outputLocation: "build",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "mdbook",
|
id: "mdbook",
|
||||||
|
@ -510,7 +509,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "mdbook serve",
|
devServerCommand: "mdbook serve",
|
||||||
devServerUrl: "http://localhost:3000",
|
devServerUrl: "http://localhost:3000",
|
||||||
outputLocation: "book",
|
outputLocation: "book",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "zola",
|
id: "zola",
|
||||||
|
@ -524,7 +523,7 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "zola serve",
|
devServerCommand: "zola serve",
|
||||||
devServerUrl: "http://localhost:1111",
|
devServerUrl: "http://localhost:1111",
|
||||||
outputLocation: "public",
|
outputLocation: "public",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "lektor",
|
id: "lektor",
|
||||||
|
@ -535,6 +534,6 @@ export const appFrameworks: FrameworkDefinition[] = [
|
||||||
devServerCommand: "lektor server",
|
devServerCommand: "lektor server",
|
||||||
devServerUrl: "http://localhost:5000",
|
devServerUrl: "http://localhost:5000",
|
||||||
outputLocation: "dist",
|
outputLocation: "dist",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
export * from './frameworks';
|
export * from "./frameworks";
|
||||||
export * from './detect';
|
export * from "./detect";
|
||||||
|
|
|
@ -46,10 +46,7 @@ export function matchLoadedConfigName(name: string) {
|
||||||
* @param configFilePath The path to the `swa-cli.config.json` file.
|
* @param configFilePath The path to the `swa-cli.config.json` file.
|
||||||
* @returns An object with the `{@link SWACLIOptions}` config or an empty object if the config file, or the config entry were not found.
|
* @returns An object with the `{@link SWACLIOptions}` config or an empty object if the config file, or the config entry were not found.
|
||||||
*/
|
*/
|
||||||
export async function getConfigFileOptions(
|
export async function getConfigFileOptions(configName: string | undefined, configFilePath: string): Promise<SWACLIConfig> {
|
||||||
configName: string | undefined,
|
|
||||||
configFilePath: string
|
|
||||||
): Promise<SWACLIConfig> {
|
|
||||||
logger.silly(`Getting config file options from ${configFilePath}...`);
|
logger.silly(`Getting config file options from ${configFilePath}...`);
|
||||||
|
|
||||||
configFilePath = path.resolve(configFilePath);
|
configFilePath = path.resolve(configFilePath);
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import process from 'process';
|
import process from "process";
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from "child_process";
|
||||||
|
|
||||||
export function runCommand(command: string, cwd?: string) {
|
export function runCommand(command: string, cwd?: string) {
|
||||||
execSync(command, {
|
execSync(command, {
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
cwd: cwd ?? process.cwd(),
|
cwd: cwd ?? process.cwd(),
|
||||||
// Set CI to avoid extra NPM logs and potentially unwanted interactive modes
|
// Set CI to avoid extra NPM logs and potentially unwanted interactive modes
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
// Internal flag to avoid duplicating user messages
|
// Internal flag to avoid duplicating user messages
|
||||||
SWA_CLI_INTERNAL_COMMAND: "1",
|
SWA_CLI_INTERNAL_COMMAND: "1",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,14 @@ describe("safeReadJson()", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should ignore JSON comments and parse JSON anyway", async () => {
|
it("should ignore JSON comments and parse JSON anyway", async () => {
|
||||||
mockFs({ "test.json": `{
|
mockFs({
|
||||||
|
"test.json": `{
|
||||||
// this is a /* tricky comment
|
// this is a /* tricky comment
|
||||||
"hello": "world",
|
"hello": "world",
|
||||||
/* and this should be removed too // */
|
/* and this should be removed too // */
|
||||||
"but": "not // this or /* this */ or /* this"
|
"but": "not // this or /* this */ or /* this"
|
||||||
}`});
|
}`,
|
||||||
|
});
|
||||||
const json = await safeReadJson("test.json");
|
const json = await safeReadJson("test.json");
|
||||||
expect(json && json.hello).toBe("world");
|
expect(json && json.hello).toBe("world");
|
||||||
});
|
});
|
||||||
|
@ -62,27 +64,26 @@ describe("findUpPackageJsonDir()", () => {
|
||||||
|
|
||||||
it("should return undefined if package.json is not found", async () => {
|
it("should return undefined if package.json is not found", async () => {
|
||||||
mockFs();
|
mockFs();
|
||||||
expect(await findUpPackageJsonDir('app', 'dist')).toBe(undefined);
|
expect(await findUpPackageJsonDir("app", "dist")).toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return undefined if package.json is not found in path range", async () => {
|
it("should return undefined if package.json is not found in path range", async () => {
|
||||||
mockFs({ 'package.json': '{}' });
|
mockFs({ "package.json": "{}" });
|
||||||
expect(await findUpPackageJsonDir('app', 'dist')).toBe(undefined);
|
expect(await findUpPackageJsonDir("app", "dist")).toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return base path", async () => {
|
it("should return base path", async () => {
|
||||||
mockFs({ 'app/package.json': '{}' });
|
mockFs({ "app/package.json": "{}" });
|
||||||
expect(await findUpPackageJsonDir('app/', 'dist')).toBe('app');
|
expect(await findUpPackageJsonDir("app/", "dist")).toBe("app");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return start path", async () => {
|
it("should return start path", async () => {
|
||||||
mockFs({ 'app/dist/package.json': '{}' });
|
mockFs({ "app/dist/package.json": "{}" });
|
||||||
expect(await findUpPackageJsonDir('app', 'dist/')).toBe('app/dist');
|
expect(await findUpPackageJsonDir("app", "dist/")).toBe("app/dist");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the correct path", async () => {
|
it("should return the correct path", async () => {
|
||||||
mockFs({ 'app/toto/package.json': '{}' });
|
mockFs({ "app/toto/package.json": "{}" });
|
||||||
expect(await findUpPackageJsonDir('app', 'toto/dist')).toBe('app/toto');
|
expect(await findUpPackageJsonDir("app", "toto/dist")).toBe("app/toto");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { stripJsonComments } from "./strings";
|
||||||
|
|
||||||
export async function safeReadJson(path: string): Promise<JsonData | undefined> {
|
export async function safeReadJson(path: string): Promise<JsonData | undefined> {
|
||||||
try {
|
try {
|
||||||
let contents = await fs.readFile(path, 'utf8');
|
let contents = await fs.readFile(path, "utf8");
|
||||||
contents = stripJsonComments(contents);
|
contents = stripJsonComments(contents);
|
||||||
return JSON.parse(contents) as JsonData;
|
return JSON.parse(contents) as JsonData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -20,7 +20,7 @@ export async function safeReadFile(path?: string): Promise<string | undefined> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await fs.readFile(path, 'utf8');
|
return await fs.readFile(path, "utf8");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(`Failed to read file at: ${path}`);
|
logger.warn(`Failed to read file at: ${path}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -44,19 +44,19 @@ export async function findUpPackageJsonDir(rootPath: string, startPath: string):
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
rootPath = (rootPath === '.' || rootPath === `.${path.sep}`) ? '' : rootPath;
|
rootPath = rootPath === "." || rootPath === `.${path.sep}` ? "" : rootPath;
|
||||||
startPath = path.join(rootPath, startPath);
|
startPath = path.join(rootPath, startPath);
|
||||||
const rootPathLength = rootPath.split(/[/\\]/).filter(c => c).length;
|
const rootPathLength = rootPath.split(/[/\\]/).filter((c) => c).length;
|
||||||
const find = async (components: string[]): Promise<string | undefined> => {
|
const find = async (components: string[]): Promise<string | undefined> => {
|
||||||
if (components.length === 0 || components.length < rootPathLength) {
|
if (components.length === 0 || components.length < rootPathLength) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dir = path.join(...components);
|
const dir = path.join(...components);
|
||||||
const packageFile = path.join(dir, 'package.json');
|
const packageFile = path.join(dir, "package.json");
|
||||||
return (await pathExists(packageFile)) ? dir : find(components.slice(0, -1));
|
return (await pathExists(packageFile)) ? dir : find(components.slice(0, -1));
|
||||||
};
|
};
|
||||||
|
|
||||||
const components = startPath.split(/[/\\]/).filter(c => c);
|
const components = startPath.split(/[/\\]/).filter((c) => c);
|
||||||
return find(components);
|
return find(components);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,9 @@ export async function configureOptions(
|
||||||
|
|
||||||
// Clean up subcommands overrides before merging
|
// Clean up subcommands overrides before merging
|
||||||
// to avoid confusing the user when printing options
|
// to avoid confusing the user when printing options
|
||||||
SWA_COMMANDS.forEach((command) => { delete configFileOptions[command]; });
|
SWA_COMMANDS.forEach((command) => {
|
||||||
|
delete configFileOptions[command];
|
||||||
|
});
|
||||||
configFileDefinedOptions = { ...configFileOptions, ...configFileCommandSpecificOptions };
|
configFileDefinedOptions = { ...configFileOptions, ...configFileCommandSpecificOptions };
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
|
|
@ -14,20 +14,24 @@ describe("dasherize()", () => {
|
||||||
|
|
||||||
describe("stripJsonComments()", () => {
|
describe("stripJsonComments()", () => {
|
||||||
it("should leave valid JSON untouched", () => {
|
it("should leave valid JSON untouched", () => {
|
||||||
expect(stripJsonComments(`{
|
expect(
|
||||||
|
stripJsonComments(`{
|
||||||
"hello": "world"
|
"hello": "world"
|
||||||
}`)).toBe(`{
|
}`)
|
||||||
|
).toBe(`{
|
||||||
"hello": "world"
|
"hello": "world"
|
||||||
}`);
|
}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should strip JSON comments", () => {
|
it("should strip JSON comments", () => {
|
||||||
expect(stripJsonComments(`{
|
expect(
|
||||||
|
stripJsonComments(`{
|
||||||
// this is a /* tricky comment
|
// this is a /* tricky comment
|
||||||
"hello": "world",
|
"hello": "world",
|
||||||
/* and this should be removed too // */
|
/* and this should be removed too // */
|
||||||
"but": "not // this or /* this */ or /* this"
|
"but": "not // this or /* this */ or /* this"
|
||||||
}`)).toBe(`{
|
}`)
|
||||||
|
).toBe(`{
|
||||||
|
|
||||||
"hello": "world",
|
"hello": "world",
|
||||||
|
|
||||||
|
@ -38,24 +42,24 @@ describe("stripJsonComments()", () => {
|
||||||
|
|
||||||
describe("removeTrailingPathSep()", () => {
|
describe("removeTrailingPathSep()", () => {
|
||||||
it("should leave path untouched", () => {
|
it("should leave path untouched", () => {
|
||||||
expect(removeTrailingPathSep('./dir')).toBe('./dir');
|
expect(removeTrailingPathSep("./dir")).toBe("./dir");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should remove trailing slash", () => {
|
it("should remove trailing slash", () => {
|
||||||
expect(removeTrailingPathSep('./')).toBe('.');
|
expect(removeTrailingPathSep("./")).toBe(".");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should remove trailing anti-slash", () => {
|
it("should remove trailing anti-slash", () => {
|
||||||
expect(removeTrailingPathSep('dir\\')).toBe('dir');
|
expect(removeTrailingPathSep("dir\\")).toBe("dir");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("hasSpaces()", () => {
|
describe("hasSpaces()", () => {
|
||||||
it("should return false", () => {
|
it("should return false", () => {
|
||||||
expect(hasSpaces('./dir')).toBe(false);
|
expect(hasSpaces("./dir")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return true", () => {
|
it("should return true", () => {
|
||||||
expect(hasSpaces('c:\\my documents\\')).toBe(true);
|
expect(hasSpaces("c:\\my documents\\")).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,12 +14,12 @@ export function stripJsonComments(json: string) {
|
||||||
// in-between slash-star comments including newlines in a non-greedy way
|
// in-between slash-star comments including newlines in a non-greedy way
|
||||||
// Only the captured group is removed, ignoring comments inside strings as a result
|
// Only the captured group is removed, ignoring comments inside strings as a result
|
||||||
/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/gm,
|
/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/gm,
|
||||||
(match, group) => group ? "" : match
|
(match, group) => (group ? "" : match)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeTrailingPathSep(filePath: string): string {
|
export function removeTrailingPathSep(filePath: string): string {
|
||||||
return filePath.replace(/[\\/]+$/, '');
|
return filePath.replace(/[\\/]+$/, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hasSpaces = (str: string): boolean => str.indexOf(' ') !== -1;
|
export const hasSpaces = (str: string): boolean => str.indexOf(" ") !== -1;
|
||||||
|
|
|
@ -64,9 +64,7 @@ export async function findSWAConfigFile(folder: string): Promise<{ filepath: str
|
||||||
const fileSize = (await stat(file.filepath)).size;
|
const fileSize = (await stat(file.filepath)).size;
|
||||||
const fileSizeInKb = Math.round(fileSize / 1024);
|
const fileSizeInKb = Math.round(fileSize / 1024);
|
||||||
if (fileSizeInKb > SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB) {
|
if (fileSizeInKb > SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB) {
|
||||||
logger.warn(
|
logger.warn(`WARNING: ${SWA_CONFIG_FILENAME} is ${fileSizeInKb} bytes. The maximum size is ${SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB} bytes.`);
|
||||||
`WARNING: ${SWA_CONFIG_FILENAME} is ${fileSizeInKb} bytes. The maximum size is ${SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB} bytes.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.silly(`Content parsed successfully`);
|
logger.silly(`Content parsed successfully`);
|
||||||
|
|
|
@ -95,7 +95,7 @@ export function isFunctionRequest(req: http.IncomingMessage, rewritePath?: strin
|
||||||
export async function validateFunctionTriggers(url: string) {
|
export async function validateFunctionTriggers(url: string) {
|
||||||
try {
|
try {
|
||||||
const functionsResponse = await fetch(`${url}/admin/functions`);
|
const functionsResponse = await fetch(`${url}/admin/functions`);
|
||||||
const functions = await functionsResponse.json() as Array<{config: {bindings: string[]}}>;
|
const functions = (await functionsResponse.json()) as Array<{ config: { bindings: string[] } }>;
|
||||||
const triggers = functions.map((f: any) => f.config.bindings.find((b: any) => /trigger$/i.test(b.type))).map((b: any) => b.type);
|
const triggers = functions.map((f: any) => f.config.bindings.find((b: any) => /trigger$/i.test(b.type))).map((b: any) => b.type);
|
||||||
|
|
||||||
if (triggers.some((t: string) => !/^httptrigger$/i.test(t))) {
|
if (triggers.some((t: string) => !/^httptrigger$/i.test(t))) {
|
||||||
|
|
|
@ -290,4 +290,4 @@ declare interface CoreToolsZipInfo {
|
||||||
sha2: string;
|
sha2: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare type NpmPackageManager = 'npm' | 'yarn' | 'pnpm';
|
declare type NpmPackageManager = "npm" | "yarn" | "pnpm";
|
||||||
|
|
Загрузка…
Ссылка в новой задаче