diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f1d69f9b83..7d82e66baf 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -5,39 +5,39 @@ on: branches: - stable +permissions: + contents: write + id-token: write + packages: write + jobs: build: name: Build, Test, and Deploy runs-on: ubuntu-latest timeout-minutes: 30 steps: - - run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc - - uses: actions/setup-node@v3 - with: - node-version: 14.x - uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Restore Dependency Cache - uses: actions/cache@v3 + - uses: actions/setup-node@v3 with: - path: ~/.npm - key: ${{ runner.OS }}-dependency-cache-${{ hashFiles('**/package.json') }} + node-version: 18 + registry-url: https://registry.npmjs.org/ + cache: npm + cache-dependency-path: '**/package.json' - run: npm install - run: npm run bootstrap - - run: npm run publish:ci -- --yes + - run: npm run publish:ci env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} GIT_AUTHOR_NAME: Ionitron GIT_AUTHOR_EMAIL: hi@ionicframework.com GIT_COMMITTER_NAME: Ionitron GIT_COMMITTER_EMAIL: hi@ionicframework.com + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Sleep while npm takes its time run: sleep 20 - name: GitHub Container Registry Login - run: echo ${GH_TOKEN} | docker login ghcr.io -u ionitron --password-stdin - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} + run: echo ${{ github.token }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin - name: Build Container run: | docker build \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1489fb1c51..dc65031b87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,16 +16,15 @@ jobs: strategy: matrix: node: - - 14.x - - 12.x - - 10.x + - 18.x + - 16.x steps: - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Restore Dependency Cache - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.npm key: ${{ runner.OS }}-dependency-cache-${{ hashFiles('**/package.json') }} diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 3ee0d8533c..0901147bdd 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -7,17 +7,14 @@ jobs: name: Build and Deploy Docker Container with latest CLI runs-on: ubuntu-latest steps: - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3 with: - node-version: 14.x - - uses: actions/checkout@v2 + node-version: 16.x + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: GitHub Container Registry Login - run: echo ${GH_TOKEN} | docker login ghcr.io -u ionitron --password-stdin - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} - + run: echo ${{ github.token }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin - name: Build Container run: | docker build \ @@ -28,4 +25,4 @@ jobs: - name: Push Container as latest run: docker push ghcr.io/${{ github.repository }}:latest - name: Push Container as version - run: docker push ghcr.io/${{ github.repository }}:$(npm info @ionic/cli dist-tags.latest) \ No newline at end of file + run: docker push ghcr.io/${{ github.repository }}:$(npm info @ionic/cli dist-tags.latest) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 685855ae55..6dd9bcd487 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,8 +54,6 @@ The Ionic CLI is organized into a monorepo. Here are the packages: ##### CLIs * [`packages/@ionic/cli`](https://github.com/ionic-team/ionic-cli/tree/develop/packages/%40ionic/cli): Ionic CLI executable and library. -* [`packages/@ionic/lab`](https://github.com/ionic-team/ionic-cli/tree/develop/packages/%40ionic/lab): Utility CLI for Ionic Lab, used by `ionic serve`. -* [`packages/@ionic/v1-toolkit`](https://github.com/ionic-team/ionic-cli/tree/develop/packages/%40ionic/v1-toolkit): Utility CLI for Ionic v1 Apps, used by `ionic serve`. ##### Libraries @@ -73,8 +71,8 @@ The Ionic CLI is organized into a monorepo. Here are the packages: #### Toolset -* npm 6+ is required. -* Node 12+ is required. +* npm 8+ is required. +* Node 16+ is required. * The codebase is written in [TypeScript](https://www.typescriptlang.org/). If you're unfamiliar with TypeScript, we recommend using [VS Code](https://code.visualstudio.com/) and finding a tutorial to familiarize @@ -101,8 +99,8 @@ such as [nvm](https://github.com/creationix/nvm) and switch between environments, e.g. ```bash -nvm install v12 -nvm alias cli-local v12 +nvm install v16 +nvm alias cli-local v16 ``` You can even set up an alias in your terminal that sets `IONIC_CONFIG_DIRECTORY` diff --git a/package.json b/package.json index edeb2ca4f7..1e5a033907 100644 --- a/package.json +++ b/package.json @@ -14,13 +14,13 @@ "docs": "node packages/cli-scripts/bin/ionic-cli-scripts docs", "docs:watch": "chokidar 'packages/cli-scripts/dist/docs/**/*.js' -c 'npm run docs'", "publish:testing": "lerna publish prerelease --preid=testing --exact --no-git-tag-version --no-push --dist-tag=testing", - "publish:ci": "lerna publish -m 'chore(release): publish [skip ci]' --exact --conventional-commits" + "publish:ci": "lerna version -m 'chore(release): publish [skip ci]' --exact --conventional-commits --yes && lerna exec --no-private --since HEAD~ -- npm publish --provenance" }, "devDependencies": { "chokidar-cli": "^2.0.0", "husky": "^4.2.0", "lerna": "^3.13.3", - "typescript": "~4.0.2" + "typescript": "~4.8.0" }, "husky": { "hooks": { diff --git a/packages/@ionic/cli-framework-output/CHANGELOG.md b/packages/@ionic/cli-framework-output/CHANGELOG.md index 822ceea84b..ce325a9d25 100644 --- a/packages/@ionic/cli-framework-output/CHANGELOG.md +++ b/packages/@ionic/cli-framework-output/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.2.8](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework-output@2.2.7...@ionic/cli-framework-output@2.2.8) (2023-12-19) + +**Note:** Version bump only for package @ionic/cli-framework-output + + + + + +## [2.2.7](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework-output@2.2.6...@ionic/cli-framework-output@2.2.7) (2023-11-07) + +**Note:** Version bump only for package @ionic/cli-framework-output + + + + + +## [2.2.6](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework-output@2.2.5...@ionic/cli-framework-output@2.2.6) (2023-03-29) + +**Note:** Version bump only for package @ionic/cli-framework-output + + + + + ## [2.2.5](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework-output@2.2.4...@ionic/cli-framework-output@2.2.5) (2022-06-16) **Note:** Version bump only for package @ionic/cli-framework-output diff --git a/packages/@ionic/cli-framework-output/package.json b/packages/@ionic/cli-framework-output/package.json index 12164e9c1e..76bab15ccb 100644 --- a/packages/@ionic/cli-framework-output/package.json +++ b/packages/@ionic/cli-framework-output/package.json @@ -1,19 +1,26 @@ { "name": "@ionic/cli-framework-output", - "version": "2.2.5", + "version": "2.2.8", "description": "The log/tasks/spinners portion of Ionic CLI Framework", "homepage": "https://ionicframework.com/", "author": "Ionic Team (https://ionicframework.com)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "engines": { - "node": ">=10.3.0" + "node": ">=16.0.0" }, "files": [ "dist/", "LICENSE", "README.md" ], + "repository": { + "type": "git", + "url": "https://github.com/ionic-team/ionic-cli.git" + }, + "bugs": { + "url": "https://github.com/ionic-team/ionic-cli/issues" + }, "scripts": { "clean": "rimraf dist", "lint": "true", @@ -24,20 +31,20 @@ }, "license": "MIT", "dependencies": { - "@ionic/utils-terminal": "2.3.3", + "@ionic/utils-terminal": "2.3.5", "debug": "^4.0.0", "tslib": "^2.0.1" }, "devDependencies": { - "@ionic/utils-stream": "3.1.5", + "@ionic/utils-stream": "3.1.7", "@types/debug": "^4.1.1", "@types/inquirer": "0.0.43", "@types/jest": "^26.0.10", - "@types/node": "~10.17.13", + "@types/node": "~16.0.0", "jest": "^26.4.2", "jest-cli": "^26.0.1", "lint-staged": "^10.0.2", "ts-jest": "~26.3.0", - "typescript": "~4.0.2" + "typescript": "~4.8.0" } } diff --git a/packages/@ionic/cli-framework-prompts/CHANGELOG.md b/packages/@ionic/cli-framework-prompts/CHANGELOG.md index d80b0ff613..4a0bd1e5ba 100644 --- a/packages/@ionic/cli-framework-prompts/CHANGELOG.md +++ b/packages/@ionic/cli-framework-prompts/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.1.13](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework-prompts@2.1.12...@ionic/cli-framework-prompts@2.1.13) (2023-12-19) + + +### Bug Fixes + +* **cli:** resolve vm2 security vulnerability ([#5070](https://github.com/ionic-team/ionic-cli/issues/5070)) ([4050419](https://github.com/ionic-team/ionic-cli/commit/4050419bef70fb92e58b0a83cd4b68b48090e596)) + + + + + +## [2.1.12](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework-prompts@2.1.11...@ionic/cli-framework-prompts@2.1.12) (2023-11-07) + +**Note:** Version bump only for package @ionic/cli-framework-prompts + + + + + +## [2.1.11](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework-prompts@2.1.10...@ionic/cli-framework-prompts@2.1.11) (2023-03-29) + +**Note:** Version bump only for package @ionic/cli-framework-prompts + + + + + ## [2.1.10](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework-prompts@2.1.9...@ionic/cli-framework-prompts@2.1.10) (2022-06-16) **Note:** Version bump only for package @ionic/cli-framework-prompts diff --git a/packages/@ionic/cli-framework-prompts/package.json b/packages/@ionic/cli-framework-prompts/package.json index 7d1d57ff12..ce4aacfe5b 100644 --- a/packages/@ionic/cli-framework-prompts/package.json +++ b/packages/@ionic/cli-framework-prompts/package.json @@ -1,19 +1,26 @@ { "name": "@ionic/cli-framework-prompts", - "version": "2.1.10", + "version": "2.1.13", "description": "The interactive prompts portion of Ionic CLI Framework", "homepage": "https://ionicframework.com/", "author": "Ionic Team (https://ionicframework.com)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "engines": { - "node": ">=10.3.0" + "node": ">=16.0.0" }, "files": [ "dist/", "LICENSE", "README.md" ], + "repository": { + "type": "git", + "url": "https://github.com/ionic-team/ionic-cli.git" + }, + "bugs": { + "url": "https://github.com/ionic-team/ionic-cli/issues" + }, "scripts": { "clean": "rimraf dist", "lint": "true", @@ -24,7 +31,7 @@ }, "license": "MIT", "dependencies": { - "@ionic/utils-terminal": "2.3.3", + "@ionic/utils-terminal": "2.3.5", "debug": "^4.0.0", "inquirer": "^7.0.0", "tslib": "^2.0.1" @@ -33,11 +40,11 @@ "@types/debug": "^4.1.1", "@types/inquirer": "0.0.43", "@types/jest": "^26.0.10", - "@types/node": "~10.17.13", + "@types/node": "~16.0.0", "jest": "^26.4.2", "jest-cli": "^26.0.1", "lint-staged": "^10.0.2", "ts-jest": "~26.3.0", - "typescript": "~4.0.2" + "typescript": "~4.8.0" } } diff --git a/packages/@ionic/cli-framework-prompts/src/index.ts b/packages/@ionic/cli-framework-prompts/src/index.ts index 153f031e00..a46722f693 100644 --- a/packages/@ionic/cli-framework-prompts/src/index.ts +++ b/packages/@ionic/cli-framework-prompts/src/index.ts @@ -1,5 +1,5 @@ import { TERMINAL_INFO } from '@ionic/utils-terminal'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; const debug = Debug('ionic:cli-framework-prompts'); diff --git a/packages/@ionic/cli-framework/CHANGELOG.md b/packages/@ionic/cli-framework/CHANGELOG.md index e21ee88571..26ddfa9909 100644 --- a/packages/@ionic/cli-framework/CHANGELOG.md +++ b/packages/@ionic/cli-framework/CHANGELOG.md @@ -3,6 +3,71 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [6.0.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework@6.0.0...@ionic/cli-framework@6.0.1) (2023-12-19) + + +### Bug Fixes + +* **cli:** resolve vm2 security vulnerability ([#5070](https://github.com/ionic-team/ionic-cli/issues/5070)) ([4050419](https://github.com/ionic-team/ionic-cli/commit/4050419bef70fb92e58b0a83cd4b68b48090e596)) + + + + + +# [6.0.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework@5.1.7...@ionic/cli-framework@6.0.0) (2023-11-08) + + +### Bug Fixes + +* use native ES2022 error cause ([#5010](https://github.com/ionic-team/ionic-cli/issues/5010)) ([a97ba2b](https://github.com/ionic-team/ionic-cli/commit/a97ba2bcac4556017ba010692f71fed2bef3f77b)) + + +### BREAKING CHANGES + +* `message`, `stack`, and `error` properties removed from `BaseError` and `SubprocessError` + + + + + +## [5.1.7](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework@5.1.6...@ionic/cli-framework@5.1.7) (2023-11-08) + + +### Reverts + +* use native ES2022 error cause ([#5060](https://github.com/ionic-team/ionic-cli/issues/5060)) ([1e64a1a](https://github.com/ionic-team/ionic-cli/commit/1e64a1ada60545adf8e7c99fbd1f8766cf2416f9)) + + + + + +## [5.1.6](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework@5.1.5...@ionic/cli-framework@5.1.6) (2023-11-07) + +**Note:** Version bump only for package @ionic/cli-framework + + + + + +## [5.1.5](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework@5.1.4...@ionic/cli-framework@5.1.5) (2023-11-07) + + +### Bug Fixes + +* use native ES2022 error cause ([#5010](https://github.com/ionic-team/ionic-cli/issues/5010)) ([0c4cd0f](https://github.com/ionic-team/ionic-cli/commit/0c4cd0f47e00b43e8c0ce4eef072351a846b566c)) + + + + + +## [5.1.4](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework@5.1.3...@ionic/cli-framework@5.1.4) (2023-03-29) + +**Note:** Version bump only for package @ionic/cli-framework + + + + + ## [5.1.3](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli-framework@5.1.2...@ionic/cli-framework@5.1.3) (2022-06-16) **Note:** Version bump only for package @ionic/cli-framework diff --git a/packages/@ionic/cli-framework/package.json b/packages/@ionic/cli-framework/package.json index 34a12671ed..d3b7b99dfe 100644 --- a/packages/@ionic/cli-framework/package.json +++ b/packages/@ionic/cli-framework/package.json @@ -1,13 +1,20 @@ { "name": "@ionic/cli-framework", - "version": "5.1.3", + "version": "6.0.1", "description": "The foundation framework of the Ionic CLI", "homepage": "https://ionicframework.com/", "author": "Ionic Team (https://ionicframework.com) ", "main": "./index.js", "types": "./index.d.ts", "engines": { - "node": ">=10.3.0" + "node": ">=16.0.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/ionic-team/ionic-cli.git" + }, + "bugs": { + "url": "https://github.com/ionic-team/ionic-cli/issues" }, "scripts": { "clean": "rimraf index.* definitions.* errors.* guards.* lib utils", @@ -19,14 +26,14 @@ }, "license": "MIT", "dependencies": { - "@ionic/cli-framework-output": "2.2.5", - "@ionic/utils-array": "2.1.5", - "@ionic/utils-fs": "3.1.6", - "@ionic/utils-object": "2.1.5", - "@ionic/utils-process": "2.1.10", - "@ionic/utils-stream": "3.1.5", - "@ionic/utils-subprocess": "2.1.11", - "@ionic/utils-terminal": "2.3.3", + "@ionic/cli-framework-output": "2.2.8", + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-object": "2.1.6", + "@ionic/utils-process": "2.1.12", + "@ionic/utils-stream": "3.1.7", + "@ionic/utils-subprocess": "3.0.1", + "@ionic/utils-terminal": "2.3.5", "chalk": "^4.0.0", "debug": "^4.0.0", "lodash": "^4.17.5", @@ -40,12 +47,12 @@ "@types/jest": "^26.0.10", "@types/lodash": "^4.14.104", "@types/minimist": "^1.2.0", - "@types/node": "~10.17.13", + "@types/node": "~16.0.0", "@types/write-file-atomic": "^3.0.0", "jest": "^26.4.2", "jest-cli": "^26.0.1", "lint-staged": "^10.0.2", "ts-jest": "~26.3.0", - "typescript": "~4.0.2" + "typescript": "~4.8.0" } } diff --git a/packages/@ionic/cli-framework/src/errors.ts b/packages/@ionic/cli-framework/src/errors.ts index 375f3b0b12..cef93d4469 100644 --- a/packages/@ionic/cli-framework/src/errors.ts +++ b/packages/@ionic/cli-framework/src/errors.ts @@ -1,4 +1,3 @@ -import * as lodash from 'lodash'; import * as util from 'util'; import { ValidationError } from './definitions'; @@ -10,25 +9,11 @@ export const ERROR_IPC_UNKNOWN_PROCEDURE = 'ERR_ICF_IPC_UNKNOWN_PROCEDURE'; export abstract class BaseError extends Error { abstract readonly name: string; - message: string; - stack: string; code?: string; - error?: Error; exitCode?: number; - constructor(message: string) { - super(message); - this.message = message; - this.stack = (new Error()).stack || ''; - } - toString(): string { - const repr = lodash.pick(this, lodash.pull(lodash.keys(this), 'error')); - - return ( - `${this.name}: ${this.message} ${util.inspect(repr, { breakLength: Infinity })} ${this.stack} ` + - `${this.error ? `\nWrapped: ${this.error.stack ? this.error.stack : this.error}` : ''}` - ); + return util.inspect(this); } inspect(): string { diff --git a/packages/@ionic/cli-framework/src/lib/__tests__/options.ts b/packages/@ionic/cli-framework/src/lib/__tests__/options.ts index 363e062dc9..7cfdfd797b 100644 --- a/packages/@ionic/cli-framework/src/lib/__tests__/options.ts +++ b/packages/@ionic/cli-framework/src/lib/__tests__/options.ts @@ -1,4 +1,4 @@ -import * as minimist from 'minimist'; +import minimist from 'minimist'; import { CommandMetadata } from '../../definitions'; import { OptionFilters, filterCommandLineOptions, filterCommandLineOptionsByGroup, metadataOptionsToParseArgsOptions, separateArgv, stripOptions, unparseArgs } from '../options'; diff --git a/packages/@ionic/cli-framework/src/lib/colors.ts b/packages/@ionic/cli-framework/src/lib/colors.ts index 6201cde2c7..cee61e2ad9 100644 --- a/packages/@ionic/cli-framework/src/lib/colors.ts +++ b/packages/@ionic/cli-framework/src/lib/colors.ts @@ -1,5 +1,5 @@ import { ColorFunction, Colors as BaseColors } from '@ionic/cli-framework-output'; -import * as chalk from 'chalk'; +import chalk from 'chalk'; import * as lodash from 'lodash'; import { MetadataGroup } from '../definitions'; diff --git a/packages/@ionic/cli-framework/src/lib/command.ts b/packages/@ionic/cli-framework/src/lib/command.ts index dd48c5ee8f..4f8dc2e113 100644 --- a/packages/@ionic/cli-framework/src/lib/command.ts +++ b/packages/@ionic/cli-framework/src/lib/command.ts @@ -29,7 +29,7 @@ export abstract class BaseCommand, N extends I if (input.validators && input.validators.length > 0) { try { validate(argv[i], input.name, [...input.validators]); - } catch (e) { + } catch (e: any) { if (!(e instanceof InputValidationError)) { throw e; } diff --git a/packages/@ionic/cli-framework/src/lib/config.ts b/packages/@ionic/cli-framework/src/lib/config.ts index e8a79824db..14023e16c2 100644 --- a/packages/@ionic/cli-framework/src/lib/config.ts +++ b/packages/@ionic/cli-framework/src/lib/config.ts @@ -32,7 +32,7 @@ export abstract class BaseConfig { get file() { try { return this._getFile(); - } catch (e) { + } catch (e: any) { return {}; } } @@ -44,7 +44,7 @@ export abstract class BaseConfig { const config = typeof navigated === 'object' ? navigated : {}; return lodash.assign({}, this.provideDefaults(config), config); - } catch (e) { + } catch (e: any) { if (e.code === 'ENOENT' || e.name === 'SyntaxError') { const value = this.provideDefaults({}); const v = this.pathPrefix.length === 0 ? value : lodash.set({}, [...this.pathPrefix], value); diff --git a/packages/@ionic/cli-framework/src/lib/executor.ts b/packages/@ionic/cli-framework/src/lib/executor.ts index 5022ef52d0..7537e048c8 100644 --- a/packages/@ionic/cli-framework/src/lib/executor.ts +++ b/packages/@ionic/cli-framework/src/lib/executor.ts @@ -121,7 +121,7 @@ export class BaseExecutor, N extends INamespac try { await command.validate(cmdinputs); - } catch (e) { + } catch (e: any) { if (e instanceof InputValidationError) { for (const err of e.errors) { this.stderr.write(`${err.message}\n`); @@ -185,7 +185,7 @@ export async function execute, N extends IName try { await executor.execute(argv, env); - } catch (e) { + } catch (e: any) { if (e instanceof BaseError) { executor.stderr.write(`Error: ${e.message}`); process.exitCode = typeof e.exitCode === 'undefined' ? 1 : e.exitCode; diff --git a/packages/@ionic/cli-framework/src/lib/help.ts b/packages/@ionic/cli-framework/src/lib/help.ts index 992abfde42..b6c88559d7 100644 --- a/packages/@ionic/cli-framework/src/lib/help.ts +++ b/packages/@ionic/cli-framework/src/lib/help.ts @@ -1,6 +1,6 @@ import { filter, map } from '@ionic/utils-array'; import { generateFillSpaceStringList, stringWidth, wordWrap } from '@ionic/utils-terminal'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import { CommandMetadata, CommandMetadataInput, CommandMetadataOption, Footnote, HydratedCommandMetadata, HydratedNamespaceMetadata, ICommand, INamespace, LinkFootnote, MetadataGroup, NamespaceLocateResult, NamespaceMetadata } from '../definitions'; @@ -223,7 +223,7 @@ export class NamespaceStringHelpFormatter, N e const filteredCommands = await filter(commands, async cmd => this.filterCommandCallback(cmd)); - const [ cmdDetails, nsDetails ] = await Promise.all([ + const [cmdDetails, nsDetails] = await Promise.all([ this.getListOfCommandDetails(filteredCommands.filter(cmd => cmd.namespace === this.namespace)), this.getListOfNamespaceDetails(filteredCommands.filter(cmd => cmd.namespace !== this.namespace)), ]); diff --git a/packages/@ionic/cli-framework/src/lib/options.ts b/packages/@ionic/cli-framework/src/lib/options.ts index b622b27c9e..dc5994737a 100644 --- a/packages/@ionic/cli-framework/src/lib/options.ts +++ b/packages/@ionic/cli-framework/src/lib/options.ts @@ -1,5 +1,5 @@ import * as lodash from 'lodash'; -import * as minimist from 'minimist'; +import minimist from 'minimist'; import { CommandLineOptions, CommandMetadataOption, HydratedParseArgsOptions, ParsedArg } from '../definitions'; @@ -19,7 +19,7 @@ export { ParsedArgs } from 'minimist'; */ export function stripOptions(pargv: readonly string[], { includeSeparated = true }: { includeSeparated?: boolean; }): string[] { const r = /^\-/; - const [ ownArgs, otherArgs ] = separateArgv(pargv); + const [ownArgs, otherArgs] = separateArgv(pargv); const filteredArgs = ownArgs.filter(arg => !r.test(arg)); if (!includeSeparated) { @@ -50,7 +50,7 @@ export function separateArgv(pargv: readonly string[]): [string[], string[]] { otherArgs.shift(); // strip separator } - return [ ownArgs, otherArgs ]; + return [ownArgs, otherArgs]; } /** @@ -166,8 +166,8 @@ export function filterCommandLineOptions(option const pairs = Object.keys(parsedArgs) .map((k): [string, O | undefined, ParsedArg | undefined] => [k, mapped.get(k), parsedArgs[k]]) - .filter(([ k, opt, value ]) => opt && predicate(opt, value)) - .map(([ k, opt, value ]) => [opt ? opt.name : k, value]); + .filter(([k, opt, value]) => opt && predicate(opt, value)) + .map(([k, opt, value]) => [opt ? opt.name : k, value]); return { ...initial, ...lodash.fromPairs(pairs) }; } @@ -221,7 +221,7 @@ export function unparseArgs(parsedArgs: minimist.ParsedArgs, { useDoubleQuotes, const dashKey = (k: string) => (k.length === 1 ? '-' : '--') + k; const pushPairs = (...pairs: [string, string | undefined][]) => { - for (const [ k, val ] of pairs) { + for (const [k, val] of pairs) { const key = dashKey(allowCamelCase ? k : k.replace(/[A-Z]/g, '-$&').toLowerCase()); if (useEquals) { @@ -274,7 +274,7 @@ export function unparseArgs(parsedArgs: minimist.ParsedArgs, { useDoubleQuotes, isKnown(k) ); - for (const [ key, val ] of pairedOptions) { + for (const [key, val] of pairedOptions) { if (val === true) { pushPairs([key, undefined]); } else if (val === false && !ignoreFalse) { diff --git a/packages/@ionic/cli-framework/src/lib/validators.ts b/packages/@ionic/cli-framework/src/lib/validators.ts index 17a0c0c142..a22ee49d81 100644 --- a/packages/@ionic/cli-framework/src/lib/validators.ts +++ b/packages/@ionic/cli-framework/src/lib/validators.ts @@ -1,4 +1,4 @@ -import * as chalk from 'chalk'; +import chalk from 'chalk'; import { ValidationError, Validator, Validators } from '../definitions'; import { InputValidationError } from '../errors'; diff --git a/packages/@ionic/cli-framework/src/utils/ipc.ts b/packages/@ionic/cli-framework/src/utils/ipc.ts index a67b22cc07..97102babc6 100644 --- a/packages/@ionic/cli-framework/src/utils/ipc.ts +++ b/packages/@ionic/cli-framework/src/utils/ipc.ts @@ -1,6 +1,6 @@ import { fork } from '@ionic/utils-subprocess'; import { ChildProcess } from 'child_process'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as fs from 'fs'; import { ERROR_IPC_UNKNOWN_PROCEDURE, IPCError } from '../errors'; @@ -64,7 +64,7 @@ export class RPCProcess { if (fn) { try { data = await fn(msg.args); - } catch (e) { + } catch (e: any) { err = e; } } else { @@ -162,7 +162,7 @@ export class RPCHost { start(): void { try { fs.accessSync(this.modulePath, fs.constants.R_OK); - } catch (e) { + } catch (e: any) { debug('Error during access check: %O', e); throw new IPCError(`Module not accessible: ${this.modulePath}`); } diff --git a/packages/@ionic/cli/.gitignore b/packages/@ionic/cli/.gitignore index 988c2bd32f..d2e88252f6 100644 --- a/packages/@ionic/cli/.gitignore +++ b/packages/@ionic/cli/.gitignore @@ -3,3 +3,4 @@ !jest.config.js !lint-staged.config.js *.d.ts +*.tgz diff --git a/packages/@ionic/cli/CHANGELOG.md b/packages/@ionic/cli/CHANGELOG.md index e5c0461956..26f61eb5f2 100644 --- a/packages/@ionic/cli/CHANGELOG.md +++ b/packages/@ionic/cli/CHANGELOG.md @@ -3,6 +3,197 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [7.2.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.2.0...@ionic/cli@7.2.1) (2025-03-18) + + +### Bug Fixes + +* **angular:** change default project type to Standalone ([#5104](https://github.com/ionic-team/ionic-cli/issues/5104)) ([cea5728](https://github.com/ionic-team/ionic-cli/commit/cea5728c8b228ab336a98bee5655364e720336a4)) + + + + + +# [7.2.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.1.6...@ionic/cli@7.2.0) (2024-01-02) + + +### Bug Fixes + +* live reload deploys app when warnings are present [#4807](https://github.com/ionic-team/ionic-cli/issues/4807) ([#5043](https://github.com/ionic-team/ionic-cli/issues/5043)) ([ef5706e](https://github.com/ionic-team/ionic-cli/commit/ef5706ed5e7ef347280320a88b5604a279667184)) + + +### Features + +* **angular:** support angulars vite dev server ([#5064](https://github.com/ionic-team/ionic-cli/issues/5064)) ([8a94434](https://github.com/ionic-team/ionic-cli/commit/8a94434a413c1b60b0e88904064edfe7a98206ab)) + + + + + +## [7.1.6](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.1.5...@ionic/cli@7.1.6) (2023-12-19) + + +### Bug Fixes + +* **cli:** resolve vm2 security vulnerability ([#5070](https://github.com/ionic-team/ionic-cli/issues/5070)) ([4050419](https://github.com/ionic-team/ionic-cli/commit/4050419bef70fb92e58b0a83cd4b68b48090e596)) + + + + + +## [7.1.5](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.1.4...@ionic/cli@7.1.5) (2023-11-08) + + +### Bug Fixes + +* use native ES2022 error cause ([#5010](https://github.com/ionic-team/ionic-cli/issues/5010)) ([d19f28e](https://github.com/ionic-team/ionic-cli/commit/d19f28e3f41b8f0248c11c49b7006754bb315218)) + + + + + +## [7.1.4](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.1.3...@ionic/cli@7.1.4) (2023-11-08) + + +### Reverts + +* use native ES2022 error cause ([#5060](https://github.com/ionic-team/ionic-cli/issues/5060)) ([1e64a1a](https://github.com/ionic-team/ionic-cli/commit/1e64a1ada60545adf8e7c99fbd1f8766cf2416f9)) + + + + + +## [7.1.3](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.1.2...@ionic/cli@7.1.3) (2023-11-07) + +**Note:** Version bump only for package @ionic/cli + + + + + +## [7.1.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.1.1...@ionic/cli@7.1.2) (2023-11-07) + + +### Bug Fixes + +* use native ES2022 error cause ([#5010](https://github.com/ionic-team/ionic-cli/issues/5010)) ([0c4cd0f](https://github.com/ionic-team/ionic-cli/commit/0c4cd0f47e00b43e8c0ce4eef072351a846b566c)) + + + + + +## [7.1.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.1.0...@ionic/cli@7.1.1) (2023-05-02) + +**Note:** Version bump only for package @ionic/cli + + + + + +# [7.1.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.0.1...@ionic/cli@7.1.0) (2023-05-01) + + +### Features + +* add standalone as an option ([#5005](https://github.com/ionic-team/ionic-cli/issues/5005)) ([707a42d](https://github.com/ionic-team/ionic-cli/commit/707a42d29579a355d9a57b65c07ec6a0b681028e)) + + + + + +## [7.0.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@7.0.0...@ionic/cli@7.0.1) (2023-04-05) + + +### Bug Fixes + +* state should not be required on OAuth Response ([#4995](https://github.com/ionic-team/ionic-cli/issues/4995)) ([c053cb3](https://github.com/ionic-team/ionic-cli/commit/c053cb3394acec5402d2ea436796775d67c98d39)) +* **cli:** ionic info returns package information ([#4991](https://github.com/ionic-team/ionic-cli/issues/4991)) ([2dd9136](https://github.com/ionic-team/ionic-cli/commit/2dd9136bb85375b03883105d04d1c1bb090d884d)), closes [#4992](https://github.com/ionic-team/ionic-cli/issues/4992) + + + + + +# [7.0.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@6.20.9...@ionic/cli@7.0.0) (2023-03-29) + + +### Bug Fixes + +* update test to reflect new output ([#4967](https://github.com/ionic-team/ionic-cli/issues/4967)) ([5c23518](https://github.com/ionic-team/ionic-cli/commit/5c23518b1a7bda4f020a82ced5557e157ccdbbc7)) + + +### chore + +* **appflow:** remove appflow from @ionic/cli ([#4777](https://github.com/ionic-team/ionic-cli/issues/4777)) ([34b07ad](https://github.com/ionic-team/ionic-cli/commit/34b07ad4b53130057ecc5a736036a87d582744c2)) + + +### Code Refactoring + +* **docs:** remove ionic docs command ([#4969](https://github.com/ionic-team/ionic-cli/issues/4969)) ([e624258](https://github.com/ionic-team/ionic-cli/commit/e62425889d07b496c0e578b61413ab00a8dae8d3)) +* **doctor:** remove ionic doctor command ([#4959](https://github.com/ionic-team/ionic-cli/issues/4959)) ([13568b1](https://github.com/ionic-team/ionic-cli/commit/13568b138bb8007e91901820656e97ac121a4913)) +* **lab:** remove ionic serve --lab option ([#4960](https://github.com/ionic-team/ionic-cli/issues/4960)) ([d6512a5](https://github.com/ionic-team/ionic-cli/commit/d6512a5139943dbe2084f99e63075db6e6b15b4d)) + + +### Features + +* add vite support for react and vue ([#4966](https://github.com/ionic-team/ionic-cli/issues/4966)) ([94e9fa2](https://github.com/ionic-team/ionic-cli/commit/94e9fa2a2146883ad857d2acad71b7605e3687d0)) +* remove ionic-angular project type ([ff1c936](https://github.com/ionic-team/ionic-cli/commit/ff1c93635a74fc26e57319123118eaf3ce6d9958)) +* remove ionicv1 project type ([6ea2d48](https://github.com/ionic-team/ionic-cli/commit/6ea2d48da10d2b964ab95961cc9600874279c59d)) +* remove v3 and v1 integrations ([d40da3d](https://github.com/ionic-team/ionic-cli/commit/d40da3d07da909f82807fe7182c035c69d20a65c)) +* use vite specific projects for starters ([#4970](https://github.com/ionic-team/ionic-cli/issues/4970)) ([f35ea72](https://github.com/ionic-team/ionic-cli/commit/f35ea7239d5b23f819084691480f1c791377d55f)) + + +### BREAKING CHANGES + +* **appflow:** The `package` commands and the `deploy build` command have been removed. Developers should migrate to the Ionic Cloud CLI instead. +- Using `deploy build`? Migrate to ionic-cloud build web and if you used the -channel option, also use ionic-cloud deploy web. +- Using `package` commands? Migrate to ionic-cloud build and ionic-cloud deploy. + +The remaining `deploy` commands have been renamed to `live-update`. + +| deploy command | live-update command | +| - | - | +| `ionic deploy add` | `ionic live-update add` | +| `ionic deploy configure` | `ionic live-update configure` | +| `ionic deploy manifest` | `ionic live-update manifest` | + +See https://ionic.io/docs/appflow/cli/overview for more information on the Ionic Cloud CLI. +* **docs:** The `ionic docs` command has been removed. See https://github.com/ionic-team/ionic-cli/pull/4905 for an alternative. +* **doctor:** The `ionic doctor` command has been removed. See https://github.com/ionic-team/ionic-cli/pull/4907 for alternatives. +* **lab:** The `--lab` option has been removed from `ionic serve`. See https://ionicframework.com/docs/developing/previewing for alternatives. +* This commit removes integrations with V1 and V3 Ionic +project types +* This commit removes the Ionic V1 project type from the +available project integrations. Please migrate to a modern Ionic +project. +* Integration with V3 (aka ionic-angular) project has +been removed. Please update to modern Ionic projects. + + + + + +## [6.20.9](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@6.20.8...@ionic/cli@6.20.9) (2023-03-17) + + +### Bug Fixes + +* **cli:** native solution preview should no longer be hidden ([#4962](https://github.com/ionic-team/ionic-cli/issues/4962)) ([a2eb4b2](https://github.com/ionic-team/ionic-cli/commit/a2eb4b2cf46e0f2004c1850acfdeada46fb211b5)) + + + + + +## [6.20.9](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@6.20.8...@ionic/cli@6.20.9) (2023-03-17) + + +### Bug Fixes + +* **cli:** native solution preview should no longer be hidden ([#4962](https://github.com/ionic-team/ionic-cli/issues/4962)) ([a2eb4b2](https://github.com/ionic-team/ionic-cli/commit/a2eb4b2cf46e0f2004c1850acfdeada46fb211b5)) + + + + + ## [6.20.8](https://github.com/ionic-team/ionic-cli/compare/@ionic/cli@6.20.7...@ionic/cli@6.20.8) (2023-01-13) **Note:** Version bump only for package @ionic/cli diff --git a/packages/@ionic/cli/assets/oauth/success/index.html b/packages/@ionic/cli/assets/oauth/success/index.html index 99c5732207..a945060dd4 100644 --- a/packages/@ionic/cli/assets/oauth/success/index.html +++ b/packages/@ionic/cli/assets/oauth/success/index.html @@ -87,13 +87,13 @@ font-weight: 400; } - .ad-box{ + .native-solutions{ display:block; text-align:center; padding-top:30px; } - .ad-box>ion-card{ + .native-solutions>ion-card{ width:300px; display:inline-block; } @@ -125,7 +125,7 @@

Your app is now building in your t

There was an error logging in. Please return to the terminal.

-
+
diff --git a/packages/@ionic/cli/bin/ionic b/packages/@ionic/cli/bin/ionic index 28305f5a08..281732e50c 100755 --- a/packages/@ionic/cli/bin/ionic +++ b/packages/@ionic/cli/bin/ionic @@ -22,13 +22,15 @@ process.on('message', function(msg) { var semver = require('semver'); var version = semver.parse(process.version); -var minversion = 'v10.3.0'; +var minversion = 'v16.0.0'; if (semver.lt(version, minversion)) { - var details = version.major === 6 - ? ' Node.js 6 reached end-of-life on 2019-04-30 and is no longer supported.' - : version.major === 8 - ? ' Node.js 8 reached end-of-life on 2019-12-31 and is no longer supported.' + var details = version.major === 10 + ? ' Node.js 10 reached end-of-life on 2021-04-30 and is no longer supported.' + : version.major === 12 + ? ' Node.js 12 reached end-of-life on 2022-04-30 and is no longer supported.' + : version.major === 14 + ? ' Node.js 14 reached end-of-life on 2023-04-30 and is no longer supported.' : ''; process.stderr.write('ERR: Your Node.js version is ' + version.raw + '.' + details + ' Please update to the latest Node LTS version.\n'); diff --git a/packages/@ionic/cli/package.json b/packages/@ionic/cli/package.json index 935a21687e..d6fa27f6e8 100644 --- a/packages/@ionic/cli/package.json +++ b/packages/@ionic/cli/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/cli", - "version": "6.20.8", + "version": "7.2.1", "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "https://ionicframework.com", "author": "Ionic Team (https://ionicframework.com) ", @@ -8,7 +8,7 @@ "ionic": "./bin/ionic" }, "engines": { - "node": ">=10.3.0" + "node": ">=16.0.0" }, "main": "./index.js", "types": "./index.d.ts", @@ -40,16 +40,16 @@ }, "license": "MIT", "dependencies": { - "@ionic/cli-framework": "5.1.3", - "@ionic/cli-framework-output": "2.2.5", - "@ionic/cli-framework-prompts": "2.1.10", - "@ionic/utils-array": "2.1.5", - "@ionic/utils-fs": "3.1.6", - "@ionic/utils-network": "2.1.5", - "@ionic/utils-process": "2.1.10", - "@ionic/utils-stream": "3.1.5", - "@ionic/utils-subprocess": "2.1.11", - "@ionic/utils-terminal": "2.3.3", + "@ionic/cli-framework": "6.0.1", + "@ionic/cli-framework-output": "2.2.8", + "@ionic/cli-framework-prompts": "2.1.13", + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-network": "2.1.7", + "@ionic/utils-process": "2.1.12", + "@ionic/utils-stream": "3.1.7", + "@ionic/utils-subprocess": "3.0.1", + "@ionic/utils-terminal": "2.3.5", "chalk": "^4.0.0", "debug": "^4.0.0", "diff": "^4.0.1", @@ -58,12 +58,12 @@ "lodash": "^4.17.5", "open": "^7.0.4", "os-name": "^4.0.0", + "proxy-agent": "^6.3.0", "semver": "^7.1.1", "split2": "^3.0.0", "ssh-config": "^1.1.1", "stream-combiner2": "^1.1.1", - "superagent": "^5.2.1", - "superagent-proxy": "^3.0.0", + "superagent": "^9.0.2", "tar": "^6.0.1", "tslib": "^2.0.1" }, @@ -73,11 +73,10 @@ "@types/elementtree": "^0.1.0", "@types/jest": "^26.0.10", "@types/lodash": "^4.14.104", - "@types/node": "~10.17.13", + "@types/node": "~16.0.0", "@types/semver": "^7.1.0", "@types/split2": "^2.1.6", "@types/superagent": "4.1.3", - "@types/superagent-proxy": "^3.0.0", "@types/tar": "^6.1.2", "jest": "^26.4.2", "jest-cli": "^26.0.1", @@ -85,6 +84,6 @@ "rimraf": "^3.0.0", "source-map": "^0.7.0", "ts-jest": "~26.3.0", - "typescript": "~4.0.2" + "typescript": "~4.8.0" } } diff --git a/packages/@ionic/cli/src/__tests__/guards.ts b/packages/@ionic/cli/src/__tests__/guards.ts index 055e58e2af..fef080a3ad 100644 --- a/packages/@ionic/cli/src/__tests__/guards.ts +++ b/packages/@ionic/cli/src/__tests__/guards.ts @@ -38,7 +38,6 @@ const OBJECT_GUARDS: Guard[] = [ guards.isSSHKeyResponse, guards.isSecurityProfile, guards.isSecurityProfileResponse, - guards.isTreatableAilment, guards.isIntegrationName, guards.isProjectConfig, guards.isMultiProjectConfig, diff --git a/packages/@ionic/cli/src/bootstrap.ts b/packages/@ionic/cli/src/bootstrap.ts index 2949b2d6e9..63ce9277d8 100644 --- a/packages/@ionic/cli/src/bootstrap.ts +++ b/packages/@ionic/cli/src/bootstrap.ts @@ -1,5 +1,5 @@ import { compileNodeModulesPaths, readPackageJsonFile } from '@ionic/cli-framework/utils/node'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as path from 'path'; import * as semver from 'semver'; @@ -16,7 +16,7 @@ export async function detectLocalCLI(): Promise { try { pkgPath = require.resolve('ionic/package', { paths: compileNodeModulesPaths(process.cwd()) }); - } catch (e) { + } catch (e: any) { // ignore } diff --git a/packages/@ionic/cli/src/commands/build.ts b/packages/@ionic/cli/src/commands/build.ts index 33593cc65b..58ae675261 100644 --- a/packages/@ionic/cli/src/commands/build.ts +++ b/packages/@ionic/cli/src/commands/build.ts @@ -60,7 +60,7 @@ export class BuildCommand extends Command implements CommandPreRun { const runnerOpts = runner.createOptionsFromCommandLine(inputs, options); await runner.run(runnerOpts); - } catch (e) { + } catch (e: any) { if (e instanceof RunnerException) { throw new FatalException(e.message); } diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index a2b837db6e..1698cc1e4d 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -121,7 +121,7 @@ export abstract class CapacitorCommand extends Command { } return version; - } catch (e) { + } catch (e: any) { if (e instanceof SubprocessError) { if (e.code === ERROR_COMMAND_NOT_FOUND) { throw new FatalException('Error while getting Capacitor CLI version. Is Capacitor installed?'); @@ -213,7 +213,7 @@ export abstract class CapacitorCommand extends Command { const runner = await this.project.requireBuildRunner(); const runnerOpts = runner.createOptionsFromCommandLine(inputs, generateOptionsForCapacitorBuild(inputs, options)); await runner.run(runnerOpts); - } catch (e) { + } catch (e: any) { if (e instanceof RunnerException) { throw new FatalException(e.message); } @@ -270,7 +270,7 @@ export abstract class CapacitorCommand extends Command { appInfo.disableAppTransportSecurity(); await appInfo.save(); } - } catch (e) { + } catch (e: any) { if (e instanceof RunnerException) { throw new FatalException(e.message); } diff --git a/packages/@ionic/cli/src/commands/capacitor/build.ts b/packages/@ionic/cli/src/commands/capacitor/build.ts index e36837adbf..7d8634346b 100644 --- a/packages/@ionic/cli/src/commands/capacitor/build.ts +++ b/packages/@ionic/cli/src/commands/capacitor/build.ts @@ -114,7 +114,7 @@ To configure your native project, see the common configuration docs[^capacitor-n try { await this.runBuild(inputs, options); - } catch (e) { + } catch (e: any) { if (e instanceof RunnerException) { throw new FatalException(e.message); } @@ -162,7 +162,7 @@ To configure your native project, see the common configuration docs[^capacitor-n build: buildRunner.createOptionsFromCommandLine(inputs, options), capacitor: await this.createOptionsFromCommandLine(inputs, options), }); - } catch (e) { + } catch (e: any) { if (e instanceof BaseError) { throw new FatalException(e.message); } diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index aa926b7d6c..0398a6e902 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -1,8 +1,8 @@ import { BaseError, Footnote, validators } from '@ionic/cli-framework'; import { sleepForever } from '@ionic/utils-process'; import { columnar } from '@ionic/utils-terminal'; -import * as chalk from 'chalk'; -import * as Debug from 'debug'; +import chalk from 'chalk'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import * as semver from 'semver'; @@ -233,7 +233,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); } - const [ platform ] = inputs; + const [platform] = inputs; const doLiveReload = !!options['livereload']; const doOpenFlow = (await this.isOldCapacitor()) || options['open'] === true; @@ -297,7 +297,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); } - const [ platform ] = inputs; + const [platform] = inputs; await this.runCapacitorRunHook('capacitor:run:before', inputs, options, { ...this.env, project: this.project }); @@ -326,7 +326,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); } - const [ platform ] = inputs; + const [platform] = inputs; await this.runCapacitorRunHook('capacitor:run:before', inputs, options, { ...this.env, project: this.project }); await this.runCapacitor(['run', platform, ...(shouldSync ? [] : ['--no-sync']), '--target', String(options['target'])]); @@ -354,7 +354,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser build: buildOptions, capacitor: await this.createOptionsFromCommandLine(inputs, options), }); - } catch (e) { + } catch (e: any) { if (e instanceof BaseError) { throw new FatalException(e.message); } diff --git a/packages/@ionic/cli/src/commands/capacitor/sync.ts b/packages/@ionic/cli/src/commands/capacitor/sync.ts index 8f78b1db5d..e04ebe6091 100644 --- a/packages/@ionic/cli/src/commands/capacitor/sync.ts +++ b/packages/@ionic/cli/src/commands/capacitor/sync.ts @@ -104,7 +104,7 @@ ${input('ionic capacitor sync')} will do the following: build: buildRunner.createOptionsFromCommandLine(inputs, options), capacitor: await this.createOptionsFromCommandLine(inputs, options), }); - } catch (e) { + } catch (e: any) { if (e instanceof BaseError) { throw new FatalException(e.message); } diff --git a/packages/@ionic/cli/src/commands/config/base.ts b/packages/@ionic/cli/src/commands/config/base.ts index a2a30a52d4..595a98062d 100644 --- a/packages/@ionic/cli/src/commands/config/base.ts +++ b/packages/@ionic/cli/src/commands/config/base.ts @@ -64,7 +64,7 @@ export abstract class BaseConfigCommand extends Command { } return serialized; - } catch (e) { + } catch (e: any) { throw new FatalException(`Cannot serialize value: ${strong(v)}`); } } @@ -80,7 +80,7 @@ export abstract class BaseConfigCommand extends Command { if (!v.match(/^\d+e\d+$/)) { v = JSON.parse(v); } - } catch (e) { + } catch (e: any) { if (e.name !== 'SyntaxError') { throw e; } diff --git a/packages/@ionic/cli/src/commands/config/get.ts b/packages/@ionic/cli/src/commands/config/get.ts index 37969695b4..6a814a757f 100644 --- a/packages/@ionic/cli/src/commands/config/get.ts +++ b/packages/@ionic/cli/src/commands/config/get.ts @@ -1,7 +1,7 @@ import { MetadataGroup } from '@ionic/cli-framework'; import { strcmp } from '@ionic/cli-framework/utils/string'; import { columnar, prettyPath } from '@ionic/utils-terminal'; -import * as chalk from 'chalk'; +import chalk from 'chalk'; import * as lodash from 'lodash'; import * as util from 'util'; diff --git a/packages/@ionic/cli/src/commands/cordova/base.ts b/packages/@ionic/cli/src/commands/cordova/base.ts index 7f3c37f8f5..08ef5b894b 100644 --- a/packages/@ionic/cli/src/commands/cordova/base.ts +++ b/packages/@ionic/cli/src/commands/cordova/base.ts @@ -160,7 +160,7 @@ export abstract class CordovaCommand extends Command { try { await this.env.shell.run('cordova', argList, { fatalOnNotFound, truncateErrorOutput, cwd: this.integration.root, ...options }); - } catch (e) { + } catch (e: any) { if (e instanceof SubprocessError) { if (e.code === ERROR_COMMAND_NOT_FOUND) { const installArgs = await pkgManagerArgs(this.env.config.get('npmClient'), { command: 'install', pkg: 'cordova', global: true }); diff --git a/packages/@ionic/cli/src/commands/cordova/build.ts b/packages/@ionic/cli/src/commands/cordova/build.ts index b133465eac..c95ea50a2e 100644 --- a/packages/@ionic/cli/src/commands/cordova/build.ts +++ b/packages/@ionic/cli/src/commands/cordova/build.ts @@ -92,7 +92,7 @@ The Cordova CLI requires a separator for platform-specific arguments for Android const runner = await this.project.requireBuildRunner(); const runnerOpts = runner.createOptionsFromCommandLine(inputs, generateOptionsForCordovaBuild(metadata, inputs, options)); await runner.run(runnerOpts); - } catch (e) { + } catch (e: any) { if (e instanceof RunnerException) { throw new FatalException(e.message); } diff --git a/packages/@ionic/cli/src/commands/cordova/prepare.ts b/packages/@ionic/cli/src/commands/cordova/prepare.ts index 6a60f5d0d1..80c4a4af78 100644 --- a/packages/@ionic/cli/src/commands/cordova/prepare.ts +++ b/packages/@ionic/cli/src/commands/cordova/prepare.ts @@ -101,7 +101,7 @@ You may wish to use ${input('ionic cordova prepare')} if you run your project wi const runner = await this.project.requireBuildRunner(); const runnerOpts = runner.createOptionsFromCommandLine(inputs, buildOptions); await runner.run(runnerOpts); - } catch (e) { + } catch (e: any) { if (e instanceof RunnerException) { throw new FatalException(e.message); } diff --git a/packages/@ionic/cli/src/commands/cordova/requirements.ts b/packages/@ionic/cli/src/commands/cordova/requirements.ts index 91aa91fa03..6ecf992d76 100644 --- a/packages/@ionic/cli/src/commands/cordova/requirements.ts +++ b/packages/@ionic/cli/src/commands/cordova/requirements.ts @@ -47,7 +47,7 @@ Like running ${input('cordova requirements')} directly, but provides friendly ch try { await this.runCordova(filterArgumentsForCordova(metadata, options), { showError: false, fatalOnError: false }); - } catch (e) { + } catch (e: any) { if (e.fatal || !isExitCodeException(e)) { throw e; } diff --git a/packages/@ionic/cli/src/commands/cordova/run.ts b/packages/@ionic/cli/src/commands/cordova/run.ts index 403bac6497..5662d59e31 100644 --- a/packages/@ionic/cli/src/commands/cordova/run.ts +++ b/packages/@ionic/cli/src/commands/cordova/run.ts @@ -1,6 +1,6 @@ import { Footnote, MetadataGroup, validators } from '@ionic/cli-framework'; import { onBeforeExit, sleepForever } from '@ionic/utils-process'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, CommandPreRun, IShellRunOptions, ServeDetails } from '../../definitions'; @@ -212,7 +212,7 @@ Just like with ${input('ionic cordova build')}, you can pass additional options inputs[0] = p.trim(); } - const [ platform ] = inputs; + const [platform] = inputs; if (platform && options['native-run'] && !SUPPORTED_PLATFORMS.includes(platform)) { this.env.log.warn(`${input(platform)} is not supported by ${input('native-run')}. Using Cordova to run the app.`); @@ -244,7 +244,7 @@ Just like with ${input('ionic cordova build')}, you can pass additional options } else { await this.runBuildDeploy(inputs, options); } - } catch (e) { + } catch (e: any) { if (e instanceof RunnerException) { throw new FatalException(e.message); } @@ -298,7 +298,7 @@ Just like with ${input('ionic cordova build')}, you can pass additional options buildOpts.stdio = options['verbose'] ? 'inherit' : ['pipe', 'ignore', 'pipe']; if (options['native-run']) { - const [ platform ] = inputs; + const [platform] = inputs; await this.runCordova(filterArgumentsForCordova({ ...metadata, name: 'build' }, options), buildOpts); @@ -325,7 +325,7 @@ Just like with ${input('ionic cordova build')}, you can pass additional options const runner = await this.project.requireBuildRunner(); const runnerOpts = runner.createOptionsFromCommandLine(inputs, generateOptionsForCordovaBuild(metadata, inputs, options)); await runner.run(runnerOpts); - } catch (e) { + } catch (e: any) { if (e instanceof RunnerException) { throw new FatalException(e.message); } @@ -336,7 +336,7 @@ Just like with ${input('ionic cordova build')}, you can pass additional options if (options['native-run']) { const conf = await loadCordovaConfig(this.integration); - const [ platform ] = inputs; + const [platform] = inputs; await this.runCordova(filterArgumentsForCordova({ ...metadata, name: 'build' }, options), { stdio: 'inherit' }); diff --git a/packages/@ionic/cli/src/commands/deploy/build.ts b/packages/@ionic/cli/src/commands/deploy/build.ts deleted file mode 100644 index 208497b41f..0000000000 --- a/packages/@ionic/cli/src/commands/deploy/build.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { CommandLineInputs, CommandLineOptions, MetadataGroup } from '@ionic/cli-framework'; -import { LOGGER_LEVELS } from '@ionic/cli-framework-output'; -import { sleep } from '@ionic/utils-process'; -import { tmpfilepath } from '@ionic/utils-fs'; -import { columnar } from '@ionic/utils-terminal'; -import * as chalk from 'chalk'; -import * as Debug from 'debug'; -import * as fs from 'fs'; - -import { CommandMetadata } from '../../definitions'; -import { isSuperAgentError } from '../../guards'; -import { input, strong, weak } from '../../lib/color'; -import { Command } from '../../lib/command'; -import { FatalException } from '../../lib/errors'; -import { createRequest, download } from '../../lib/utils/http'; -import { IONIC_CLOUD_CLI_MIGRATION } from '../../lib/updates'; - -const debug = Debug('ionic:commands:deploy:build'); - -interface DeployBuild { - artifact_name: string; - job_id: number; - id: string; - caller_id: number; - created: string; - finished: string; - state: string; - commit: any; - automation_id: number; - environment_id: number; - native_config_id: number; - automation_name: string; - environment_name: string; - job: any; - pending_channels: string[]; -} - -interface DownloadUrl { - url: string | null; -} - -export class BuildCommand extends Command { - async getMetadata(): Promise { - const dashUrl = this.env.config.getDashUrl(); - - return { - name: 'build', - type: 'project', - groups: [MetadataGroup.PAID, MetadataGroup.DEPRECATED], - summary: 'Create a deploy build on Appflow', - description: ` -This command creates a deploy build on Appflow. While the build is running, it prints the remote build log to the terminal. If the build is successful, it downloads the created web build zip file in the current directory. Downloading build artifacts can be skipped by supplying the flag ${input('skip-download')}. - -Apart from ${input('--commit')}, every option can be specified using the full name setup within the Appflow Dashboard[^dashboard]. - -Customizing the build: -- The ${input('--environment')} and ${input('--channel')} options can be used to customize the groups of values exposed to the build. -`, - footnotes: [ - { - id: 'dashboard', - url: dashUrl, - }, - ], - exampleCommands: [ - '', - '--environment="My Custom Environment Name"', - '--commit=2345cd3305a1cf94de34e93b73a932f25baac77c', - '--channel="Master"', - '--channel="Master" --skip-download', - '--channel="Master" --channel="My Custom Channel"', - ], - options: [ - { - name: 'environment', - summary: 'The group of environment variables exposed to your build', - type: String, - spec: { value: 'name' }, - }, - { - name: 'channel', - summary: 'The channel you want to auto deploy the build to. This can be repeated multiple times if multiple channels need to be specified.', - type: String, - spec: { value: 'name' }, - }, - { - name: 'commit', - summary: 'Commit (defaults to HEAD)', - type: String, - groups: [MetadataGroup.ADVANCED], - spec: { value: 'sha1' }, - }, - { - name: 'skip-download', - summary: `Skip downloading build artifacts after command succeeds.`, - type: Boolean, - spec: { value: 'name' }, - default: false, - }, - ], - }; - } - - async preRun(inputs: CommandLineInputs, optinos: CommandLineOptions): Promise { - this.env.log.warn(IONIC_CLOUD_CLI_MIGRATION); - } - - async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - if (!this.project) { - throw new FatalException(`Cannot run ${input('ionic deploy build')} outside a project directory.`); - } - - const token = await this.env.session.getUserToken(); - const appflowId = await this.project.requireAppflowId(); - - if (!options.commit) { - options.commit = (await this.env.shell.output('git', ['rev-parse', 'HEAD'], { cwd: this.project.directory })).trim(); - debug(`Commit hash: ${strong(options.commit)}`); - } - - let build = await this.createDeployBuild(appflowId, token, options); - const buildId = build.job_id; - - const details = columnar([ - ['App ID', strong(appflowId)], - ['Build ID', strong(buildId.toString())], - ['Commit', strong(`${build.commit.sha.substring(0, 6)} ${build.commit.note}`)], - ['Environment', build.environment_name ? strong(build.environment_name) : weak('not set')], - ['Channels', build.pending_channels.length ? build.pending_channels.map(v => strong(`"${v}"`)).join(', ') : weak('not set')], - ], { vsep: ':' }); - - this.env.log.ok( - `Build created\n` + - details + '\n\n' - ); - - build = await this.tailBuildLog(appflowId, buildId, token); - if (build.state !== 'success') { - throw new Error(`Build ${build.state}`); - } - - if (options['skip-download']) { - return; - } - - const url = await this.getDownloadUrl(appflowId, buildId, token); - - if (!url.url) { - throw new Error('Missing URL in response'); - } - - const filename = await this.downloadBuild(url.url, build.artifact_name); - this.env.log.ok(`Artifact downloaded: ${filename}`); - - } - - async createDeployBuild(appflowId: string, token: string, options: CommandLineOptions): Promise { - const { req } = await this.env.client.make('POST', `/apps/${appflowId}/deploys/verbose_post`); - - let channels: string[] = []; - if (options.channel) { - if (typeof(options.channel) === 'string') { - channels.push(String(options.channel)); - } else if (typeof(options.channel) === 'object') { - channels = channels.concat(options.channel); - } - } - - req.set('Authorization', `Bearer ${token}`).send({ - commit_sha: options.commit, - environment_name: options.environment, - channel_names: channels ? channels : undefined, - }); - - try { - const res = await this.env.client.do(req); - return res.data as DeployBuild; - } catch (e) { - if (isSuperAgentError(e)) { - if (e.response.status === 401) { - this.env.log.error('Try logging out and back in again.'); - } - const apiErrorMessage = (e.response.body.error && e.response.body.error.message) ? e.response.body.error.message : 'Api Error'; - throw new FatalException(`Unable to create build: ` + apiErrorMessage); - } else { - throw e; - } - } - } - - async tailBuildLog(appflowId: string, buildId: number, token: string): Promise { - let build; - let start = 0; - const ws = this.env.log.createWriteStream(LOGGER_LEVELS.INFO, false); - - let isCreatedMessage = false; - let errorsEncountered = 0; - while (!(build && ['success', 'failed', 'canceled'].includes(build.state))) { - try { - await sleep(5000); - build = await this.getDeployBuild(appflowId, buildId, token); - if (build && build.state === 'created' && !isCreatedMessage) { - ws.write(chalk.yellow('Concurrency limit reached: build will start as soon as other builds finish.')); - isCreatedMessage = true; - } - const trace = build.job.trace; - if (trace.length > start) { - ws.write(trace.substring(start)); - start = trace.length; - } - errorsEncountered = 0; - } catch (e) { - // Retry up to 3 times in the case of an error. - errorsEncountered++; - ws.write(chalk.yellow(`Encountered error: ${e} while fetching build data retrying.`)); - if (errorsEncountered >= 3) { - ws.write(chalk.red(`Encountered ${errorsEncountered} errors in a row. Job will now fail.`)); - throw e; - } - } - } - ws.end(); - - return build; - } - - async getDeployBuild(appflowId: string, buildId: number, token: string): Promise { - const { req } = await this.env.client.make('GET', `/apps/${appflowId}/deploys/${buildId}`); - req.set('Authorization', `Bearer ${token}`).send(); - - try { - const res = await this.env.client.do(req); - return res.data as DeployBuild; - } catch (e) { - if (isSuperAgentError(e)) { - if (e.response.status === 401) { - this.env.log.error('Try logging out and back in again.'); - } - const apiErrorMessage = (e.response.body.error && e.response.body.error.message) ? e.response.body.error.message : 'Api Error'; - throw new FatalException(`Unable to get build ${buildId}: ` + apiErrorMessage); - } else { - throw e; - } - } - } - - async getDownloadUrl(appflowId: string, buildId: number, token: string): Promise { - const { req } = await this.env.client.make('GET', `/apps/${appflowId}/packages/${buildId}/download?artifact_type=WWW_ZIP`); - req.set('Authorization', `Bearer ${token}`).send(); - - try { - const res = await this.env.client.do(req); - return res.data as DownloadUrl; - } catch (e) { - if (isSuperAgentError(e)) { - if (e.response.status === 401) { - this.env.log.error('Try logging out and back in again.'); - } - const apiErrorMessage = (e.response.body.error && e.response.body.error.message) ? e.response.body.error.message : 'Api Error'; - throw new FatalException(`Unable to get download URL for build ${buildId}: ` + apiErrorMessage); - } else { - throw e; - } - } - } - - async downloadBuild(url: string, filename: string): Promise { - const { req } = await createRequest('GET', url, this.env.config.getHTTPConfig()); - - const tmpFile = tmpfilepath('ionic-package-build'); - const ws = fs.createWriteStream(tmpFile); - await download(req, ws, {}); - fs.copyFileSync(tmpFile, filename); - fs.unlinkSync(tmpFile); - return filename; - } - -} diff --git a/packages/@ionic/cli/src/commands/docs.ts b/packages/@ionic/cli/src/commands/docs.ts deleted file mode 100644 index 65700ca986..0000000000 --- a/packages/@ionic/cli/src/commands/docs.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { MetadataGroup } from '@ionic/cli-framework'; - -import { CommandLineInputs, CommandLineOptions, CommandMetadata } from '../definitions'; -import { isSuperAgentError } from '../guards'; -import { input } from '../lib/color'; -import { Command } from '../lib/command'; -import { openUrl } from '../lib/open'; -import { BROWSERS } from '../lib/serve'; -import { createRequest } from '../lib/utils/http'; - -export class DocsCommand extends Command { - async getMetadata(): Promise { - return { - name: 'docs', - type: 'global', - summary: 'Open the Ionic documentation website', - options: [ - { - name: 'browser', - summary: `Specifies the browser to use (${BROWSERS.map(b => input(b)).join(', ')})`, - aliases: ['w'], - groups: [MetadataGroup.ADVANCED], - }, - ], - }; - } - - async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - const browser = options['browser'] ? String(options['browser']) : undefined; - - const homepage = 'https://ion.link/docs'; - const url = this.project ? await this.project.getDocsUrl() : homepage; - - this.env.log.warn(`The ${input('ionic docs')} command has been deprecated and will be removed in an upcoming major release of the Ionic CLI. Developers should bookmark ${url} in their browser for easy access.`); - - try { - const { req } = await createRequest('HEAD', url, this.env.config.getHTTPConfig()); - await req; - } catch (e) { - if (isSuperAgentError(e) && e.response.status === 404) { - this.env.log.warn(`Docs not found for your specific version of Ionic. Directing you to docs homepage.`); - await openUrl(homepage, { app: browser }); - return; - } - - throw e; - } - - await openUrl(url, { app: browser }); - this.env.log.ok('Launched Ionic docs in your browser!'); - } -} diff --git a/packages/@ionic/cli/src/commands/doctor/base.ts b/packages/@ionic/cli/src/commands/doctor/base.ts deleted file mode 100644 index 02525df9cc..0000000000 --- a/packages/@ionic/cli/src/commands/doctor/base.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { concurrentFilter } from '@ionic/utils-array'; -import * as Debug from 'debug'; - -import { IAilment, IAilmentRegistry, TreatableAilment } from '../../definitions'; -import { isTreatableAilment } from '../../guards'; -import { failure, input, strong } from '../../lib/color'; -import { Command } from '../../lib/command'; -import { FatalException } from '../../lib/errors'; - -const debug = Debug('ionic:commands:doctor:base'); - -export abstract class DoctorCommand extends Command { - async getRegistry(): Promise { - if (!this.project) { - throw new FatalException(`Cannot use ${input('ionic doctor')} outside a project directory.`); - } - - const { AilmentRegistry } = await import('../../lib/doctor'); - - const registry = new AilmentRegistry(); - await this.project.registerAilments(registry); - - return registry; - } - - async detectAilments(): Promise { - const registry = await this.getRegistry(); - let count = 0; - - const tasks = this.createTaskChain(); - const isLoggedIn = this.env.session.isLoggedIn(); - - this.env.log.warn(`The ${input('ionic doctor')} command has been deprecated and will be removed in an upcoming major release of the Ionic CLI. Developers can safely remove any references to this command as many of the checks are no longer needed.`); - - if (!isLoggedIn) { - this.env.log.warn(`For best results, please make sure you're logged in to Ionic.\nSome issues can't be detected without authentication. Run:\n\n ${input('ionic login')}`); - } - - const detectTask = tasks.next('Detecting issues'); - - const ailments = registry.ailments.filter(ailment => { - if (this.env.config.get(`doctor.issues.${ailment.id}.ignored` as any)) { - debug('Issue %s ignored by config', ailment.id); - return false; - } - - if (!ailment.implicit) { - debug('Issue %s will not be implicitly detected', ailment.id); - return false; - } - - return true; - }); - - const detectedAilments = await concurrentFilter(ailments, async (ailment): Promise => { - let detected = false; - - try { - detected = await ailment.detected(); - debug('Detected %s: %s', ailment.id, detected); - } catch (e) { - this.env.log.error( - `Error while checking ${strong(ailment.id)}:\n` + - `${failure(e.stack ? e.stack : e)}` - ); - } - - count++; - detectTask.msg = `Detecting issues: ${strong(`${count} / ${ailments.length}`)} complete`; - - return detected; - }); - - detectTask.msg = `Detecting issues: ${strong(`${ailments.length} / ${ailments.length}`)} complete`; - tasks.end(); - - return detectedAilments; - } - - async detectTreatableAilments(): Promise { - const ailments = await this.detectAilments(); - - return ailments.filter((ailment): ailment is TreatableAilment => isTreatableAilment(ailment)); - } -} diff --git a/packages/@ionic/cli/src/commands/doctor/check.ts b/packages/@ionic/cli/src/commands/doctor/check.ts deleted file mode 100644 index 5f84688058..0000000000 --- a/packages/@ionic/cli/src/commands/doctor/check.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { contains, MetadataGroup, validate } from '@ionic/cli-framework'; - -import { CommandLineInputs, CommandLineOptions, CommandMetadata, IAilment } from '../../definitions'; -import { isTreatableAilment } from '../../guards'; -import { input, strong } from '../../lib/color'; -import { FatalException } from '../../lib/errors'; - -import { DoctorCommand } from './base'; - -export class DoctorCheckCommand extends DoctorCommand { - async getMetadata(): Promise { - return { - name: 'check', - type: 'project', - summary: 'Check the health of your Ionic project', - groups: [MetadataGroup.DEPRECATED], - description: ` -This command detects and prints common issues and suggested steps to fix them. - -Some issues can be fixed automatically. See ${input('ionic doctor treat --help')}. - -Optionally supply the ${input('id')} argument to check a single issue. Use ${input('ionic doctor list')} to list all known issues. - `, - exampleCommands: [ - '', - 'git-not-used', - ], - inputs: [ - { - name: 'id', - summary: 'The issue identifier', - }, - ], - }; - } - - async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - const [ id ] = inputs; - - if (id) { - const registry = await this.getRegistry(); - const ailmentIds = registry.ailments.map(a => a.id); - validate(id, 'id', [contains(ailmentIds, {})]); - const ailment = registry.get(id); - - if (!ailment) { - throw new FatalException(`Issue not found by ID: ${input(id)}`); - } - - await this.checkAilment(ailment); - } else { - const ailments = await this.detectAilments(); - await this.checkAilments(ailments); - } - } - - async checkAilments(ailments: IAilment[]) { - let treatableAilments = 0; - - if (ailments.length > 0) { - for (const ailment of ailments) { - if (isTreatableAilment(ailment)) { - treatableAilments += 1; - } - - await this.checkAilment(ailment); - } - } - - const msg = ( - 'Doctor Summary\n' + - `- Detected ${strong(String(ailments.length))} issue${ailments.length === 1 ? '' : 's'}.` + - `${ailments.length === 0 ? ' Aww yeah! 💪' : ''}\n` + - `- ${strong(String(treatableAilments))} issue${treatableAilments === 1 ? '' : 's'} can be fixed automatically${treatableAilments > 0 ? ` by running: ${input('ionic doctor fix')}` : ''}` - ); - - if (ailments.length > 0) { - this.env.log.info(msg); - throw new FatalException(''); // exit 1 - } else { - this.env.log.ok(msg); - } - } - - async checkAilment(ailment: IAilment) { - const { formatAilmentMessage } = await import('../../lib/doctor'); - - if (await ailment.detected()) { - this.env.log.warn(await formatAilmentMessage(ailment)); - } else { - this.env.log.ok(`${input(ailment.id)} was not detected.`); - } - } -} diff --git a/packages/@ionic/cli/src/commands/doctor/index.ts b/packages/@ionic/cli/src/commands/doctor/index.ts deleted file mode 100644 index 2fb7ed4287..0000000000 --- a/packages/@ionic/cli/src/commands/doctor/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CommandMap, Namespace } from '../../lib/namespace'; - -export class DoctorNamespace extends Namespace { - async getMetadata() { - return { - name: 'doctor', - summary: 'Commands for checking the health of your Ionic project', - }; - } - - async getCommands(): Promise { - return new CommandMap([ - ['check', async () => { const { DoctorCheckCommand } = await import('./check'); return new DoctorCheckCommand(this); }], - ['treat', async () => { const { DoctorTreatCommand } = await import('./treat'); return new DoctorTreatCommand(this); }], - ['list', async () => { const { DoctorListCommand } = await import('./list'); return new DoctorListCommand(this); }], - ['ls', 'list'], - ['checkup', 'check'], - ['validate', 'check'], - ['fix', 'treat'], - ]); - } -} diff --git a/packages/@ionic/cli/src/commands/doctor/list.ts b/packages/@ionic/cli/src/commands/doctor/list.ts deleted file mode 100644 index 3d2d014c87..0000000000 --- a/packages/@ionic/cli/src/commands/doctor/list.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MetadataGroup } from '@ionic/cli-framework'; -import { strcmp } from '@ionic/cli-framework/utils/string'; -import { columnar } from '@ionic/utils-terminal'; - -import { COLUMNAR_OPTIONS } from '../../constants'; -import { CommandLineInputs, CommandLineOptions, CommandMetadata } from '../../definitions'; -import { isTreatableAilment } from '../../guards'; -import { input, strong } from '../../lib/color'; - -import { DoctorCommand } from './base'; - -export class DoctorListCommand extends DoctorCommand { - async getMetadata(): Promise { - return { - name: 'list', - type: 'project', - summary: 'List all issues and their identifiers', - groups: [MetadataGroup.DEPRECATED], - description: ` -Issues can have various tags: -- ${strong('treatable')}: ${input('ionic doctor treat')} can attempt to fix the issue -- ${strong('ignored')}: configured not to be detected in ${input('ionic doctor check')} or ${input('ionic doctor treat')} -- ${strong('explicit-detection')}: issue is only detected explicitly with ${input('ionic doctor check ')} - -You can flip whether an issue is ignored or not by using ${input('ionic config set -g doctor.issues..ignored true/false')}, where ${input('')} matches an ID listed with this command. - `, - }; - } - - async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - const registry = await this.getRegistry(); - - const rows = registry.ailments.map(ailment => { - const tags: string[] = []; - const ignored = this.env.config.get(`doctor.issues.${ailment.id}.ignored` as any); - - if (ignored) { - tags.push('ignored'); - } - - if (isTreatableAilment(ailment)) { - tags.push('treatable'); - } - - if (!ailment.implicit) { - tags.push('explicit-detection'); - } - - return [ - input(ailment.id), - ailment.projects ? ailment.projects.map(t => strong(t)).join(', ') : 'all', - tags.map(t => strong(t)).join(', '), - ]; - }); - - rows.sort((row1, row2) => strcmp(row1[0], row2[0])); - - this.env.log.rawmsg(columnar(rows, { ...COLUMNAR_OPTIONS, headers: ['id', 'affected projects', 'tags'].map(h => strong(h)) })); - } -} diff --git a/packages/@ionic/cli/src/commands/doctor/treat.ts b/packages/@ionic/cli/src/commands/doctor/treat.ts deleted file mode 100644 index ce800388a8..0000000000 --- a/packages/@ionic/cli/src/commands/doctor/treat.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { contains, MetadataGroup, validate } from '@ionic/cli-framework'; - -import { CommandLineInputs, CommandLineOptions, CommandMetadata, TreatableAilment } from '../../definitions'; -import { isExitCodeException, isTreatableAilment } from '../../guards'; -import { input, strong } from '../../lib/color'; -import { FatalException } from '../../lib/errors'; - -import { DoctorCommand } from './base'; - -const ERROR_AILMENT_IGNORED = 'AILMENT_IGNORED'; -const ERROR_AILMENT_SKIPPED = 'AILMENT_SKIPPED'; - -export class DoctorTreatCommand extends DoctorCommand { - async getMetadata(): Promise { - return { - name: 'treat', - type: 'project', - summary: 'Attempt to fix issues in your Ionic project', - groups: [MetadataGroup.DEPRECATED], - description: ` -This command detects and attempts to fix common issues. Before a fix is attempted, the steps are printed and a confirmation prompt is displayed. - -Optionally supply the ${input('id')} argument to attempt to fix a single issue. Use ${input('ionic doctor list')} to list all known issues. - `, - exampleCommands: [ - '', - 'git-not-used', - ], - inputs: [ - { - name: 'id', - summary: 'The issue identifier', - }, - ], - }; - } - - async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - const { formatAilmentMessage } = await import('../../lib/doctor'); - - const [ id ] = inputs; - - if (id) { - const registry = await this.getRegistry(); - const ailmentIds = registry.ailments.map(a => a.id); - validate(id, 'id', [contains(ailmentIds, {})]); - const ailment = registry.get(id); - - if (!ailment) { - throw new FatalException(`Issue not found by ID: ${input(id)}`); - } - - const detected = await ailment.detected(); - - if (!detected) { - this.env.log.ok(`${input(ailment.id)} does not need fixing as it was not detected.`); - return; - } - - if (!isTreatableAilment(ailment)) { - this.env.log.warn(await formatAilmentMessage(ailment)); - - throw new FatalException( - `Issue cannot be fixed automatically: ${input(ailment.id)}\n` + - `Unfortunately the CLI can't automatically fix the specified issue at this time. However, please see the steps above for manually fixing the issue.` - ); - } - - if (this.env.config.get(`doctor.issues.${ailment.id}.ignored` as any)) { - const confirm = await this.env.prompt({ - type: 'confirm', - name: 'confirm', - message: `${input(ailment.id)} is ignored, are you sure you want to continue?`, - }); - - if (!confirm) { - return; - } - - this.env.config.unset(`doctor.issues.${ailment.id}.ignored` as any); - } - - try { - await this.treatAilment(ailment); - } catch (e) { - this.handleError(e); - } - } else { - const ailments = await this.detectTreatableAilments(); - await this.treatAilments(ailments); - } - } - - async treatAilments(ailments: TreatableAilment[]) { - let treatedAilments = 0; - - for (const ailment of ailments) { - try { - const treated = await this.treatAilment(ailment); - - if (treated) { - treatedAilments += 1; - } - } catch (e) { - this.handleError(e); - } - } - - this.env.log.info( - 'Doctor Summary\n' + - `- Detected ${strong(String(ailments.length))} treatable issue${ailments.length === 1 ? '' : 's'}\n` + - (treatedAilments > 0 ? `- ${strong(String(treatedAilments))} ${treatedAilments === 1 ? 'issue was' : 'issues were'} fixed automatically` : '') - ); - } - - handleError(e: any) { - if (e !== ERROR_AILMENT_SKIPPED && e !== ERROR_AILMENT_IGNORED) { - if (isExitCodeException(e)) { - this.env.log.error(`Error occurred during automatic fix: ${e.message}`); - } else { - this.env.log.error(`Error occurred during automatic fix: ${e.stack ? e.stack : e}`); - } - } - } - - async treatAilment(ailment: TreatableAilment) { - const { formatAilmentMessage } = await import('../../lib/doctor'); - - const treatmentSteps = await ailment.getTreatmentSteps(); - this.env.log.warn(await formatAilmentMessage(ailment)); - - const CHOICE_YES = 'yes'; - const CHOICE_NO = 'no'; - const CHOICE_IGNORE = 'ignore'; - - const choice = await this.env.prompt({ - type: 'list', - name: 'choice', - message: `Fix automatically?`, - choices: [ - { - name: 'Yes', - value: CHOICE_YES, - }, - { - name: 'No', - value: CHOICE_NO, - }, - { - name: 'Ignore forever', - value: CHOICE_IGNORE, - }, - ], - }); - - if (choice === CHOICE_YES) { - for (const i in treatmentSteps) { - const step = treatmentSteps[i]; - - try { - await step.treat(); - } catch (e) { - if (!isExitCodeException(e) || e.exitCode > 0) { - throw e; - } - } - } - - return true; - } else if (choice === CHOICE_NO) { - throw ERROR_AILMENT_SKIPPED; - } else if (choice === CHOICE_IGNORE) { - this.env.config.set(`doctor.issues.${ailment.id}.ignored` as any, true); - - throw ERROR_AILMENT_IGNORED; - } - - return false; - } -} diff --git a/packages/@ionic/cli/src/commands/index.ts b/packages/@ionic/cli/src/commands/index.ts index 427a73aabf..4b8dcf6db8 100644 --- a/packages/@ionic/cli/src/commands/index.ts +++ b/packages/@ionic/cli/src/commands/index.ts @@ -44,13 +44,11 @@ export class IonicNamespace extends Namespace { ['config', async () => { const { ConfigNamespace } = await import('./config/index'); return new ConfigNamespace(this); }], ['cordova', async () => { const { CordovaNamespace } = await import('./cordova/index'); return new CordovaNamespace(this); }], ['capacitor', async () => { const { CapacitorNamespace } = await import('./capacitor/index'); return new CapacitorNamespace(this); }], - ['deploy', async () => { const { DeployNamespace } = await import('./deploy/index'); return new DeployNamespace(this); }], + ['live-update', async () => { const { LiveUpdatesNamespace } = await import('./live-update/index'); return new LiveUpdatesNamespace(this); }], ['git', async () => { const { GitNamespace } = await import('./git/index'); return new GitNamespace(this); }], - ['package', async () => { const { PackageNamespace } = await import('./package/index'); return new PackageNamespace(this); }], ['ssl', async () => { const { SSLNamespace } = await import('./ssl/index'); return new SSLNamespace(this); }], ['ssh', async () => { const { SSHNamespace } = await import('./ssh/index'); return new SSHNamespace(this); }], ['monitoring', async () => { const { MonitoringNamespace } = await import('./monitoring/index'); return new MonitoringNamespace(this); }], - ['doctor', async () => { const { DoctorNamespace } = await import('./doctor/index'); return new DoctorNamespace(this); }], ['integrations', async () => { const { IntegrationsNamespace } = await import('./integrations/index'); return new IntegrationsNamespace(this); }], ['enterprise', async () => { const { EnterpriseNamespace } = await import('./enterprise/index'); return new EnterpriseNamespace(this); }], ['cap', 'capacitor'], @@ -64,7 +62,6 @@ export class IonicNamespace extends Namespace { return new CommandMap([ ['build', async () => { const { BuildCommand } = await import('./build'); return new BuildCommand(this); }], ['completion', async () => { const { CompletionCommand } = await import('./completion'); return new CompletionCommand(this); }], - ['docs', async () => { const { DocsCommand } = await import('./docs'); return new DocsCommand(this); }], ['generate', async () => { const { GenerateCommand } = await import('./generate'); return new GenerateCommand(this); }], ['help', async () => { const { HelpCommand } = await import('./help'); return new HelpCommand(this); }], ['info', async () => { const { InfoCommand } = await import('./info'); return new InfoCommand(this); }], @@ -81,7 +78,6 @@ export class IonicNamespace extends Namespace { ['state', async () => { const { StateCommand } = await import('./state'); return new StateCommand(this); }], ['telemetry', async () => { const { TelemetryCommand } = await import('./telemetry'); return new TelemetryCommand(this); }], ['version', async () => { const { VersionCommand } = await import('./version'); return new VersionCommand(this); }], - ['lab', async () => { const { LabCommand } = await import('./serve'); return new LabCommand(this); }], ['g', 'generate'], ['s', 'serve'], ]); diff --git a/packages/@ionic/cli/src/commands/integrations/disable.ts b/packages/@ionic/cli/src/commands/integrations/disable.ts index 7da3dbf2ad..cdb715e792 100644 --- a/packages/@ionic/cli/src/commands/integrations/disable.ts +++ b/packages/@ionic/cli/src/commands/integrations/disable.ts @@ -46,7 +46,7 @@ Integrations, such as Cordova, can be disabled with this command. await integration.disable(); this.env.log.ok(`Integration ${input(name)} disabled!`); } - } catch (e) { + } catch (e: any) { if (e instanceof BaseError) { throw new FatalException(e.message); } diff --git a/packages/@ionic/cli/src/commands/integrations/enable.ts b/packages/@ionic/cli/src/commands/integrations/enable.ts index 35a5fb7f5c..e8ea0b9415 100644 --- a/packages/@ionic/cli/src/commands/integrations/enable.ts +++ b/packages/@ionic/cli/src/commands/integrations/enable.ts @@ -86,7 +86,7 @@ Integrations can be re-added with the ${input('--add')} option. this.env.log.ok(`Integration ${input(integration.name)} enabled!`); } } - } catch (e) { + } catch (e: any) { if (e instanceof BaseError) { if (quiet) { this.env.log.error(e.message); diff --git a/packages/@ionic/cli/src/commands/integrations/list.ts b/packages/@ionic/cli/src/commands/integrations/list.ts index 450338e433..f629df1ca0 100644 --- a/packages/@ionic/cli/src/commands/integrations/list.ts +++ b/packages/@ionic/cli/src/commands/integrations/list.ts @@ -1,5 +1,5 @@ import { columnar } from '@ionic/utils-terminal'; -import * as chalk from 'chalk'; +import chalk from 'chalk'; import { CommandLineInputs, CommandLineOptions, CommandMetadata, IntegrationName } from '../../definitions'; import { input, strong } from '../../lib/color'; diff --git a/packages/@ionic/cli/src/commands/link.ts b/packages/@ionic/cli/src/commands/link.ts index 51871c0167..d9e4e5ad63 100644 --- a/packages/@ionic/cli/src/commands/link.ts +++ b/packages/@ionic/cli/src/commands/link.ts @@ -1,7 +1,7 @@ import { MetadataGroup, validators } from '@ionic/cli-framework'; import { createPromptChoiceSeparator } from '@ionic/cli-framework-prompts'; import { prettyPath } from '@ionic/utils-terminal'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import { PROJECT_FILE } from '../constants'; import { App, CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandPreRun, GithubBranch, GithubRepo } from '../definitions'; @@ -434,7 +434,7 @@ If you are having issues linking, please get in touch with our Support[^support- try { // TODO: maybe we can use a PUT instead of DELETE now + POST later? await appClient.deleteAssociation(app.id); - } catch (e) { + } catch (e: any) { if (isSuperAgentError(e)) { if (e.response.status === 401) { await this.oAuthProcess(userId); @@ -460,7 +460,7 @@ If you are having issues linking, please get in touch with our Support[^support- const association = await appClient.createAssociation(app.id, { repoId, type: 'github', branches }); this.env.log.ok(`App ${input(app.id)} connected to ${strong(association.repository.html_url)}`); return association.repository.html_url; - } catch (e) { + } catch (e: any) { if (isSuperAgentError(e) && e.response.status === 403) { throw new FatalException(e.response.body.error.message); } @@ -468,7 +468,7 @@ If you are having issues linking, please get in touch with our Support[^support- } formatRepoName(fullName: string) { - const [ org, name ] = fullName.split('/'); + const [org, name] = fullName.split('/'); return `${weak(`${org} /`)} ${name}`; } @@ -518,7 +518,7 @@ If you are having issues linking, please get in touch with our Support[^support- task.msg = `Looking up your GitHub repositories: ${strong(String(repos.length))} found`; } - } catch (e) { + } catch (e: any) { tasks.fail(); if (isSuperAgentError(e) && e.response.status === 401) { @@ -592,7 +592,7 @@ If you are having issues linking, please get in touch with our Support[^support- task.msg = `Looking up the available branches on your GitHub repository: ${strong(String(availableBranches.length))} found`; } - } catch (e) { + } catch (e: any) { tasks.fail(); throw e; } diff --git a/packages/@ionic/cli/src/commands/deploy/add.ts b/packages/@ionic/cli/src/commands/live-update/add.ts similarity index 84% rename from packages/@ionic/cli/src/commands/deploy/add.ts rename to packages/@ionic/cli/src/commands/live-update/add.ts index ae2b7ec4a5..b25dc7fba6 100644 --- a/packages/@ionic/cli/src/commands/deploy/add.ts +++ b/packages/@ionic/cli/src/commands/live-update/add.ts @@ -4,18 +4,18 @@ import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMeta import { input } from '../../lib/color'; import { runCommand } from '../../lib/executor'; -import { DeployConfCommand } from './core'; +import { LiveUpdatesConfCommand } from './core'; -export class AddCommand extends DeployConfCommand { +export class AddCommand extends LiveUpdatesConfCommand { async getMetadata(): Promise { return { name: 'add', type: 'project', groups: [MetadataGroup.PAID], - summary: 'Adds Appflow Deploy to the project', + summary: 'Adds Ionic Live Updates plugin to the project', description: ` -This command adds the Appflow Deploy plugin (${input('cordova-plugin-ionic')}) for both Capacitor and Cordova projects. +This command adds the Ionic Live Updates plugin (${input('cordova-plugin-ionic')}) for both Capacitor and Cordova projects. For Capacitor projects it runs all the steps necessary to install the plugin, sync with the native projects and add the configuration to the proper iOS and Android configuration files. @@ -74,7 +74,7 @@ For Cordova projects it just takes care of running the proper Cordova CLI comman }; } - protected buildCordovaDeployOptions(options: CommandLineOptions): string[] { + protected buildCordovaLiveUpdateOptions(options: CommandLineOptions): string[] { const optionsToCordova = { 'app-id': 'APP_ID', 'channel-name': 'CHANNEL_NAME', @@ -101,12 +101,12 @@ For Cordova projects it just takes care of running the proper Cordova CLI comman async addPlugin(options: CommandLineOptions, runinfo: CommandInstanceInfo, integration?: string) { if (integration === 'cordova') { - let deployCommand = ['cordova', 'plugin', 'add', 'cordova-plugin-ionic']; - const userOptions = this.buildCordovaDeployOptions(options); + let addPluginCommand = ['cordova', 'plugin', 'add', 'cordova-plugin-ionic']; + const userOptions = this.buildCordovaLiveUpdateOptions(options); if (userOptions) { - deployCommand = deployCommand.concat(userOptions); + addPluginCommand = addPluginCommand.concat(userOptions); } - await runCommand(runinfo, deployCommand); + await runCommand(runinfo, addPluginCommand); } if (integration === 'capacitor') { const { pkgManagerArgs } = await import('../../lib/utils/npm'); @@ -124,7 +124,7 @@ For Cordova projects it just takes care of running the proper Cordova CLI comman const integration = await this.getAppIntegration(); // check if it is already installed - const alreadyAdded = await this.checkDeployInstalled(); + const alreadyAdded = await this.checkLiveUpdatesInstalled(); if (!alreadyAdded) { await this.addPlugin(options, runinfo, integration); } else { @@ -133,7 +133,7 @@ For Cordova projects it just takes care of running the proper Cordova CLI comman if (integration === 'capacitor') { // generate the manifest - await runCommand(runinfo, ['deploy', 'manifest']); + await runCommand(runinfo, ['live-update', 'manifest']); // run capacitor sync await runCommand(runinfo, ['capacitor', 'sync']); // update the ios project if present @@ -142,7 +142,7 @@ For Cordova projects it just takes care of running the proper Cordova CLI comman await this.addConfToAndroidString(options); } - this.env.log.ok(`Appflow Deploy plugin added to the project!\n`); + this.env.log.ok(`Ionic Live Updates plugin added to the project!\n`); } } diff --git a/packages/@ionic/cli/src/commands/deploy/capacitor.ts b/packages/@ionic/cli/src/commands/live-update/capacitor.ts similarity index 100% rename from packages/@ionic/cli/src/commands/deploy/capacitor.ts rename to packages/@ionic/cli/src/commands/live-update/capacitor.ts diff --git a/packages/@ionic/cli/src/commands/deploy/configure.ts b/packages/@ionic/cli/src/commands/live-update/configure.ts similarity index 82% rename from packages/@ionic/cli/src/commands/deploy/configure.ts rename to packages/@ionic/cli/src/commands/live-update/configure.ts index d6b0f3ce68..078d9f5c3a 100644 --- a/packages/@ionic/cli/src/commands/deploy/configure.ts +++ b/packages/@ionic/cli/src/commands/live-update/configure.ts @@ -5,17 +5,17 @@ import { input } from '../../lib/color'; import { FatalException } from '../../lib/errors'; -import { DeployConfCommand } from './core'; +import { LiveUpdatesConfCommand } from './core'; -export class ConfigureCommand extends DeployConfCommand { +export class ConfigureCommand extends LiveUpdatesConfCommand { async getMetadata(): Promise { return { name: 'configure', type: 'project', groups: [MetadataGroup.PAID], - summary: 'Overrides Appflow Deploy configuration', + summary: 'Overrides Ionic Live Updates plugin configuration', description: ` -This command overrides configuration for the Appflow Deploy plugin (${input('cordova-plugin-ionic')}) in Capacitor projects. +This command overrides configuration for the Ionic Live Updates plugin (${input('cordova-plugin-ionic')}) in Capacitor projects. For Capacitor projects, if the plugin is already installed, it overrides the configuration variables in the native projects. @@ -81,11 +81,11 @@ For Cordova projects this is not implemented because it is better to reinstall t async preRun(inputs: CommandLineInputs, options: CommandLineOptions): Promise { // check if it is already installed - const alreadyAdded = await this.checkDeployInstalled(); + const alreadyAdded = await this.checkLiveUpdatesInstalled(); if (!alreadyAdded) { throw new FatalException( - `Deploy (cordova-plugin-ionic) not yet installed.\n` + - `Please run ${input('ionic deploy add')}` + `Live Updates (cordova-plugin-ionic) not yet installed.\n` + + `Please run ${input('ionic live-update add')}` ); } // check if there are native integration installed @@ -101,8 +101,8 @@ For Cordova projects this is not implemented because it is better to reinstall t const integration = await this.getAppIntegration(); if (integration === 'cordova') { throw new FatalException( - `Deploy (cordova-plugin-ionic) configuration cannot be overridden for a Cordova project.\n` + - `Please uninstall the plugin and run ${input('ionic deploy add')} again.` + `Live Updates (cordova-plugin-ionic) configuration cannot be overridden for a Cordova project.\n` + + `Please uninstall the plugin and run ${input('ionic live-update add')} again.` ); } if (integration === 'capacitor') { @@ -120,7 +120,7 @@ For Cordova projects this is not implemented because it is better to reinstall t printOkMessage = await this.addConfToAndroidString(options); } if (printOkMessage) { - this.env.log.ok(`Deploy (cordova-plugin-ionic) configs successfully overridden for the project\n`); + this.env.log.ok(`Live Updates (cordova-plugin-ionic) configs successfully overridden for the project\n`); } } } diff --git a/packages/@ionic/cli/src/commands/deploy/core.ts b/packages/@ionic/cli/src/commands/live-update/core.ts similarity index 95% rename from packages/@ionic/cli/src/commands/deploy/core.ts rename to packages/@ionic/cli/src/commands/live-update/core.ts index 38c4b1683f..cd8ce14faa 100644 --- a/packages/@ionic/cli/src/commands/deploy/core.ts +++ b/packages/@ionic/cli/src/commands/live-update/core.ts @@ -7,7 +7,7 @@ import { input, strong } from '../../lib/color'; import { Command } from '../../lib/command'; import { FatalException } from '../../lib/errors'; -export abstract class DeployCoreCommand extends Command { +export abstract class LiveUpdatesCoreCommand extends Command { protected async getAppIntegration(): Promise { if (this.project) { if (this.project.getIntegration('capacitor') !== undefined) { @@ -26,7 +26,7 @@ export abstract class DeployCoreCommand extends Command { if (!integration) { throw new FatalException( `It looks like your app isn't integrated with Capacitor or Cordova.\n` + - `In order to use the Appflow Deploy plugin, you will need to integrate your app with Capacitor or Cordova. See the docs for setting up native projects:\n\n` + + `In order to use the Ionic Live Updates plugin, you will need to integrate your app with Capacitor or Cordova. See the docs for setting up native projects:\n\n` + `iOS: ${strong('https://ionicframework.com/docs/building/ios')}\n` + `Android: ${strong('https://ionicframework.com/docs/building/android')}\n` ); @@ -34,7 +34,7 @@ export abstract class DeployCoreCommand extends Command { } } -export abstract class DeployConfCommand extends DeployCoreCommand { +export abstract class LiveUpdatesConfCommand extends LiveUpdatesCoreCommand { protected readonly optionsToPlistKeys: {[key: string]: string} = { 'app-id': 'IonAppId', @@ -78,7 +78,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { return undefined; } - protected async checkDeployInstalled(): Promise { + protected async checkLiveUpdatesInstalled(): Promise { if (!this.project) { return false; } @@ -144,7 +144,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { let plistPath; try { plistPath = await this.getIosCapPlist(); - } catch (e) { + } catch (e: any) { this.env.log.warn(e.message); this.printPlistInstructions(options); return false; @@ -152,7 +152,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { if (!plistPath) { this.env.log.warn( `No ${strong('Capacitor iOS')} project found\n` + - `You will need to rerun ${input('ionic deploy configure')} if you add it later.\n` + `You will need to rerun ${input('ionic live-update configure')} if you add it later.\n` ); return false; } @@ -161,7 +161,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { try { const plistFile = await readFile(plistPath); plistData = plistFile.toString(); - } catch (e) { + } catch (e: any) { this.env.log.error(`The iOS Info.plist could not be read.`); this.printPlistInstructions(options); return false; @@ -170,7 +170,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { let etree; try { etree = et.parse(plistData); - } catch (e) { + } catch (e: any) { this.env.log.error(`Impossible to parse the XML in the Info.plist`); this.printPlistInstructions(options); return false; @@ -261,7 +261,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { newXML; try { await writeFile(plistPath, xmlToWrite, { encoding: 'utf-8' }); - } catch (e) { + } catch (e: any) { this.env.log.error(`Changes to Info.plist could not be written.`); this.printPlistInstructions(options); } @@ -273,7 +273,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { let stringXmlPath; try { stringXmlPath = await this.getAndroidCapString(); - } catch (e) { + } catch (e: any) { this.env.log.warn(e.message); this.printPlistInstructions(options); return false; @@ -281,7 +281,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { if (!stringXmlPath) { this.env.log.warn( `No ${strong('Capacitor Android')} project found\n`+ - `You will need to rerun ${input('ionic deploy configure')} if you add it later.\n`); + `You will need to rerun ${input('ionic live-update configure')} if you add it later.\n`); return false; } // try to load the plist file first @@ -289,7 +289,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { try { const stringFile = await readFile(stringXmlPath); stringData = stringFile.toString(); - } catch (e) { + } catch (e: any) { this.env.log.error(`The Android string.xml could not be read.`); this.printStringXmlInstructions(options); return false; @@ -298,7 +298,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { let etree; try { etree = et.parse(stringData); - } catch (e) { + } catch (e: any) { this.env.log.error(`Impossible to parse the XML in the string.xml`); this.printStringXmlInstructions(options); return false; @@ -356,7 +356,7 @@ export abstract class DeployConfCommand extends DeployCoreCommand { }); try { await writeFile(stringXmlPath, newXML, { encoding: 'utf-8' }); - } catch (e) { + } catch (e: any) { this.env.log.error(`Changes to string.xml could not be written.`); this.printStringXmlInstructions(options); } diff --git a/packages/@ionic/cli/src/commands/deploy/index.ts b/packages/@ionic/cli/src/commands/live-update/index.ts similarity index 55% rename from packages/@ionic/cli/src/commands/deploy/index.ts rename to packages/@ionic/cli/src/commands/live-update/index.ts index 430caf5e1a..e5cef4677b 100644 --- a/packages/@ionic/cli/src/commands/deploy/index.ts +++ b/packages/@ionic/cli/src/commands/live-update/index.ts @@ -3,15 +3,15 @@ import { MetadataGroup } from '@ionic/cli-framework'; import { strong } from '../../lib/color'; import { CommandMap, Namespace } from '../../lib/namespace'; -export class DeployNamespace extends Namespace { +export class LiveUpdatesNamespace extends Namespace { async getMetadata() { return { - name: 'deploy', - summary: 'Appflow Deploy functionality', + name: 'live-update', + summary: 'Ionic Live Updates functionality', description: ` -These commands integrate with Ionic Appflow to configure the deploy plugin in your project and run remote builds. +These commands integrate with Ionic Cloud to configure the Live Updates plugin in your project and run remote builds. -Appflow deploy documentation: +Ionic Live Updates documentation: - Overview: ${strong('https://ion.link/appflow-deploy-docs')} `, groups: [MetadataGroup.PAID], @@ -22,8 +22,7 @@ Appflow deploy documentation: return new CommandMap([ ['add', async () => { const { AddCommand } = await import('./add'); return new AddCommand(this); }], ['configure', async () => { const { ConfigureCommand } = await import('./configure'); return new ConfigureCommand(this); }], - ['build', async () => { const { BuildCommand } = await import('./build'); return new BuildCommand(this); }], - ['manifest', async () => { const { DeployManifestCommand } = await import('./manifest'); return new DeployManifestCommand(this); }], + ['manifest', async () => { const { LiveUpdatesManifestCommand } = await import('./manifest'); return new LiveUpdatesManifestCommand(this); }], ]); } } diff --git a/packages/@ionic/cli/src/commands/deploy/manifest.ts b/packages/@ionic/cli/src/commands/live-update/manifest.ts similarity index 85% rename from packages/@ionic/cli/src/commands/deploy/manifest.ts rename to packages/@ionic/cli/src/commands/live-update/manifest.ts index c9287e5b52..18cf415957 100644 --- a/packages/@ionic/cli/src/commands/deploy/manifest.ts +++ b/packages/@ionic/cli/src/commands/live-update/manifest.ts @@ -7,7 +7,7 @@ import * as crypto from 'crypto'; import * as fs from 'fs'; import lodash = require('lodash'); import * as path from 'path'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import { CommandMetadata } from '../../definitions'; import { input } from '../../lib/color'; @@ -16,24 +16,24 @@ import { prependNodeModulesBinToPath, Shell } from '../../lib/shell'; import { createDefaultLoggerHandlers, Logger } from '../../lib/utils/logger'; import { CapacitorCLIConfig, CapacitorConfig, CapacitorJSONConfig } from './capacitor'; -import { DeployCoreCommand } from './core'; +import { LiveUpdatesCoreCommand } from './core'; -const debug = Debug('ionic:commands:deploy:manifest'); +const debug = Debug('ionic:commands:live-update:manifest'); const CAPACITOR_CONFIG_JSON_FILE = 'capacitor.config.json'; -interface DeployManifestItem { +interface LiveUpdatesManifestItem { href: string; size: number; integrity: string; } -export class DeployManifestCommand extends DeployCoreCommand { +export class LiveUpdatesManifestCommand extends LiveUpdatesCoreCommand { async getMetadata(): Promise { // This command is set as type 'global' in order to support Capacitor apps without an ionic.config.json return { name: 'manifest', type: 'global', - summary: 'Generates a manifest file for the deploy service from a built app directory', + summary: 'Generates a manifest file for the Ionic Live Updates service from a built app directory', groups: [MetadataGroup.PAID], }; } @@ -41,7 +41,7 @@ export class DeployManifestCommand extends DeployCoreCommand { async run(): Promise { const capacitorConfig = await this.getCapacitorConfig(); if (!this.project && !capacitorConfig) { - throw new FatalException(`Cannot run ${input('ionic deploy manifest')} outside a project directory.`); + throw new FatalException(`Cannot run ${input('ionic live-update manifest')} outside a project directory.`); } let buildDir: string; @@ -56,20 +56,20 @@ export class DeployManifestCommand extends DeployCoreCommand { const manifestPath = path.resolve(buildDir, 'pro-manifest.json'); await writeFile(manifestPath, JSON.stringify(manifest, undefined, 2), { encoding: 'utf8' }); - this.env.log.ok(`Appflow Deploy manifest written to ${input(prettyPath(manifestPath))}!`); + this.env.log.ok(`Ionic Live Updates manifest written to ${input(prettyPath(manifestPath))}!`); } - private async getFilesAndSizesAndHashesForGlobPattern(buildDir: string): Promise { + private async getFilesAndSizesAndHashesForGlobPattern(buildDir: string): Promise { const contents = await readdirp(buildDir, { filter: item => !/(css|js)\.map$/.test(item.path) }); const stats = await map(contents, async (f): Promise<[string, fs.Stats]> => [f, await stat(f)]); - const files = stats.filter(([ , s ]) => !s.isDirectory()); + const files = stats.filter(([, s]) => !s.isDirectory()); const items = await Promise.all(files.map(([f, s]) => this.getFileAndSizeAndHashForFile(buildDir, f, s))); return items.filter(item => item.href !== 'pro-manifest.json'); } - private async getFileAndSizeAndHashForFile(buildDir: string, file: string, s: fs.Stats): Promise { + private async getFileAndSizeAndHashForFile(buildDir: string, file: string, s: fs.Stats): Promise { const buffer = await this.readFile(file); return { @@ -113,7 +113,7 @@ export class DeployManifestCommand extends DeployCoreCommand { level: LOGGER_LEVELS.INFO, handlers: createDefaultLoggerHandlers(), }); - const shell = new Shell({ log }, { alterPath: p => { return prependNodeModulesBinToPath(this.env.ctx.execPath, p)} }); + const shell = new Shell({ log }, { alterPath: p => { return prependNodeModulesBinToPath(this.env.ctx.execPath, p) } }); debug('Getting config with Capacitor CLI: %O', args); @@ -126,7 +126,7 @@ export class DeployManifestCommand extends DeployCoreCommand { try { return JSON.parse(output); - } catch(e) { + } catch (e) { debug('Could not get config from Capacitor CLI (probably old version)', e); return; } diff --git a/packages/@ionic/cli/src/commands/login.ts b/packages/@ionic/cli/src/commands/login.ts index 9b9905a6e3..46f709c107 100644 --- a/packages/@ionic/cli/src/commands/login.ts +++ b/packages/@ionic/cli/src/commands/login.ts @@ -1,5 +1,5 @@ import { combine, validators } from '@ionic/cli-framework'; -import * as chalk from 'chalk'; +import chalk from 'chalk'; import * as readline from 'readline'; import { CommandLineInputs, CommandLineOptions, CommandMetadata, CommandPreRun } from '../definitions'; @@ -155,7 +155,7 @@ If you are having issues logging in, please get in touch with our Support[^suppo } async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - const [ email, password ] = inputs; + const [email, password] = inputs; if (email && password) { await this.logout(); @@ -182,7 +182,7 @@ If you are having issues logging in, please get in touch with our Support[^suppo await this.logout(); await this.env.session.webLogin(); } else { - return ; + return; } } diff --git a/packages/@ionic/cli/src/commands/monitoring/index.ts b/packages/@ionic/cli/src/commands/monitoring/index.ts index 807dd9ea0e..3029833c92 100644 --- a/packages/@ionic/cli/src/commands/monitoring/index.ts +++ b/packages/@ionic/cli/src/commands/monitoring/index.ts @@ -6,9 +6,7 @@ export class MonitoringNamespace extends Namespace { async getMetadata() { const groups: MetadataGroup[] = []; - if (!this.project || this.project.type !== 'ionic-angular') { - groups.push(MetadataGroup.HIDDEN); - } + groups.push(MetadataGroup.HIDDEN); return { name: 'monitoring', diff --git a/packages/@ionic/cli/src/commands/monitoring/syncmaps.ts b/packages/@ionic/cli/src/commands/monitoring/syncmaps.ts index 4efa6f675f..e27afa5a19 100644 --- a/packages/@ionic/cli/src/commands/monitoring/syncmaps.ts +++ b/packages/@ionic/cli/src/commands/monitoring/syncmaps.ts @@ -1,6 +1,6 @@ import { pathExists, readFile, readdirSafe } from '@ionic/utils-fs'; import { columnar, prettyPath } from '@ionic/utils-terminal'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as path from 'path'; import { APIResponseSuccess, CommandLineInputs, CommandLineOptions, CommandMetadata } from '../../definitions'; @@ -48,7 +48,7 @@ By default, ${input('ionic monitoring syncmaps')} will upload the sourcemap file const token = await this.env.session.getUserToken(); const appflowId = await this.project.requireAppflowId(); - const [ snapshotId ] = inputs; + const [snapshotId] = inputs; const doBuild = options.build ? true : false; const cordova = this.project.requireIntegration('cordova'); @@ -125,7 +125,7 @@ By default, ${input('ionic monitoring syncmaps')} will upload the sourcemap file const res = await this.env.client.do(req); return this.uploadSourcemap(res, file); - } catch (e) { + } catch (e: any) { if (isSuperAgentError(e)) { this.env.log.error(`Unable to sync map ${file}: ` + e.message); if (e.response.status === 401) { diff --git a/packages/@ionic/cli/src/commands/package/build.ts b/packages/@ionic/cli/src/commands/package/build.ts deleted file mode 100644 index 29a9c52202..0000000000 --- a/packages/@ionic/cli/src/commands/package/build.ts +++ /dev/null @@ -1,531 +0,0 @@ -import { CommandLineInputs, CommandLineOptions, MetadataGroup, combine, contains, validators } from '@ionic/cli-framework'; -import { LOGGER_LEVELS } from '@ionic/cli-framework-output'; -import { tmpfilepath } from '@ionic/utils-fs'; -import { columnar } from '@ionic/utils-terminal'; -import { sleep } from '@ionic/utils-process'; -import * as chalk from 'chalk'; -import * as Debug from 'debug'; -import * as fs from 'fs'; - -import { CommandMetadata } from '../../definitions'; -import { isSuperAgentError } from '../../guards'; -import { input, strong, weak } from '../../lib/color'; -import { Command } from '../../lib/command'; -import { FatalException } from '../../lib/errors'; -import { fileUtils } from '../../lib/utils/file'; -import { createRequest, download } from '../../lib/utils/http'; -import { IONIC_CLOUD_CLI_MIGRATION } from '../../lib/updates'; - -const debug = Debug('ionic:commands:package:build'); -const PLATFORMS = ['android', 'ios']; - -const ANDROID_BUILD_TYPES = ['debug', 'release']; -const IOS_BUILD_TYPES = ['development', 'ad-hoc', 'app-store', 'enterprise']; -const APP_STORE_COMPATIBLE_TYPES = ['release', 'app-store', 'enterprise']; -const BUILD_TYPES = ANDROID_BUILD_TYPES.concat(IOS_BUILD_TYPES); -const ANDROID_ARTIFACT_TYPES = ['aab', 'apk']; -const IOS_ARTIFACT_TYPES = ['ipa', 'dsym']; -const ARTIFACT_TYPES = ANDROID_ARTIFACT_TYPES.concat(IOS_ARTIFACT_TYPES); -const TARGET_PLATFORM = ['Android', 'iOS - Xcode 11 (Preferred)', 'iOS - Xcode 10']; - -interface Artifact { - name: string; - url: string; - artifact_type: string; -} - -export interface PackageBuild { - job_id: number; - id: string; - caller_id: number; - platform: string; - build_type: string; - created: string; - finished: string; - state: string; - commit: any; - stack: any; - profile_tag: string; - automation_id: number; - environment_id: number; - native_config_id: number; - automation_name: string; - environment_name: string; - native_config_name: string; - distribution_credential_name: string; - job: any; - distribution_trace: string; - artifacts: Artifact[]; -} - -interface DownloadUrl { - url: string | null; -} - -export class BuildCommand extends Command { - async getMetadata(): Promise { - const dashUrl = this.env.config.getDashUrl(); - - return { - name: 'build', - type: 'project', - groups: [MetadataGroup.PAID, MetadataGroup.DEPRECATED], - summary: 'Create a package build on Appflow', - description: ` -This command creates a package build on Appflow. While the build is running, it prints the remote build log to the terminal. If the build is successful, it downloads the created app package file in the current directory. Downloading build artifacts can be skipped by supplying the flag ${input('skip-download')}. - -Apart from ${input('--commit')}, every option can be specified using the full name setup within the Dashboard[^dashboard]. - -The ${input('--signing-certificate')} option is mandatory for any iOS build but not for Android debug builds. - -Customizing the build: -- The ${input('--environment')} and ${input('--native-config')} options can be used to customize the groups of values exposed to the build. -- Override the preferred platform with ${input('--build-stack')}. This is useful for building older iOS apps. - -Deploying the build to an App Store: -- The ${input('--destination')} option can be used to deliver the app created by the build to the configured App Store. \ -This can be used only together with build type ${input('release')} for Android and build types ${input('app-store')} or ${input('enterprise')} for iOS. - -Downloading build artifacts: -- By default once the build is complete, all artifacts are downloaded for the selected platform. ${input('aab')} and ${input('apk')} for Android \ -${input('ipa')} and ${input('dsym')} for iOS. -- The ${input('--artifact-type')} option can be used to limit artifact downloads to only of that type. For instance, with Android, you can specify ${input('aab')} \ -if you do not wish to download ${input('apk')}. -`, - footnotes: [ - { - id: 'dashboard', - url: dashUrl, - }, - ], - exampleCommands: [ - 'android debug', - 'ios development --signing-certificate="iOS Signing Certificate Name"', - 'android debug --environment="My Custom Environment Name"', - 'android debug --native-config="My Custom Native Config Name"', - 'android debug --commit=2345cd3305a1cf94de34e93b73a932f25baac77c', - 'android debug --artifact-type=aab', - 'android debug --skip-download', - 'android debug --aab-name="my-app-prod.aab" --apk-name="my-app-prod.apk"', - 'ios development --signing-certificate="iOS Signing Certificate Name" --build-stack="iOS - Xcode 9"', - 'ios development --signing-certificate="iOS Signing Certificate Name" --ipa-name=my_custom_file_name.ipa', - 'ios app-store --signing-certificate="iOS Signing Certificate Name" --destination="Apple App Store Destination"', - ], - inputs: [ - { - name: 'platform', - summary: `The platform to package (${PLATFORMS.map(v => input(v)).join(', ')})`, - validators: [validators.required, contains(PLATFORMS, {})], - }, - { - name: 'type', - summary: `The build type (${BUILD_TYPES.map(v => input(v)).join(', ')})`, - validators: [validators.required, contains(BUILD_TYPES, {})], - }, - ], - options: [ - { - name: 'signing-certificate', - summary: 'Signing certificate', - type: String, - spec: { value: 'name' }, - }, - { - name: 'environment', - summary: 'The group of environment variables exposed to your build', - type: String, - spec: { value: 'name' }, - }, - { - name: 'native-config', - summary: 'The group of native config variables exposed to your build', - type: String, - spec: { value: 'name' }, - }, - { - name: 'destination', - summary: 'The configuration to deploy the build artifact to the app store', - type: String, - spec: { value: 'name' }, - }, - { - name: 'commit', - summary: 'Commit (defaults to HEAD)', - type: String, - groups: [MetadataGroup.ADVANCED], - spec: { value: 'sha1' }, - }, - { - name: 'build-stack', - summary: `Target platform (${TARGET_PLATFORM.map(v => input(`"${v}"`)).join(', ')})`, - type: String, - groups: [MetadataGroup.ADVANCED], - spec: { value: 'name' }, - }, - { - name: 'build-file-name', - summary: 'The name for the downloaded build file', - type: String, - groups: [MetadataGroup.DEPRECATED], - spec: { value: 'name' }, - }, - { - name: 'ipa-name', - summary: 'The name for the downloaded ipa file', - type: String, - groups: [MetadataGroup.ADVANCED], - spec: { value: 'name' }, - }, - { - name: 'dsym-name', - summary: 'The name for the downloaded dsym file', - type: String, - groups: [MetadataGroup.ADVANCED], - spec: { value: 'name' }, - }, - { - name: 'apk-name', - summary: 'The name for the downloaded apk file', - type: String, - groups: [MetadataGroup.ADVANCED], - spec: { value: 'name' }, - }, - { - name: 'aab-name', - summary: 'The name for the downloaded aab file', - type: String, - groups: [MetadataGroup.ADVANCED], - spec: { value: 'name' }, - }, - { - name: 'artifact-type', - summary: `The artifact type (${ARTIFACT_TYPES.map(v => input(v)).join(', ')})`, - type: String, - spec: { value: 'name' }, - }, - { - name: 'skip-download', - summary: `Skip downloading build artifacts after command succeeds.`, - type: Boolean, - spec: { value: 'name' }, - default: false, - }, - ], - }; - } - - async preRun(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - this.env.log.warn(IONIC_CLOUD_CLI_MIGRATION); - - if (!inputs[0]) { - const platformInput = await this.env.prompt({ - type: 'list', - name: 'platform', - choices: PLATFORMS, - message: `Platform to package:`, - validate: v => combine(validators.required, contains(PLATFORMS, {}))(v), - }); - - inputs[0] = platformInput; - } - - const buildTypes = inputs[0] === 'ios' ? IOS_BUILD_TYPES : ANDROID_BUILD_TYPES; - - // validate that the build type is valid for the platform - let reenterBuildType = false; - if (inputs[1] && !buildTypes.includes(inputs[1])) { - reenterBuildType = true; - this.env.log.nl(); - this.env.log.warn(`Build type ${strong(inputs[1])} incompatible for ${strong(inputs[0])}; please choose a correct one`); - } - - if (!inputs[1] || reenterBuildType) { - const typeInput = await this.env.prompt({ - type: 'list', - name: 'type', - choices: buildTypes, - message: `Build type:`, - validate: v => combine(validators.required, contains(buildTypes, {}))(v), - }); - - inputs[1] = typeInput; - } - - if (options['target-platform']) { - this.env.log.warn(`The ${input('--target-platform')} option has been deprecated. Please use ${input('--build-stack')}.`); - options['build-stack'] = options['target-platform']; - } - - if (options['security-profile']) { - this.env.log.warn(`The ${input('--security-profile')} option has been deprecated. Please use ${input('--signing-certificate')}.`); - options['signing-certificate'] = options['security-profile']; - } - - // the signing certificate is mandatory for iOS packages, so prompting if it is missing - if (inputs[0] === 'ios' && !options['signing-certificate']) { - if (this.env.flags.interactive) { - this.env.log.nl(); - this.env.log.warn(`A signing certificate is mandatory to build an iOS package`); - } - - const securityProfileOption = await this.env.prompt({ - type: 'input', - name: 'signing-certificate', - message: `Signing Certificate Name:`, - }); - - options['signing-certificate'] = securityProfileOption; - } - - // if destination is present, validate that a proper build type has been been specified - if (options['destination'] && !APP_STORE_COMPATIBLE_TYPES.includes(inputs[1])) { - throw new FatalException(`Build with type ${strong(String(inputs[1]))} cannot be deployed to App Store`); - } - - if (options['artifact-type'] && (Array.isArray(options['artifact-type']) || typeof options['artifact-type'] === 'string')) { - const artifactTypes = Array.isArray(options['artifact-type']) ? options['artifact-type'] : [options['artifact-type']]; - let unsupported: string[]; - - switch (inputs[0]) { - case 'android': - unsupported = artifactTypes.filter(type => !ANDROID_ARTIFACT_TYPES.includes(type)); - break; - case 'ios': - unsupported = artifactTypes.filter(type => !IOS_ARTIFACT_TYPES.includes(type)); - break; - default: - throw new FatalException(`Unsupported platform ${inputs[0]}`); - } - - if (unsupported.length) { - throw new FatalException(`Unsupported artifact types for platform ${inputs[0]}: ${[...unsupported]}`); - } - } - } - - async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - if (!this.project) { - throw new FatalException(`Cannot run ${input('ionic package build')} outside a project directory.`); - } - - const token = await this.env.session.getUserToken(); - const appflowId = await this.project.requireAppflowId(); - const [ platform, buildType ] = inputs; - - if (!options.commit) { - options.commit = (await this.env.shell.output('git', ['rev-parse', 'HEAD'], { cwd: this.project.directory })).trim(); - debug(`Commit hash: ${strong(options.commit)}`); - } - - let build = await this.createPackageBuild(appflowId, token, platform, buildType, options); - const buildId = build.job_id; - - const details = columnar([ - ['App ID', strong(appflowId)], - ['Build ID', strong(buildId.toString())], - ['Commit', strong(`${build.commit.sha.substring(0, 6)} ${build.commit.note}`)], - ['Target Platform', strong(build.stack.friendly_name)], - ['Build Type', strong(build.build_type)], - ['Artifact Type(s)', options['artifact-type'] ? strong(`${options['artifact-type']}`.toUpperCase()) : weak('all available')], - ['Security Profile', build.profile_tag ? strong(build.profile_tag) : weak('not set')], - ['Environment', build.environment_name ? strong(build.environment_name) : weak('not set')], - ['Native Config', build.native_config_name ? strong(build.native_config_name) : weak('not set')], - ['Destination', build.distribution_credential_name ? strong(build.distribution_credential_name) : weak('not set')], - ], { vsep: ':' }); - - this.env.log.ok( - `Build created\n` + - details + '\n\n' - ); - - build = await this.tailBuildLog(appflowId, buildId, token); - if (build.state !== 'success') { - throw new Error(`Build ${build.state}`); - } - - if (options['skip-download']) { - return; - } - - const availableArtifactTypes = build.artifacts.map((artifact) => artifact.artifact_type); - - if (Array.isArray(options['artifact-type'])) { - let errors = []; - for (const artifactType of new Set(options['artifact-type'])) { - try { - await this.downloadArtifact(appflowId, buildId, artifactType, token, options); - } catch (error) { - errors.push(error); - } - } - - if (errors.length) { - throw new FatalException(`There were issues downloading artifacts: ${errors.join('\n')}`); - } - - } else if (typeof options['artifact-type'] == 'string') { - await this.downloadArtifact(appflowId, buildId, options['artifact-type'], token, options); - } else { - for (const artifactType of availableArtifactTypes) { - await this.downloadArtifact(appflowId, buildId, artifactType.toUpperCase(), token, options); - } - } - } - - async sanitizeString(value: string | string[] | boolean | null | undefined): Promise { - - if (!value || typeof (value) !== 'string') { - return ''; - } - - if (!fileUtils.isValidFileName(value)) { - throw new FatalException(`${strong(String(value))} is not a valid file name`); - } - - return String(value); - } - - async createPackageBuild(appflowId: string, token: string, platform: string, buildType: string, options: CommandLineOptions): Promise { - const { req } = await this.env.client.make('POST', `/apps/${appflowId}/packages/verbose_post`); - req.set('Authorization', `Bearer ${token}`).send({ - platform, - build_type: buildType, - commit_sha: options.commit, - stack_name: options['build-stack'], - profile_name: options['signing-certificate'], - environment_name: options.environment, - native_config_name: options['native-config'], - distribution_credential_name: options.destination, - }); - - try { - const res = await this.env.client.do(req); - return res.data as PackageBuild; - } catch (e) { - if (isSuperAgentError(e)) { - if (e.response.status === 401) { - this.env.log.error('Try logging out and back in again.'); - } - const apiErrorMessage = (e.response.body.error && e.response.body.error.message) ? e.response.body.error.message : 'Api Error'; - throw new FatalException(`Unable to create build: ` + apiErrorMessage); - } else { - throw e; - } - } - } - - async getPackageBuild(appflowId: string, buildId: number, token: string): Promise { - const { req } = await this.env.client.make('GET', `/apps/${appflowId}/packages/${buildId}`); - req.set('Authorization', `Bearer ${token}`).send(); - - try { - const res = await this.env.client.do(req); - return res.data as PackageBuild; - } catch (e) { - if (isSuperAgentError(e)) { - if (e.response.status === 401) { - this.env.log.error('Try logging out and back in again.'); - } - const apiErrorMessage = (e.response.body.error && e.response.body.error.message) ? e.response.body.error.message : 'Api Error'; - throw new FatalException(`Unable to get build ${buildId}: ` + apiErrorMessage); - } else { - throw e; - } - } - } - - async getDownloadUrl(appflowId: string, buildId: number, artifactType: string, token: string): Promise { - const { req } = await this.env.client.make('GET', `/apps/${appflowId}/packages/${buildId}/download?artifact_type=${artifactType}`); - req.set('Authorization', `Bearer ${token}`).send(); - - try { - const res = await this.env.client.do(req); - return res.data as DownloadUrl; - } catch (e) { - if (isSuperAgentError(e)) { - if (e.response.status === 401) { - this.env.log.error('Try logging out and back in again.'); - } - const apiErrorMessage = (e.response.body.error && e.response.body.error.message) ? e.response.body.error.message : 'Api Error'; - throw new FatalException(`Unable to get download URL for build ${buildId}: ` + apiErrorMessage); - } else { - throw e; - } - } - } - - async tailBuildLog(appflowId: string, buildId: number, token: string): Promise { - let build; - let start = 0; - const ws = this.env.log.createWriteStream(LOGGER_LEVELS.INFO, false); - - let isCreatedMessage = false; - let errorsEncountered = 0; - while (!(build && ['success', 'failed', 'canceled'].includes(build.state))) { - try { - await sleep(5000); - build = await this.getPackageBuild(appflowId, buildId, token); - if (build && build.state === 'created' && !isCreatedMessage) { - ws.write(chalk.yellow('Concurrency limit reached: build will start as soon as other builds finish.')); - isCreatedMessage = true; - } - const trace = build.job.trace; - if (trace.length > start) { - ws.write(trace.substring(start)); - start = trace.length; - } - errorsEncountered = 0; - } catch (e) { - // Retry up to 3 times in the case of an error. - errorsEncountered++; - ws.write(chalk.yellow(`Encountered error: ${e} while fetching build data retrying.`)); - if (errorsEncountered >= 3) { - ws.write(chalk.red(`Encountered ${errorsEncountered} errors in a row. Job will now fail.`)); - throw e; - } - } - } - ws.end(); - - return build; - } - - async downloadBuild(url: string, filename: string): Promise { - const { req } = await createRequest('GET', url, this.env.config.getHTTPConfig()); - - if (!filename) { - req.on('response', res => { - const contentDisposition = res.header['content-disposition']; - filename = contentDisposition ? contentDisposition.split('=')[1].replace(/([/?<>*|\"])/g, '_') : 'output.bin'; - }); - } - - const tmpFile = tmpfilepath('ionic-package-build'); - const ws = fs.createWriteStream(tmpFile); - await download(req, ws, {}); - fs.copyFileSync(tmpFile, filename); - fs.unlinkSync(tmpFile); - return filename; - } - - async downloadArtifact(appflowId: string, buildId: number, artifactType: string, token: string, options: CommandLineOptions) { - - const url = await this.getDownloadUrl(appflowId, buildId, artifactType.toUpperCase(), token); - - if (!url.url) { - throw new Error(`Artifact type '${artifactType}' not found`); - } - - let customBuildFileName = ''; - - if (options['build-file-name']) { - this.env.log.warn(`The ${input('--build-file-name')} option has been deprecated. Please use ${input('--ipa-name')}, ${input('--apk-name')} or ${input('--{ artifact }-name')}.`); - customBuildFileName = await this.sanitizeString(options['build-file-name']); - } else { - customBuildFileName = await this.sanitizeString(options[`${artifactType.toLowerCase()}-name`]); - } - - const filename = await this.downloadBuild(url.url, customBuildFileName); - this.env.log.ok(`Artifact downloaded: ${filename}`); - } -} diff --git a/packages/@ionic/cli/src/commands/package/deploy.ts b/packages/@ionic/cli/src/commands/package/deploy.ts deleted file mode 100644 index 3712b0e669..0000000000 --- a/packages/@ionic/cli/src/commands/package/deploy.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { - CommandLineInputs, - CommandLineOptions, - MetadataGroup, - combine, - validators, -} from '@ionic/cli-framework'; -import { LOGGER_LEVELS } from '@ionic/cli-framework-output'; -import { sleep } from '@ionic/utils-process'; -import { columnar } from '@ionic/utils-terminal'; -import * as chalk from 'chalk'; - -import { CommandMetadata } from '../../definitions'; -import { isSuperAgentError } from '../../guards'; -import { input, strong } from '../../lib/color'; -import { Command } from '../../lib/command'; -import { FatalException } from '../../lib/errors'; -import { IONIC_CLOUD_CLI_MIGRATION } from '../../lib/updates'; - -import { PackageBuild } from './build'; - -interface BinaryDeployment { - id: number; - user: any; - build: any; - type: string; - distributionCredential: any; - destination: string; - message: string; - distributionBuildId: number; - status: string; -} - -interface DistributionBuild { - job_id: number; - id: string; - caller_id: number; - created: string; - state: string; - distribution_credential_name: string; - package_build: PackageBuild; - binary_deployment: BinaryDeployment; - distribution_trace: string; -} - -export class DeployCommand extends Command { - async getMetadata(): Promise { - const dashUrl = this.env.config.getDashUrl(); - - return { - name: 'deploy', - type: 'project', - groups: [MetadataGroup.PAID, MetadataGroup.DEPRECATED], - summary: 'Deploys a binary to a destination, such as an app store using Appflow', - description: ` -This command deploys a binary to a destination using Appflow. While running, the remote log is printed to the terminal. - -The command takes two parameters: the numeric ID of the package build that previously created the binary and the name of the destination where the binary is going to be deployed. -Both can be retrieved from the Dashboard[^dashboard]. - `, - footnotes: [ - { - id: 'dashboard', - url: dashUrl, - }, - ], - exampleCommands: ['123456789 "My app store destination"'], - inputs: [ - { - name: 'build-id', - summary: `The build id of the desired successful package build`, - validators: [validators.required, validators.numeric], - }, - { - name: 'destination', - summary: - 'The destination to deploy the build artifact to the app store', - validators: [validators.required], - }, - ], - }; - } - - async preRun( - inputs: CommandLineInputs, - options: CommandLineOptions - ): Promise { - this.env.log.warn(IONIC_CLOUD_CLI_MIGRATION); - if (!inputs[0]) { - const buildIdInputInput = await this.env.prompt({ - type: 'input', - name: 'build-id', - message: `The build ID on Appflow:`, - validate: v => combine(validators.required, validators.numeric)(v), - }); - inputs[0] = buildIdInputInput; - } - - if (!inputs[1]) { - const destinationInputInput = await this.env.prompt({ - type: 'input', - name: 'destination', - message: `The destination to deploy the build artifact to the app store:`, - validate: v => combine(validators.required)(v), - }); - inputs[1] = destinationInputInput; - } - } - - async run( - inputs: CommandLineInputs, - options: CommandLineOptions - ): Promise { - if (!this.project) { - throw new FatalException( - `Cannot run ${input( - 'ionic package build' - )} outside a project directory.` - ); - } - - const token = await this.env.session.getUserToken(); - const appflowId = await this.project.requireAppflowId(); - const [buildId, destination] = inputs; - - let build: - | DistributionBuild - | PackageBuild = await this.createDeploymentBuild( - appflowId, - token, - buildId, - destination - ); - const distBuildID = build.job_id; - - const details = columnar( - [ - ['App ID', strong(appflowId)], - ['Deployment ID', strong(build.binary_deployment.id.toString())], - ['Package Build ID', strong(buildId.toString())], - ['Destination', strong(build.distribution_credential_name)], - ], - { vsep: ':' } - ); - - this.env.log.ok(`Deployment initiated\n` + details + '\n\n'); - - build = await this.tailBuildLog(appflowId, distBuildID, token); - if (build.state !== 'success') { - throw new Error('Build failed'); - } - } - - async createDeploymentBuild( - appflowId: string, - token: string, - buildId: string, - destination: string - ): Promise { - const { req } = await this.env.client.make( - 'POST', - `/apps/${appflowId}/distributions/verbose_post` - ); - req.set('Authorization', `Bearer ${token}`).send({ - package_build_id: buildId, - distribution_credential_name: destination, - }); - - try { - const res = await this.env.client.do(req); - return res.data as DistributionBuild; - } catch (e) { - if (isSuperAgentError(e)) { - if (e.response.status === 401) { - this.env.log.error('Try logging out and back in again.'); - } - const apiErrorMessage = - e.response.body.error && e.response.body.error.message - ? e.response.body.error.message - : 'Api Error'; - throw new FatalException( - `Unable to create deployment build: ` + apiErrorMessage - ); - } else { - throw e; - } - } - } - - async tailBuildLog( - appflowId: string, - buildId: number, - token: string - ): Promise { - let build; - let start = 0; - const ws = this.env.log.createWriteStream(LOGGER_LEVELS.INFO, false); - - let isCreatedMessage = false; - while ( - !(build && (build.state === 'success' || build.state === 'failed')) - ) { - await sleep(5000); - build = await this.getGenericBuild(appflowId, buildId, token); - if (build && build.state === 'created' && !isCreatedMessage) { - ws.write( - chalk.yellow( - 'Concurrency limit reached: build will start as soon as other builds finish.' - ) - ); - isCreatedMessage = true; - } - const trace = build.distribution_trace; - if (trace && trace.length > start) { - ws.write(trace.substring(start)); - start = trace.length; - } - } - ws.end(); - - return build; - } - - async getGenericBuild( - appflowId: string, - buildId: number, - token: string - ): Promise { - const { req } = await this.env.client.make( - 'GET', - `/apps/${appflowId}/builds/${buildId}` - ); - req.set('Authorization', `Bearer ${token}`).send(); - - try { - const res = await this.env.client.do(req); - return res.data as PackageBuild; - } catch (e) { - if (isSuperAgentError(e)) { - if (e.response.status === 401) { - this.env.log.error('Try logging out and back in again.'); - } - const apiErrorMessage = - e.response.body.error && e.response.body.error.message - ? e.response.body.error.message - : 'Api Error'; - throw new FatalException( - `Unable to get build ${buildId}: ` + apiErrorMessage - ); - } else { - throw e; - } - } - } -} diff --git a/packages/@ionic/cli/src/commands/package/index.ts b/packages/@ionic/cli/src/commands/package/index.ts deleted file mode 100644 index d86ce5b174..0000000000 --- a/packages/@ionic/cli/src/commands/package/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MetadataGroup, NamespaceMetadata } from '@ionic/cli-framework'; - -import { strong } from '../../lib/color'; -import { CommandMap, Namespace } from '../../lib/namespace'; -import { IONIC_CLOUD_CLI_MIGRATION } from '../../lib/updates'; - -export class PackageNamespace extends Namespace { - async getMetadata(): Promise { - return { - name: 'package', - summary: 'Appflow package functionality', - description: ` -${IONIC_CLOUD_CLI_MIGRATION} -Interface to execute commands about package builds and deployments on Ionic Appflow. - -Appflow package documentation: -- Overview: ${strong('https://ion.link/appflow-package-docs')} - `, - groups: [MetadataGroup.PAID, MetadataGroup.DEPRECATED], - }; - } - - async getCommands(): Promise { - return new CommandMap([ - ['build', async () => { const { BuildCommand } = await import('./build'); return new BuildCommand(this); }], - ['deploy', async () => { const { DeployCommand } = await import('./deploy'); return new DeployCommand(this); }], - ]); - } -} diff --git a/packages/@ionic/cli/src/commands/serve.ts b/packages/@ionic/cli/src/commands/serve.ts index e40b74f914..1cb20a381c 100644 --- a/packages/@ionic/cli/src/commands/serve.ts +++ b/packages/@ionic/cli/src/commands/serve.ts @@ -3,11 +3,10 @@ import { sleepForever } from '@ionic/utils-process'; import * as lodash from 'lodash'; import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, CommandPreRun } from '../definitions'; -import { input, strong, weak } from '../lib/color'; +import { input } from '../lib/color'; import { Command } from '../lib/command'; import { FatalException, RunnerException } from '../lib/errors'; -import { getFullCommandParts } from '../lib/executor'; -import { BROWSERS, COMMON_SERVE_COMMAND_OPTIONS, DEFAULT_LAB_PORT } from '../lib/serve'; +import { BROWSERS, COMMON_SERVE_COMMAND_OPTIONS } from '../lib/serve'; export class ServeCommand extends Command implements CommandPreRun { async getMetadata(): Promise { @@ -15,22 +14,6 @@ export class ServeCommand extends Command implements CommandPreRun { let options: CommandMetadataOption[] = [ ...COMMON_SERVE_COMMAND_OPTIONS, - { - name: 'lab-host', - summary: 'Use specific host for Ionic Lab server', - default: 'localhost', - groups: [MetadataGroup.HIDDEN, MetadataGroup.ADVANCED], - spec: { value: 'host' }, - hint: weak('(--lab)'), - }, - { - name: 'lab-port', - summary: 'Use specific port for Ionic Lab server', - default: DEFAULT_LAB_PORT.toString(), - groups: [MetadataGroup.HIDDEN, MetadataGroup.ADVANCED], - spec: { value: 'port' }, - hint: weak('(--lab)'), - }, { name: 'open', summary: 'Do not open a browser window', @@ -51,13 +34,6 @@ export class ServeCommand extends Command implements CommandPreRun { groups: [MetadataGroup.ADVANCED], spec: { value: 'path' }, }, - { - name: 'lab', - summary: 'Test your apps on multiple platform types in the browser', - type: Boolean, - aliases: ['l'], - groups: [MetadataGroup.HIDDEN], - }, ]; const exampleCommands = ['', '--external']; @@ -66,9 +42,7 @@ export class ServeCommand extends Command implements CommandPreRun { let description = ` Easily spin up a development server which launches in your browser. It watches for changes in your source files and automatically reloads with the updated build. -By default, ${input('ionic serve')} boots up a development server on ${input('localhost')}. To serve to your LAN, specify the ${input('--external')} option, which will use all network interfaces and print the external address(es) on which your app is being served. - -Try the ${input('--lab')} option to see multiple platforms at once.`; +By default, ${input('ionic serve')} boots up a development server on ${input('localhost')}. To serve to your LAN, specify the ${input('--external')} option, which will use all network interfaces and print the external address(es) on which your app is being served.`; const runner = this.project && await this.project.getServeRunner(); @@ -94,17 +68,6 @@ Try the ${input('--lab')} option to see multiple platforms at once.`; } async preRun(inputs: CommandLineInputs, options: CommandLineOptions, { location }: CommandInstanceInfo): Promise { - const parts = getFullCommandParts(location); - const alias = lodash.last(parts); - - if (alias === 'lab') { - options['lab'] = true; - } - - if (options['lab']) { - this.env.log.warn(`The ${input('--lab')} option has been deprecated and will be removed in an upcoming major release of the Ionic CLI. Please visit ${strong('https://ionicframework.com/docs/developing/previewing')} for alternatives to Ionic Lab.`); - } - if (options['nolivereload']) { this.env.log.warn(`The ${input('--nolivereload')} option has been deprecated. Please use ${input('--no-livereload')}.`); options['livereload'] = false; @@ -139,7 +102,7 @@ Try the ${input('--lab')} option to see multiple platforms at once.`; const runnerOpts = runner.createOptionsFromCommandLine(inputs, options); await runner.run(runnerOpts); - } catch (e) { + } catch (e: any) { if (e instanceof RunnerException) { throw new FatalException(e.message); } @@ -150,22 +113,3 @@ Try the ${input('--lab')} option to see multiple platforms at once.`; await sleepForever(); } } - -export class LabCommand extends ServeCommand { - async getMetadata(): Promise { - const metadata = await super.getMetadata(); - const groups = [...metadata.groups || [], MetadataGroup.HIDDEN]; - const exampleCommands = [...metadata.exampleCommands || []].filter(c => !c.includes('--lab')); - - return { - ...metadata, - summary: 'Start Ionic Lab for multi-platform dev/testing', - description: ` -Start an instance of ${strong('Ionic Lab')}, a tool for developing Ionic apps for multiple platforms at once side-by-side. - -${input('ionic lab')} is just a convenient shortcut for ${input('ionic serve --lab')}.`, - groups, - exampleCommands, - }; - } -} diff --git a/packages/@ionic/cli/src/commands/ssh/add.ts b/packages/@ionic/cli/src/commands/ssh/add.ts index 4b4e6c88d2..4bbb3d44d2 100644 --- a/packages/@ionic/cli/src/commands/ssh/add.ts +++ b/packages/@ionic/cli/src/commands/ssh/add.ts @@ -1,4 +1,4 @@ -import { validators } from '@ionic/cli-framework'; +import { validators, MetadataGroup } from '@ionic/cli-framework'; import { pathAccessible, pathExists } from '@ionic/utils-fs'; import { expandPath, prettyPath } from '@ionic/utils-terminal'; import * as fs from 'fs'; @@ -33,6 +33,7 @@ export class SSHAddCommand extends SSHBaseCommand implements CommandPreRun { type: Boolean, }, ], + groups: [MetadataGroup.DEPRECATED], }; } @@ -62,7 +63,7 @@ export class SSHAddCommand extends SSHBaseCommand implements CommandPreRun { try { [ pubkey ] = await parsePublicKeyFile(pubkeyPath); - } catch (e) { + } catch (e: any) { if (e.code === 'ENOENT') { throw new FatalException( `${strong(prettyPath(pubkeyPath))} does not appear to exist. Please specify a valid SSH public key.\n` + @@ -85,7 +86,7 @@ export class SSHAddCommand extends SSHBaseCommand implements CommandPreRun { try { const key = await sshkeyClient.create({ pubkey }); this.env.log.ok(`Your public key (${strong(key.fingerprint)}) has been added to Ionic!`); - } catch (e) { + } catch (e: any) { if (isSuperAgentError(e) && e.response.status === 409) { this.env.log.msg('Pubkey already added to Ionic.'); } else { diff --git a/packages/@ionic/cli/src/commands/ssh/base.ts b/packages/@ionic/cli/src/commands/ssh/base.ts index dadbd7fd03..b923da3c6f 100644 --- a/packages/@ionic/cli/src/commands/ssh/base.ts +++ b/packages/@ionic/cli/src/commands/ssh/base.ts @@ -8,7 +8,7 @@ export abstract class SSHBaseCommand extends Command { async checkForOpenSSH() { try { await this.env.shell.run('ssh', ['-V'], { stdio: 'ignore', showCommand: false, fatalOnNotFound: false }); - } catch (e) { + } catch (e: any) { if (!(e instanceof SubprocessError && e.code === ERROR_COMMAND_NOT_FOUND)) { throw e; } diff --git a/packages/@ionic/cli/src/commands/ssh/delete.ts b/packages/@ionic/cli/src/commands/ssh/delete.ts index 39ef5b73ef..571a2cb969 100644 --- a/packages/@ionic/cli/src/commands/ssh/delete.ts +++ b/packages/@ionic/cli/src/commands/ssh/delete.ts @@ -1,4 +1,4 @@ -import { validators } from '@ionic/cli-framework'; +import { validators, MetadataGroup } from '@ionic/cli-framework'; import { CommandLineInputs, CommandLineOptions, CommandMetadata, CommandPreRun } from '../../definitions'; import { input, strong } from '../../lib/color'; @@ -18,6 +18,7 @@ export class SSHDeleteCommand extends SSHBaseCommand implements CommandPreRun { validators: [validators.required], }, ], + groups: [MetadataGroup.DEPRECATED], }; } diff --git a/packages/@ionic/cli/src/commands/ssh/generate.ts b/packages/@ionic/cli/src/commands/ssh/generate.ts index 370da34ddc..20600efd9e 100644 --- a/packages/@ionic/cli/src/commands/ssh/generate.ts +++ b/packages/@ionic/cli/src/commands/ssh/generate.ts @@ -44,6 +44,7 @@ export class SSHGenerateCommand extends SSHBaseCommand implements CommandPreRun groups: [MetadataGroup.ADVANCED], }, ], + groups: [MetadataGroup.DEPRECATED], }; } diff --git a/packages/@ionic/cli/src/commands/ssh/index.ts b/packages/@ionic/cli/src/commands/ssh/index.ts index b00f68d1df..3e92e81832 100644 --- a/packages/@ionic/cli/src/commands/ssh/index.ts +++ b/packages/@ionic/cli/src/commands/ssh/index.ts @@ -1,3 +1,4 @@ +import { MetadataGroup } from '@ionic/cli-framework'; import { input } from '../../lib/color'; import { CommandMap, Namespace } from '../../lib/namespace'; @@ -12,6 +13,8 @@ export class SSHNamespace extends Namespace { These commands help automate your SSH configuration for Ionic. As an alternative, SSH configuration can be done entirely manually by visiting your Personal Settings[^dashboard-settings-ssh-keys]. To begin, run ${input('ionic ssh setup')}, which lets you use existing keys or generate new ones just for Ionic. + +Deprecated. Developers should configure SSH by visiting their Personal Settings at https://dashboard.ionicframework.com/settings/ssh-keys. `, footnotes: [ { @@ -19,6 +22,7 @@ To begin, run ${input('ionic ssh setup')}, which lets you use existing keys or g url: `${dashUrl}/settings/ssh-keys`, }, ], + groups: [MetadataGroup.DEPRECATED], }; } diff --git a/packages/@ionic/cli/src/commands/ssh/list.ts b/packages/@ionic/cli/src/commands/ssh/list.ts index 15496cff43..29e74ece6c 100644 --- a/packages/@ionic/cli/src/commands/ssh/list.ts +++ b/packages/@ionic/cli/src/commands/ssh/list.ts @@ -1,3 +1,4 @@ +import { MetadataGroup } from '@ionic/cli-framework'; import { columnar } from '@ionic/utils-terminal'; import { COLUMNAR_OPTIONS } from '../../constants'; @@ -19,6 +20,7 @@ export class SSHListCommand extends SSHBaseCommand implements CommandPreRun { type: Boolean, }, ], + groups: [MetadataGroup.DEPRECATED], }; } diff --git a/packages/@ionic/cli/src/commands/ssh/setup.ts b/packages/@ionic/cli/src/commands/ssh/setup.ts index f1a21061c0..b8abd7a680 100644 --- a/packages/@ionic/cli/src/commands/ssh/setup.ts +++ b/packages/@ionic/cli/src/commands/ssh/setup.ts @@ -1,3 +1,4 @@ +import { MetadataGroup } from '@ionic/cli-framework'; import { pathExists } from '@ionic/utils-fs'; import { prettyPath } from '@ionic/utils-terminal'; @@ -31,6 +32,7 @@ If you are having issues setting up SSH keys, please get in touch with our Suppo url: 'https://ion.link/support-request', }, ], + groups: [MetadataGroup.DEPRECATED], }; } diff --git a/packages/@ionic/cli/src/commands/ssh/use.ts b/packages/@ionic/cli/src/commands/ssh/use.ts index f251b703fe..5e30aed4c2 100644 --- a/packages/@ionic/cli/src/commands/ssh/use.ts +++ b/packages/@ionic/cli/src/commands/ssh/use.ts @@ -1,4 +1,4 @@ -import { validators } from '@ionic/cli-framework'; +import { validators, MetadataGroup } from '@ionic/cli-framework'; import { fileToString, writeFile } from '@ionic/utils-fs'; import { expandPath, prettyPath } from '@ionic/utils-terminal'; @@ -32,6 +32,7 @@ Before making changes, ${input('ionic ssh use')} will print a diff and ask for p validators: [validators.required], }, ], + groups: [MetadataGroup.DEPRECATED], }; } @@ -43,7 +44,7 @@ Before making changes, ${input('ionic ssh use')} will print a diff and ask for p try { await validatePrivateKey(keyPath); - } catch (e) { + } catch (e: any) { if (e === ERROR_SSH_MISSING_PRIVKEY) { throw new FatalException( `${strong(prettyPath(keyPath))} does not appear to exist. Please specify a valid SSH private key.\n` + diff --git a/packages/@ionic/cli/src/commands/ssl/base.ts b/packages/@ionic/cli/src/commands/ssl/base.ts index e2fb233ef4..d449ff13d1 100644 --- a/packages/@ionic/cli/src/commands/ssl/base.ts +++ b/packages/@ionic/cli/src/commands/ssl/base.ts @@ -8,7 +8,7 @@ export abstract class SSLBaseCommand extends Command { async checkForOpenSSL() { try { await this.env.shell.run('openssl', ['version'], { stdio: 'ignore', showCommand: false, fatalOnNotFound: false }); - } catch (e) { + } catch (e: any) { if (!(e instanceof SubprocessError && e.code === ERROR_COMMAND_NOT_FOUND)) { throw e; } diff --git a/packages/@ionic/cli/src/commands/ssl/generate.ts b/packages/@ionic/cli/src/commands/ssl/generate.ts index 485923bc1b..acb1a3e595 100644 --- a/packages/@ionic/cli/src/commands/ssl/generate.ts +++ b/packages/@ionic/cli/src/commands/ssl/generate.ts @@ -53,6 +53,8 @@ Uses OpenSSL to create a self-signed certificate for ${strong('localhost')} (by After the certificate is generated, you will still need to add it to your system or browser as a trusted certificate. The default directory for ${input('--key-path')} and ${input('--cert-path')} is ${input('.ionic/ssl/')}. + +Deprecated. Developers should generate an SSL certificate locally and then configure it using their project tooling such as Vite or Angular CLI. `, options: [ { @@ -110,7 +112,7 @@ The default directory for ${input('--key-path')} and ${input('--cert-path')} is groups: [MetadataGroup.ADVANCED], }, ], - groups: [MetadataGroup.EXPERIMENTAL], + groups: [MetadataGroup.DEPRECATED], }; } diff --git a/packages/@ionic/cli/src/commands/ssl/index.ts b/packages/@ionic/cli/src/commands/ssl/index.ts index 89fb4bf472..21988e0309 100644 --- a/packages/@ionic/cli/src/commands/ssl/index.ts +++ b/packages/@ionic/cli/src/commands/ssl/index.ts @@ -8,7 +8,7 @@ export class SSLNamespace extends Namespace { return { name: 'ssl', summary: 'Commands for managing SSL keys & certificates', - groups: [MetadataGroup.EXPERIMENTAL], + groups: [MetadataGroup.DEPRECATED], description: ` These commands make it easy to manage SSL certificates for using HTTPS with ${input('ionic serve')}. `, diff --git a/packages/@ionic/cli/src/commands/start.ts b/packages/@ionic/cli/src/commands/start.ts index 672346fc82..2beb5db709 100644 --- a/packages/@ionic/cli/src/commands/start.ts +++ b/packages/@ionic/cli/src/commands/start.ts @@ -2,20 +2,45 @@ import { MetadataGroup, validators } from '@ionic/cli-framework'; import { isValidURL, slugify } from '@ionic/cli-framework/utils/string'; import { mkdir, pathExists, remove, unlink } from '@ionic/utils-fs'; import { columnar, prettyPath } from '@ionic/utils-terminal'; -import * as chalk from 'chalk'; -import * as Debug from 'debug'; +import chalk from 'chalk'; +import { debug as Debug } from 'debug'; import * as path from 'path'; -import { COLUMNAR_OPTIONS, PROJECT_FILE } from '../constants'; -import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandPreRun, IProject, IShellRunOptions, ProjectType, ResolvedStarterTemplate, StarterManifest } from '../definitions'; +import { COLUMNAR_OPTIONS, PROJECT_FILE, ANGULAR_STANDALONE } from '../constants'; +import { + CommandInstanceInfo, + CommandLineInputs, + CommandLineOptions, + CommandMetadata, + CommandPreRun, + IProject, + IShellRunOptions, + ProjectType, + ResolvedStarterTemplate, + StarterManifest, +} from '../definitions'; import { failure, input, strong } from '../lib/color'; import { Command } from '../lib/command'; import { FatalException } from '../lib/errors'; import { runCommand } from '../lib/executor'; -import { createProjectFromDetails, createProjectFromDirectory, isValidProjectId } from '../lib/project'; +import { + createProjectFromDetails, + createProjectFromDirectory, + isValidProjectId, +} from '../lib/project'; import { promptToSignup } from '../lib/session'; import { prependNodeModulesBinToPath } from '../lib/shell'; -import { AppSchema, STARTER_BASE_URL, STARTER_TEMPLATES, SUPPORTED_FRAMEWORKS, getAdvertisement, getStarterList, getStarterProjectTypes, readStarterManifest, verifyOptions } from '../lib/start'; +import { + AppSchema, + STARTER_BASE_URL, + STARTER_TEMPLATES, + SUPPORTED_FRAMEWORKS, + getAdvertisement, + getStarterList, + getStarterProjectTypes, + readStarterManifest, + verifyOptions, +} from '../lib/start'; import { emoji } from '../lib/utils/emoji'; import { createRequest } from '../lib/utils/http'; @@ -49,36 +74,53 @@ export class StartCommand extends Command implements CommandPreRun { description: ` This command creates a working Ionic app. It installs dependencies for you and sets up your project. -Running ${input('ionic start')} without any arguments will prompt you for information about your new project. - -The first argument is your app's ${input('name')}. Don't worry--you can always change this later. The ${input('--project-id')} is generated from ${input('name')} unless explicitly specified. - -The second argument is the ${input('template')} from which to generate your app. You can list all templates with the ${input('--list')} option. You can also specify a git repository URL for ${input('template')}, in which case the existing project will be cloned. - -Use the ${input('--type')} option to start projects using older versions of Ionic. For example, you can start an Ionic 3 project with ${input('--type=ionic-angular')}. Use ${input('--list')} to see all project types and templates. +Running ${input( + 'ionic start' + )} without any arguments will prompt you for information about your new project. + +The first argument is your app's ${input( + 'name' + )}. Don't worry--you can always change this later. The ${input( + '--project-id' + )} is generated from ${input('name')} unless explicitly specified. + +The second argument is the ${input( + 'template' + )} from which to generate your app. You can list all templates with the ${input( + '--list' + )} option. You can also specify a git repository URL for ${input( + 'template' + )}, in which case the existing project will be cloned. + +Use the ${input( + '--type' + )} option to start projects using a different JavaScript Framework. Use ${input( + '--list' + )} to see all project types and templates. `, exampleCommands: [ '', '--list', 'myApp', 'myApp blank', - 'myApp tabs --cordova', 'myApp tabs --capacitor', - 'myApp super --type=ionic-angular', - 'myApp blank --type=ionic1', - 'cordovaApp tabs --cordova', + 'myApp list --type=vue', '"My App" blank', '"Conference App" https://github.com/ionic-team/ionic-conference-app', ], inputs: [ { name: 'name', - summary: `The name of your new project (e.g. ${input('myApp')}, ${input('"My App"')})`, + summary: `The name of your new project (e.g. ${input( + 'myApp' + )}, ${input('"My App"')})`, validators: [validators.required], }, { name: 'template', - summary: `The starter template to use (e.g. ${['blank', 'tabs'].map(t => input(t)).join(', ')}; use ${input('--list')} to see all)`, + summary: `The starter template to use (e.g. ${['blank', 'tabs'] + .map((t) => input(t)) + .join(', ')}; use ${input('--list')} to see all)`, validators: [validators.required], }, ], @@ -91,14 +133,16 @@ Use the ${input('--type')} option to start projects using older versions of Ioni }, { name: 'type', - summary: `Type of project to start (e.g. ${getStarterProjectTypes().map(type => input(type)).join(', ')})`, + summary: `Type of project to start (e.g. ${getStarterProjectTypes() + .map((type) => input(type)) + .join(', ')})`, type: String, }, { name: 'cordova', summary: 'Include Cordova integration', type: Boolean, - groups: [MetadataGroup.DEPRECATED] + groups: [MetadataGroup.DEPRECATED], }, { name: 'capacitor', @@ -131,25 +175,34 @@ Use the ${input('--type')} option to start projects using older versions of Ioni }, { name: 'project-id', - summary: 'Specify a slug for your app (used for the directory name and package name)', + summary: + 'Specify a slug for your app (used for the directory name and package name)', groups: [MetadataGroup.ADVANCED], spec: { value: 'slug' }, }, { name: 'package-id', - summary: 'Specify the bundle ID/application ID for your app (reverse-DNS notation)', + summary: + 'Specify the bundle ID/application ID for your app (reverse-DNS notation)', groups: [MetadataGroup.ADVANCED], spec: { value: 'id' }, }, { name: 'start-id', - summary: 'Used by the Ionic app start experience to generate an associated app locally', + summary: + 'Used by the Ionic app start experience to generate an associated app locally', groups: [MetadataGroup.HIDDEN], spec: { value: 'id' }, }, { name: 'tag', - summary: `Specify a tag to use for the starters (e.g. ${['latest', 'testing', 'next'].map(t => input(t)).join(', ')})`, + summary: `Specify a tag to use for the starters (e.g. ${[ + 'latest', + 'testing', + 'next', + ] + .map((t) => input(t)) + .join(', ')})`, default: 'latest', groups: [MetadataGroup.HIDDEN], }, @@ -160,12 +213,21 @@ Use the ${input('--type')} option to start projects using older versions of Ioni async startIdStart(inputs: CommandLineInputs, options: CommandLineOptions) { const startId = options['start-id']; - const wizardApiUrl = process.env.START_WIZARD_URL_BASE || `https://ionicframework.com`; + const wizardApiUrl = + process.env.START_WIZARD_URL_BASE || `https://ionicframework.com`; - const { req } = await createRequest('GET', `${wizardApiUrl}/api/v1/wizard/app/${startId}`, this.env.config.getHTTPConfig()); + const { req } = await createRequest( + 'GET', + `${wizardApiUrl}/api/v1/wizard/app/${startId}`, + this.env.config.getHTTPConfig() + ); const error = (e?: Error) => { - this.env.log.error(`No such app ${chalk.bold(startId)}. This app configuration may have expired. Please retry at https://ionicframework.com/start`); + this.env.log.error( + `No such app ${chalk.bold( + startId + )}. This app configuration may have expired. Please retry at https://ionicframework.com/start` + ); if (e) { throw e; } @@ -183,7 +245,7 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (!data) { return error(); } - } catch (e) { + } catch (e: any) { return error(e); } @@ -199,13 +261,19 @@ Use the ${input('--type')} option to start projects using older versions of Ioni await this.startIdConvert(startId as string); - const appIconBuffer = data.appIcon ? - Buffer.from(data.appIcon.replace(/^data:image\/\w+;base64,/, ''), 'base64') : - undefined; + const appIconBuffer = data.appIcon + ? Buffer.from( + data.appIcon.replace(/^data:image\/\w+;base64,/, ''), + 'base64' + ) + : undefined; - const splashBuffer = data.appSplash ? - Buffer.from(data.appSplash.replace(/^data:image\/\w+;base64,/, ''), 'base64') : - undefined; + const splashBuffer = data.appSplash + ? Buffer.from( + data.appSplash.replace(/^data:image\/\w+;base64,/, ''), + 'base64' + ) + : undefined; this.schema = { cloned: false, @@ -223,17 +291,22 @@ Use the ${input('--type')} option to start projects using older versions of Ioni } async startIdConvert(id: string) { - const wizardApiUrl = process.env.START_WIZARD_URL_BASE || `https://ionicframework.com`; + const wizardApiUrl = + process.env.START_WIZARD_URL_BASE || `https://ionicframework.com`; if (!wizardApiUrl) { return; } - const { req } = await createRequest('POST', `${wizardApiUrl}/api/v1/wizard/app/${id}/start`, this.env.config.getHTTPConfig()); + const { req } = await createRequest( + 'POST', + `${wizardApiUrl}/api/v1/wizard/app/${id}/start`, + this.env.config.getHTTPConfig() + ); try { await req; - } catch (e) { + } catch (e: any) { this.env.log.warn(`Unable to set app flag on server: ${e.message}`); } } @@ -243,7 +316,10 @@ Use the ${input('--type')} option to start projects using older versions of Ioni * We should use if they ran `ionic start` or `ionic start --capacitor` * and they are in an interactive environment. */ - async shouldUseStartWizard(inputs: CommandLineInputs, options: CommandLineOptions) { + async shouldUseStartWizard( + inputs: CommandLineInputs, + options: CommandLineOptions + ) { const flagsToTestFor = [ 'list', 'l', @@ -258,7 +334,7 @@ Use the ${input('--type')} option to start projects using older versions of Ioni 'start-id', ]; - let didUseFlags = false ; + let didUseFlags = false; for (const key of flagsToTestFor) { if (options[key] !== null) { @@ -267,10 +343,19 @@ Use the ${input('--type')} option to start projects using older versions of Ioni } } - return inputs.length === 0 && options['interactive'] && options['deps'] && options['git'] && !didUseFlags; + return ( + inputs.length === 0 && + options['interactive'] && + options['deps'] && + options['git'] && + !didUseFlags + ); } - async preRun(inputs: CommandLineInputs, options: CommandLineOptions): Promise { + async preRun( + inputs: CommandLineInputs, + options: CommandLineOptions + ): Promise { const { promptToLogin } = await import('../lib/session'); verifyOptions(options, this.env); @@ -286,7 +371,9 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (confirm) { const startId = await this.env.session.wizardLogin(); if (!startId) { - this.env.log.error('There was an issue using the web wizard. Falling back to CLI wizard.'); + this.env.log.error( + 'There was an issue using the web wizard. Falling back to CLI wizard.' + ); } else { options['start-id'] = startId; } @@ -307,14 +394,20 @@ Use the ${input('--type')} option to start projects using older versions of Ioni return; } - const projectType = isValidURL(inputs[1]) ? 'custom' : options['type'] ? String(options['type']) : await this.getProjectType(); + let projectType = isValidURL(inputs[1]) + ? 'custom' + : options['type'] + ? String(options['type']) + : await this.getProjectType(); if (options['cordova']) { - const { checkForUnsupportedProject } = await import('../lib/integrations/cordova/utils'); + const { checkForUnsupportedProject } = await import( + '../lib/integrations/cordova/utils' + ); try { await checkForUnsupportedProject(projectType as ProjectType); - } catch (e) { + } catch (e: any) { this.env.log.error(e.message); options['cordova'] = false; } @@ -330,7 +423,11 @@ Use the ${input('--type')} option to start projects using older versions of Ioni const app = await appClient.load(appflowId); // TODO: can ask to clone via repo_url tasks.end(); - this.env.log.info(`Using ${strong(app.name)} for ${input('name')} and ${strong(app.slug)} for ${input('--project-id')}.`); + this.env.log.info( + `Using ${strong(app.name)} for ${input('name')} and ${strong( + app.slug + )} for ${input('--project-id')}.` + ); inputs[0] = app.name; options['project-id'] = app.slug; } else { @@ -338,7 +435,9 @@ Use the ${input('--type')} option to start projects using older versions of Ioni this.env.log.nl(); this.env.log.msg( `${strong(`Every great app needs a name! ${emoji('😍', '')}`)}\n` + - `Please enter the full name of your app. You can change this at any time. To bypass this prompt next time, supply ${input('name')}, the first argument to ${input('ionic start')}.\n\n` + `Please enter the full name of your app. You can change this at any time. To bypass this prompt next time, supply ${input( + 'name' + )}, the first argument to ${input('ionic start')}.\n\n` ); } @@ -346,7 +445,7 @@ Use the ${input('--type')} option to start projects using older versions of Ioni type: 'input', name: 'name', message: 'Project name:', - validate: v => validators.required(v), + validate: (v) => validators.required(v), }); inputs[0] = name; @@ -357,8 +456,12 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (this.env.flags.interactive) { this.env.log.nl(); this.env.log.msg( - `${strong(`Let's pick the perfect starter template! ${emoji('💪', '')}`)}\n` + - `Starter templates are ready-to-go Ionic apps that come packed with everything you need to build your app. To bypass this prompt next time, supply ${input('template')}, the second argument to ${input('ionic start')}.\n\n` + `${strong( + `Let's pick the perfect starter template! ${emoji('💪', '')}` + )}\n` + + `Starter templates are ready-to-go Ionic apps that come packed with everything you need to build your app. To bypass this prompt next time, supply ${input( + 'template' + )}, the second argument to ${input('ionic start')}.\n\n` ); } @@ -367,11 +470,23 @@ Use the ${input('--type')} option to start projects using older versions of Ioni name: 'template', message: 'Starter template:', choices: () => { - const starterTemplateList = STARTER_TEMPLATES.filter(st => st.projectType === projectType); - const cols = columnar(starterTemplateList.map(({ name, description }) => [input(name), description || '']), COLUMNAR_OPTIONS).split('\n'); + const starterTemplateList = STARTER_TEMPLATES.filter( + (st) => st.projectType === projectType + ); + const cols = columnar( + starterTemplateList.map(({ name, description }) => [ + input(name), + description || '', + ]), + COLUMNAR_OPTIONS + ).split('\n'); if (starterTemplateList.length === 0) { - throw new FatalException(`No starter templates found for project type: ${input(projectType)}.`); + throw new FatalException( + `No starter templates found for project type: ${input( + projectType + )}.` + ); } return starterTemplateList.map((starter, i) => { @@ -387,7 +502,54 @@ Use the ${input('--type')} option to start projects using older versions of Ioni inputs[1] = template; } - const starterTemplate = STARTER_TEMPLATES.find(t => t.name === inputs[1] && t.projectType === projectType); + + let starterTemplate = STARTER_TEMPLATES.find( + (t) => t.name === inputs[1] && t.projectType === projectType + ); + + + if (projectType === 'angular') { + const angularMode = await this.env.prompt({ + type: 'list', + name: 'standalone', + message: 'Would you like to build your app with Standalone Components or NgModules? \n Standalone components are the default way to build with Angular that simplifies the way you build your app. \n To learn more, visit the Angular docs:\n https://angular.dev/guide/components\n\n', + choices: () => [ + { + name: 'Standalone', + short: 'Standalone', + value: 'standalone', + }, + { + name: 'NgModules', + short: 'NgModules', + value: 'ngModules', + } + ], + }); + + /** + * If the developer wants to use standalone + * components then we need to get the correct starter. + */ + if (angularMode === 'standalone') { + /** + * Attempt to find the same type of starter + * but with standalone components. + */ + const standaloneStarter = STARTER_TEMPLATES.find((t) => t.name === inputs[1] && t.projectType === ANGULAR_STANDALONE); + + /** + * If found, update the projectType and + * starterTemplate vars to use the new project. + * If no project is found it will continue + * to use the NgModule version. + */ + if (standaloneStarter !== undefined) { + projectType = ANGULAR_STANDALONE; + starterTemplate = standaloneStarter; + } + } + } if (starterTemplate && starterTemplate.type === 'repo') { inputs[1] = starterTemplate.repo; @@ -399,7 +561,8 @@ Use the ${input('--type')} option to start projects using older versions of Ioni const confirm = await this.env.prompt({ type: 'confirm', name: 'confirm', - message: 'You are already in an Ionic project directory. Do you really want to start another project here?', + message: + 'You are already in an Ionic project directory. Do you really want to start another project here?', default: false, }); @@ -413,7 +576,11 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (cloned) { if (!options['git']) { - this.env.log.warn(`The ${input('--no-git')} option has no effect when cloning apps. Git must be used.`); + this.env.log.warn( + `The ${input( + '--no-git' + )} option has no effect when cloning apps. Git must be used.` + ); } options['git'] = true; @@ -422,33 +589,55 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (options['v1'] || options['v2']) { throw new FatalException( `The ${input('--v1')} and ${input('--v2')} flags have been removed.\n` + - `Use the ${input('--type')} option. (see ${input('ionic start --help')})` + `Use the ${input('--type')} option. (see ${input( + 'ionic start --help' + )})` ); } if (options['app-name']) { - this.env.log.warn(`The ${input('--app-name')} option has been removed. Use the ${input('name')} argument with double quotes: e.g. ${input('ionic start "My App"')}`); + this.env.log.warn( + `The ${input('--app-name')} option has been removed. Use the ${input( + 'name' + )} argument with double quotes: e.g. ${input('ionic start "My App"')}` + ); } if (options['display-name']) { - this.env.log.warn(`The ${input('--display-name')} option has been removed. Use the ${input('name')} argument with double quotes: e.g. ${input('ionic start "My App"')}`); + this.env.log.warn( + `The ${input( + '--display-name' + )} option has been removed. Use the ${input( + 'name' + )} argument with double quotes: e.g. ${input('ionic start "My App"')}` + ); } if (options['bundle-id']) { - this.env.log.warn(`The ${input('--bundle-id')} option has been deprecated. Please use ${input('--package-id')}.`); + this.env.log.warn( + `The ${input( + '--bundle-id' + )} option has been deprecated. Please use ${input('--package-id')}.` + ); options['package-id'] = options['bundle-id']; } - let projectId = options['project-id'] ? String(options['project-id']) : undefined; + let projectId = options['project-id'] + ? String(options['project-id']) + : undefined; if (projectId) { await this.validateProjectId(projectId); } else { - projectId = options['project-id'] = isValidProjectId(inputs[0]) ? inputs[0] : slugify(inputs[0]); + projectId = options['project-id'] = isValidProjectId(inputs[0]) + ? inputs[0] + : slugify(inputs[0]); } const projectDir = path.resolve(projectId); - const packageId = options['package-id'] ? String(options['package-id']) : undefined; + const packageId = options['package-id'] + ? String(options['package-id']) + : undefined; if (projectId) { await this.checkForExisting(projectDir); @@ -481,7 +670,9 @@ Use the ${input('--type')} option to start projects using older versions of Ioni this.env.log.nl(); this.env.log.msg( `${strong(`Pick a framework! ${emoji('😁', '')}`)}\n\n` + - `Please select the JavaScript framework to use for your new app. To bypass this prompt next time, supply a value for the ${input('--type')} option.\n\n` + `Please select the JavaScript framework to use for your new app. To bypass this prompt next time, supply a value for the ${input( + '--type' + )} option.\n\n` ); } @@ -491,7 +682,13 @@ Use the ${input('--type')} option to start projects using older versions of Ioni message: 'Framework:', default: 'angular', choices: () => { - const cols = columnar(SUPPORTED_FRAMEWORKS.map(({ name, description }) => [input(name), description]), COLUMNAR_OPTIONS).split('\n'); + const cols = columnar( + SUPPORTED_FRAMEWORKS.map(({ name, description }) => [ + input(name), + description, + ]), + COLUMNAR_OPTIONS + ).split('\n'); return SUPPORTED_FRAMEWORKS.map((starterTemplate, i) => { return { name: cols[i], @@ -505,7 +702,11 @@ Use the ${input('--type')} option to start projects using older versions of Ioni return frameworkChoice; } - async run(inputs: CommandLineInputs, options: CommandLineOptions, runinfo: CommandInstanceInfo): Promise { + async run( + inputs: CommandLineInputs, + options: CommandLineOptions, + runinfo: CommandInstanceInfo + ): Promise { const { pkgManagerArgs } = await import('../lib/utils/npm'); const { getTopLevel, isGitInstalled } = await import('../lib/git'); @@ -522,10 +723,13 @@ Use the ${input('--type')} option to start projects using older versions of Ioni const gitInstalled = await isGitInstalled(this.env); const gitTopLevel = await getTopLevel(this.env); - let gitIntegration = gitDesired && gitInstalled && !gitTopLevel ? true : false; + let gitIntegration = + gitDesired && gitInstalled && !gitTopLevel ? true : false; if (!gitInstalled) { - const installationDocs = `See installation docs for git: ${strong('https://git-scm.com/book/en/v2/Getting-Started-Installing-Git')}`; + const installationDocs = `See installation docs for git: ${strong( + 'https://git-scm.com/book/en/v2/Getting-Started-Installing-Git' + )}`; if (appflowId) { throw new FatalException( @@ -537,13 +741,19 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (this.schema.cloned) { throw new FatalException( `Git CLI not found on your PATH.\n` + - `Git must be installed to clone apps with ${input('ionic start')}. ${installationDocs}` + `Git must be installed to clone apps with ${input( + 'ionic start' + )}. ${installationDocs}` ); } } if (gitTopLevel && !this.schema.cloned) { - this.env.log.info(`Existing git project found (${strong(gitTopLevel)}). Git operations are disabled.`); + this.env.log.info( + `Existing git project found (${strong( + gitTopLevel + )}). Git operations are disabled.` + ); } const tasks = this.createTaskChain(); @@ -558,23 +768,52 @@ Use the ${input('--type')} option to start projects using older versions of Ioni tasks.end(); if (this.schema.cloned) { - await this.env.shell.run('git', ['clone', this.schema.url, projectDir, '--progress'], { stdio: 'inherit' }); + await this.env.shell.run( + 'git', + ['clone', this.schema.url, projectDir, '--progress'], + { stdio: 'inherit' } + ); } else { - const starterTemplate = await this.findStarterTemplate(this.schema.template, this.schema.type, tag); + const starterTemplate = await this.findStarterTemplate( + this.schema.template, + this.schema.type, + tag + ); await this.downloadStarterTemplate(projectDir, starterTemplate); } let project: IProject | undefined; - if (this.project && this.project.details.context === 'multiapp' && !this.schema.cloned) { + if ( + this.project && + this.project.details.context === 'multiapp' && + !this.schema.cloned + ) { // We're in a multi-app setup, so the new config file isn't wanted. await unlink(path.resolve(projectDir, PROJECT_FILE)); - project = await createProjectFromDetails({ context: 'multiapp', configPath: path.resolve(this.project.rootDirectory, PROJECT_FILE), id: projectId, type: this.schema.type, errors: [] }, this.env); + project = await createProjectFromDetails( + { + context: 'multiapp', + configPath: path.resolve(this.project.rootDirectory, PROJECT_FILE), + id: projectId, + type: this.schema.type, + errors: [], + }, + this.env + ); project.config.set('type', this.schema.type); - project.config.set('root', path.relative(this.project.rootDirectory, projectDir)); + project.config.set( + 'root', + path.relative(this.project.rootDirectory, projectDir) + ); } else { - project = await createProjectFromDirectory(projectDir, { _: [] }, this.env, { logErrors: false }); + project = await createProjectFromDirectory( + projectDir, + { _: [] }, + this.env, + { logErrors: false } + ); } // start is weird, once the project directory is created, it becomes a @@ -586,24 +825,34 @@ Use the ${input('--type')} option to start projects using older versions of Ioni throw new FatalException('Error while loading project.'); } - this.env.shell.alterPath = p => prependNodeModulesBinToPath(projectDir, p); + this.env.shell.alterPath = (p) => + prependNodeModulesBinToPath(projectDir, p); if (!this.schema.cloned) { if (this.schema.type === 'react' || this.schema.type === 'vue') { - options['capacitor'] = true; + options['capacitor'] = true; } - if (this.schema.type === 'angular' && options['cordova'] === null) { + if ( + (this.schema.type === 'angular' || this.schema.type === 'angular-standalone') + && options['cordova'] === null + ) { options['capacitor'] = true; } - if (options['cordova']) { - const { confirmCordovaUsage } = await import('../lib/integrations/cordova/utils'); + const { confirmCordovaUsage } = await import( + '../lib/integrations/cordova/utils' + ); const confirm = await confirmCordovaUsage(this.env); if (confirm) { - await runCommand(runinfo, ['integrations', 'enable', 'cordova', '--quiet']); + await runCommand(runinfo, [ + 'integrations', + 'enable', + 'cordova', + '--quiet', + ]); } else { options['cordova'] = false; } @@ -613,7 +862,8 @@ Use the ${input('--type')} option to start projects using older versions of Ioni const confirm = await this.env.prompt({ type: 'confirm', name: 'confirm', - message: 'Integrate your new app with Capacitor to target native iOS and Android?', + message: + 'Integrate your new app with Capacitor to target native iOS and Android?', default: false, }); @@ -623,7 +873,15 @@ Use the ${input('--type')} option to start projects using older versions of Ioni } if (options['capacitor']) { - await runCommand(runinfo, ['integrations', 'enable', 'capacitor', '--quiet', '--', this.schema.name, packageId ? packageId : 'io.ionic.starter']); + await runCommand(runinfo, [ + 'integrations', + 'enable', + 'capacitor', + '--quiet', + '--', + this.schema.name, + packageId ? packageId : 'io.ionic.starter', + ]); } await this.project.personalize({ @@ -638,27 +896,41 @@ Use the ${input('--type')} option to start projects using older versions of Ioni this.env.log.nl(); } - const shellOptions: IShellRunOptions = { cwd: projectDir, stdio: 'inherit' }; + const shellOptions: IShellRunOptions = { + cwd: projectDir, + stdio: 'inherit', + }; if (options['deps']) { this.env.log.msg('Installing dependencies may take several minutes.'); this.env.log.rawmsg(getAdvertisement()); - const [installer, ...installerArgs] = await pkgManagerArgs(this.env.config.get('npmClient'), { command: 'install' }); + const [installer, ...installerArgs] = await pkgManagerArgs( + this.env.config.get('npmClient'), + { command: 'install' } + ); await this.env.shell.run(installer, installerArgs, shellOptions); if (options['cordova']) { try { - await this.env.shell.run('ng', ['add', '@ionic/cordova-builders', '--skip-confirmation'], { cwd: this.project.rootDirectory }); - } catch (e) { + await this.env.shell.run( + 'ng', + ['add', '@ionic/cordova-builders', '--skip-confirmation'], + { cwd: this.project.rootDirectory } + ); + } catch (e: any) { debug('Error while adding @ionic/cordova-builders: %O', e); } } } else { // --no-deps flag was used so skip installing dependencies, this also results in the package.json being out sync with the package.json so warn the user - this.env.log.warn('Using the --no-deps flag results in an out of date package lock file. The lock file can be updated by performing an `install` with your package manager.'); + this.env.log.warn( + 'Using the --no-deps flag results in an out of date package lock file. The lock file can be updated by performing an `install` with your package manager.' + ); if (options['cordova']) { - this.env.log.warn('@ionic/cordova-builders couldn\'t be added, make sure you run `ng add @ionic/cordova-builders` after performing an `install` with your package manager.'); + this.env.log.warn( + "@ionic/cordova-builders couldn't be added, make sure you run `ng add @ionic/cordova-builders` after performing an `install` with your package manager." + ); } } @@ -666,8 +938,10 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (gitIntegration) { try { await this.env.shell.run('git', ['init'], shellOptions); // TODO: use initializeRepo()? - } catch (e) { - this.env.log.warn('Error encountered during repo initialization. Disabling further git operations.'); + } catch (e: any) { + this.env.log.warn( + 'Error encountered during repo initialization. Disabling further git operations.' + ); gitIntegration = false; } } @@ -700,9 +974,15 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (gitIntegration) { try { await this.env.shell.run('git', ['add', '-A'], shellOptions); - await this.env.shell.run('git', ['commit', '-m', 'Initial commit', '--no-gpg-sign'], shellOptions); - } catch (e) { - this.env.log.warn('Error encountered during commit. Disabling further git operations.'); + await this.env.shell.run( + 'git', + ['commit', '-m', 'Initial commit', '--no-gpg-sign'], + shellOptions + ); + } catch (e: any) { + this.env.log.warn( + 'Error encountered during commit. Disabling further git operations.' + ); gitIntegration = false; } } @@ -714,7 +994,12 @@ Use the ${input('--type')} option to start projects using older versions of Ioni this.env.log.nl(); - await this.showNextSteps(projectDir, this.schema.cloned, linkConfirmed, !options['cordova']); + await this.showNextSteps( + projectDir, + this.schema.cloned, + linkConfirmed, + !options['cordova'] + ); } async checkForExisting(projectDir: string) { @@ -724,12 +1009,16 @@ Use the ${input('--type')} option to start projects using older versions of Ioni const confirm = await this.env.prompt({ type: 'confirm', name: 'confirm', - message: `${input(prettyPath(projectDir))} exists. ${failure('Overwrite?')}`, + message: `${input(prettyPath(projectDir))} exists. ${failure( + 'Overwrite?' + )}`, default: false, }); if (!confirm) { - this.env.log.msg(`Not erasing existing project in ${input(prettyPath(projectDir))}.`); + this.env.log.msg( + `Not erasing existing project in ${input(prettyPath(projectDir))}.` + ); throw new FatalException(); } @@ -737,13 +1026,20 @@ Use the ${input('--type')} option to start projects using older versions of Ioni } } - async findStarterTemplate(template: string, type: string, tag: string): Promise { - const starterTemplate = STARTER_TEMPLATES.find(t => t.projectType === type && t.name === template); + async findStarterTemplate( + template: string, + type: string, + tag: string + ): Promise { + const starterTemplate = STARTER_TEMPLATES.find( + (t) => t.projectType === type && t.name === template + ); if (starterTemplate && starterTemplate.type === 'managed') { return { ...starterTemplate, - archive: `${STARTER_BASE_URL}/${tag === 'latest' ? '' : `${tag}/`}${starterTemplate.id}.tar.gz`, + archive: `${STARTER_BASE_URL}/${tag === 'latest' ? '' : `${tag}/`}${starterTemplate.id + }.tar.gz`, }; } @@ -751,7 +1047,9 @@ Use the ${input('--type')} option to start projects using older versions of Ioni tasks.next('Looking up starter'); const starterList = await getStarterList(this.env.config, tag); - const starter = starterList.starters.find(t => t.type === type && t.name === template); + const starter = starterList.starters.find( + (t) => t.type === type && t.name === template + ); if (starter) { tasks.end(); @@ -759,12 +1057,15 @@ Use the ${input('--type')} option to start projects using older versions of Ioni return { name: starter.name, projectType: starter.type, - archive: `${STARTER_BASE_URL}/${tag === 'latest' ? '' : `${tag}/`}${starter.id}.tar.gz`, + archive: `${STARTER_BASE_URL}/${tag === 'latest' ? '' : `${tag}/`}${starter.id + }.tar.gz`, }; } else { throw new FatalException( `Unable to find starter template for ${input(template)}\n` + - `If this is not a typo, please make sure it is a valid starter template within the starters repo: ${strong('https://github.com/ionic-team/starters')}` + `If this is not a typo, please make sure it is a valid starter template within the starters repo: ${strong( + 'https://github.com/ionic-team/starters' + )}` ); } } @@ -775,7 +1076,9 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (!['custom', ...projectTypes].includes(type)) { throw new FatalException( `${input(type)} is not a valid project type.\n` + - `Please choose a different ${input('--type')}. Use ${input('ionic start --list')} to list all available starter templates.` + `Please choose a different ${input('--type')}. Use ${input( + 'ionic start --list' + )} to list all available starter templates.` ); } } @@ -784,16 +1087,22 @@ Use the ${input('--type')} option to start projects using older versions of Ioni if (!isValidProjectId(projectId)) { throw new FatalException( `${input(projectId)} is not a valid package or directory name.\n` + - `Please choose a different ${input('--project-id')}. Alphanumeric characters are always safe.` + `Please choose a different ${input( + '--project-id' + )}. Alphanumeric characters are always safe.` ); } } - async loadManifest(manifestPath: string): Promise { + async loadManifest( + manifestPath: string + ): Promise { try { return await readStarterManifest(manifestPath); - } catch (e) { - debug(`Error with manifest file ${strong(prettyPath(manifestPath))}: ${e}`); + } catch (e: any) { + debug( + `Error with manifest file ${strong(prettyPath(manifestPath))}: ${e}` + ); } } @@ -805,38 +1114,80 @@ Use the ${input('--type')} option to start projects using older versions of Ioni } } - async downloadStarterTemplate(projectDir: string, starterTemplate: ResolvedStarterTemplate) { + async downloadStarterTemplate( + projectDir: string, + starterTemplate: ResolvedStarterTemplate + ) { const { createRequest, download } = await import('../lib/utils/http'); const { tar } = await import('../lib/utils/archive'); const tasks = this.createTaskChain(); - const task = tasks.next(`Downloading and extracting ${input(starterTemplate.name.toString())} starter`); + const task = tasks.next( + `Downloading and extracting ${input( + starterTemplate.name.toString() + )} starter` + ); debug('Tar extraction created for %s', projectDir); const ws = tar.extract({ cwd: projectDir }); - const { req } = await createRequest('GET', starterTemplate.archive, this.env.config.getHTTPConfig()); - await download(req, ws, { progress: (loaded, total) => task.progress(loaded, total) }); + const { req } = await createRequest( + 'GET', + starterTemplate.archive, + this.env.config.getHTTPConfig() + ); + await download(req, ws, { + progress: (loaded, total) => task.progress(loaded, total), + }); tasks.end(); } - async showNextSteps(projectDir: string, cloned: boolean, linkConfirmed: boolean, isCapacitor: boolean) { - const cordovaResCommand = isCapacitor ? 'cordova-res --skip-config --copy' : 'cordova-res'; + async showNextSteps( + projectDir: string, + cloned: boolean, + linkConfirmed: boolean, + isCapacitor: boolean + ) { + const cordovaResCommand = isCapacitor + ? 'cordova-res --skip-config --copy' + : 'cordova-res'; const steps = [ - `Go to your ${cloned ? 'cloned' : 'new'} project: ${input(`cd ${prettyPath(projectDir)}`)}`, - `Run ${input('ionic serve')} within the app directory to see your app in the browser`, - isCapacitor ? - `Run ${input('ionic capacitor add')} to add a native iOS or Android project using Capacitor` : - `Run ${input('ionic cordova platform add')} to add a native iOS or Android project using Cordova`, - `Generate your app icon and splash screens using ${input(cordovaResCommand)}`, - `Explore the Ionic docs for components, tutorials, and more: ${strong('https://ion.link/docs')}`, - `Building an enterprise app? Ionic has Enterprise Support and Features: ${strong('https://ion.link/enterprise-edition')}`, + `Go to your ${cloned ? 'cloned' : 'new'} project: ${input( + `cd ${prettyPath(projectDir)}` + )}`, + `Run ${input( + 'ionic serve' + )} within the app directory to see your app in the browser`, + isCapacitor + ? `Run ${input( + 'ionic capacitor add' + )} to add a native iOS or Android project using Capacitor` + : `Run ${input( + 'ionic cordova platform add' + )} to add a native iOS or Android project using Cordova`, + `Generate your app icon and splash screens using ${input( + cordovaResCommand + )}`, + `Explore the Ionic docs for components, tutorials, and more: ${strong( + 'https://ion.link/docs' + )}`, + `Building an enterprise app? Ionic has Enterprise Support and Features: ${strong( + 'https://ion.link/enterprise-edition' + )}`, ]; if (linkConfirmed) { - steps.push(`Push your code to Ionic Appflow to perform real-time updates, and more: ${input('git push ionic master')}`); + steps.push( + `Push your code to Ionic Appflow to perform real-time updates, and more: ${input( + 'git push ionic master' + )}` + ); } - this.env.log.msg(`${strong('Your Ionic app is ready! Follow these next steps')}:\n${steps.map(s => ` - ${s}`).join('\n')}`); + this.env.log.msg( + `${strong('Your Ionic app is ready! Follow these next steps')}:\n${steps + .map((s) => ` - ${s}`) + .join('\n')}` + ); } } diff --git a/packages/@ionic/cli/src/constants.ts b/packages/@ionic/cli/src/constants.ts index bb7b3bf22d..1d31eb7bed 100644 --- a/packages/@ionic/cli/src/constants.ts +++ b/packages/@ionic/cli/src/constants.ts @@ -1,14 +1,15 @@ -import * as chalk from 'chalk'; +import chalk from 'chalk'; import * as lodash from 'lodash'; import * as path from 'path'; import { ProjectType } from './definitions'; export const ASSETS_DIRECTORY = path.resolve(__dirname, 'assets'); +export const ANGULAR_STANDALONE = 'angular-standalone'; export const PROJECT_FILE = process.env['IONIC_CONFIG_FILE'] ?? 'ionic.config.json'; -export const PROJECT_TYPES: ProjectType[] = ['angular', 'react', 'vue', 'ionic-angular', 'ionic1', 'custom']; -export const LEGACY_PROJECT_TYPES: ProjectType[] = ['ionic-angular', 'ionic1']; +export const PROJECT_TYPES: ProjectType[] = ['angular', ANGULAR_STANDALONE, 'react', 'vue', 'custom', 'vue-vite', 'react-vite']; +export const LEGACY_PROJECT_TYPES: ProjectType[] = []; export const MODERN_PROJECT_TYPES: ProjectType[] = lodash.difference(PROJECT_TYPES, LEGACY_PROJECT_TYPES); export const COLUMNAR_OPTIONS = { hsep: chalk.dim('-'), vsep: chalk.dim('|') }; diff --git a/packages/@ionic/cli/src/definitions.ts b/packages/@ionic/cli/src/definitions.ts index 7b49e6216b..4f17d688e9 100644 --- a/packages/@ionic/cli/src/definitions.ts +++ b/packages/@ionic/cli/src/definitions.ts @@ -78,7 +78,7 @@ export interface Runner { run(options: T): Promise; } -export type ProjectType = 'angular' | 'ionic-angular' | 'ionic1' | 'custom' | 'bare' | 'react' | 'vue'; +export type ProjectType = 'angular' | 'angular-standalone' | 'custom' | 'bare' | 'react' | 'vue' | 'react-vite' | 'vue-vite'; export type HookName = 'build:before' | 'build:after' | 'serve:before' | 'serve:after' | 'capacitor:run:before' | 'capacitor:build:before' | 'capacitor:sync:after'; export type CapacitorRunHookName = 'capacitor:run:before'; @@ -95,8 +95,8 @@ export interface BaseHookContext { env: NodeJS.ProcessEnv; } -export type AnyServeOptions = ReactServeOptions | AngularServeOptions | IonicAngularServeOptions | Ionic1ServeOptions; -export type AnyBuildOptions = ReactBuildOptions | AngularBuildOptions | IonicAngularBuildOptions | Ionic1BuildOptions; +export type AnyServeOptions = ReactServeOptions | AngularServeOptions; +export type AnyBuildOptions = ReactBuildOptions | AngularBuildOptions; export interface CapacitorSyncHookInput { readonly name: CapacitorSyncHookName; @@ -118,17 +118,17 @@ export interface CapacitorBuildHookInput { export interface BuildHookInput { readonly name: 'build:before' | 'build:after'; - readonly build: AngularBuildOptions | IonicAngularBuildOptions | Ionic1BuildOptions; + readonly build: AngularBuildOptions; } export interface ServeBeforeHookInput { readonly name: 'serve:before'; - readonly serve: AngularServeOptions | IonicAngularServeOptions | Ionic1ServeOptions; + readonly serve: AngularServeOptions; } export interface ServeAfterHookInput { readonly name: 'serve:after'; - readonly serve: (AngularServeOptions | IonicAngularServeOptions | Ionic1ServeOptions) & ServeDetails; + readonly serve: (AngularServeOptions) & ServeDetails; } export type HookInput = BuildHookInput | ServeBeforeHookInput | ServeAfterHookInput | CapacitorRunHookInput | CapacitorBuildHookInput | CapacitorSyncHookInput; @@ -363,7 +363,6 @@ export interface IProject { readonly config: BaseConfig; readonly details: import('./lib/project').ProjectDetailsResult; - getDocsUrl(): Promise; getSourceDir(sourceRoot?: string): Promise; getDefaultDistDir(): Promise; getDistDir(): Promise; @@ -379,7 +378,6 @@ export interface IProject { getPackageJson(pkgName?: string, options?: { logErrors?: boolean }): Promise<[PackageJson | undefined, string | undefined]>; requirePackageJson(pkgName?: string): Promise; personalize(details: ProjectPersonalizationDetails): Promise; - registerAilments(registry: IAilmentRegistry): Promise; getBuildRunner(): Promise | undefined>; getServeRunner(): Promise | undefined>; getGenerateRunner(): Promise | undefined>; @@ -676,30 +674,19 @@ export interface IonicCapacitorOptions extends CapacitorConfig { verbose?: boolean; } -export interface IonicAngularBuildOptions extends BuildOptions<'ionic-angular'> { - prod: boolean; - sourcemaps?: boolean; - aot: boolean; - minifyjs: boolean; - minifycss: boolean; - optimizejs: boolean; - env?: string; -} - -export interface Ionic1BuildOptions extends BuildOptions<'ionic1'> {} - export interface CustomBuildOptions extends BuildOptions<'custom'> {} export interface GenerateOptions { - type: string; name: string; } export interface AngularGenerateOptions extends GenerateOptions { [key: string]: any; // TODO + schematic: string; } export interface IonicAngularGenerateOptions extends GenerateOptions { + type: string; module: boolean; constants: boolean; } @@ -711,9 +698,6 @@ export interface ServeOptions { publicHost?: string; livereload: boolean; proxy: boolean; - lab: boolean; - labHost: string; - labPort: number; open: boolean; browser?: string; browserOption?: string; @@ -748,22 +732,6 @@ export interface VueServeOptions extends ServeOptions { sourcemaps?: boolean; } -export interface IonicAngularServeOptions extends ServeOptions { - sourcemaps?: boolean; - consolelogs: boolean; - serverlogs: boolean; - env?: string; - livereloadPort: number; - notificationPort: number; -} - -export interface Ionic1ServeOptions extends ServeOptions { - consolelogs: boolean; - serverlogs: boolean; - livereloadPort: number; - notificationPort: number; -} - export interface CustomServeOptions extends ServeOptions {} export interface LabServeDetails { @@ -782,35 +750,6 @@ export interface ServeDetails { externallyAccessible: boolean; } -export interface IAilment { - readonly id: string; - implicit: boolean; - projects?: ProjectType[]; - getMessage(): Promise; - detected(): Promise; - getTreatmentSteps(): Promise; -} - -export interface TreatableAilment extends IAilment { - readonly treatable: boolean; - getTreatmentSteps(): Promise; -} - -export interface PatientTreatmentStep { - message: string; -} - -export interface DoctorTreatmentStep extends PatientTreatmentStep { - treat(): Promise; -} - -export interface IAilmentRegistry { - ailments: IAilment[]; - - register(ailment: IAilment): void; - get(id: string): IAilment | undefined; -} - export interface IonicContext { readonly binPath: string; readonly libPath: string; diff --git a/packages/@ionic/cli/src/guards.ts b/packages/@ionic/cli/src/guards.ts index cf1cc6117c..15948b4c4f 100644 --- a/packages/@ionic/cli/src/guards.ts +++ b/packages/@ionic/cli/src/guards.ts @@ -30,7 +30,6 @@ import { Snapshot, StarterManifest, SuperAgentError, - TreatableAilment, User } from './definitions'; import { AuthConnection } from './lib/oauth/auth'; @@ -342,10 +341,6 @@ export function isSecurityProfileResponse(r: APIResponse): r is Response { try { executor = await loadExecutor(await generateContext(), pargv); - } catch (e) { + } catch (e: any) { process.stderr.write(`${e.message ? e.message : (e.stack ? e.stack : e)}\n`); process.exitCode = 1; return; @@ -125,14 +126,14 @@ export async function run(pargv: string[]): Promise { const location = await executor.locate(pargv); - const [ , [ cmd = '' ] = [] ] = location.path; + const [, [cmd = ''] = []] = location.path; if (!['config', 'completion', 'help', 'login', 'logout', 'version'].includes(cmd)) { await authenticateFromEnvironment(ienv); } await executor.execute(location, process.env); - } catch (e) { + } catch (e: any) { err = e; } finally { if (ienv.flags.interactive) { @@ -171,11 +172,7 @@ export async function run(pargv: string[]): Promise { } else if (err instanceof BaseError) { ienv.log.error(err.message); } else { - ienv.log.msg(failure(String(err.stack ? err.stack : err))); - - if (err.stack) { - debug(failure(String(err.stack))); - } + ienv.log.rawmsg(failure(util.inspect(err))); } } } diff --git a/packages/@ionic/cli/src/lib/__tests__/serve.ts b/packages/@ionic/cli/src/lib/__tests__/serve.ts index e9da53993f..48c25bb61c 100644 --- a/packages/@ionic/cli/src/lib/__tests__/serve.ts +++ b/packages/@ionic/cli/src/lib/__tests__/serve.ts @@ -26,9 +26,6 @@ describe('@ionic/cli', () => { browserOption: undefined, engine: 'browser', externalAddressRequired: false, - lab: false, - labHost: 'localhost', - labPort: 8200, livereload: true, open: false, port: 8100, @@ -45,8 +42,8 @@ describe('@ionic/cli', () => { it('should provide options from negations of cli flag defaults', () => { const runner = new MyServeRunner({}); - const result = runner.createOptionsFromCommandLine([], { _: [], livereload: false, proxy: false, lab: true, open: true, externalAddressRequired: true }); - expect(result).toEqual({ ...defaults, livereload: false, proxy: false, lab: true, open: true, externalAddressRequired: true }); + const result = runner.createOptionsFromCommandLine([], { _: [], livereload: false, proxy: false, open: true, externalAddressRequired: true }); + expect(result).toEqual({ ...defaults, livereload: false, proxy: false, open: true, externalAddressRequired: true }); }); it('should allow overrides of default values', () => { diff --git a/packages/@ionic/cli/src/lib/__tests__/ssh-config.ts b/packages/@ionic/cli/src/lib/__tests__/ssh-config.ts index 52e1628728..479ed17679 100644 --- a/packages/@ionic/cli/src/lib/__tests__/ssh-config.ts +++ b/packages/@ionic/cli/src/lib/__tests__/ssh-config.ts @@ -1,6 +1,6 @@ import * as path from 'path'; -import * as SSHConfig from 'ssh-config'; +import SSHConfig from 'ssh-config'; import { readFile } from '@ionic/utils-fs'; import { ensureHostAndKeyPath } from '../ssh-config'; diff --git a/packages/@ionic/cli/src/lib/build.ts b/packages/@ionic/cli/src/lib/build.ts index e7db4becc3..dc07de3a3b 100644 --- a/packages/@ionic/cli/src/lib/build.ts +++ b/packages/@ionic/cli/src/lib/build.ts @@ -2,7 +2,7 @@ import { BaseError, MetadataGroup } from '@ionic/cli-framework'; import { PromptModule } from '@ionic/cli-framework-prompts'; import { createProcessEnv } from '@ionic/utils-process'; import { ERROR_COMMAND_NOT_FOUND, SubprocessError } from '@ionic/utils-subprocess'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import { BaseBuildOptions, BuildOptions, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, IConfig, ILogger, IProject, IShell, NpmClient, Runner } from '../definitions'; @@ -62,7 +62,7 @@ export abstract class BuildRunner> implements Runner createBaseOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): BaseBuildOptions { const separatedArgs = options['--']; - const [ platform ] = options['platform'] ? [String(options['platform'])] : inputs; + const [platform] = options['platform'] ? [String(options['platform'])] : inputs; const engine = this.determineEngineFromCommandLine(options); const project = options['project'] ? String(options['project']) : undefined; const verbose = !!options['verbose']; @@ -87,7 +87,7 @@ export abstract class BuildRunner> implements Runner try { await hook.run({ name: hook.name, build: options }); - } catch (e) { + } catch (e: any) { if (e instanceof BaseError) { throw new FatalException(e.message); } @@ -113,7 +113,7 @@ export abstract class BuildRunner> implements Runner try { await hook.run({ name: hook.name, build: options }); - } catch (e) { + } catch (e: any) { if (e instanceof BaseError) { throw new FatalException(e.message); } @@ -153,7 +153,7 @@ export abstract class BuildCLI { private _resolvedProgram?: string; - constructor(protected readonly e: BuildRunnerDeps) {} + constructor(protected readonly e: BuildRunnerDeps) { } get resolvedProgram() { if (this._resolvedProgram) { @@ -194,7 +194,7 @@ export abstract class BuildCLI { protected async runWrapper(options: T): Promise { try { return await this.run(options); - } catch (e) { + } catch (e: any) { if (!(e instanceof BuildCLIProgramNotFoundException)) { throw e; } @@ -227,7 +227,7 @@ export abstract class BuildCLI { try { await this.e.shell.run(this.resolvedProgram, args, { stdio: 'inherit', cwd: this.e.project.directory, fatalOnNotFound: false, env: createProcessEnv(env) }); - } catch (e) { + } catch (e: any) { if (e instanceof SubprocessError && e.code === ERROR_COMMAND_NOT_FOUND) { throw new BuildCLIProgramNotFoundException(`${strong(this.resolvedProgram)} command not found.`); } @@ -251,7 +251,7 @@ export abstract class BuildCLI { protected async promptToInstall(): Promise { const { pkgManagerArgs } = await import('./utils/npm'); - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: this.pkg, saveDev: true, saveExact: true }); + const [manager, ...managerArgs] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: this.pkg, saveDev: true, saveExact: true }); this.e.log.nl(); @@ -283,7 +283,7 @@ abstract class PkgManagerBuildCLI extends BuildCLI { protected async buildArgs(options: BaseBuildOptions): Promise { const { pkgManagerArgs } = await import('./utils/npm'); - const [ , ...pkgArgs ] = await pkgManagerArgs(this.program, { command: 'run', script: this.script, scriptArgs: [...options['--'] || []] }); + const [, ...pkgArgs] = await pkgManagerArgs(this.program, { command: 'run', script: this.script, scriptArgs: [...options['--'] || []] }); return pkgArgs; } diff --git a/packages/@ionic/cli/src/lib/color.ts b/packages/@ionic/cli/src/lib/color.ts index e57bb07009..789ff736d6 100644 --- a/packages/@ionic/cli/src/lib/color.ts +++ b/packages/@ionic/cli/src/lib/color.ts @@ -1,5 +1,5 @@ import { Colors, DEFAULT_COLORS, HelpColors } from '@ionic/cli-framework'; -import * as chalk from 'chalk'; +import chalk from 'chalk'; const HELP_COLORS: Partial = { title: chalk.bold, diff --git a/packages/@ionic/cli/src/lib/command.ts b/packages/@ionic/cli/src/lib/command.ts index 45d263e236..92c03fae9d 100644 --- a/packages/@ionic/cli/src/lib/command.ts +++ b/packages/@ionic/cli/src/lib/command.ts @@ -53,7 +53,7 @@ export abstract class Command extends BaseCommand { try { return await which('cordova-res'); - } catch (e) { + } catch (e: any) { if (e.code !== 'ENOENT') { throw e; } diff --git a/packages/@ionic/cli/src/lib/doctor/ailments/base.ts b/packages/@ionic/cli/src/lib/doctor/ailments/base.ts deleted file mode 100644 index 3d64d5110d..0000000000 --- a/packages/@ionic/cli/src/lib/doctor/ailments/base.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { PackageJson } from '@ionic/cli-framework'; -import * as Debug from 'debug'; - -import { IAilment, IAilmentRegistry, IClient, IConfig, ILogger, IProject, ISession, IShell, PatientTreatmentStep, ProjectType } from '../../../definitions'; - -export interface AilmentDeps { - client: IClient; - config: IConfig; - log: ILogger; - project: IProject; - shell: IShell; - session: ISession; -} - -export abstract class Ailment implements IAilment { - protected readonly client: IClient; - protected readonly config: IConfig; - protected readonly log: ILogger; - protected readonly project: IProject; - protected readonly shell: IShell; - protected readonly session: ISession; - private _debug?: Debug.IDebugger; - - abstract readonly id: string; - readonly projects?: ProjectType[] = undefined; - readonly implicit: boolean = true; - - constructor({ client, config, log, project, shell, session }: AilmentDeps) { - this.client = client; - this.config = config; - this.log = log; - this.project = project; - this.shell = shell; - this.session = session; - } - - get debug() { - if (!this._debug) { - this._debug = Debug(`ionic:lib:doctor:ailments:${this.id}`); - } - - return this._debug; - } - - abstract async getMessage(): Promise; - abstract async detected(): Promise; - abstract async getTreatmentSteps(): Promise; - - async getLocalPackageJson(pkgName: string): Promise { - try { - return await this.project.requirePackageJson(pkgName); - } catch (e) { - if (e.fatal) { - throw e; - } - } - } -} - -export class AilmentRegistry implements IAilmentRegistry { - protected _ailments: IAilment[] = []; - - register(ailment: IAilment) { - this._ailments.push(ailment); - } - - get ailments(): IAilment[] { - return this._ailments; - } - - get(id: string): IAilment | undefined { - return this._ailments.find(a => a.id === id); - } -} diff --git a/packages/@ionic/cli/src/lib/doctor/ailments/index.ts b/packages/@ionic/cli/src/lib/doctor/ailments/index.ts deleted file mode 100644 index 6addc087b5..0000000000 --- a/packages/@ionic/cli/src/lib/doctor/ailments/index.ts +++ /dev/null @@ -1,327 +0,0 @@ -import { readFile } from '@ionic/utils-fs'; -import * as chalk from 'chalk'; -import * as path from 'path'; - -import { TreatableAilment } from '../../../definitions'; -import { AppClient } from '../../app'; -import { ancillary, input, strong } from '../../color'; -import { getIonicRemote, isRepoInitialized } from '../../git'; -import { loadCordovaConfig } from '../../integrations/cordova/config'; -import { getPlatforms } from '../../integrations/cordova/project'; -import { pkgManagerArgs } from '../../utils/npm'; - -import { Ailment } from './base'; - -export * from './base'; -export * from './utils'; - -export class NpmInstalledLocally extends Ailment implements TreatableAilment { - readonly id = 'npm-installed-locally'; - readonly treatable = true; - - async getMessage() { - return ( - `${strong('npm')} is installed locally.\n` + - `${strong('npm')} is typically installed globally and may cause some confusion about versions when other CLIs use it.\n` - ).trim(); - } - - async detected() { - const pkg = await this.getLocalPackageJson('npm'); - return pkg !== undefined; - } - - async getTreatmentSteps() { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.config.get('npmClient'), { command: 'uninstall', pkg: 'npm' }); - - return [ - { - message: `Run: ${input(manager + ' ' + managerArgs.join(' '))}`, - treat: async () => { - await this.shell.run(manager, managerArgs, {}); - }, - }, - ]; - } -} - -export class IonicCLIInstalledLocally extends Ailment implements TreatableAilment { - readonly id = 'ionic-installed-locally'; - readonly treatable = true; - - async getMessage() { - return ( - `The Ionic CLI is installed locally.\n` + - `While the CLI can run locally, there's no longer a reason to have it installed locally and it may cause some confusion over configuration and versions.\n` - ).trim(); - } - - async detected() { - const pkg = await this.getLocalPackageJson('@ionic/cli'); - return pkg !== undefined; - } - - async getTreatmentSteps() { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.config.get('npmClient'), { command: 'uninstall', pkg: '@ionic/cli' }); - - return [ - { - message: `Run: ${input(manager + ' ' + managerArgs.join(' '))}`, - treat: async () => { - await this.shell.run(manager, managerArgs, {}); - }, - }, - ]; - } -} - -export class GitNotUsed extends Ailment { - readonly id = 'git-not-used'; - - async getMessage() { - return ( - `Git doesn't appear to be in use.\n` + - `We highly recommend using source control software such as git (${strong('https://git-scm.com')}) to track changes in your code throughout time.\n` - ).trim(); - } - - async detected() { - if (!(await isRepoInitialized(this.project.directory))) { - return true; - } - - const cmdInstalled = await this.shell.cmdinfo('git', ['--version']); - - if (!cmdInstalled) { - return true; - } - - const [ revListCount, status ] = await Promise.all([ - this.shell.output('git', ['rev-list', '--count', 'HEAD'], { fatalOnError: false, showCommand: false, showError: false }), - this.shell.output('git', ['status', '--porcelain'], { fatalOnError: false, showCommand: false, showError: false }), - ]); - - this.debug('rev-list count: %s, status: %s', revListCount.trim(), status); - - if (!revListCount) { - return true; - } - - const commitCount = Number(revListCount); - const changes = Boolean(status); - - return commitCount === 1 && changes; - } - - async getTreatmentSteps() { - return [ - { message: `Download git if you don't have it installed: ${strong('https://git-scm.com/downloads')}` }, - { message: `Learn the basics if you're unfamiliar with git: ${strong('https://try.github.io')}` }, - { message: `Make your first commit and start tracking code changes! 😍` }, - ]; - } -} - -export class GitConfigInvalid extends Ailment { - readonly id = 'git-config-invalid'; - - async getMessage() { - const appflowId = await this.project.requireAppflowId(); - - return ( - `App linked to ${strong(appflowId)} with invalid git configuration.\n` + - `This app is linked to an app on Ionic (${strong(appflowId)}), but the git configuration is not valid.\n` - ).trim(); - } - - async detected() { - const isLoggedIn = this.session.isLoggedIn(); - - if (!isLoggedIn) { - return false; - } - - const appflowId = this.project.config.get('id'); - - if (!appflowId) { - return false; - } - - if (!(await isRepoInitialized(this.project.directory))) { - return false; - } - - const remote = await getIonicRemote({ shell: this.shell }, this.project.directory); - - if (!remote) { - return true; - } - - const token = await this.session.getUserToken(); - const appClient = new AppClient(token, { client: this.client }); - const app = await appClient.load(appflowId); - - if (app.repo_url !== remote) { - return true; - } - - return false; - } - - async getTreatmentSteps() { - return [ - { message: `Run: ${input('ionic git remote')}` }, - ]; - } -} - -export class IonicNativeOldVersionInstalled extends Ailment { - readonly id = 'ionic-native-old-version-installed'; - - async getMessage() { - return ( - `Old version of Ionic Native installed.\n` + - `Ionic Native ${strong('ionic-native')} has been restructured into individual packages under the ${strong('@ionic-native/')} namespace to allow for better bundling and faster apps.\n` - ).trim(); - } - - async detected() { - const pkg = await this.getLocalPackageJson('ionic-native'); - return pkg !== undefined; - } - - async getTreatmentSteps() { - const args = await pkgManagerArgs(this.config.get('npmClient'), { command: 'uninstall', pkg: 'ionic-native' }); - - return [ - { message: `Run ${input(args.join(' '))}` }, - { message: `Refer to ${strong('https://ion.link/native-docs')} for installation & usage instructions` }, - ]; - } -} - -export class UnsavedCordovaPlatforms extends Ailment { - readonly id = 'unsaved-cordova-platforms'; - - async getMessage() { - return ( - `Cordova platforms unsaved.\n` + - `There are Cordova platforms installed that are not saved in ${strong('config.xml')} or ${strong('package.json')}. It is good practice to manage Cordova platforms and their versions. See the Cordova docs${ancillary('[1]')} for more information.\n\n` + - `${ancillary('[1]')}: ${strong('https://cordova.apache.org/docs/en/latest/platform_plugin_versioning_ref/')}\n` - ).trim(); - } - - async detected() { - const cordova = this.project.getIntegration('cordova'); - - if (!cordova || !cordova.enabled) { - return false; - } - - const platforms = await getPlatforms(cordova.root); - const conf = await loadCordovaConfig(cordova); - const configuredPlatforms = new Set([...conf.getConfiguredPlatforms().map(e => e.name)]); - - const configXmlDiff = platforms.filter(p => !configuredPlatforms.has(p)); - - return configXmlDiff.length > 0; - } - - async getTreatmentSteps() { - return [ - { message: `Run: ${input('ionic cordova platform save')}` }, - ]; - } -} - -export class DefaultCordovaBundleIdUsed extends Ailment { - readonly id = 'default-cordova-bundle-id-used'; - - async getMessage() { - return ( - `Package ID unchanged in ${strong('config.xml')}.\n` + - `The Package Identifier (AKA "Bundle ID" for iOS and "Application ID" for Android) is a unique ID (usually written in reverse DNS notation, such as ${strong('com.mycompany.MyApp')}) that Cordova uses when compiling the native build of your app. When your app is submitted to the App Store or Play Store, the Package ID can't be changed. This issue was detected because this app's Package ID is ${input('"io.ionic.starter"')}, which is the default Package ID provided after running ${input('ionic start')}.` - ).trim(); - } - - async detected() { - const cordova = this.project.getIntegration('cordova'); - - if (!cordova || !cordova.enabled) { - return false; - } - - const conf = await loadCordovaConfig(cordova); - - return conf.getBundleId() === 'io.ionic.starter'; - } - - async getTreatmentSteps() { - return [ - { message: `Change the ${strong('id')} attribute of ${strong('')} (root element) to something other than ${input('"io.ionic.starter"')}` }, - ]; - } -} - -export class ViewportFitNotSet extends Ailment { - readonly id: 'viewport-fit-not-set' = 'viewport-fit-not-set'; - - async getMessage() { - return ( - `${strong('viewport-fit=cover')} not set in ${strong('index.html')}\n` + - `iOS 11 introduces new "safe regions" for webviews, which can throw off component sizing, squish the header into the status bar, letterbox the app on iPhone X, etc. Fixing this issue will ensure the webview takes up the full size of the screen. See ${strong('https://ionicframework.com/blog/ios-11-checklist/')} for more information.` - ).trim(); - } - - async detected() { - const indexHtml = await readFile(path.resolve(await this.project.getSourceDir(), 'index.html'), { encoding: 'utf8' }); - const m = indexHtml.match(/\/); - return !Boolean(m); - } - - async getTreatmentSteps() { - return [ - { message: `Add ${strong('viewport-fit=cover')} to the ${strong('')} tag in your ${strong('index.html')} file` }, - ]; - } -} - -export class CordovaPlatformsCommitted extends Ailment { - readonly id = 'cordova-platforms-committed'; - - async getMessage() { - return ( - `Cordova ${strong('platforms/')} directory is committed to git.\n` + - `Cordova considers ${strong('platforms/')} and ${strong('plugins/')} build artifacts${ancillary('[1]')}, and routinely overwrites files.\n\n` + - `While committing these files might be necessary for some projects${ancillary('[2]')}, generally platforms should be configured using ${strong('config.xml')} and Cordova hooks${ancillary('[3]')} so that your project is more portable and SDK updates are easier.\n\n` + - `${ancillary('[1]')}: ${strong('https://cordova.apache.org/docs/en/latest/reference/cordova-cli/#version-control')}\n` + - `${ancillary('[2]')}: ${strong('https://cordova.apache.org/docs/en/latest/reference/cordova-cli/#platforms')}\n` + - `${ancillary('[3]')}: ${strong('https://cordova.apache.org/docs/en/latest/guide/appdev/hooks/index.html')}\n\n` + - `${chalk.yellow(`${strong('WARNING')}: Attempting to fix this could be dangerous. Only proceed if you're sure you haven't made manual modifications to these files.`)}\n` - ).trim(); - } - - async detected() { - if (!(await isRepoInitialized(this.project.directory))) { - return false; - } - - const cmdInstalled = await this.shell.cmdinfo('git', ['--version']); - - if (!cmdInstalled) { - return false; - } - - const files = (await this.shell.output('git', ['ls-tree', '--name-only', 'HEAD'], { fatalOnError: false, showCommand: false, showError: false })).split('\n'); - - return files.includes('platforms'); // TODO - } - - async getTreatmentSteps() { - return [ - { message: `Remove ${strong('platforms/')} from source control: ${input('git rm -rf platforms/')} and ${input('git commit')}` }, - { message: `Make sure the ${strong('platforms/')} directory has been removed: ${input('rm -rf platforms/')}` }, - { message: `Allow Cordova to repopulate your platforms: ${input('ionic cordova prepare')}` }, - ]; - } -} diff --git a/packages/@ionic/cli/src/lib/doctor/ailments/utils.ts b/packages/@ionic/cli/src/lib/doctor/ailments/utils.ts deleted file mode 100644 index 9367f51339..0000000000 --- a/packages/@ionic/cli/src/lib/doctor/ailments/utils.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { IAilment } from '../../../definitions'; -import { isTreatableAilment } from '../../../guards'; -import { input, weak } from '../../color'; - -export async function formatAilmentMessage(ailment: IAilment): Promise { - const treatable = isTreatableAilment(ailment); - - return ( - `${await ailment.getMessage()}\n` + - `${await formatAilmentSteps(ailment)}\n\n` + - `${weak('$')} ${input(`ionic config set -g doctor.issues.${ailment.id}.ignored true`)} ${weak('(ignore this issue in the future)')}\n` + - `${treatable ? `${weak('$')} ${input(`ionic doctor treat ${ailment.id}`)} ${weak('(attempt to fix this issue)')}\n` : ''}` - ); -} - -async function formatAilmentSteps(ailment: IAilment): Promise { - const steps = await ailment.getTreatmentSteps(); - - if (steps.length === 0) { - return ''; - } - - const treatable = isTreatableAilment(ailment); - const msg = treatable ? `To fix, the following step(s) need to be taken:` : `To fix, take the following step(s):`; - - return `\n${msg}\n\n${steps.map((step, i) => ` ${weak(String(i + 1) + ')')} ${step.message}`).join('\n')}`; -} diff --git a/packages/@ionic/cli/src/lib/doctor/index.ts b/packages/@ionic/cli/src/lib/doctor/index.ts deleted file mode 100644 index ce6904ec38..0000000000 --- a/packages/@ionic/cli/src/lib/doctor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ailments'; diff --git a/packages/@ionic/cli/src/lib/events.ts b/packages/@ionic/cli/src/lib/events.ts index f05e486e9a..3c52b85650 100644 --- a/packages/@ionic/cli/src/lib/events.ts +++ b/packages/@ionic/cli/src/lib/events.ts @@ -1,4 +1,4 @@ -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import { ServeDetails } from '../definitions'; diff --git a/packages/@ionic/cli/src/lib/generate.ts b/packages/@ionic/cli/src/lib/generate.ts index 2299845f78..f453ff44e1 100644 --- a/packages/@ionic/cli/src/lib/generate.ts +++ b/packages/@ionic/cli/src/lib/generate.ts @@ -15,8 +15,8 @@ export abstract class GenerateRunner implements Runne protected abstract readonly e: GenerateRunnerDeps; createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): GenerateOptions { - const [ type, name ] = inputs; - return { type, name }; + const [ name ] = inputs; + return { name }; } async ensureCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): Promise { /* overwritten in subclasses */ } diff --git a/packages/@ionic/cli/src/lib/hooks.ts b/packages/@ionic/cli/src/lib/hooks.ts index fb5c77d27a..8f9fe41ea1 100644 --- a/packages/@ionic/cli/src/lib/hooks.ts +++ b/packages/@ionic/cli/src/lib/hooks.ts @@ -1,6 +1,6 @@ import { conform } from '@ionic/utils-array'; import { prettyPath } from '@ionic/utils-terminal'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import * as path from 'path'; @@ -24,7 +24,7 @@ export abstract class Hook { return `ionic:${this.name}`; } - constructor(protected readonly e: HookDeps) {} + constructor(protected readonly e: HookDeps) { } async run(input: HookInput) { const { pkgManagerArgs } = await import('./utils/npm'); @@ -35,7 +35,7 @@ export abstract class Hook { return; // TODO: will we need hooks outside a project? } - const [ pkg ] = await this.e.project.getPackageJson(undefined, { logErrors: false }); + const [pkg] = await this.e.project.getPackageJson(undefined, { logErrors: false }); if (!pkg) { return; @@ -47,7 +47,7 @@ export abstract class Hook { if (pkg.scripts && pkg.scripts[this.script]) { debug(`Invoking ${ancillary(this.script)} npm script.`); - const [ pkgManager, ...pkgArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script }); + const [pkgManager, ...pkgArgs] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script }); await this.e.shell.run(pkgManager, pkgArgs, { env: ctxEnvironment, }); @@ -82,7 +82,7 @@ export abstract class Hook { ...ctxEnvironment, }, })); - } catch (e) { + } catch (e: any) { throw new HookException( `An error occurred while running an Ionic CLI hook defined in ${strong(prettyPath(this.e.project.filePath))}.\n` + `Hook: ${strong(this.name)}\n` + diff --git a/packages/@ionic/cli/src/lib/http.ts b/packages/@ionic/cli/src/lib/http.ts index 9f27646be1..4363110e8e 100644 --- a/packages/@ionic/cli/src/lib/http.ts +++ b/packages/@ionic/cli/src/lib/http.ts @@ -1,4 +1,4 @@ -import * as chalk from 'chalk'; +import chalk from 'chalk'; import * as lodash from 'lodash'; import * as util from 'util'; @@ -36,7 +36,7 @@ export const ERROR_UNKNOWN_CONTENT_TYPE = 'UNKNOWN_CONTENT_TYPE'; export const ERROR_UNKNOWN_RESPONSE_FORMAT = 'UNKNOWN_RESPONSE_FORMAT'; export class Client implements IClient { - constructor(public config: IConfig) {} + constructor(public config: IConfig) { } async make(method: HttpMethod, path: string, contentType: ContentType = ContentType.JSON): Promise<{ req: SuperAgentRequest; }> { const url = path.startsWith('http://') || path.startsWith('https://') ? path : `${this.config.getAPIUrl()}${path}`; @@ -259,7 +259,7 @@ export function formatSuperAgentError(e: SuperAgentError): string { try { const r = transformAPIResponse(res); f += formatAPIResponse(req, r); - } catch (e) { + } catch (e: any) { f += ( `HTTP Error ${statusCode}: ${req.method.toUpperCase()} ${req.url}\n` + '\n' + (res.text ? res.text.substring(0, FORMAT_ERROR_BODY_MAX_LENGTH) : '') diff --git a/packages/@ionic/cli/src/lib/index.ts b/packages/@ionic/cli/src/lib/index.ts index 1effce3290..2d54055fcc 100644 --- a/packages/@ionic/cli/src/lib/index.ts +++ b/packages/@ionic/cli/src/lib/index.ts @@ -1,7 +1,7 @@ import { LOGGER_LEVELS } from '@ionic/cli-framework-output'; import { createPromptModule } from '@ionic/cli-framework-prompts'; import { TERMINAL_INFO, prettyPath } from '@ionic/utils-terminal'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as path from 'path'; import { ERROR_VERSION_TOO_OLD } from '../bootstrap'; @@ -49,13 +49,13 @@ export async function generateIonicEnvironment(ctx: IonicContext, pargv: string[ const proxyVars = PROXY_ENVIRONMENT_VARIABLES.map((e): [string, string | undefined] => [e, process.env[e]]).filter(([, v]) => !!v); const getInfo = async () => { - const osName = await import('os-name'); + const osName = (await import('os-name')).default; const semver = await import('semver'); const { getUpdateConfig } = await import('./updates'); const os = osName(); - const [ npm, nativeRun, cordovaRes ] = await Promise.all([ + const [npm, nativeRun, cordovaRes] = await Promise.all([ shell.cmdinfo('npm', ['-v']), shell.cmdinfo('native-run', ['--version']), shell.cmdinfo('cordova-res', ['--version']), diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/android.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/android.ts index f0ece072d9..31d8a3b58d 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/android.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/android.ts @@ -38,7 +38,7 @@ export class CapacitorAndroidManifest { try { this._doc = et.parse(this.origManifestContent); - } catch (e) { + } catch (e: any) { throw new Error(`Cannot parse ${ANDROID_MANIFEST_FILE} file: ${e.stack ?? e}`); } } diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index 223daabbcf..1fbc6ed3c3 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -1,8 +1,8 @@ import { parseArgs } from '@ionic/cli-framework'; import { mkdirp, pathExists } from '@ionic/utils-fs'; import { prettyPath } from '@ionic/utils-terminal'; -import * as chalk from 'chalk'; -import * as Debug from 'debug'; +import chalk from 'chalk'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import * as path from 'path'; @@ -99,17 +99,17 @@ export class Integration extends BaseIntegration { } async installCapacitorCore() { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/core@latest' }); + const [manager, ...managerArgs] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/core@latest' }); await this.e.shell.run(manager, managerArgs, { cwd: this.root }); } async installCapacitorCLI() { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/cli@latest', saveDev: true }); + const [manager, ...managerArgs] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/cli@latest', saveDev: true }); await this.e.shell.run(manager, managerArgs, { cwd: this.root }); } async installCapacitorPlugins() { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: ['@capacitor/haptics', '@capacitor/app', '@capacitor/keyboard', '@capacitor/status-bar'] }); + const [manager, ...managerArgs] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: ['@capacitor/haptics', '@capacitor/app', '@capacitor/keyboard', '@capacitor/status-bar'] }); await this.e.shell.run(manager, managerArgs, { cwd: this.root }); } @@ -131,10 +131,10 @@ export class Integration extends BaseIntegration { const bundleId = conf?.appId; const [ - [ capacitorCorePkg, capacitorCorePkgPath ], + [capacitorCorePkg, capacitorCorePkgPath], capacitorCLIVersion, - [ capacitorIOSPkg, capacitorIOSPkgPath ], - [ capacitorAndroidPkg, capacitorAndroidPkgPath ], + [capacitorIOSPkg, capacitorIOSPkgPath], + [capacitorAndroidPkg, capacitorAndroidPkgPath], ] = await (Promise.all([ this.e.project.getPackageJson('@capacitor/core'), this.getCapacitorCLIVersion(), @@ -219,7 +219,7 @@ export class Integration extends BaseIntegration { // Capacitor 1 returns the `command not found` error in stdout instead of stderror like in Capacitor 2 // This ensures that the output from the command is valid JSON to account for this return JSON.parse(output); - } catch(e) { + } catch (e) { debug('Could not get config from Capacitor CLI (probably old version)', e); return; } diff --git a/packages/@ionic/cli/src/lib/integrations/cordova/__tests__/project.ts b/packages/@ionic/cli/src/lib/integrations/cordova/__tests__/project.ts index 13777cd1be..34c1a8f211 100644 --- a/packages/@ionic/cli/src/lib/integrations/cordova/__tests__/project.ts +++ b/packages/@ionic/cli/src/lib/integrations/cordova/__tests__/project.ts @@ -1,4 +1,4 @@ -import * as fsExtraSpy from 'fs-extra'; +import fsExtraSpy from 'fs-extra'; import * as fsSafeSpy from '@ionic/utils-fs/dist/safe'; import * as project from '../project'; diff --git a/packages/@ionic/cli/src/lib/integrations/cordova/android.ts b/packages/@ionic/cli/src/lib/integrations/cordova/android.ts index ecf02b8cba..cedf632619 100644 --- a/packages/@ionic/cli/src/lib/integrations/cordova/android.ts +++ b/packages/@ionic/cli/src/lib/integrations/cordova/android.ts @@ -8,7 +8,7 @@ export async function getAndroidSdkToolsVersion(): Promise { try { const f = await readFile(path.join(androidHome, 'tools', 'source.properties'), { encoding: 'utf8' }); return `${await parseSDKVersion(f)} (${androidHome})`; - } catch (e) { + } catch (e: any) { if (e.code !== 'ENOENT') { throw e; } diff --git a/packages/@ionic/cli/src/lib/integrations/cordova/config.ts b/packages/@ionic/cli/src/lib/integrations/cordova/config.ts index cf89a24998..c342061cee 100644 --- a/packages/@ionic/cli/src/lib/integrations/cordova/config.ts +++ b/packages/@ionic/cli/src/lib/integrations/cordova/config.ts @@ -1,7 +1,7 @@ import { readPackageJsonFile } from '@ionic/cli-framework/utils/node'; import { readFile, writeFile } from '@ionic/utils-fs'; import { prettyPath } from '@ionic/utils-terminal'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as et from 'elementtree'; import * as path from 'path'; @@ -24,7 +24,7 @@ export class CordovaConfig { protected _sessionid?: string; protected saving = false; - constructor(readonly configXmlPath: string, readonly packageJsonPath: string) {} + constructor(readonly configXmlPath: string, readonly packageJsonPath: string) { } get doc(): et.ElementTree { if (!this._doc) { @@ -71,7 +71,7 @@ export class CordovaConfig { try { this._doc = et.parse(configXml); this._sessionid = shortid(); - } catch (e) { + } catch (e: any) { throw new Error(`Cannot parse config.xml file: ${e.stack ? e.stack : e}`); } @@ -234,14 +234,14 @@ export async function loadCordovaConfig(integration: Required { if (!overwrite) { blacklist.push(f); } - } catch (e) { + } catch (e: any) { if (e.code !== 'ENOENT') { throw e; } @@ -125,7 +125,7 @@ export class Integration extends BaseIntegration { async getCordovaConfig(): Promise { try { return await this.requireConfig(); - } catch (e) { + } catch (e: any) { // ignore } } @@ -149,7 +149,7 @@ export class Integration extends BaseIntegration { iosDeploy, iosSim, androidSdkToolsVersion, - ] = await (Promise.all([ + ] = await (Promise.all([ this.getCordovaVersion(), this.getCordovaPlatformVersions(), this.getCordovaPluginVersions(), @@ -207,7 +207,7 @@ export class Integration extends BaseIntegration { try { const integration = this.e.project.requireIntegration('cordova'); return this.e.shell.cmdinfo('cordova', ['-v', '--no-telemetry', '--no-update-notifier'], { cwd: integration.root }); - } catch (e) { + } catch (e: any) { debug('Error while getting Cordova version: %O', e); } } @@ -228,7 +228,7 @@ export class Integration extends BaseIntegration { } return platforms.join(', '); - } catch (e) { + } catch (e: any) { debug('Error while getting Cordova platforms: %O', e); return 'not available'; } @@ -251,8 +251,8 @@ export class Integration extends BaseIntegration { .map(m => [m[1], m[2]]); const whitelistedPlugins = plugins - .filter(([ plugin, version ]) => whitelist.some(re => re.test(plugin))) - .map(([ plugin, version ]) => `${plugin} ${version}`); + .filter(([plugin, version]) => whitelist.some(re => re.test(plugin))) + .map(([plugin, version]) => `${plugin} ${version}`); const count = plugins.length - whitelistedPlugins.length; @@ -261,7 +261,7 @@ export class Integration extends BaseIntegration { } return `${whitelistedPlugins.join(', ')}${count > 0 ? `, (and ${count} other plugins)` : ''}`; - } catch (e) { + } catch (e: any) { debug('Error while getting Cordova plugins: %O', e); return 'not available'; } diff --git a/packages/@ionic/cli/src/lib/integrations/cordova/project.ts b/packages/@ionic/cli/src/lib/integrations/cordova/project.ts index 99f58c9fb6..7ffa6fb988 100644 --- a/packages/@ionic/cli/src/lib/integrations/cordova/project.ts +++ b/packages/@ionic/cli/src/lib/integrations/cordova/project.ts @@ -1,6 +1,6 @@ import { filter } from '@ionic/utils-array'; import { readJson, readdirSafe, statSafe } from '@ionic/utils-fs'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as path from 'path'; import { AndroidBuildOutput, LegacyAndroidBuildOutputEntry } from '../../../definitions'; @@ -37,7 +37,7 @@ export async function getAndroidBuildOutputJson(paths: string[]): Promise { try { const res = await this.e.client.do(req); return res.data as ProductKey; - } catch (e) { + } catch (e: any) { if (isSuperAgentError(e)) { if (e.response.status === 401 || e.response.status === 403) { throw new FatalException('Authorization Failed. Make sure you\'re logged into the correct account with access to the key. Try logging out and back in again.'); @@ -192,7 +192,7 @@ export class Integration extends BaseIntegration { try { const res = await this.e.client.do(req); return res.data as ProductKey; - } catch (e) { + } catch (e: any) { if (isSuperAgentError(e)) { if (e.response.status === 401 || e.response.status === 403) { throw new FatalException('Authorization Failed. Make sure you\'re logged into the correct account with access to the key. Try logging out and back in again.'); @@ -217,7 +217,7 @@ export class Integration extends BaseIntegration { let npmrc = ''; try { npmrc = await readFile(path.join(this.e.project.directory , '.npmrc'), 'utf8'); - } catch (e) { + } catch (e: any) { if (!e.message.includes('ENOENT')) { throw e; } diff --git a/packages/@ionic/cli/src/lib/ionitron.ts b/packages/@ionic/cli/src/lib/ionitron.ts index 8637fdef80..f2a4522ed0 100644 --- a/packages/@ionic/cli/src/lib/ionitron.ts +++ b/packages/@ionic/cli/src/lib/ionitron.ts @@ -1,4 +1,4 @@ -import * as chalk from 'chalk'; +import chalk from 'chalk'; export function getIonitronString(quote: string) { const quoteFormatted = quote diff --git a/packages/@ionic/cli/src/lib/native-run.ts b/packages/@ionic/cli/src/lib/native-run.ts index 3acea17512..d483e99110 100644 --- a/packages/@ionic/cli/src/lib/native-run.ts +++ b/packages/@ionic/cli/src/lib/native-run.ts @@ -81,7 +81,7 @@ export async function runNativeRun({ config, log, shell }: RunNativeRunDeps, arg try { await shell.run('native-run', args, { showCommand: !args.includes('--json'), fatalOnNotFound: false, stream, ...options }); - } catch (e) { + } catch (e: any) { if (e instanceof SubprocessError && e.code === ERROR_COMMAND_NOT_FOUND) { throw createNativeRunNotFoundError(config.get('npmClient')); } @@ -112,7 +112,7 @@ export async function checkNativeRun({ config }: CheckNativeRunDeps): Promise { try { return await which('native-run'); - } catch (e) { + } catch (e: any) { if (e.code !== 'ENOENT') { throw e; } @@ -153,7 +153,7 @@ export async function getNativeTargets({ log, shell }: RunNativeRunDeps, platfor const output = await proc.output(); return JSON.parse(output); - } catch (e) { + } catch (e: any) { if (e instanceof SubprocessError && e.code === ERROR_NON_ZERO_EXIT) { const output = e.output ? JSON.parse(e.output) : {}; diff --git a/packages/@ionic/cli/src/lib/oauth/oauth.ts b/packages/@ionic/cli/src/lib/oauth/oauth.ts index 3927e6fed8..96c3328dd3 100644 --- a/packages/@ionic/cli/src/lib/oauth/oauth.ts +++ b/packages/@ionic/cli/src/lib/oauth/oauth.ts @@ -125,7 +125,7 @@ export abstract class OAuth2Flow { const authResult = { code: Array.isArray(params.code) ? params.code[0] : params.code, - state: Array.isArray(params.state) ? decodeURI(params.state[0]) : decodeURI(params.state), + state: params.state ? (Array.isArray(params.state) ? decodeURI(params.state[0]) : decodeURI(params.state)) : '', }; resolve(authResult); diff --git a/packages/@ionic/cli/src/lib/open.ts b/packages/@ionic/cli/src/lib/open.ts index cb910a4120..e33f21c826 100644 --- a/packages/@ionic/cli/src/lib/open.ts +++ b/packages/@ionic/cli/src/lib/open.ts @@ -1,4 +1,4 @@ -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; const debug = Debug('ionic:lib:open'); @@ -7,7 +7,7 @@ export interface OpenUrlOptions { } export async function openUrl(target: string, options: OpenUrlOptions = {}): Promise { - const o = await import ('open'); + const o = (await import('open')).default; const p = await o(target, { ...options, wait: false, url: true }); const e = (err: Error) => debug('Error during open: %O', err); const n = p.on.bind(p); diff --git a/packages/@ionic/cli/src/lib/project/angular/__tests__/generate.ts b/packages/@ionic/cli/src/lib/project/angular/__tests__/generate.ts index aa8aa8ae5d..62cc285182 100644 --- a/packages/@ionic/cli/src/lib/project/angular/__tests__/generate.ts +++ b/packages/@ionic/cli/src/lib/project/angular/__tests__/generate.ts @@ -12,7 +12,7 @@ describe('@ionic/cli', () => { const defaults = { name: undefined, project: 'app', - type: undefined, + schematic: undefined, }; it('should provide defaults with no inputs or options', () => { @@ -24,7 +24,7 @@ describe('@ionic/cli', () => { it('should provide options from inputs', () => { const runner = new AngularGenerateRunner({} as any); const result = runner.createOptionsFromCommandLine(['service', 'FancyBar'], { _: [] }); - expect(result).toEqual({ ...defaults, name: 'FancyBar', type: 'service' }); + expect(result).toEqual({ ...defaults, name: 'FancyBar', schematic: 'service' }); }); it('should respect --project', () => { diff --git a/packages/@ionic/cli/src/lib/project/angular/__tests__/serve.ts b/packages/@ionic/cli/src/lib/project/angular/__tests__/serve.ts index 3c369e4a72..dbfe0e850c 100644 --- a/packages/@ionic/cli/src/lib/project/angular/__tests__/serve.ts +++ b/packages/@ionic/cli/src/lib/project/angular/__tests__/serve.ts @@ -17,9 +17,6 @@ describe('@ionic/cli', () => { browserOption: undefined, engine: 'browser', externalAddressRequired: false, - lab: false, - labHost: 'localhost', - labPort: 8200, livereload: true, open: false, port: 8100, @@ -38,8 +35,8 @@ describe('@ionic/cli', () => { it('should provide options from negations of cli flag defaults', () => { const runner = new AngularServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [], livereload: false, proxy: false, lab: true, open: true, externalAddressRequired: true }); - expect(result).toEqual({ ...defaults, livereload: false, proxy: false, lab: true, open: true, externalAddressRequired: true }); + const result = runner.createOptionsFromCommandLine([], { _: [], livereload: false, proxy: false, open: true, externalAddressRequired: true }); + expect(result).toEqual({ ...defaults, livereload: false, proxy: false, open: true, externalAddressRequired: true }); }); it('should allow overrides of default values', () => { diff --git a/packages/@ionic/cli/src/lib/project/angular/ailments.ts b/packages/@ionic/cli/src/lib/project/angular/ailments.ts deleted file mode 100644 index 597eaec7a6..0000000000 --- a/packages/@ionic/cli/src/lib/project/angular/ailments.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { AilmentDeps } from '../../doctor'; - -import { AngularProject } from './'; - -export interface AngularAilmentDeps extends AilmentDeps { - readonly project: AngularProject; -} - -// abstract class AngularAilment extends Ailment { -// readonly projects: ProjectType[] = ['angular']; -// protected readonly project: AngularProject; - -// constructor(deps: AngularAilmentDeps) { -// super(deps); -// this.project = deps.project; -// } -// } diff --git a/packages/@ionic/cli/src/lib/project/angular/generate.ts b/packages/@ionic/cli/src/lib/project/angular/generate.ts index b05866ed3b..7324685b27 100644 --- a/packages/@ionic/cli/src/lib/project/angular/generate.ts +++ b/packages/@ionic/cli/src/lib/project/angular/generate.ts @@ -1,5 +1,5 @@ import { unparseArgs, validators } from '@ionic/cli-framework'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import { AngularGenerateOptions, CommandLineInputs, CommandLineOptions, CommandMetadata } from '../../../definitions'; @@ -58,7 +58,7 @@ To test a generator before file modifications are made, use the ${input('--dry-r ], inputs: [ { - name: 'type', + name: 'schematic', summary: `The type of feature (e.g. ${['page', 'component', 'directive', 'service'].map(c => input(c)).join(', ')})`, validators: [validators.required], }, @@ -75,22 +75,22 @@ To test a generator before file modifications are made, use the ${input('--dry-r if (inputs[0]) { this.validateFeatureType(inputs[0]); } else { - const type = await this.e.prompt({ + const schematic = await this.e.prompt({ type: 'list', - name: 'type', + name: 'schematic', message: 'What would you like to generate?', choices: SCHEMATICS, }); - inputs[0] = type; + inputs[0] = schematic; } if (!inputs[1]) { - const type = SCHEMATIC_ALIAS.get(inputs[0]) || inputs[0]; + const schematic = SCHEMATIC_ALIAS.get(inputs[0]) || inputs[0]; const name = await this.e.prompt({ type: 'input', name: 'name', - message: `Name/path of ${input(type)}:`, + message: `Name/path of ${input(schematic)}:`, validate: v => validators.required(v), }); @@ -99,7 +99,8 @@ To test a generator before file modifications are made, use the ${input('--dry-r } createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): AngularGenerateOptions { - const baseOptions = super.createOptionsFromCommandLine(inputs, options); + const [schematic, name] = inputs; + const baseOptions = { name, schematic } const project = options['project'] ? String(options['project']) : 'app'; // TODO: this is a little gross, is there a better way to pass in all the @@ -113,22 +114,22 @@ To test a generator before file modifications are made, use the ${input('--dry-r async run(options: AngularGenerateOptions) { const { name } = options; - const type = SCHEMATIC_ALIAS.get(options.type) || options.type; + const schematic = SCHEMATIC_ALIAS.get(options.schematic) || options.schematic; try { - await this.generateComponent(type, name, lodash.omit(options, 'type', 'name')); - } catch (e) { + await this.generateComponent(schematic, name, lodash.omit(options, 'schematic', 'name')); + } catch (e: any) { debug(e); - throw new FatalException(`Could not generate ${input(type)}.`); + throw new FatalException(`Could not generate ${input(schematic)}.`); } if (!options['dry-run']) { - this.e.log.ok(`Generated ${input(type)}!`); + this.e.log.ok(`Generated ${input(schematic)}!`); } } - private validateFeatureType(type: string) { - if (type === 'provider') { + private validateFeatureType(schematic: string) { + if (schematic === 'provider') { throw new FatalException( `Please use ${input('ionic generate service')} for generating service providers.\n` + `For more information, please see the Angular documentation${ancillary('[1]')} on services.\n\n` + @@ -136,17 +137,17 @@ To test a generator before file modifications are made, use the ${input('--dry-r ); } - if (!SCHEMATICS.includes(type) && !SCHEMATIC_ALIAS.get(type)) { + if (!SCHEMATICS.includes(schematic) && !SCHEMATIC_ALIAS.get(schematic)) { throw new FatalException( - `${input(type)} is not a known feature.\n` + + `${input(schematic)} is not a known feature.\n` + `Use ${input('npx ng g --help')} to list available types of features.` ); } } - private async generateComponent(type: string, name: string, options: { [key: string]: string | boolean; }) { + private async generateComponent(schematic: string, name: string, options: { [key: string]: string | boolean; }) { const args = { - _: ['generate', type, name], + _: ['generate', schematic, name], // convert `--no-` style options to `--=false` ...lodash.mapValues(options, v => v === false ? 'false' : v), }; diff --git a/packages/@ionic/cli/src/lib/project/angular/index.ts b/packages/@ionic/cli/src/lib/project/angular/index.ts index 444da518a1..71803ab1d7 100644 --- a/packages/@ionic/cli/src/lib/project/angular/index.ts +++ b/packages/@ionic/cli/src/lib/project/angular/index.ts @@ -1,9 +1,9 @@ -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import * as path from 'path'; import { Project } from '../'; -import { IAilmentRegistry, InfoItem } from '../../../definitions'; +import { InfoItem } from '../../../definitions'; import { strong } from '../../color'; const debug = Debug('ionic:lib:project:angular'); @@ -13,11 +13,11 @@ export class AngularProject extends Project { async getInfo(): Promise { const [ - [ ionicAngularPkg, ionicAngularPkgPath ], - [ ionicAngularToolkitPkg, ionicAngularToolkitPkgPath ], - [ angularCLIPkg, angularCLIPkgPath ], - [ angularDevKitBuildAngularPkg, angularDevKitBuildAngularPkgPath ], - [ angularDevKitSchematicsPkg, angularDevKitSchematicsPkgPath ], + [ionicAngularPkg, ionicAngularPkgPath], + [ionicAngularToolkitPkg, ionicAngularToolkitPkgPath], + [angularCLIPkg, angularCLIPkgPath], + [angularDevKitBuildAngularPkg, angularDevKitBuildAngularPkgPath], + [angularDevKitSchematicsPkg, angularDevKitSchematicsPkgPath], ] = await Promise.all([ this.getPackageJson('@ionic/angular'), this.getPackageJson('@ionic/angular-toolkit'), @@ -73,7 +73,7 @@ export class AngularProject extends Project { debug(`${strong('@ionic/angular')} detected in ${strong('package.json')}`); return true; } - } catch (e) { + } catch (e: any) { // ignore } @@ -98,11 +98,6 @@ export class AngularProject extends Project { return new AngularGenerateRunner(deps); } - async registerAilments(registry: IAilmentRegistry): Promise { - await super.registerAilments(registry); - // TODO: register angular project ailments - } - setPrimaryTheme(themeColor: string): Promise { const themePath = path.join(this.directory, 'src', 'theme', 'variables.scss'); return this.writeThemeColor(themePath, themeColor); diff --git a/packages/@ionic/cli/src/lib/project/angular/serve.ts b/packages/@ionic/cli/src/lib/project/angular/serve.ts index 21f75d27c9..a306d0484c 100644 --- a/packages/@ionic/cli/src/lib/project/angular/serve.ts +++ b/packages/@ionic/cli/src/lib/project/angular/serve.ts @@ -2,7 +2,7 @@ import { MetadataGroup, ParsedArgs, unparseArgs } from '@ionic/cli-framework'; import { str2num } from '@ionic/cli-framework/utils/string'; import { findClosestOpenPort } from '@ionic/utils-network'; import { stripAnsi } from '@ionic/utils-terminal'; -import * as chalk from 'chalk'; +import chalk from 'chalk'; import { AngularServeOptions, CommandLineInputs, CommandLineOptions, CommandMetadata, ServeDetails } from '../../../definitions'; import { input, strong, weak } from '../../color'; @@ -123,7 +123,7 @@ The dev server can use HTTPS via the ${input('--ssl')} option ${chalk.bold.red(' } async serveProject(options: AngularServeOptions): Promise { - const [ externalIP, availableInterfaces ] = await this.selectExternalIP(options); + const [externalIP, availableInterfaces] = await this.selectExternalIP(options); const port = options.port = await findClosestOpenPort(options.port); @@ -173,8 +173,11 @@ export class AngularServeCLI extends ServeCLI { } const strippedLine = stripAnsi(line); - - if (strippedLine.includes('Development Server is listening')) { + const compileMsgs = [ + 'Development Server is listening', + 'Watching for file changes' + ] + if (compileMsgs.some((msg) => strippedLine.includes(msg))) { this.emit('ready'); return false; } @@ -200,7 +203,7 @@ export class AngularServeCLI extends ServeCLI { if (this.resolvedProgram === this.program) { return [...this.buildArchitectCommand(options), ...args]; } else { - const [ , ...pkgArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script, scriptArgs: [...args] }); + const [, ...pkgArgs] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script, scriptArgs: [...args] }); return pkgArgs; } } diff --git a/packages/@ionic/cli/src/lib/project/common.ts b/packages/@ionic/cli/src/lib/project/common.ts deleted file mode 100644 index fdb9281545..0000000000 --- a/packages/@ionic/cli/src/lib/project/common.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { findClosestOpenPort } from '@ionic/utils-network'; -import * as Debug from 'debug'; - -import { input, strong } from '../color'; -import { FatalException } from '../errors'; - -const debug = Debug('ionic:lib:project:common'); - -export interface Ports { - port: number; - livereloadPort: number; - notificationPort: number; -} - -/** - * Convenience function for finding open ports of old-style projects. - * - * For `ionic-angular` and `ionic1`, Ionic provides the livereload server and - * "dev logger" server. - */ -export async function findOpenIonicPorts(address: string, ports: Ports): Promise { - try { - const [ port, livereloadPort, notificationPort ] = await Promise.all([ - findClosestOpenPort(ports.port), - findClosestOpenPort(ports.livereloadPort), - findClosestOpenPort(ports.notificationPort), - ]); - - if (ports.port !== port) { - debug(`Port ${strong(String(ports.port))} taken, using ${strong(String(port))}.`); - ports.port = port; - } - - if (ports.livereloadPort !== livereloadPort) { - debug(`Port ${strong(String(ports.livereloadPort))} taken, using ${strong(String(livereloadPort))}.`); - ports.livereloadPort = livereloadPort; - } - - if (ports.notificationPort !== notificationPort) { - debug(`Port ${strong(String(ports.notificationPort))} taken, using ${strong(String(notificationPort))}.`); - ports.notificationPort = notificationPort; - } - - return { port, livereloadPort, notificationPort }; - } catch (e) { - if (e.code !== 'EADDRNOTAVAIL') { - throw e; - } - - throw new FatalException(`${input(address)} is not available--cannot bind.`); - } -} diff --git a/packages/@ionic/cli/src/lib/project/index.ts b/packages/@ionic/cli/src/lib/project/index.ts index 349123f31e..9e42e3db94 100644 --- a/packages/@ionic/cli/src/lib/project/index.ts +++ b/packages/@ionic/cli/src/lib/project/index.ts @@ -4,12 +4,12 @@ import { resolveValue } from '@ionic/cli-framework/utils/fn'; import { ERROR_INVALID_PACKAGE_JSON, compileNodeModulesPaths, isValidPackageName, readPackageJsonFile } from '@ionic/cli-framework/utils/node'; import { ensureDir, findBaseDirectory, readFile, writeFile, writeJson } from '@ionic/utils-fs'; import { TTY_WIDTH, prettyPath, wordWrap } from '@ionic/utils-terminal'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import * as path from 'path'; -import { PROJECT_FILE, PROJECT_TYPES } from '../../constants'; -import { IAilmentRegistry, IClient, IConfig, IIntegration, ILogger, IMultiProjectConfig, IProject, IProjectConfig, ISession, IShell, InfoItem, IntegrationName, IonicContext, IonicEnvironmentFlags, ProjectIntegration, ProjectPersonalizationDetails, ProjectType } from '../../definitions'; +import { PROJECT_FILE, PROJECT_TYPES, ANGULAR_STANDALONE } from '../../constants'; +import { IClient, IConfig, IIntegration, ILogger, IMultiProjectConfig, IProject, IProjectConfig, ISession, IShell, InfoItem, IntegrationName, IonicContext, IonicEnvironmentFlags, ProjectIntegration, ProjectPersonalizationDetails, ProjectType } from '../../definitions'; import { isMultiProjectConfig, isProjectConfig } from '../../guards'; import { ancillary, failure, input, strong } from '../color'; import { BaseException, FatalException, IntegrationNotFoundException, RunnerNotFoundException } from '../errors'; @@ -55,9 +55,9 @@ export class ProjectDetailsError extends BaseException { /** * The underlying error that caused this error. */ - readonly error?: Error + cause?: Error ) { - super(msg); + super(msg, { cause }); } } @@ -193,7 +193,7 @@ export class ProjectDetails { if (e1) { log.error( `Error while loading config (project config: ${strong(prettyPath(result.configPath))})\n` + - `${e1.error ? `${e1.message}: ${failure(e1.error.toString())}` : failure(e1.message)}. ` + + `${e1.cause ? `${e1.message}: ${failure(e1.cause.toString())}` : failure(e1.message)}. ` + `Run ${input('ionic init')} to re-initialize your Ionic project. Without a valid project config, the CLI will not have project context.` ); @@ -225,8 +225,6 @@ export class ProjectDetails { log.warn( `Could not determine project type (project config: ${strong(prettyPath(result.configPath))}).\n` + `- ${wordWrap(`For ${strong(prettyProjectName('angular'))} projects, make sure ${input('@ionic/angular')} is listed as a dependency in ${strong('package.json')}.`, listWrapOptions)}\n` + - `- ${wordWrap(`For ${strong(prettyProjectName('ionic-angular'))} projects, make sure ${input('ionic-angular')} is listed as a dependency in ${strong('package.json')}.`, listWrapOptions)}\n` + - `- ${wordWrap(`For ${strong(prettyProjectName('ionic1'))} projects, make sure ${input('ionic')} is listed as a dependency in ${strong('bower.json')}.`, listWrapOptions)}\n\n` + `Alternatively, set ${strong('type')} attribute in ${strong(prettyPath(result.configPath))} to one of: ${PROJECT_TYPES.map(v => input(v)).join(', ')}.\n\n` + `If the Ionic CLI does not know what type of project this is, ${input('ionic build')}, ${input('ionic serve')}, and other commands may not work. You can use the ${input('custom')} project type if that's okay.` ); @@ -254,7 +252,7 @@ export class ProjectDetails { } return await JSON.parse(configContents); - } catch (e) { + } catch (e: any) { throw new ProjectDetailsError('Could not read project file', 'ERR_INVALID_PROJECT_FILE', e); } } @@ -286,7 +284,7 @@ export class ProjectDetails { } throw new ProjectDetailsError('Unknown project file structure', 'ERR_INVALID_PROJECT_FILE'); - } catch (e) { + } catch (e: any) { errors.push(e); } @@ -299,6 +297,7 @@ export async function createProjectFromDetails(details: ProjectDetailsResult, de switch (type) { case 'angular': + case ANGULAR_STANDALONE: const { AngularProject } = await import('./angular'); return new AngularProject(details, deps); case 'react': @@ -307,12 +306,12 @@ export async function createProjectFromDetails(details: ProjectDetailsResult, de case 'vue': const { VueProject } = await import('./vue'); return new VueProject(details, deps); - case 'ionic-angular': - const { IonicAngularProject } = await import('./ionic-angular'); - return new IonicAngularProject(details, deps); - case 'ionic1': - const { Ionic1Project } = await import('./ionic1'); - return new Ionic1Project(details, deps); + case 'vue-vite': + const { VueViteProject } = await import('./vue-vite'); + return new VueViteProject(details, deps); + case 'react-vite': + const { ReactViteProject } = await import('./react-vite'); + return new ReactViteProject(details, deps); case 'custom': const { CustomProject } = await import('./custom'); return new CustomProject(details, deps); @@ -459,7 +458,7 @@ export abstract class Project implements IProject { async getBuildRunner(): Promise | undefined> { try { return await this.requireBuildRunner(); - } catch (e) { + } catch (e: any) { if (!(e instanceof RunnerNotFoundException)) { throw e; } @@ -469,7 +468,7 @@ export abstract class Project implements IProject { async getServeRunner(): Promise | undefined> { try { return await this.requireServeRunner(); - } catch (e) { + } catch (e: any) { if (!(e instanceof RunnerNotFoundException)) { throw e; } @@ -479,7 +478,7 @@ export abstract class Project implements IProject { async getGenerateRunner(): Promise | undefined> { try { return await this.requireGenerateRunner(); - } catch (e) { + } catch (e: any) { if (!(e instanceof RunnerNotFoundException)) { throw e; } @@ -508,9 +507,9 @@ export abstract class Project implements IProject { let pkgPath: string | undefined; try { - pkgPath = pkgName ? require.resolve(`${pkgName}/package`, { paths: compileNodeModulesPaths(this.directory) }) : this.packageJsonPath; + pkgPath = pkgName ? require.resolve(`${pkgName}/package.json`, { paths: compileNodeModulesPaths(this.directory) }) : this.packageJsonPath; pkg = await readPackageJsonFile(pkgPath); - } catch (e) { + } catch (e: any) { if (logErrors) { this.e.log.warn(`Error loading ${strong(pkgName ? pkgName : `project's`)} ${strong('package.json')}: ${e}`); } @@ -521,9 +520,9 @@ export abstract class Project implements IProject { async requirePackageJson(pkgName?: string): Promise { try { - const pkgPath = pkgName ? require.resolve(`${pkgName}/package`, { paths: compileNodeModulesPaths(this.directory) }) : this.packageJsonPath; + const pkgPath = pkgName ? require.resolve(`${pkgName}/package.json`, { paths: compileNodeModulesPaths(this.directory) }) : this.packageJsonPath; return await readPackageJsonFile(pkgPath); - } catch (e) { + } catch (e: any) { if (e instanceof SyntaxError) { throw new FatalException(`Could not parse ${strong(pkgName ? pkgName : `project's`)} ${strong('package.json')}. Is it a valid JSON file?`); } else if (e === ERROR_INVALID_PACKAGE_JSON) { @@ -534,10 +533,6 @@ export abstract class Project implements IProject { } } - async getDocsUrl(): Promise { - return 'https://ion.link/docs'; - } - async getSourceDir(): Promise { return path.resolve(this.directory, 'src'); } @@ -684,7 +679,7 @@ export abstract class Project implements IProject { } await writeFile(variablesPath, themeVarsContents); - } catch (e) { + } catch (e: any) { const { log } = this.e; log.error(`Unable to modify theme variables, theme will need to be set manually: ${e}`); } @@ -700,27 +695,12 @@ export abstract class Project implements IProject { await writeFile(iconPath, appIcon); await writeFile(splashPath, splash); - } catch (e) { + } catch (e: any) { const { log } = this.e; log.error(`Unable to find or create the resources directory. Skipping icon generation: ${e}`); } } - async registerAilments(registry: IAilmentRegistry): Promise { - const ailments = await import('../doctor/ailments'); - const deps = { ...this.e, project: this }; - - registry.register(new ailments.NpmInstalledLocally(deps)); - registry.register(new ailments.IonicCLIInstalledLocally(deps)); - registry.register(new ailments.GitNotUsed(deps)); - registry.register(new ailments.GitConfigInvalid(deps)); - registry.register(new ailments.IonicNativeOldVersionInstalled(deps)); - registry.register(new ailments.UnsavedCordovaPlatforms(deps)); - registry.register(new ailments.DefaultCordovaBundleIdUsed(deps)); - registry.register(new ailments.ViewportFitNotSet(deps)); - registry.register(new ailments.CordovaPlatformsCommitted(deps)); - } - async createIntegration(name: 'capacitor'): Promise; async createIntegration(name: 'cordova'): Promise; async createIntegration(name: 'enterprise'): Promise; @@ -775,7 +755,7 @@ export abstract class Project implements IProject { const integrations: (IIntegration | undefined)[] = await Promise.all(integrationNames.map(async name => { try { return await this.createIntegration(name); - } catch (e) { + } catch (e: any) { if (!(e instanceof IntegrationNotFoundException)) { throw e; } @@ -799,10 +779,6 @@ export function prettyProjectName(type?: string): string { return '@ionic/react'; } else if (type === 'vue') { return '@ionic/vue'; - } else if (type === 'ionic-angular') { - return 'Ionic 2/3'; - } else if (type === 'ionic1') { - return 'Ionic 1'; } else if (type === 'custom') { return 'Custom'; } diff --git a/packages/@ionic/cli/src/lib/project/ionic-angular/__tests__/build.ts b/packages/@ionic/cli/src/lib/project/ionic-angular/__tests__/build.ts deleted file mode 100644 index e254a7b6ad..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic-angular/__tests__/build.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { IonicAngularBuildCLI } from '../build'; - -describe('@ionic/cli', () => { - - describe('lib/ionic-angular', () => { - - describe('IonicAngularBuildCLI', () => { - - describe('buildOptionsToAppScriptsArgs', () => { - - const options = { - '--': ['--generateSourceMap', 'false'], - prod: true, - aot: true, - minifyjs: true, - minifycss: true, - optimizejs: true, - }; - - it('should transform defaults', async () => { - const appscripts = new IonicAngularBuildCLI({} as any); - const result = await (appscripts as any).buildOptionsToAppScriptsArgs({ _: [], '--': [] }); - expect(result).toEqual([]); - }); - - it('should transform options', async () => { - const appscripts = new IonicAngularBuildCLI({} as any); - const result = await (appscripts as any).buildOptionsToAppScriptsArgs(options); - expect(result).toEqual(['--prod', '--aot', '--minifyjs', '--minifycss', '--optimizejs', '--generateSourceMap', 'false']); - }); - - }); - - }); - - }); - -}); diff --git a/packages/@ionic/cli/src/lib/project/ionic-angular/__tests__/index.ts b/packages/@ionic/cli/src/lib/project/ionic-angular/__tests__/index.ts deleted file mode 100644 index b44e7c9ba6..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic-angular/__tests__/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as path from 'path' -import { IonicAngularProject } from '../'; - -describe('@ionic/cli', () => { - - describe('lib/project/ionic-angular', () => { - - describe('IonicAngularProject', () => { - - let p: IonicAngularProject; - - beforeEach(() => { - p = new IonicAngularProject({ context: 'app', configPath: '/path/to/proj/file' } as any, {} as any); - jest.spyOn(p, 'config', 'get').mockImplementation(() => ({ get: () => undefined } as any)); - }); - - it('should set directory attribute', async () => { - expect(p.directory).toEqual(path.resolve('/path/to/proj')); - }); - - describe('getSourceDir', () => { - - it('should default to src', async () => { - const result = await p.getSourceDir(); - expect(result).toEqual(path.resolve('/path/to/proj/src')); - }); - - }); - - }); - - }); - -}); diff --git a/packages/@ionic/cli/src/lib/project/ionic-angular/__tests__/serve.ts b/packages/@ionic/cli/src/lib/project/ionic-angular/__tests__/serve.ts deleted file mode 100644 index d8baa3727d..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic-angular/__tests__/serve.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { IonicAngularServeRunner } from '../serve'; - -describe('@ionic/cli', () => { - - describe('lib/project/ionic-angular/serve', () => { - - describe('IonicAngularServeRunner', () => { - - describe('createOptionsFromCommandLine', () => { - - const defaults = { - '--': [], - publicHost: undefined, - host: 'localhost', - browser: undefined, - browserOption: undefined, - consolelogs: false, - engine: 'browser', - env: undefined, - externalAddressRequired: false, - lab: false, - labHost: 'localhost', - labPort: 8200, - livereload: true, - livereloadPort: 35729, - notificationPort: 53703, - open: false, - port: 8100, - proxy: true, - serverlogs: false, - project: undefined, - verbose: false, - }; - - it('should provide defaults with no options', () => { - const runner = new IonicAngularServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [] }); - expect(result).toEqual(defaults); - }); - - it('should provide options from negations of cli flag defaults', () => { - const runner = new IonicAngularServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [], livereload: false, proxy: false, lab: true, open: true, externalAddressRequired: true }); - expect(result).toEqual({ ...defaults, livereload: false, proxy: false, lab: true, open: true, externalAddressRequired: true }); - }); - - it('should allow overrides of default values', () => { - const runner = new IonicAngularServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [], host: '0.0.0.0', port: '1111', 'livereload-port': '2222', 'dev-logger-port': '3333', env: 'prod' }); - expect(result).toEqual({ ...defaults, host: '0.0.0.0', port: 1111, livereloadPort: 2222, notificationPort: 3333, env: 'prod' }); - }); - - it('should respect --external flag', () => { - const runner = new IonicAngularServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [], host: 'localhost', external: true }); - expect(result).toEqual({ ...defaults, host: '0.0.0.0' }); - }); - - it('should pass on separated args', () => { - const runner = new IonicAngularServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [], '--': ['foo', '--bar'] }); - expect(result).toEqual({ ...defaults, '--': ['foo', '--bar'] }); - }); - - }); - - }); - - }); - -}); diff --git a/packages/@ionic/cli/src/lib/project/ionic-angular/ailments.ts b/packages/@ionic/cli/src/lib/project/ionic-angular/ailments.ts deleted file mode 100644 index 99b8ac81fa..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic-angular/ailments.ts +++ /dev/null @@ -1,272 +0,0 @@ -import * as semver from 'semver'; - -import { ProjectType, TreatableAilment } from '../../../definitions'; -import { BUILD_SCRIPT } from '../../build'; -import { ancillary, input, strong } from '../../color'; -import { Ailment, AilmentDeps } from '../../doctor'; -import { SERVE_SCRIPT } from '../../serve'; -import { pkgFromRegistry, pkgManagerArgs } from '../../utils/npm'; - -import { IonicAngularProject } from './'; -import { DEFAULT_BUILD_SCRIPT_VALUE } from './build'; -import { DEFAULT_SERVE_SCRIPT_VALUE } from './serve'; - -export interface IonicAngularAilmentDeps extends AilmentDeps { - readonly project: IonicAngularProject; -} - -export abstract class IonicAngularAilment extends Ailment { - readonly projects: ProjectType[] = ['ionic-angular']; - protected readonly project: IonicAngularProject; - - constructor(deps: IonicAngularAilmentDeps) { - super(deps); - this.project = deps.project; - } -} - -export class IonicAngularUpdateAvailable extends IonicAngularAilment { - readonly id = 'ionic-angular-update-available'; - currentVersion?: string; - latestVersion?: string; - - async getVersionPair(): Promise<[string, string]> { - if (!this.currentVersion || !this.latestVersion) { - const [ currentPkg ] = await this.project.getPackageJson('ionic-angular'); - const latestPkg = await pkgFromRegistry(this.config.get('npmClient'), { pkg: 'ionic-angular' }); - this.currentVersion = currentPkg ? currentPkg.version : undefined; - this.latestVersion = latestPkg ? latestPkg.version : undefined; - } - - if (!this.currentVersion || !this.latestVersion) { - return ['0.0.0', '0.0.0']; - } - - return [ this.currentVersion, this.latestVersion ]; - } - - async getMessage() { - const [ currentVersion, latestVersion ] = await this.getVersionPair(); - - return ( - `Update available for Ionic Framework.\n` + - `An update is available for ${strong('ionic-angular')} (${ancillary(currentVersion)} => ${ancillary(latestVersion)}).\n` - ).trim(); - } - - async detected() { - const [ currentVersion, latestVersion ] = await this.getVersionPair(); - const diff = semver.diff(currentVersion, latestVersion); - - return diff === 'minor' || diff === 'patch'; - } - - async getTreatmentSteps() { - const [ , latestVersion ] = await this.getVersionPair(); - const args = await pkgManagerArgs(this.config.get('npmClient'), { command: 'install', pkg: `ionic-angular@${latestVersion ? latestVersion : 'latest'}` }); - - return [ - { message: `Visit ${strong('https://github.com/ionic-team/ionic/releases')} for each upgrade's instructions` }, - { message: `If no instructions, run: ${input(args.join(' '))}` }, - { message: `Watch for npm warnings about peer dependencies--they may need manual updating` }, - ]; - } -} - -export class IonicAngularMajorUpdateAvailable extends IonicAngularAilment { - readonly id = 'ionic-angular-major-update-available'; - currentVersion?: string; - latestVersion?: string; - - async getVersionPair(): Promise<[string, string]> { - if (!this.currentVersion || !this.latestVersion) { - const [ currentPkg ] = await this.project.getPackageJson('ionic-angular'); - const latestPkg = await pkgFromRegistry(this.config.get('npmClient'), { pkg: 'ionic-angular' }); - this.currentVersion = currentPkg ? currentPkg.version : undefined; - this.latestVersion = latestPkg ? latestPkg.version : undefined; - } - - if (!this.currentVersion || !this.latestVersion) { - return ['0.0.0', '0.0.0']; - } - - return [ this.currentVersion, this.latestVersion ]; - } - - async getMessage() { - const [ currentVersion, latestVersion ] = await this.getVersionPair(); - - return ( - `Major update available for Ionic Framework.\n` + - `A major update is available for ${strong('ionic-angular')} (${ancillary(currentVersion)} => ${ancillary(latestVersion)}).\n` - ).trim(); - } - - async detected() { - const [ currentVersion, latestVersion ] = await this.getVersionPair(); - const diff = semver.diff(currentVersion, latestVersion); - - return diff === 'major'; - } - - async getTreatmentSteps() { - return [ - { message: `Visit ${strong('https://ionicframework.com/blog')} and ${strong('https://github.com/ionic-team/ionic/releases')} for upgrade instructions` }, - ]; - } -} - -export class AppScriptsUpdateAvailable extends IonicAngularAilment implements TreatableAilment { - readonly id = 'app-scripts-update-available'; - readonly treatable = true; - currentVersion?: string; - latestVersion?: string; - - async getVersionPair(): Promise<[string, string]> { - if (!this.currentVersion || !this.latestVersion) { - const [ currentPkg ] = await this.project.getPackageJson('@ionic/app-scripts'); - const latestPkg = await pkgFromRegistry(this.config.get('npmClient'), { pkg: '@ionic/app-scripts' }); - this.currentVersion = currentPkg ? currentPkg.version : undefined; - this.latestVersion = latestPkg ? latestPkg.version : undefined; - } - - if (!this.currentVersion || !this.latestVersion) { - return ['0.0.0', '0.0.0']; - } - - return [ this.currentVersion, this.latestVersion ]; - } - - async getMessage() { - const [ currentVersion, latestVersion ] = await this.getVersionPair(); - - return ( - `Update available for ${strong('@ionic/app-scripts')}.\n` + - `An update is available for ${strong('@ionic/app-scripts')} (${ancillary(currentVersion)} => ${ancillary(latestVersion)}).\n` - ).trim(); - } - - async detected() { - const [ currentVersion, latestVersion ] = await this.getVersionPair(); - const diff = semver.diff(currentVersion, latestVersion); - - return diff === 'minor' || diff === 'patch'; - } - - async getTreatmentSteps() { - const [ , latestVersion ] = await this.getVersionPair(); - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.config.get('npmClient'), { command: 'install', pkg: `@ionic/app-scripts@${latestVersion ? latestVersion : 'latest'}`, saveDev: true }); - - return [ - { - message: `Run: ${input(manager + ' ' + managerArgs.join(' '))}`, - treat: async () => { - await this.shell.run(manager, managerArgs, {}); - }, - }, - ]; - } -} - -export class AppScriptsMajorUpdateAvailable extends IonicAngularAilment { - readonly id = 'app-scripts-major-update-available'; - currentVersion?: string; - latestVersion?: string; - - async getVersionPair(): Promise<[string, string]> { - if (!this.currentVersion || !this.latestVersion) { - const [ currentPkg ] = await this.project.getPackageJson('@ionic/app-scripts'); - const latestPkg = await pkgFromRegistry(this.config.get('npmClient'), { pkg: '@ionic/app-scripts' }); - this.currentVersion = currentPkg ? currentPkg.version : undefined; - this.latestVersion = latestPkg ? latestPkg.version : undefined; - } - - if (!this.currentVersion || !this.latestVersion) { - return ['0.0.0', '0.0.0']; - } - - return [ this.currentVersion, this.latestVersion ]; - } - - async getMessage() { - const [ currentVersion, latestVersion ] = await this.getVersionPair(); - - return ( - `Major update available for ${strong('@ionic/app-scripts')}.\n` + - `A major update is available for ${strong('@ionic/app-scripts')} (${ancillary(currentVersion)} => ${ancillary(latestVersion)}).\n` - ).trim(); - } - - async detected() { - const [ currentVersion, latestVersion ] = await this.getVersionPair(); - const diff = semver.diff(currentVersion, latestVersion); - - return diff === 'major'; - } - - async getTreatmentSteps() { - return [ - { message: `Visit ${strong('https://github.com/ionic-team/ionic-app-scripts/releases')} for upgrade instructions` }, - ]; - } -} - -export class IonicAngularPackageJsonHasDefaultIonicBuildCommand extends IonicAngularAilment { - readonly id = 'ionic-angular-package-json-has-default-ionic-build-command'; - currentVersion?: string; - latestVersion?: string; - - async getMessage() { - return ( - `The ${strong(BUILD_SCRIPT)} npm script is unchanged.\n` + - `The Ionic CLI now looks for the ${strong(BUILD_SCRIPT)} npm script in ${strong('package.json')} for a custom build script to run instead of the default (${input(DEFAULT_BUILD_SCRIPT_VALUE)}). If you don't use it, it's considered quicker and cleaner to just remove it.` - ).trim(); - } - - async detected() { - const pkg = await this.project.requirePackageJson(); - - if (pkg.scripts && pkg.scripts[BUILD_SCRIPT] === DEFAULT_BUILD_SCRIPT_VALUE) { - return true; - } - - return false; - } - - async getTreatmentSteps() { - return [ - { message: `Remove the ${strong(BUILD_SCRIPT)} npm script from ${strong('package.json')}` }, - { message: `Continue using ${input('ionic build')} normally` }, - ]; - } -} - -export class IonicAngularPackageJsonHasDefaultIonicServeCommand extends IonicAngularAilment { - readonly id = 'ionic-angular-package-json-has-default-ionic-serve-command'; - currentVersion?: string; - latestVersion?: string; - - async getMessage() { - return ( - `The ${strong(SERVE_SCRIPT)} npm script is unchanged.\n` + - `The Ionic CLI now looks for the ${strong(SERVE_SCRIPT)} npm script in ${strong('package.json')} for a custom serve script to run instead of the default (${input(DEFAULT_SERVE_SCRIPT_VALUE)}). If you don't use it, it's considered quicker and cleaner to just remove it.` - ).trim(); - } - - async detected() { - const pkg = await this.project.requirePackageJson(); - - if (pkg.scripts && pkg.scripts[SERVE_SCRIPT] === DEFAULT_SERVE_SCRIPT_VALUE) { - return true; - } - - return false; - } - - async getTreatmentSteps() { - return [ - { message: `Remove the ${strong(SERVE_SCRIPT)} npm script from ${strong('package.json')}` }, - { message: `Continue using ${input('ionic serve')} normally` }, - ]; - } -} diff --git a/packages/@ionic/cli/src/lib/project/ionic-angular/app-scripts.ts b/packages/@ionic/cli/src/lib/project/ionic-angular/app-scripts.ts deleted file mode 100644 index 793be39ee4..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic-angular/app-scripts.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { MetadataGroup } from '@ionic/cli-framework'; -import { compileNodeModulesPaths } from '@ionic/cli-framework/utils/node'; -import * as Debug from 'debug'; - -import { CommandMetadataOption } from '../../../definitions'; -import { weak } from '../../color'; - -const debug = Debug('ionic:lib:project:ionic-angular:app-scripts'); - -export async function importAppScripts(projectDir: string): Promise { - const pkg = '@ionic/app-scripts'; - - debug('Importing %s', pkg); - const p = require.resolve(pkg, { paths: compileNodeModulesPaths(projectDir) }); - const m = require(p); - debug('fin'); - - return m; -} - -export const APP_SCRIPTS_OPTIONS: CommandMetadataOption[] = [ - { - name: 'prod', - summary: 'Build the application for production', - type: Boolean, - groups: ['app-scripts', 'cordova'], - hint: weak('[app-scripts]'), - }, - { - name: 'aot', - summary: 'Perform ahead-of-time compilation for this build', - type: Boolean, - groups: [MetadataGroup.ADVANCED, 'app-scripts', 'cordova'], - hint: weak('[app-scripts]'), - }, - { - name: 'minifyjs', - summary: 'Minify JS for this build', - type: Boolean, - groups: [MetadataGroup.ADVANCED, 'app-scripts', 'cordova'], - hint: weak('[app-scripts]'), - }, - { - name: 'minifycss', - summary: 'Minify CSS for this build', - type: Boolean, - groups: [MetadataGroup.ADVANCED, 'app-scripts', 'cordova'], - hint: weak('[app-scripts]'), - }, - { - name: 'optimizejs', - summary: 'Perform JS optimizations for this build', - type: Boolean, - groups: [MetadataGroup.ADVANCED, 'app-scripts', 'cordova'], - hint: weak('[app-scripts]'), - }, - { - name: 'env', - summary: '', - groups: [MetadataGroup.HIDDEN, MetadataGroup.ADVANCED, 'app-scripts', 'cordova'], - hint: weak('[app-scripts]'), - }, -]; diff --git a/packages/@ionic/cli/src/lib/project/ionic-angular/build.ts b/packages/@ionic/cli/src/lib/project/ionic-angular/build.ts deleted file mode 100644 index 8d4b7da8d9..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic-angular/build.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { MetadataGroup, unparseArgs } from '@ionic/cli-framework'; -import * as Debug from 'debug'; - -import { CommandLineInputs, CommandLineOptions, CommandMetadata, IonicAngularBuildOptions } from '../../../definitions'; -import { BUILD_SCRIPT, BuildCLI, BuildRunner, BuildRunnerDeps } from '../../build'; -import { ancillary, input, strong, weak } from '../../color'; - -import { IonicAngularProject } from './'; -import { APP_SCRIPTS_OPTIONS } from './app-scripts'; - -const debug = Debug('ionic:lib:project:ionic-angular:build'); - -export const DEFAULT_PROGRAM = 'ionic-app-scripts'; -export const DEFAULT_BUILD_SCRIPT_VALUE = `${DEFAULT_PROGRAM} build`; - -export interface IonicAngularBuildRunnerDeps extends BuildRunnerDeps { - readonly project: IonicAngularProject; -} - -export class IonicAngularBuildRunner extends BuildRunner { - constructor(protected readonly e: IonicAngularBuildRunnerDeps) { - super(); - } - - async getCommandMetadata(): Promise> { - return { - description: ` -${input('ionic build')} uses ${strong('@ionic/app-scripts')}. See the project's ${strong('README.md')}[^app-scripts-readme] for documentation. Options not listed below are considered advanced and can be passed to the ${input('ionic-app-scripts')} CLI using the ${input('--')} separator after the Ionic CLI arguments. See the examples. - `, - footnotes: [ - { - id: 'app-scripts-readme', - url: 'https://github.com/ionic-team/ionic-app-scripts/blob/master/README.md', - }, - ], - options: [ - { - name: 'source-map', - summary: 'Output sourcemaps', - type: Boolean, - groups: [MetadataGroup.ADVANCED, 'cordova'], - hint: weak('[app-scripts]'), - }, - ...APP_SCRIPTS_OPTIONS, - ], - exampleCommands: [ - '--prod', - ], - }; - } - - createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): IonicAngularBuildOptions { - const baseOptions = super.createBaseOptionsFromCommandLine(inputs, options); - const sourcemaps = typeof options['source-map'] === 'boolean' ? Boolean(options['source-map']) : undefined; - - return { - ...baseOptions, - type: 'ionic-angular', - prod: options['prod'] ? true : false, - sourcemaps, - aot: options['aot'] ? true : false, - minifyjs: options['minifyjs'] ? true : false, - minifycss: options['minifycss'] ? true : false, - optimizejs: options['optimizejs'] ? true : false, - env: options['env'] ? String(options['env']) : undefined, - }; - } - - async buildProject(options: IonicAngularBuildOptions): Promise { - const appscripts = new IonicAngularBuildCLI(this.e); - await appscripts.build(options); - } -} - -export class IonicAngularBuildCLI extends BuildCLI { - readonly name = 'Ionic App Scripts'; - readonly pkg = '@ionic/app-scripts'; - readonly program = DEFAULT_PROGRAM; - readonly prefix = 'app-scripts'; - readonly script?: string = BUILD_SCRIPT; - - protected buildOptionsToAppScriptsArgs(options: IonicAngularBuildOptions): string[] { - const minimistArgs = { - _: [], - prod: options.prod ? true : false, - aot: options.aot ? true : false, - minifyjs: options.minifyjs ? true : false, - minifycss: options.minifycss ? true : false, - optimizejs: options.optimizejs ? true : false, - generateSourceMap: typeof options.sourcemaps !== 'undefined' ? options.sourcemaps ? 'true' : 'false' : undefined, - target: options.engine === 'cordova' ? 'cordova' : undefined, - platform: options.platform, - env: options.env, - }; - - return [...unparseArgs(minimistArgs, { allowCamelCase: true, useEquals: false }), ...options['--']]; - } - - protected async buildArgs(options: IonicAngularBuildOptions): Promise { - const { pkgManagerArgs } = await import('../../utils/npm'); - - const args = this.buildOptionsToAppScriptsArgs(options); - - if (this.resolvedProgram === this.program) { - return ['build', ...args]; - } else { - const [ , ...pkgArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script, scriptArgs: [...args] }); - return pkgArgs; - } - } - - protected async resolveProgram(): Promise { - if (typeof this.script !== 'undefined') { - debug(`Looking for ${ancillary(this.script)} npm script.`); - - const pkg = await this.e.project.requirePackageJson(); - - if (pkg.scripts && pkg.scripts[this.script]) { - if (pkg.scripts[this.script] === DEFAULT_BUILD_SCRIPT_VALUE) { - debug(`Found ${ancillary(this.script)}, but it is the default. Not running.`); - } else { - debug(`Using ${ancillary(this.script)} npm script.`); - return this.e.config.get('npmClient'); - } - } - } - - return this.program; - } -} diff --git a/packages/@ionic/cli/src/lib/project/ionic-angular/generate.ts b/packages/@ionic/cli/src/lib/project/ionic-angular/generate.ts deleted file mode 100644 index ddc7fbfa8f..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic-angular/generate.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { contains, unparseArgs, validators } from '@ionic/cli-framework'; - -import { CommandLineInputs, CommandLineOptions, CommandMetadata, IonicAngularGenerateOptions } from '../../../definitions'; -import { input, strong, weak } from '../../color'; -import { GenerateRunner, GenerateRunnerDeps } from '../../generate'; - -import { IonicAngularProject } from './'; -import { importAppScripts } from './app-scripts'; - -const GENERATOR_TYPES = ['component', 'directive', 'page', 'pipe', 'provider', 'tabs']; - -export interface IonicAngularGenerateRunnerDeps extends GenerateRunnerDeps { - readonly project: IonicAngularProject; -} - -export class IonicAngularGenerateRunner extends GenerateRunner { - constructor(protected readonly e: IonicAngularGenerateRunnerDeps) { - super(); - } - - async getCommandMetadata(): Promise> { - return { - groups: [], - summary: `Generate pipes, components, pages, directives, providers, and tabs`, - description: ` -Automatically create components for your Ionic app. - -The given ${input('name')} is normalized into an appropriate naming convention. For example, ${input('ionic generate page neat')} creates a page by the name of ${input('NeatPage')} in ${input('src/pages/neat/')}. - `, - exampleCommands: [ - '', - ...GENERATOR_TYPES, - 'component foo', - 'page Login', - 'page Detail --no-module', - 'page About --constants', - 'pipe MyFilterPipe', - ], - inputs: [ - { - name: 'type', - summary: `The type of generator (e.g. ${GENERATOR_TYPES.map(t => input(t)).join(', ')})`, - validators: [validators.required, contains(GENERATOR_TYPES, {})], - }, - { - name: 'name', - summary: 'The name of the component being generated', - validators: [validators.required], - }, - ], - options: [ - { - name: 'module', - summary: 'Do not generate an NgModule for the page', - hint: weak('[page]'), - type: Boolean, - default: true, - }, - { - name: 'constants', - summary: 'Generate a page constant file for lazy-loaded pages', - hint: weak('[page]'), - type: Boolean, - default: false, - }, - ], - }; - } - - async ensureCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - if (!inputs[0]) { - const generatorType = await this.e.prompt({ - type: 'list', - name: 'generatorType', - message: 'What would you like to generate:', - choices: GENERATOR_TYPES, - }); - - inputs[0] = generatorType; - } - - if (!inputs[1]) { - const generatorName = await this.e.prompt({ - type: 'input', - name: 'generatorName', - message: 'What should the name be?', - validate: v => validators.required(v), - }); - - inputs[1] = generatorName; - } - } - - createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): IonicAngularGenerateOptions { - const baseOptions = super.createOptionsFromCommandLine(inputs, options); - - return { - ...baseOptions, - module: options['module'] ? true : false, - constants: options['constants'] ? true : false, - }; - } - - async run(options: IonicAngularGenerateOptions) { - const AppScripts = await importAppScripts(this.e.project.directory); - - const appScriptsArgs = unparseArgs({ _: [], module: options.module, constants: options.constants }, { useEquals: false, ignoreFalse: true, allowCamelCase: true }); - AppScripts.setProcessArgs(['node', 'appscripts'].concat(appScriptsArgs)); - AppScripts.setCwd(this.e.project.directory); - - const context = AppScripts.generateContext(); - - switch (options.type) { - case 'page': - await AppScripts.processPageRequest(context, options.name, options); - break; - case 'component': - const componentData = await this.getModules(context, 'component'); - await AppScripts.processComponentRequest(context, options.name, componentData); - break; - case 'directive': - const directiveData = await this.getModules(context, 'directive'); - await AppScripts.processDirectiveRequest(context, options.name, directiveData); - break; - case 'pipe': - const pipeData = await this.getModules(context, 'pipe'); - await AppScripts.processPipeRequest(context, options.name, pipeData); - break; - case 'provider': - const providerData = context.appNgModulePath; - await AppScripts.processProviderRequest(context, options.name, providerData); - break; - case 'tabs': - const tabsData = await this.tabsPrompt(); - await AppScripts.processTabsRequest(context, options.name, tabsData, options); - break; - } - - this.e.log.ok(`Generated a ${strong(options.type)}${options.type === 'tabs' ? ' page' : ''} named ${strong(options.name)}!`); - } - - async tabsPrompt() { - const tabNames = []; - - const howMany = await this.e.prompt({ - type: 'input', - name: 'howMany', - message: 'How many tabs?', - validate: v => validators.numeric(v), - }); - - for (let i = 0; i < parseInt(howMany, 10); i++) { - const tabName = await this.e.prompt({ - type: 'input', - name: 'tabName', - message: 'Name of this tab:', - }); - - tabNames.push(tabName); - } - - return tabNames; - } - - async getModules(context: any, kind: string) { - switch (kind) { - case 'component': - return context.componentsNgModulePath ? context.componentsNgModulePath : context.appNgModulePath; - case 'pipe': - return context.pipesNgModulePath ? context.pipesNgModulePath : context.appNgModulePath; - case 'directive': - return context.directivesNgModulePath ? context.directivesNgModulePath : context.appNgModulePath; - } - } -} diff --git a/packages/@ionic/cli/src/lib/project/ionic-angular/index.ts b/packages/@ionic/cli/src/lib/project/ionic-angular/index.ts deleted file mode 100644 index 1e93ef8b5f..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic-angular/index.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as Debug from 'debug'; -import * as lodash from 'lodash'; - -import { Project } from '../'; -import { IAilmentRegistry, InfoItem } from '../../../definitions'; -import { strong } from '../../color'; - -const debug = Debug('ionic:lib:project:ionic-angular'); - -export class IonicAngularProject extends Project { - readonly type: 'ionic-angular' = 'ionic-angular'; - - async getInfo(): Promise { - const [ - [ ionicAngularPkg ], - [ appScriptsPkg ], - ] = await Promise.all([ - this.getPackageJson('ionic-angular'), - this.getPackageJson('@ionic/app-scripts'), - ]); - - return [ - ...(await super.getInfo()), - { - group: 'ionic', - name: 'Ionic Framework', - key: 'framework', - value: ionicAngularPkg ? `ionic-angular ${ionicAngularPkg.version}` : 'not installed', - }, - { - group: 'ionic', - name: '@ionic/app-scripts', - key: 'app_scripts_version', - value: appScriptsPkg ? appScriptsPkg.version : 'not installed', - }, - ]; - } - - async getDocsUrl(): Promise { - return 'https://ion.link/v3-docs'; - } - - async registerAilments(registry: IAilmentRegistry): Promise { - await super.registerAilments(registry); - const ailments = await import('./ailments'); - const deps = { ...this.e, project: this }; - - registry.register(new ailments.IonicAngularUpdateAvailable(deps)); - registry.register(new ailments.IonicAngularMajorUpdateAvailable(deps)); - registry.register(new ailments.AppScriptsUpdateAvailable(deps)); - registry.register(new ailments.AppScriptsMajorUpdateAvailable(deps)); - registry.register(new ailments.IonicAngularPackageJsonHasDefaultIonicBuildCommand(deps)); - registry.register(new ailments.IonicAngularPackageJsonHasDefaultIonicServeCommand(deps)); - } - - async detected() { - try { - const pkg = await this.requirePackageJson(); - const deps = lodash.assign({}, pkg.dependencies, pkg.devDependencies); - - if (typeof deps['ionic-angular'] === 'string') { - debug(`${strong('ionic-angular')} detected in ${strong('package.json')}`); - return true; - } - } catch (e) { - // ignore - } - - return false; - } - - async requireBuildRunner(): Promise { - const { IonicAngularBuildRunner } = await import('./build'); - const deps = { ...this.e, project: this }; - return new IonicAngularBuildRunner(deps); - } - - async requireServeRunner(): Promise { - const { IonicAngularServeRunner } = await import('./serve'); - const deps = { ...this.e, project: this }; - return new IonicAngularServeRunner(deps); - } - - async requireGenerateRunner(): Promise { - const { IonicAngularGenerateRunner } = await import('./generate'); - const deps = { ...this.e, project: this }; - return new IonicAngularGenerateRunner(deps); - } -} diff --git a/packages/@ionic/cli/src/lib/project/ionic-angular/serve.ts b/packages/@ionic/cli/src/lib/project/ionic-angular/serve.ts deleted file mode 100644 index 045947e716..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic-angular/serve.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { MetadataGroup, ParsedArgs, unparseArgs } from '@ionic/cli-framework'; -import { str2num } from '@ionic/cli-framework/utils/string'; -import * as Debug from 'debug'; - -import { CommandLineInputs, CommandLineOptions, CommandMetadata, IonicAngularServeOptions, ServeDetails } from '../../../definitions'; -import { ancillary, weak } from '../../color'; -import { BIND_ALL_ADDRESS, DEFAULT_DEV_LOGGER_PORT, DEFAULT_LIVERELOAD_PORT, LOCAL_ADDRESSES, SERVE_SCRIPT, ServeCLI, ServeRunner, ServeRunnerDeps } from '../../serve'; -import { findOpenIonicPorts } from '../common'; - -import { IonicAngularProject } from './'; -import { APP_SCRIPTS_OPTIONS } from './app-scripts'; - -const debug = Debug('ionic:lib:project:ionic-angular:serve'); - -const DEFAULT_PROGRAM = 'ionic-app-scripts'; -export const DEFAULT_SERVE_SCRIPT_VALUE = `${DEFAULT_PROGRAM} serve`; - -export interface IonicAngularServeRunnerDeps extends ServeRunnerDeps { - readonly project: IonicAngularProject; -} - -export class IonicAngularServeRunner extends ServeRunner { - constructor(protected readonly e: IonicAngularServeRunnerDeps) { - super(); - } - - async getCommandMetadata(): Promise> { - return { - options: [ - { - name: 'consolelogs', - summary: 'Print app console logs to Ionic CLI', - type: Boolean, - groups: ['cordova'], - aliases: ['c'], - hint: weak('[app-scripts]'), - }, - { - name: 'serverlogs', - summary: 'Print dev server logs to Ionic CLI', - type: Boolean, - aliases: ['s'], - groups: [MetadataGroup.HIDDEN, 'cordova'], - hint: weak('[app-scripts]'), - }, - { - name: 'livereload-port', - summary: 'Use specific port for live-reload', - default: DEFAULT_LIVERELOAD_PORT.toString(), - aliases: ['r'], - groups: [MetadataGroup.ADVANCED, 'cordova'], - hint: weak('[app-scripts]'), - spec: { value: 'port' }, - }, - { - name: 'dev-logger-port', - summary: 'Use specific port for dev server', - default: DEFAULT_DEV_LOGGER_PORT.toString(), - groups: [MetadataGroup.ADVANCED, 'cordova'], - hint: weak('[app-scripts]'), - spec: { value: 'port' }, - }, - { - name: 'proxy', - summary: 'Do not add proxies', - type: Boolean, - default: true, - groups: [MetadataGroup.ADVANCED, 'cordova'], - hint: weak('[app-scripts]'), - // TODO: Adding 'x' to aliases here has some weird behavior with minimist. - }, - { - name: 'source-map', - summary: 'Output sourcemaps', - type: Boolean, - groups: [MetadataGroup.ADVANCED, 'cordova'], - hint: weak('[app-scripts]'), - }, - ...APP_SCRIPTS_OPTIONS, - ], - exampleCommands: [ - '-c', '-- --enableLint false', - ], - }; - } - - createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): IonicAngularServeOptions { - const baseOptions = super.createOptionsFromCommandLine(inputs, options); - const sourcemaps = typeof options['source-map'] === 'boolean' ? Boolean(options['source-map']) : undefined; - const livereloadPort = str2num(options['livereload-port'], DEFAULT_LIVERELOAD_PORT); - const notificationPort = str2num(options['dev-logger-port'], DEFAULT_DEV_LOGGER_PORT); - - return { - ...baseOptions, - sourcemaps, - consolelogs: options['consolelogs'] ? true : false, - serverlogs: options['serverlogs'] ? true : false, - livereloadPort, - notificationPort, - env: options['env'] ? String(options['env']) : undefined, - }; - } - - modifyOpenUrl(url: string, options: IonicAngularServeOptions): string { - return `${url}${options.browserOption ? options.browserOption : ''}${options.platform ? `?ionicplatform=${options.platform}` : ''}`; - } - - async serveProject(options: IonicAngularServeOptions): Promise { - const [ externalIP, availableInterfaces ] = await this.selectExternalIP(options); - const { port, livereloadPort, notificationPort } = await findOpenIonicPorts(options.host, options); - - options.port = port; - options.livereloadPort = livereloadPort; - options.notificationPort = notificationPort; - - const appscripts = new IonicAngularServeCLI(this.e); - await appscripts.serve(options); - - return { - custom: appscripts.resolvedProgram !== appscripts.program, - protocol: 'http', - localAddress: 'localhost', - externalAddress: externalIP, - externalNetworkInterfaces: availableInterfaces, - port, - externallyAccessible: ![BIND_ALL_ADDRESS, ...LOCAL_ADDRESSES].includes(externalIP), - }; - } - - getUsedPorts(options: IonicAngularServeOptions, details: ServeDetails): number[] { - return [ - ...super.getUsedPorts(options, details), - ...options.livereloadPort ? [options.livereloadPort] : [], - ...options.notificationPort ? [options.notificationPort] : [], - ]; - } -} - -class IonicAngularServeCLI extends ServeCLI { - readonly name = 'Ionic App Scripts'; - readonly pkg = '@ionic/app-scripts'; - readonly program = DEFAULT_PROGRAM; - readonly prefix = 'app-scripts'; - readonly script?: string = SERVE_SCRIPT; - - protected stdoutFilter(line: string): boolean { - if (this.resolvedProgram !== this.program) { - return super.stdoutFilter(line); - } - - if (line.includes('server running')) { - this.emit('ready'); - return false; - } - - return true; - } - - protected async buildArgs(options: IonicAngularServeOptions): Promise { - const { pkgManagerArgs } = await import('../../utils/npm'); - - const args = this.serveOptionsToAppScriptsArgs(options); - - if (this.resolvedProgram === this.program) { - return ['serve', ...args]; - } else { - const [ , ...pkgArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script, scriptArgs: [...args] }); - return pkgArgs; - } - } - - protected serveOptionsToAppScriptsArgs(options: IonicAngularServeOptions): string[] { - const args: ParsedArgs = { - _: [], - address: options.host, - port: String(options.port), - 'livereload-port': String(options.livereloadPort), - 'dev-logger-port': String(options.notificationPort), - consolelogs: options.consolelogs, - serverlogs: options.serverlogs, - nobrowser: true, - nolivereload: !options.livereload, - noproxy: !options.proxy, - iscordovaserve: options.engine === 'cordova', - generateSourceMap: typeof options.sourcemaps !== 'undefined' ? options.sourcemaps ? 'true' : 'false' : undefined, - platform: options.platform, - target: options.engine === 'cordova' ? 'cordova' : undefined, - env: options.env, - }; - - return [...unparseArgs(args, { allowCamelCase: true, useEquals: false }), ...options['--']]; - } - - protected async resolveProgram(): Promise { - if (typeof this.script !== 'undefined') { - debug(`Looking for ${ancillary(this.script)} npm script.`); - - const pkg = await this.e.project.requirePackageJson(); - - if (pkg.scripts && pkg.scripts[this.script]) { - if (pkg.scripts[this.script] === DEFAULT_SERVE_SCRIPT_VALUE) { - debug(`Found ${ancillary(this.script)}, but it is the default. Not running.`); - } else { - debug(`Using ${ancillary(this.script)} npm script.`); - return this.e.config.get('npmClient'); - } - } - } - - return this.program; - } -} diff --git a/packages/@ionic/cli/src/lib/project/ionic1/__tests__/index.ts b/packages/@ionic/cli/src/lib/project/ionic1/__tests__/index.ts deleted file mode 100644 index 1e330ef2f0..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic1/__tests__/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as path from 'path' -import { Ionic1Project } from '../'; - -describe('@ionic/cli', () => { - - describe('lib/project/ionic1', () => { - - describe('Ionic1Project', () => { - - let p: Ionic1Project; - - beforeEach(() => { - p = new Ionic1Project({ context: 'app', configPath: '/path/to/proj/file' } as any, {} as any); - jest.spyOn(p, 'config', 'get').mockImplementation(() => ({ get: () => undefined } as any)); - }); - - it('should set directory attribute', async () => { - expect(p.directory).toEqual(path.resolve('/path/to/proj')); - }); - - }); - - }); - -}); diff --git a/packages/@ionic/cli/src/lib/project/ionic1/__tests__/serve.ts b/packages/@ionic/cli/src/lib/project/ionic1/__tests__/serve.ts deleted file mode 100644 index 50e1888d6d..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic1/__tests__/serve.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Ionic1ServeRunner } from '../serve'; - -describe('@ionic/cli', () => { - - describe('lib/project/ionic1/serve', () => { - - describe('Ionic1ServeRunner', () => { - - describe('createOptionsFromCommandLine', () => { - - const defaults = { - '--': [], - publicHost: undefined, - host: 'localhost', - browser: undefined, - browserOption: undefined, - consolelogs: false, - engine: 'browser', - externalAddressRequired: false, - lab: false, - labHost: 'localhost', - labPort: 8200, - livereload: true, - livereloadPort: 35729, - notificationPort: 53703, - open: false, - port: 8100, - proxy: true, - serverlogs: false, - project: undefined, - verbose: false, - }; - - it('should provide defaults with no options', () => { - const runner = new Ionic1ServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [] }); - expect(result).toEqual(defaults); - }); - - it('should provide options from negations of cli flag defaults', () => { - const runner = new Ionic1ServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [], livereload: false, proxy: false, lab: true, open: true, externalAddressRequired: true }); - expect(result).toEqual({ ...defaults, livereload: false, proxy: false, lab: true, open: true, externalAddressRequired: true }); - }); - - it('should allow overrides of default values', () => { - const runner = new Ionic1ServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [], host: '0.0.0.0', port: '1111', 'livereload-port': '2222', 'dev-logger-port': '3333' }); - expect(result).toEqual({ ...defaults, host: '0.0.0.0', port: 1111, livereloadPort: 2222, notificationPort: 3333 }); - }); - - it('should respect --external flag', () => { - const runner = new Ionic1ServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [], host: 'localhost', external: true }); - expect(result).toEqual({ ...defaults, host: '0.0.0.0' }); - }); - - it('should pass on separated args', () => { - const runner = new Ionic1ServeRunner({} as any); - const result = runner.createOptionsFromCommandLine([], { _: [], '--': ['foo', '--bar'] }); - expect(result).toEqual({ ...defaults, '--': ['foo', '--bar'] }); - }); - - }); - - }); - - }); - -}); diff --git a/packages/@ionic/cli/src/lib/project/ionic1/build.ts b/packages/@ionic/cli/src/lib/project/ionic1/build.ts deleted file mode 100644 index ed8e7a1963..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic1/build.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { CommandLineInputs, CommandLineOptions, CommandMetadata, Ionic1BuildOptions } from '../../../definitions'; -import { BUILD_SCRIPT, BuildCLI, BuildRunner, BuildRunnerDeps } from '../../build'; - -import { Ionic1Project } from './'; - -export interface Ionic1BuildRunnerDeps extends BuildRunnerDeps { - readonly project: Ionic1Project; -} - -export class Ionic1BuildRunner extends BuildRunner { - constructor(protected readonly e: Ionic1BuildRunnerDeps) { - super(); - } - - async getCommandMetadata(): Promise> { - return {}; - } - - createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): Ionic1BuildOptions { - const baseOptions = super.createBaseOptionsFromCommandLine(inputs, options); - - return { - ...baseOptions, - type: 'ionic1', - }; - } - - async buildProject(options: Ionic1BuildOptions): Promise { - const v1 = new Ionic1BuildCLI(this.e); - await v1.build(options); - } -} - -class Ionic1BuildCLI extends BuildCLI { - readonly name = 'Ionic 1 Toolkit'; - readonly pkg = '@ionic/v1-toolkit'; - readonly program = 'ionic-v1'; - readonly prefix = 'v1'; - readonly script = BUILD_SCRIPT; - - protected async buildArgs(options: Ionic1BuildOptions): Promise { - const { pkgManagerArgs } = await import('../../utils/npm'); - - if (this.resolvedProgram === this.program) { - return ['build']; - } else { - const [ , ...pkgArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script }); - return pkgArgs; - } - } -} diff --git a/packages/@ionic/cli/src/lib/project/ionic1/index.ts b/packages/@ionic/cli/src/lib/project/ionic1/index.ts deleted file mode 100644 index 5d2bb181ad..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic1/index.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { PackageJson } from '@ionic/cli-framework'; -import { readJson } from '@ionic/utils-fs'; -import { prettyPath } from '@ionic/utils-terminal'; -import * as Debug from 'debug'; -import * as lodash from 'lodash'; -import * as path from 'path'; - -import { Project } from '../'; -import { InfoItem } from '../../../definitions'; -import { strong } from '../../color'; -import { FatalException, RunnerNotFoundException } from '../../errors'; - -const debug = Debug('ionic:lib:project:angular'); - -export const ERROR_INVALID_BOWER_JSON = 'INVALID_BOWER_JSON'; - -export interface BowerJson { - name: string; - dependencies?: { [key: string]: string | undefined; }; - devDependencies?: { [key: string]: string | undefined; }; -} - -function isBowerJson(obj: any): obj is BowerJson { - return obj && typeof obj.name === 'string'; -} - -async function readBowerJsonFile(p: string): Promise { - const bowerJson = await readJson(p); - - if (!isBowerJson(bowerJson)) { - throw ERROR_INVALID_BOWER_JSON; - } - - return bowerJson; -} - -export class Ionic1Project extends Project { - readonly type: 'ionic1' = 'ionic1'; - protected bowerJsonFile?: BowerJson; - - async getInfo(): Promise { - const [ - ionic1Version, - [ v1ToolkitPkg ], - ] = await (Promise.all([ - this.getFrameworkVersion(), - this.getPackageJson('@ionic/v1-toolkit'), - ])); - - return [ - ...(await super.getInfo()), - { - group: 'ionic', - name: 'Ionic Framework', - key: 'framework', - value: ionic1Version ? `ionic1 ${ionic1Version}` : 'unknown', - }, - { - group: 'ionic', - name: '@ionic/v1-toolkit', - value: v1ToolkitPkg ? v1ToolkitPkg.version : 'not installed', - }, - ]; - } - - async detected() { - try { - const bwr = await readBowerJsonFile(path.resolve(this.directory, 'bower.json')); - const deps = lodash.assign({}, bwr.dependencies, bwr.devDependencies); - - if (typeof deps['ionic'] === 'string') { - debug(`${strong('ionic')} detected in ${strong('bower.json')}`); - return true; - } - } catch (e) { - // ignore - } - - return false; - } - - async getSourceDir(): Promise { - return this.getDistDir(); // ionic1's source directory is the dist directory - } - - async getDocsUrl(): Promise { - return 'https://ion.link/v1-docs'; - } - - // this method search not only package.json - async getFrameworkVersion(): Promise { - const ionicVersionFilePath = path.resolve(await this.getDistDir(), 'lib', 'ionic', 'version.json'); // TODO - const bowerJsonPath = path.resolve(this.directory, 'bower.json'); - - try { - try { - const ionicVersionJson = await readJson(ionicVersionFilePath); - return ionicVersionJson['version']; - } catch (e) { - const bwr = await this.loadBowerJson(); - const deps = lodash.assign({}, bwr.dependencies, bwr.devDependencies); - - const ionicEntry = deps['ionic']; - - if (!ionicEntry) { - return; - } - - const m = ionicEntry.match(/.+#(.+)/); - - if (m && m[1]) { - return m[1]; - } - } - } catch (e) { - this.e.log.error(`Error with ${strong(prettyPath(bowerJsonPath))} file: ${e}`); - } - } - - async loadBowerJson(): Promise { - if (!this.bowerJsonFile) { - const bowerJsonPath = path.resolve(this.directory, 'bower.json'); - try { - this.bowerJsonFile = await readBowerJsonFile(bowerJsonPath); - } catch (e) { - if (e instanceof SyntaxError) { - throw new FatalException(`Could not parse ${strong('bower.json')}. Is it a valid JSON file?`); - } else if (e === ERROR_INVALID_BOWER_JSON) { - throw new FatalException(`The ${strong('bower.json')} file seems malformed.`); - } - - throw e; // Probably file not found - } - } - - return this.bowerJsonFile; - } - - async requireBuildRunner(): Promise { - const { Ionic1BuildRunner } = await import('./build'); - const deps = { ...this.e, project: this }; - return new Ionic1BuildRunner(deps); - } - - async requireServeRunner(): Promise { - const { Ionic1ServeRunner } = await import('./serve'); - const deps = { ...this.e, project: this }; - return new Ionic1ServeRunner(deps); - } - - async requireGenerateRunner(): Promise { - throw new RunnerNotFoundException('Generators are not supported in Ionic 1 projects.'); - } -} diff --git a/packages/@ionic/cli/src/lib/project/ionic1/serve.ts b/packages/@ionic/cli/src/lib/project/ionic1/serve.ts deleted file mode 100644 index 8a26d68cd9..0000000000 --- a/packages/@ionic/cli/src/lib/project/ionic1/serve.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { MetadataGroup } from '@ionic/cli-framework'; -import { str2num } from '@ionic/cli-framework/utils/string'; - -import { CommandLineInputs, CommandLineOptions, CommandMetadata, Ionic1ServeOptions, ServeDetails } from '../../../definitions'; -import { BIND_ALL_ADDRESS, DEFAULT_DEV_LOGGER_PORT, DEFAULT_LIVERELOAD_PORT, LOCAL_ADDRESSES, SERVE_SCRIPT, ServeCLI, ServeRunner, ServeRunnerDeps } from '../../serve'; -import { findOpenIonicPorts } from '../common'; - -import { Ionic1Project } from './'; - -export interface Ionic1ServeRunnerDeps extends ServeRunnerDeps { - readonly project: Ionic1Project; -} - -export class Ionic1ServeRunner extends ServeRunner { - constructor(protected readonly e: Ionic1ServeRunnerDeps) { - super(); - } - - async getCommandMetadata(): Promise> { - return { - options: [ - { - name: 'consolelogs', - summary: 'Print app console logs to Ionic CLI', - type: Boolean, - groups: ['cordova'], - aliases: ['c'], - }, - { - name: 'serverlogs', - summary: 'Print dev server logs to Ionic CLI', - type: Boolean, - aliases: ['s'], - groups: [MetadataGroup.HIDDEN, 'cordova'], - }, - { - name: 'livereload-port', - summary: 'Use specific port for live-reload', - default: DEFAULT_LIVERELOAD_PORT.toString(), - aliases: ['r'], - groups: [MetadataGroup.ADVANCED, 'cordova'], - spec: { value: 'port' }, - }, - { - name: 'dev-logger-port', - summary: 'Use specific port for dev server communication', - default: DEFAULT_DEV_LOGGER_PORT.toString(), - groups: [MetadataGroup.ADVANCED, 'cordova'], - spec: { value: 'port' }, - }, - { - name: 'proxy', - summary: 'Do not add proxies', - type: Boolean, - default: true, - groups: [MetadataGroup.ADVANCED, 'cordova'], - // TODO: Adding 'x' to aliases here has some weird behavior with minimist. - }, - ], - exampleCommands: [ - '-c', - ], - }; - } - - createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): Ionic1ServeOptions { - const baseOptions = super.createOptionsFromCommandLine(inputs, options); - const livereloadPort = str2num(options['livereload-port'], DEFAULT_LIVERELOAD_PORT); - const notificationPort = str2num(options['dev-logger-port'], DEFAULT_DEV_LOGGER_PORT); - - return { - ...baseOptions, - consolelogs: options['consolelogs'] ? true : false, - serverlogs: options['serverlogs'] ? true : false, - livereloadPort, - notificationPort, - }; - } - - modifyOpenUrl(url: string, options: Ionic1ServeOptions): string { - return `${url}${options.browserOption ? options.browserOption : ''}${options.platform ? `?ionicplatform=${options.platform}` : ''}`; - } - - async serveProject(options: Ionic1ServeOptions): Promise { - const [ externalIP, availableInterfaces ] = await this.selectExternalIP(options); - const { port, livereloadPort, notificationPort } = await findOpenIonicPorts(options.host, options); - - options.port = port; - options.livereloadPort = livereloadPort; - options.notificationPort = notificationPort; - - const v1 = new Ionic1ServeCLI(this.e); - await v1.serve(options); - - return { - custom: v1.resolvedProgram !== v1.program, - protocol: 'http', - localAddress: 'localhost', - externalAddress: externalIP, - externalNetworkInterfaces: availableInterfaces, - port, - externallyAccessible: ![BIND_ALL_ADDRESS, ...LOCAL_ADDRESSES].includes(externalIP), - }; - } - - getUsedPorts(options: Ionic1ServeOptions, details: ServeDetails): number[] { - return [ - ...super.getUsedPorts(options, details), - ...options.livereloadPort ? [options.livereloadPort] : [], - ...options.notificationPort ? [options.notificationPort] : [], - ]; - } -} - -class Ionic1ServeCLI extends ServeCLI { - readonly name = 'Ionic 1 Toolkit'; - readonly pkg = '@ionic/v1-toolkit'; - readonly program = 'ionic-v1'; - readonly prefix = 'v1'; - readonly script = SERVE_SCRIPT; - - protected stdoutFilter(line: string): boolean { - if (this.resolvedProgram !== this.program) { - return super.stdoutFilter(line); - } - - if (line.includes('server running')) { - this.emit('ready'); - return false; - } - - return true; - } - - protected async buildArgs(options: Ionic1ServeOptions): Promise { - const { pkgManagerArgs } = await import('../../utils/npm'); - - const args = [ - `--host=${options.host}`, - `--port=${String(options.port)}`, - `--livereload-port=${String(options.livereloadPort)}`, - `--dev-port=${String(options.notificationPort)}`, - `--engine=${options.engine}`, - ]; - - if (options.platform) { - args.push(`--platform=${options.platform}`); - } - - if (options.consolelogs) { - args.push('-c'); - } - - if (this.resolvedProgram === this.program) { - return ['serve', ...args]; - } else { - const [ , ...pkgArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script, scriptArgs: [...args] }); - return pkgArgs; - } - } -} diff --git a/packages/@ionic/cli/src/lib/project/react-vite/build.ts b/packages/@ionic/cli/src/lib/project/react-vite/build.ts new file mode 100755 index 0000000000..2e6c45a55e --- /dev/null +++ b/packages/@ionic/cli/src/lib/project/react-vite/build.ts @@ -0,0 +1,55 @@ +import { CommandLineInputs, CommandLineOptions, CommandMetadata, ReactBuildOptions } from '../../../definitions'; +import { BUILD_SCRIPT, BuildCLI, BuildRunner, BuildRunnerDeps } from '../../build'; + +import { ReactViteProject } from './'; + +export interface ReactViteBuildRunnerDeps extends BuildRunnerDeps { + readonly project: ReactViteProject; +} +export class ReactViteBuildRunner extends BuildRunner { + constructor(protected readonly e: ReactViteBuildRunnerDeps) { + super(); + } + + async getCommandMetadata(): Promise> { + return {}; + } + + createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): ReactBuildOptions { + const baseOptions = super.createBaseOptionsFromCommandLine(inputs, options); + + return { + ...baseOptions, + type: 'react', + }; + } + + async buildProject(options: ReactBuildOptions): Promise { + const reactVite = new ReactViteBuildCLI(this.e); + await reactVite.build(options); + } +} + +export class ReactViteBuildCLI extends BuildCLI { + readonly name = 'Vite CLI Service'; + readonly pkg = 'vite'; + readonly program = 'vite'; + readonly prefix = 'vite'; + readonly script = BUILD_SCRIPT; + + protected async buildArgs(options: ReactBuildOptions): Promise { + const { pkgManagerArgs } = await import('../../utils/npm'); + + if (this.resolvedProgram === this.program) { + return ['build', ...(options['--'] || [])]; + } else { + const [ , ...pkgArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script, scriptArgs: options['--'] }); + return pkgArgs; + } + } + + protected async buildEnvVars(options: ReactBuildOptions): Promise { + const env: NodeJS.ProcessEnv = {}; + return { ...await super.buildEnvVars(options), ...env }; + } +} diff --git a/packages/@ionic/cli/src/lib/project/react-vite/index.ts b/packages/@ionic/cli/src/lib/project/react-vite/index.ts new file mode 100755 index 0000000000..136ab3e124 --- /dev/null +++ b/packages/@ionic/cli/src/lib/project/react-vite/index.ts @@ -0,0 +1,80 @@ +import chalk from 'chalk'; +import { debug as Debug } from 'debug'; +import * as lodash from 'lodash'; +import * as path from 'path'; + +import { Project } from '../'; +import { InfoItem } from '../../../definitions'; +import { RunnerNotFoundException } from '../../errors'; + +const debug = Debug('ionic:lib:project:react'); + +export class ReactViteProject extends Project { + readonly type: 'react' = 'react'; + + async getInfo(): Promise { + const [ + [ionicReact, ionicReactPath], + ] = await Promise.all([ + this.getPackageJson('@ionic/react'), + ]); + + return [ + ...(await super.getInfo()), + { + group: 'ionic', + name: 'Ionic Framework', + key: 'framework', + value: ionicReact ? `@ionic/react ${ionicReact.version}` : 'not installed', + path: ionicReactPath, + }, + ]; + } + + /** + * We can't detect React project types. We don't know what they look like! + */ + async detected() { + try { + const pkg = await this.requirePackageJson(); + const deps = lodash.assign({}, pkg.dependencies, pkg.devDependencies); + + if (typeof deps['@ionic/react'] === 'string') { + debug(`${chalk.bold('@ionic/react')} detected in ${chalk.bold('package.json')}`); + return true; + } + } catch (e: any) { + // ignore + } + + return false; + } + + async getDefaultDistDir(): Promise { + return 'dist'; + } + + async requireBuildRunner(): Promise { + const { ReactViteBuildRunner } = await import('./build'); + const deps = { ...this.e, project: this }; + return new ReactViteBuildRunner(deps); + } + + async requireServeRunner(): Promise { + const { ReactViteServeRunner } = await import('./serve'); + const deps = { ...this.e, project: this }; + return new ReactViteServeRunner(deps); + } + + async requireGenerateRunner(): Promise { + throw new RunnerNotFoundException( + `Cannot perform generate for React projects.\n` + + `Since you're using the ${chalk.bold('React')} project type, this command won't work. The Ionic CLI doesn't know how to generate framework components for React projects.` + ); + } + + setPrimaryTheme(themeColor: string): Promise { + const themePath = path.join(this.directory, 'src', 'theme', 'variables.css'); + return this.writeThemeColor(themePath, themeColor); + } +} diff --git a/packages/@ionic/cli/src/lib/project/react-vite/serve.ts b/packages/@ionic/cli/src/lib/project/react-vite/serve.ts new file mode 100755 index 0000000000..308407129c --- /dev/null +++ b/packages/@ionic/cli/src/lib/project/react-vite/serve.ts @@ -0,0 +1,152 @@ +import { ParsedArgs, unparseArgs } from '@ionic/cli-framework'; +import { stripAnsi } from '@ionic/cli-framework-output'; +import { findClosestOpenPort } from '@ionic/utils-network'; +import { CommandMetadata, ServeDetails, ReactServeOptions, } from '../../../definitions'; + +import { strong } from '../../color'; +import { BIND_ALL_ADDRESS, DEFAULT_ADDRESS, LOCAL_ADDRESSES, SERVE_SCRIPT, ServeCLI, ServeRunner, ServeRunnerDeps, } from '../../serve'; + +export class ReactViteServeRunner extends ServeRunner { + constructor(protected readonly e: ServeRunnerDeps) { + super(); + } + + async getCommandMetadata(): Promise> { + return {}; + } + + modifyOpenUrl(url: string, _options: ReactServeOptions): string { + return url; + } + + async serveProject(options: ReactServeOptions): Promise { + const [externalIP, availableInterfaces] = await this.selectExternalIP(options); + + const port = (options.port = await findClosestOpenPort(options.port)); + + const reactScripts = new ReactViteServeCLI(this.e); + await reactScripts.serve(options); + + return { + custom: reactScripts.resolvedProgram !== reactScripts.program, + protocol: options.https ? 'https' : 'http', + localAddress: 'localhost', + externalAddress: externalIP, + externalNetworkInterfaces: availableInterfaces, + port, + externallyAccessible: ![BIND_ALL_ADDRESS, ...LOCAL_ADDRESSES].includes( + externalIP + ), + }; + } +} + +export class ReactViteServeCLI extends ServeCLI { + readonly name = 'Vite CLI Service'; + readonly pkg = 'vite'; + readonly program = 'vite'; + readonly prefix = 'vite'; + readonly script = SERVE_SCRIPT; + protected chunks = 0; + + async serve(options: ReactServeOptions): Promise { + this.on('compile', (chunks) => { + if (chunks > 0) { + this.e.log.info( + `... and ${strong(chunks.toString())} additional chunks` + ); + } + }); + + return super.serve(options); + } + + protected stdoutFilter(line: string): boolean { + if (this.resolvedProgram !== this.program) { + return super.stdoutFilter(line); + } + const strippedLine = stripAnsi(line); + const compileMsgs = [ + 'Compiled successfully', + 'Compiled with warnings', + 'Failed to compile', + "ready in" + ]; + if (compileMsgs.some((msg) => strippedLine.includes(msg))) { + this.emit('ready'); + return false; + } + + if (strippedLine.match(/.*chunk\s{\d+}.+/)) { + this.chunks++; + return false; + } + + if (strippedLine.includes('Compiled successfully')) { + this.emit('compile', this.chunks); + this.chunks = 0; + } + + if(strippedLine.includes('has unexpectedly closed')) { + return false; + } + return true; + } + + protected stderrFilter(line: string): boolean { + if (this.resolvedProgram !== this.program) { + return super.stderrFilter(line); + } + const strippedLine = stripAnsi(line); + if (strippedLine.includes('webpack.Progress')) { + return false; + } + if(strippedLine.includes('has unexpectedly closed')) { + return false; + } + + return true; + } + + protected async buildArgs(options: ReactServeOptions): Promise { + const args: ParsedArgs = { + _: [], + host: options.host, + port: options.port ? options.port.toString() : undefined, + }; + const { pkgManagerArgs } = await import('../../utils/npm'); + + const separatedArgs = options['--']; + + if (this.resolvedProgram === this.program) { + return [...unparseArgs(args), ...separatedArgs]; + } else { + const [, ...pkgArgs] = await pkgManagerArgs( + this.e.config.get('npmClient'), + { + command: 'run', + script: this.script, + scriptArgs: [...unparseArgs(args), ...separatedArgs], + } + ); + return pkgArgs; + } + } + + protected async buildEnvVars( + options: ReactServeOptions + ): Promise { + const env: NodeJS.ProcessEnv = {}; + // // Vite binds to `localhost` by default, but if specified it prints a + // // warning, so don't set `HOST` if the host is set to `localhost`. + if (options.host !== DEFAULT_ADDRESS) { + env.HOST = options.host; + } + + env.PORT = String(options.port); + + env.HTTPS = options.https ? 'true' : 'false'; + + return { ...(await super.buildEnvVars(options)), ...env }; + } +} diff --git a/packages/@ionic/cli/src/lib/project/react/index.ts b/packages/@ionic/cli/src/lib/project/react/index.ts index ea60bfa491..05a05929a9 100644 --- a/packages/@ionic/cli/src/lib/project/react/index.ts +++ b/packages/@ionic/cli/src/lib/project/react/index.ts @@ -1,5 +1,5 @@ -import * as chalk from 'chalk'; -import * as Debug from 'debug'; +import chalk from 'chalk'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import * as path from 'path'; @@ -14,7 +14,7 @@ export class ReactProject extends Project { async getInfo(): Promise { const [ - [ ionicReactPkg, ionicReactPkgPath ], + [ionicReactPkg, ionicReactPkgPath], ] = await Promise.all([ this.getPackageJson('@ionic/react'), ]); @@ -43,7 +43,7 @@ export class ReactProject extends Project { debug(`${chalk.bold('@ionic/React')} detected in ${chalk.bold('package.json')}`); return true; } - } catch (e) { + } catch (e: any) { // ignore } diff --git a/packages/@ionic/cli/src/lib/project/react/serve.ts b/packages/@ionic/cli/src/lib/project/react/serve.ts index cc3b697ee1..d085b50854 100644 --- a/packages/@ionic/cli/src/lib/project/react/serve.ts +++ b/packages/@ionic/cli/src/lib/project/react/serve.ts @@ -112,7 +112,7 @@ export class ReactServeCLI extends ServeCLI { const strippedLine = stripAnsi(line); - const compileMsgs = ['Compiled successfully', 'Compiled with warnings', 'Failed to compile']; + const compileMsgs = ['Compiled successfully', 'Compiled with', 'Failed to compile']; if (compileMsgs.some(msg => strippedLine.includes(msg))) { this.emit('ready'); return false; diff --git a/packages/@ionic/cli/src/lib/project/vue-vite/build.ts b/packages/@ionic/cli/src/lib/project/vue-vite/build.ts new file mode 100755 index 0000000000..87f56ca3b8 --- /dev/null +++ b/packages/@ionic/cli/src/lib/project/vue-vite/build.ts @@ -0,0 +1,55 @@ +import { CommandLineInputs, CommandLineOptions, CommandMetadata, VueBuildOptions } from '../../../definitions'; +import { BUILD_SCRIPT, BuildCLI, BuildRunner, BuildRunnerDeps } from '../../build'; + +import { VueViteProject } from './'; + +export interface VueBuildRunnerDeps extends BuildRunnerDeps { + readonly project: VueViteProject; +} +export class VueViteBuildRunner extends BuildRunner { + constructor(protected readonly e: VueBuildRunnerDeps) { + super(); + } + + async getCommandMetadata(): Promise> { + return {}; + } + + createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): VueBuildOptions { + const baseOptions = super.createBaseOptionsFromCommandLine(inputs, options); + + return { + ...baseOptions, + type: 'vue', + }; + } + + async buildProject(options: VueBuildOptions): Promise { + const vueScripts = new VueViteBuildCLI(this.e); + await vueScripts.build(options); + } +} + +export class VueViteBuildCLI extends BuildCLI { + readonly name = 'Vite CLI Service'; + readonly pkg = 'vite'; + readonly program = 'vite'; + readonly prefix = 'vite'; + readonly script = BUILD_SCRIPT; + + protected async buildArgs(options: VueBuildOptions): Promise { + const { pkgManagerArgs } = await import('../../utils/npm'); + + if (this.resolvedProgram === this.program) { + return ['build', ...(options['--'] || [])]; + } else { + const [ , ...pkgArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script, scriptArgs: options['--'] }); + return pkgArgs; + } + } + + protected async buildEnvVars(options: VueBuildOptions): Promise { + const env: NodeJS.ProcessEnv = {}; + return { ...await super.buildEnvVars(options), ...env }; + } +} diff --git a/packages/@ionic/cli/src/lib/project/vue-vite/index.ts b/packages/@ionic/cli/src/lib/project/vue-vite/index.ts new file mode 100755 index 0000000000..9304bd7b7f --- /dev/null +++ b/packages/@ionic/cli/src/lib/project/vue-vite/index.ts @@ -0,0 +1,80 @@ +import chalk from 'chalk'; +import { debug as Debug } from 'debug'; +import * as lodash from 'lodash'; +import * as path from 'path'; + +import { Project } from '../'; +import { InfoItem } from '../../../definitions'; +import { RunnerNotFoundException } from '../../errors'; + +const debug = Debug('ionic:lib:project:vue'); + +export class VueViteProject extends Project { + readonly type: 'vue' = 'vue'; + + async getInfo(): Promise { + const [ + [ionicVuePkg, ionicVuePkgPath], + ] = await Promise.all([ + this.getPackageJson('@ionic/vue'), + ]); + + return [ + ...(await super.getInfo()), + { + group: 'ionic', + name: 'Ionic Framework', + key: 'framework', + value: ionicVuePkg ? `@ionic/vue ${ionicVuePkg.version}` : 'not installed', + path: ionicVuePkgPath, + }, + ]; + } + + /** + * We can't detect Vue project types. We don't know what they look like! + */ + async detected() { + try { + const pkg = await this.requirePackageJson(); + const deps = lodash.assign({}, pkg.dependencies, pkg.devDependencies); + + if (typeof deps['@ionic/vue'] === 'string') { + debug(`${chalk.bold('@ionic/vue')} detected in ${chalk.bold('package.json')}`); + return true; + } + } catch (e: any) { + // ignore + } + + return false; + } + + async getDefaultDistDir(): Promise { + return 'dist'; + } + + async requireBuildRunner(): Promise { + const { VueViteBuildRunner } = await import('./build'); + const deps = { ...this.e, project: this }; + return new VueViteBuildRunner(deps); + } + + async requireServeRunner(): Promise { + const { VueServeRunner } = await import('./serve'); + const deps = { ...this.e, project: this }; + return new VueServeRunner(deps); + } + + async requireGenerateRunner(): Promise { + throw new RunnerNotFoundException( + `Cannot perform generate for Vue projects.\n` + + `Since you're using the ${chalk.bold('Vue')} project type, this command won't work. The Ionic CLI doesn't know how to generate framework components for Vue projects.` + ); + } + + setPrimaryTheme(themeColor: string): Promise { + const themePath = path.join(this.directory, 'src', 'theme', 'variables.css'); + return this.writeThemeColor(themePath, themeColor); + } +} diff --git a/packages/@ionic/cli/src/lib/project/vue-vite/serve.ts b/packages/@ionic/cli/src/lib/project/vue-vite/serve.ts new file mode 100755 index 0000000000..a2656f2b36 --- /dev/null +++ b/packages/@ionic/cli/src/lib/project/vue-vite/serve.ts @@ -0,0 +1,159 @@ +import { ParsedArgs, unparseArgs } from '@ionic/cli-framework'; +import { stripAnsi } from '@ionic/cli-framework-output'; +import { findClosestOpenPort } from '@ionic/utils-network'; + +import { + CommandMetadata, + ServeDetails, + VueServeOptions, +} from '../../../definitions'; +import { strong } from '../../color'; +import { + BIND_ALL_ADDRESS, + DEFAULT_ADDRESS, + LOCAL_ADDRESSES, + SERVE_SCRIPT, + ServeCLI, + ServeRunner, + ServeRunnerDeps, +} from '../../serve'; + +export class VueServeRunner extends ServeRunner { + constructor(protected readonly e: ServeRunnerDeps) { + super(); + } + + async getCommandMetadata(): Promise> { + return {}; + } + + modifyOpenUrl(url: string, _options: VueServeOptions): string { + return url; + } + + async serveProject(options: VueServeOptions): Promise { + const [externalIP, availableInterfaces] = await this.selectExternalIP( + options + ); + + const port = (options.port = await findClosestOpenPort(options.port)); + + const vueScripts = new VueViteServeCLI(this.e); + await vueScripts.serve(options); + + return { + custom: vueScripts.resolvedProgram !== vueScripts.program, + protocol: options.https ? 'https' : 'http', + localAddress: 'localhost', + externalAddress: externalIP, + externalNetworkInterfaces: availableInterfaces, + port, + externallyAccessible: ![BIND_ALL_ADDRESS, ...LOCAL_ADDRESSES].includes( + externalIP + ), + }; + } +} + +export class VueViteServeCLI extends ServeCLI { + readonly name = 'Vite CLI Service'; + readonly pkg = 'vite'; + readonly program = 'vite'; + readonly prefix = 'vite'; + readonly script = SERVE_SCRIPT; + protected chunks = 0; + + async serve(options: VueServeOptions): Promise { + this.on('compile', (chunks) => { + if (chunks > 0) { + this.e.log.info( + `... and ${strong(chunks.toString())} additional chunks` + ); + } + }); + + return super.serve(options); + } + + protected stdoutFilter(line: string): boolean { + if (this.resolvedProgram !== this.program) { + return super.stdoutFilter(line); + } + const strippedLine = stripAnsi(line); + const compileMsgs = [ + 'Compiled successfully', + 'Compiled with warnings', + 'Failed to compile', + "ready in" + ]; + if (compileMsgs.some((msg) => strippedLine.includes(msg))) { + this.emit('ready'); + return false; + } + + if (strippedLine.match(/.*chunk\s{\d+}.+/)) { + this.chunks++; + return false; + } + + if (strippedLine.includes('Compiled successfully')) { + this.emit('compile', this.chunks); + this.chunks = 0; + } + + return true; + } + protected stderrFilter(line: string): boolean { + if (this.resolvedProgram !== this.program) { + return super.stderrFilter(line); + } + const strippedLine = stripAnsi(line); + if (strippedLine.includes('webpack.Progress')) { + return false; + } + + return true; + } + + protected async buildArgs(options: VueServeOptions): Promise { + const args: ParsedArgs = { + _: [], + host: options.host, + port: options.port ? options.port.toString() : undefined, + }; + const { pkgManagerArgs } = await import('../../utils/npm'); + + const separatedArgs = options['--']; + + if (this.resolvedProgram === this.program) { + return [...unparseArgs(args), ...separatedArgs]; + } else { + const [, ...pkgArgs] = await pkgManagerArgs( + this.e.config.get('npmClient'), + { + command: 'run', + script: this.script, + scriptArgs: [...unparseArgs(args), ...separatedArgs], + } + ); + return pkgArgs; + } + } + + protected async buildEnvVars( + options: VueServeOptions + ): Promise { + const env: NodeJS.ProcessEnv = {}; + // // Vue CLI binds to `localhost` by default, but if specified it prints a + // // warning, so don't set `HOST` if the host is set to `localhost`. + if (options.host !== DEFAULT_ADDRESS) { + env.HOST = options.host; + } + + env.PORT = String(options.port); + + env.HTTPS = options.https ? 'true' : 'false'; + + return { ...(await super.buildEnvVars(options)), ...env }; + } +} diff --git a/packages/@ionic/cli/src/lib/project/vue/index.ts b/packages/@ionic/cli/src/lib/project/vue/index.ts index d1eea5c605..da584e1e50 100644 --- a/packages/@ionic/cli/src/lib/project/vue/index.ts +++ b/packages/@ionic/cli/src/lib/project/vue/index.ts @@ -1,5 +1,5 @@ -import * as chalk from 'chalk'; -import * as Debug from 'debug'; +import chalk from 'chalk'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import * as path from 'path'; @@ -43,7 +43,7 @@ export class VueProject extends Project { debug(`${chalk.bold('@ionic/vue')} detected in ${chalk.bold('package.json')}`); return true; } - } catch (e) { + } catch (e: any) { // ignore } diff --git a/packages/@ionic/cli/src/lib/project/vue/serve.ts b/packages/@ionic/cli/src/lib/project/vue/serve.ts index b207117720..999fdda433 100644 --- a/packages/@ionic/cli/src/lib/project/vue/serve.ts +++ b/packages/@ionic/cli/src/lib/project/vue/serve.ts @@ -63,7 +63,7 @@ export class VueServeCLI extends ServeCLI { } const strippedLine = stripAnsi(line); - const compileMsgs = ['Compiled successfully', 'Compiled with warnings', 'Failed to compile']; + const compileMsgs = ['Compiled successfully', 'Compiled with', 'Failed to compile']; if (compileMsgs.some(msg => strippedLine.includes(msg))) { this.emit('ready'); return false; diff --git a/packages/@ionic/cli/src/lib/serve.ts b/packages/@ionic/cli/src/lib/serve.ts index 0027a043cc..0e8ff5b6eb 100644 --- a/packages/@ionic/cli/src/lib/serve.ts +++ b/packages/@ionic/cli/src/lib/serve.ts @@ -2,16 +2,16 @@ import { BaseError, MetadataGroup, ParsedArgs, unparseArgs } from '@ionic/cli-fr import { LOGGER_LEVELS, createPrefixedFormatter } from '@ionic/cli-framework-output'; import { PromptModule } from '@ionic/cli-framework-prompts'; import { str2num } from '@ionic/cli-framework/utils/string'; -import { NetworkInterface, findClosestOpenPort, getExternalIPv4Interfaces, isHostConnectable } from '@ionic/utils-network'; +import { NetworkInterface, getExternalIPv4Interfaces, isHostConnectable } from '@ionic/utils-network'; import { createProcessEnv, killProcessTree, onBeforeExit, processExit } from '@ionic/utils-process'; -import * as chalk from 'chalk'; -import * as Debug from 'debug'; +import chalk from 'chalk'; +import { debug as Debug } from 'debug'; import { EventEmitter } from 'events'; import * as lodash from 'lodash'; -import * as split2 from 'split2'; +import split2 from 'split2'; import * as stream from 'stream'; -import { CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, IConfig, ILogger, IProject, IShell, IonicEnvironmentFlags, LabServeDetails, NpmClient, Runner, ServeDetails, ServeOptions } from '../definitions'; +import { CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, IConfig, ILogger, IProject, IShell, IonicEnvironmentFlags, NpmClient, Runner, ServeDetails, ServeOptions } from '../definitions'; import { ancillary, input, strong, weak } from './color'; import { FatalException, ServeCLIProgramNotFoundException } from './errors'; @@ -25,7 +25,6 @@ const debug = Debug('ionic:lib:serve'); export const DEFAULT_DEV_LOGGER_PORT = 53703; export const DEFAULT_LIVERELOAD_PORT = 35729; export const DEFAULT_SERVER_PORT = 8100; -export const DEFAULT_LAB_PORT = 8200; export const DEFAULT_DEVAPP_COMM_PORT = 53233; export const DEFAULT_ADDRESS = 'localhost'; @@ -138,9 +137,8 @@ export abstract class ServeRunner implements Runner implements Runner implements Runner implements Runner `${details.protocol}://${host}:${details.port}`; - const labHost = labDetails ? `http://${labDetails.host}:${labDetails.port}` : undefined; this.e.log.nl(); this.e.log.info( `Development server running!` + - (labHost ? `\nLab: ${strong(labHost)}` : '') + `\nLocal: ${strong(localAddress)}` + (details.externalNetworkInterfaces.length > 0 ? `\nExternal: ${details.externalNetworkInterfaces.map(v => strong(fmtExternalAddress(v.address))).join(', ')}` : '') + `\n\n${chalk.yellow('Use Ctrl+C to quit this process')}` @@ -212,7 +204,7 @@ export abstract class ServeRunner implements Runner implements Runner implements Runner { - const labDetails: LabServeDetails = { - projectType: this.e.project.type, - host: options.labHost, - port: await findClosestOpenPort(options.labPort), - }; - - const lab = new IonicLabServeCLI(this.e); - await lab.serve({ serveDetails, ...labDetails }); - - return labDetails; - } - async selectExternalIP(options: T): Promise<[string, NetworkInterface[]]> { let availableInterfaces: NetworkInterface[] = []; let chosenIP = options.host; @@ -322,7 +301,7 @@ export abstract class ServeRunner implements Runner extends EventEmitter { return; } - const [ pkg ] = await this.e.project.getPackageJson(undefined, { logErrors: false }); + const [pkg] = await this.e.project.getPackageJson(undefined, { logErrors: false }); if (!pkg) { return; @@ -463,7 +442,7 @@ export abstract class ServeCLI extends EventEmitter { protected async spawnWrapper(options: T): Promise { try { return await this.spawn(options); - } catch (e) { + } catch (e: any) { if (!(e instanceof ServeCLIProgramNotFoundException)) { throw e; } @@ -534,8 +513,8 @@ export abstract class ServeCLI extends EventEmitter { const ws = this.createLoggerStream(); - p.stdout.pipe(split2()).pipe(this.createStreamFilter(line => this.stdoutFilter(line))).pipe(ws); - p.stderr.pipe(split2()).pipe(this.createStreamFilter(line => this.stderrFilter(line))).pipe(ws); + p.stdout?.pipe(split2()).pipe(this.createStreamFilter(line => this.stdoutFilter(line))).pipe(ws); + p.stderr?.pipe(split2()).pipe(this.createStreamFilter(line => this.stderrFilter(line))).pipe(ws); this.once('ready', () => { resolve(); @@ -578,7 +557,7 @@ export abstract class ServeCLI extends EventEmitter { protected async promptToInstall(): Promise { const { pkgManagerArgs } = await import('./utils/npm'); - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: this.pkg, saveDev: true, saveExact: true }); + const [manager, ...managerArgs] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: this.pkg, saveDev: true, saveExact: true }); this.e.log.nl(); @@ -621,7 +600,7 @@ abstract class PkgManagerServeCLI extends ServeCLI { }; const scriptArgs = [...unparseArgs(args), ...options['--'] || []]; - const [ , ...pkgArgs ] = await pkgManagerArgs(this.program, { command: 'run', script: this.script, scriptArgs }); + const [, ...pkgArgs] = await pkgManagerArgs(this.program, { command: 'run', script: this.script, scriptArgs }); return pkgArgs; } @@ -647,37 +626,3 @@ export class YarnServeCLI extends PkgManagerServeCLI { readonly program = 'yarn'; readonly prefix = 'yarn'; } - -interface IonicLabServeCLIOptions extends Readonly { - readonly serveDetails: Readonly; -} - -class IonicLabServeCLI extends ServeCLI { - readonly name = 'Ionic Lab'; - readonly pkg = '@ionic/lab'; - readonly program = 'ionic-lab'; - readonly prefix = 'lab'; - readonly script = undefined; - - protected stdoutFilter(line: string): boolean { - if (line.includes('running')) { - this.emit('ready'); - } - - return false; // no stdout - } - - protected async buildArgs(options: IonicLabServeCLIOptions): Promise { - const { serveDetails, ...labDetails } = options; - - const pkg = await this.e.project.requirePackageJson(); - - const url = `${serveDetails.protocol}://localhost:${serveDetails.port}`; - const appName = this.e.project.config.get('name'); - const labArgs = [url, '--host', labDetails.host, '--port', String(labDetails.port), '--project-type', labDetails.projectType]; - const nameArgs = appName ? ['--app-name', appName] : []; - const versionArgs = pkg.version ? ['--app-version', pkg.version] : []; - - return [...labArgs, ...nameArgs, ...versionArgs]; - } -} diff --git a/packages/@ionic/cli/src/lib/session.ts b/packages/@ionic/cli/src/lib/session.ts index 5221666d40..70cd7b8a88 100644 --- a/packages/@ionic/cli/src/lib/session.ts +++ b/packages/@ionic/cli/src/lib/session.ts @@ -23,7 +23,7 @@ export class BaseSession { .send({}); try { await this.e.client.do(req); - } catch (e) {} + } catch (e: any) {} } this.e.config.unset('org.id'); this.e.config.unset('user.id'); @@ -120,7 +120,7 @@ export class ProSession extends BaseSession implements ISession { this.e.config.set('user.id', user.id); this.e.config.set('user.email', email); this.e.config.set('tokens.user', token); - } catch (e) { + } catch (e: any) { if (isSuperAgentError(e) && (e.response.status === 401 || e.response.status === 403)) { throw new SessionException('Incorrect email or password.'); } @@ -149,7 +149,7 @@ export class ProSession extends BaseSession implements ISession { this.e.config.set('user.id', user_id); this.e.config.set('user.email', user.email); this.e.config.set('tokens.user', token); - } catch (e) { + } catch (e: any) { if (isSuperAgentError(e) && (e.response.status === 401 || e.response.status === 403)) { throw new SessionException('Invalid auth token.'); } diff --git a/packages/@ionic/cli/src/lib/shell.ts b/packages/@ionic/cli/src/lib/shell.ts index a634948665..e4a60a01de 100644 --- a/packages/@ionic/cli/src/lib/shell.ts +++ b/packages/@ionic/cli/src/lib/shell.ts @@ -2,12 +2,12 @@ import { LOGGER_LEVELS } from '@ionic/cli-framework-output'; import { createProcessEnv, killProcessTree, onBeforeExit } from '@ionic/utils-process'; import { ERROR_COMMAND_NOT_FOUND, Subprocess, SubprocessError, SubprocessOptions, WhichOptions, which } from '@ionic/utils-subprocess'; import { TERMINAL_INFO } from '@ionic/utils-terminal'; -import * as chalk from 'chalk'; +import chalk from 'chalk'; import { ChildProcess, SpawnOptions } from 'child_process'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as path from 'path'; -import * as split2 from 'split2'; -import * as combineStreams from 'stream-combiner2'; +import split2 from 'split2'; +import combineStreams from 'stream-combiner2'; import { ILogger, IShell, IShellOutputOptions, IShellRunOptions, IShellSpawnOptions } from '../definitions'; import { isExitCodeException } from '../guards'; @@ -55,7 +55,7 @@ export class Shell implements IShell { debug('Error in subprocess stdout pipe: %o', err); }); - promise.p.stdout.pipe(s); + promise.p.stdout?.pipe(s); } if (promise.p.stderr) { @@ -66,7 +66,7 @@ export class Shell implements IShell { debug('Error in subprocess stderr pipe: %o', err); }); - promise.p.stderr.pipe(s); + promise.p.stderr?.pipe(s); } if (killOnExit) { @@ -78,7 +78,7 @@ export class Shell implements IShell { } await promise; - } catch (e) { + } catch (e: any) { if (e instanceof SubprocessError && e.code === ERROR_COMMAND_NOT_FOUND) { if (fatalOnNotFound) { throw new FatalException(`Command not found: ${input(command)}`, 127); @@ -132,7 +132,7 @@ export class Shell implements IShell { try { return await proc.output(); - } catch (e) { + } catch (e: any) { if (e instanceof SubprocessError && e.code === ERROR_COMMAND_NOT_FOUND) { if (fatalOnNotFound) { throw new FatalException(`Command not found: ${input(command)}`, 127); @@ -171,7 +171,7 @@ export class Shell implements IShell { if (TERMINAL_INFO.windows) { try { return await this.which(command, { PATH: options.env && options.env.PATH ? options.env.PATH : process.env.PATH }); - } catch (e) { + } catch (e: any) { // ignore } } @@ -202,7 +202,7 @@ export class Shell implements IShell { try { const out = await proc.output(); return out.split('\n').join(' ').trim(); - } catch (e) { + } catch (e: any) { // no command info at this point } } diff --git a/packages/@ionic/cli/src/lib/ssh-config.ts b/packages/@ionic/cli/src/lib/ssh-config.ts index aff90fbf1f..edbeda7e33 100644 --- a/packages/@ionic/cli/src/lib/ssh-config.ts +++ b/packages/@ionic/cli/src/lib/ssh-config.ts @@ -1,7 +1,7 @@ import { fileToString } from '@ionic/utils-fs'; -import * as os from 'os'; +import os from 'os'; import * as path from 'path'; -import * as SSHConfig from 'ssh-config'; +import SSHConfig from 'ssh-config'; export { SSHConfig }; diff --git a/packages/@ionic/cli/src/lib/ssh.ts b/packages/@ionic/cli/src/lib/ssh.ts index aa7e25f2ca..5f89a3deff 100644 --- a/packages/@ionic/cli/src/lib/ssh.ts +++ b/packages/@ionic/cli/src/lib/ssh.ts @@ -43,7 +43,7 @@ export function parsePublicKey(pubkey: string): [string, string, string, string] export async function validatePrivateKey(keyPath: string): Promise { try { await stat(keyPath); - } catch (e) { + } catch (e: any) { if (e.code === 'ENOENT') { throw ERROR_SSH_MISSING_PRIVKEY; } diff --git a/packages/@ionic/cli/src/lib/start.ts b/packages/@ionic/cli/src/lib/start.ts index 9d1acc54b5..cf0a68d043 100644 --- a/packages/@ionic/cli/src/lib/start.ts +++ b/packages/@ionic/cli/src/lib/start.ts @@ -2,7 +2,7 @@ import { readJson } from '@ionic/utils-fs'; import { columnar } from '@ionic/utils-terminal'; import * as lodash from 'lodash'; -import { COLUMNAR_OPTIONS, PROJECT_TYPES } from '../constants'; +import { COLUMNAR_OPTIONS, PROJECT_TYPES, ANGULAR_STANDALONE } from '../constants'; import { CommandLineOptions, IConfig, ILogger, ProjectType, StarterList, StarterManifest, StarterTemplate } from '../definitions'; import { isStarterManifest } from '../guards'; @@ -103,7 +103,7 @@ export async function readStarterManifest(p: string): Promise { } return manifest; - } catch (e) { + } catch (e: any) { if (e.code === 'ENOENT') { throw new Error(`${p} not found`); } else if (e instanceof SyntaxError) { @@ -213,28 +213,28 @@ export const STARTER_TEMPLATES: StarterTemplate[] = [ projectType: 'vue', type: 'managed', description: 'A starting project with a simple tabbed interface', - id: 'vue-official-tabs', + id: 'vue-vite-official-tabs', }, { name: 'sidemenu', projectType: 'vue', type: 'managed', description: 'A starting project with a side menu with navigation in the content area', - id: 'vue-official-sidemenu', + id: 'vue-vite-official-sidemenu', }, { name: 'blank', projectType: 'vue', type: 'managed', description: 'A blank starter project', - id: 'vue-official-blank', + id: 'vue-vite-official-blank', }, { name: 'list', projectType: 'vue', type: 'managed', description: 'A starting project with a list', - id: 'vue-official-list', + id: 'vue-vite-official-list', }, // Angular { @@ -272,112 +272,75 @@ export const STARTER_TEMPLATES: StarterTemplate[] = [ description: 'A template for the "Build Your First App" tutorial', repo: 'https://github.com/ionic-team/photo-gallery-capacitor-ng', }, - // React - { - name: 'blank', - projectType: 'react', - type: 'managed', - description: 'A blank starter project', - id: 'react-official-blank', - }, - { - name: 'list', - projectType: 'react', - type: 'managed', - description: 'A starting project with a list', - id: 'react-official-list', - }, - { - name: 'my-first-app', - projectType: 'react', - type: 'repo', - description: 'A template for the "Build Your First App" tutorial', - repo: 'https://github.com/ionic-team/photo-gallery-capacitor-react', - }, - { - name: 'sidemenu', - projectType: 'react', - type: 'managed', - description: 'A starting project with a side menu with navigation in the content area', - id: 'react-official-sidemenu', - }, { name: 'tabs', - projectType: 'react', + projectType: ANGULAR_STANDALONE, type: 'managed', description: 'A starting project with a simple tabbed interface', - id: 'react-official-tabs', - }, - // Old Ionic V3 - { - name: 'tabs', - projectType: 'ionic-angular', - type: 'managed', - description: 'A starting project with a simple tabbed interface', - id: 'ionic-angular-official-tabs', + id: 'angular-standalone-official-tabs', }, { name: 'sidemenu', - projectType: 'ionic-angular', + projectType: ANGULAR_STANDALONE, type: 'managed', description: 'A starting project with a side menu with navigation in the content area', - id: 'ionic-angular-official-sidemenu', + id: 'angular-standalone-official-sidemenu', }, { name: 'blank', - projectType: 'ionic-angular', + projectType: ANGULAR_STANDALONE, type: 'managed', description: 'A blank starter project', - id: 'ionic-angular-official-blank', + id: 'angular-standalone-official-blank', }, { - name: 'super', - projectType: 'ionic-angular', + name: 'list', + projectType: ANGULAR_STANDALONE, type: 'managed', - description: 'A starting project complete with pre-built pages, providers and best practices for Ionic development.', - id: 'ionic-angular-official-super', + description: 'A starting project with a list', + id: 'angular-standalone-official-list', }, { - name: 'tutorial', - projectType: 'ionic-angular', - type: 'managed', - description: 'A tutorial based project that goes along with the Ionic documentation', - id: 'ionic-angular-official-tutorial', + name: 'my-first-app', + projectType: ANGULAR_STANDALONE, + type: 'repo', + description: 'A template for the "Build Your First App" tutorial', + repo: 'https://github.com/ionic-team/photo-gallery-capacitor-ng', }, + // React { - name: 'aws', - projectType: 'ionic-angular', + name: 'blank', + projectType: 'react', type: 'managed', - description: 'AWS Mobile Hub Starter', - id: 'ionic-angular-official-aws', + description: 'A blank starter project', + id: 'react-vite-official-blank', }, - // Older Ionic V1 { - name: 'tabs', - projectType: 'ionic1', + name: 'list', + projectType: 'react', type: 'managed', - description: 'A starting project for Ionic using a simple tabbed interface', - id: 'ionic1-official-tabs', + description: 'A starting project with a list', + id: 'react-vite-official-list', }, { - name: 'sidemenu', - projectType: 'ionic1', - type: 'managed', - description: 'A starting project for Ionic using a side menu with navigation in the content area', - id: 'ionic1-official-sidemenu', + name: 'my-first-app', + projectType: 'react', + type: 'repo', + description: 'A template for the "Build Your First App" tutorial', + repo: 'https://github.com/ionic-team/photo-gallery-capacitor-react', }, { - name: 'blank', - projectType: 'ionic1', + name: 'sidemenu', + projectType: 'react', type: 'managed', - description: 'A blank starter project for Ionic', - id: 'ionic1-official-blank', + description: 'A starting project with a side menu with navigation in the content area', + id: 'react-vite-official-sidemenu', }, { - name: 'maps', - projectType: 'ionic1', + name: 'tabs', + projectType: 'react', type: 'managed', - description: 'An Ionic starter project using Google Maps and a side menu', - id: 'ionic1-official-maps', + description: 'A starting project with a simple tabbed interface', + id: 'react-vite-official-tabs', }, ]; diff --git a/packages/@ionic/cli/src/lib/telemetry.ts b/packages/@ionic/cli/src/lib/telemetry.ts index 62a813b09b..3ae1703037 100644 --- a/packages/@ionic/cli/src/lib/telemetry.ts +++ b/packages/@ionic/cli/src/lib/telemetry.ts @@ -1,4 +1,4 @@ -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as lodash from 'lodash'; import { IClient, IConfig, IProject, ISession, ITelemetry, InfoItem, IonicContext } from '../definitions'; @@ -44,7 +44,7 @@ export class Telemetry implements ITelemetry { async function getLeek({ config, version }: { config: IConfig; version: string; }): Promise { if (!_gaTracker) { - const Leek = await import('leek'); + const Leek = (await import('leek')).default; let telemetryToken = config.get('tokens.telemetry'); if (!telemetryToken) { @@ -76,7 +76,7 @@ export async function sendCommand({ config, client, getInfo, ctx, session, proje const leek = await getLeek({ config, version: ctx.version }); try { await leek.track({ name, message }); - } catch (e) { + } catch (e: any) { debug(`leek track error: ${e.stack ? e.stack : e}`); } })(), @@ -117,7 +117,7 @@ export async function sendCommand({ config, client, getInfo, ctx, session, proje try { await client.do(req); - } catch (e) { + } catch (e: any) { debug(`metric send error: ${e.stack ? e.stack : e}`); } })(), diff --git a/packages/@ionic/cli/src/lib/updates.ts b/packages/@ionic/cli/src/lib/updates.ts index d2934f5da0..6d35334ce2 100644 --- a/packages/@ionic/cli/src/lib/updates.ts +++ b/packages/@ionic/cli/src/lib/updates.ts @@ -43,7 +43,7 @@ export async function getUpdateConfig({ config }: GetUpdateConfigDeps): Promise< try { return await readUpdateConfig(dir); - } catch (e) { + } catch (e: any) { if (e.code !== 'ENOENT') { process.stderr.write(`${e.stack ? e.stack : e}\n`); } diff --git a/packages/@ionic/cli/src/lib/utils/http.ts b/packages/@ionic/cli/src/lib/utils/http.ts index 74ca72ed3c..2498d5f7e5 100644 --- a/packages/@ionic/cli/src/lib/utils/http.ts +++ b/packages/@ionic/cli/src/lib/utils/http.ts @@ -1,6 +1,8 @@ import { conform } from '@ionic/utils-array'; import { readFile } from '@ionic/utils-fs'; -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; + +import superagentProxy from './superagent-proxy'; import { CreateRequestOptions, HttpMethod } from '../../definitions'; @@ -21,7 +23,7 @@ function getGlobalProxy(): { envvar: string; envval: string; } | undefined { } export async function createRequest(method: HttpMethod, url: string, { userAgent, proxy, ssl }: CreateRequestOptions): Promise<{ req: SuperAgentRequest; }> { - const superagent = await import('superagent'); + const superagent = (await import('superagent')).default; if (!proxy) { const gproxy = getGlobalProxy(); @@ -31,14 +33,13 @@ export async function createRequest(method: HttpMethod, url: string, { userAgent } } - const req = superagent(method, url); + const req = superagent(method, url) as SuperAgentRequest & { proxy?: (uri: string) => void }; req .set('User-Agent', userAgent) .redirects(25); if (proxy) { - const superagentProxy = await import('superagent-proxy'); superagentProxy(superagent); if (req.proxy) { diff --git a/packages/@ionic/cli/src/lib/utils/logger.ts b/packages/@ionic/cli/src/lib/utils/logger.ts index 27d4807022..0fae1f193f 100644 --- a/packages/@ionic/cli/src/lib/utils/logger.ts +++ b/packages/@ionic/cli/src/lib/utils/logger.ts @@ -1,6 +1,6 @@ import { DEFAULT_COLORS } from '@ionic/cli-framework'; import { CreateTaggedFormatterOptions, DEFAULT_LOGGER_HANDLERS, LOGGER_LEVELS, Logger as BaseLogger, LoggerFormatter, LoggerLevelWeight, createPrefixedFormatter, createTaggedFormatter } from '@ionic/cli-framework-output'; -import * as chalk from 'chalk'; +import chalk from 'chalk'; import { ILogger } from '../../definitions'; import { weak } from '../color'; diff --git a/packages/@ionic/cli/src/lib/utils/superagent-proxy.ts b/packages/@ionic/cli/src/lib/utils/superagent-proxy.ts new file mode 100644 index 0000000000..d2aa6fe691 --- /dev/null +++ b/packages/@ionic/cli/src/lib/utils/superagent-proxy.ts @@ -0,0 +1,131 @@ +/** + * (The MIT License) + * + * Copyright (c) 2013 Nathan Rajlich + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * This is a fork of the original source, upgraded to proxy-agent v6 and TypeScript. + * Original source: https://github.com/TooTallNate/superagent-proxy/blob/ef2cc112926b574547cf2f765ae3dfd80bdeb9a0/index.js + * + */ +import { ProxyAgent } from 'proxy-agent'; +import { debug as Debug } from 'debug'; +import { SuperAgent, SuperAgentRequest } from 'superagent'; + +const debug = Debug('superagent-proxy'); + +/** + * Adds a `.proxy(uri)` function to the "superagent" module's Request class. + * + * ``` js + * var request = require('superagent'); + * require('superagent-proxy')(request); + * + * request + * .get(uri) + * .proxy(uri) + * .end(fn); + * ``` + * + * Or, you can pass in a `superagent.Request` instance, and it's like calling the + * `.proxy(uri)` function on it, but without extending the prototype: + * + * ``` js + * var request = require('superagent'); + * var proxy = require('superagent-proxy'); + * + * proxy(request.get(uri), uri).end(fn); + * ``` + * + * @param {Object} superagent The `superagent` exports object + * @api public + */ + +export default function setup(superagent: any, uri?: string): SuperAgent & { proxy: (uri?: string) => SuperAgent } { + const Request = superagent.Request; + if (Request) { + // the superagent exports object - extent Request with "proxy" + Request.prototype.proxy = proxy; + return superagent; + } else { + // assume it's a `superagent.Request` instance + return proxy.call(superagent, uri); + } +} + +/** + * Sets the proxy server to use for this HTTP(s) request. + * + * @param {String} uri proxy url + * @api public + */ + +function proxy(this: any, uri?: string) { + debug('Request#proxy(%o)', uri); + // we need to observe the `url` field from now on... Superagent sometimes + // re-uses the `req` instance but changes its `url` field (i.e. in the case of + // a redirect), so when that happens we need to potentially re-set the proxy + // agent + setupUrl(this); + + // attempt to get a proxying `http.Agent` instance + const agent = new ProxyAgent(uri !== undefined ? { getProxyForUrl: () => uri } : {}); + + // if we have an `http.Agent` instance then call the .agent() function + if (agent) this.agent(agent); + + // store the proxy URI in case of changes to the `url` prop in the future + this._proxyUri = uri; + + return this; +} + +/** + * Sets up a get/set descriptor for the `url` property of the provided `req` + * Request instance. This is so that we can re-run the "proxy agent" logic when + * the `url` field is changed, i.e. during a 302 Redirect scenario. + * + * @api private + */ + +function setupUrl(req: any): void { + var desc = Object.getOwnPropertyDescriptor(req, 'url'); + if (desc?.get == getUrl && desc?.set == setUrl) return; // already patched + + // save current value + req._url = req.url; + + if (desc) { + desc.get = getUrl; + desc.set = setUrl; + delete desc.value; + delete desc.writable; + + Object.defineProperty(req, 'url', desc); + debug('patched superagent Request "url" property for changes'); + } +} + +/** + * `url` property getter. + * + * @api protected + */ + +function getUrl(this: any): string { + return this._url; +} + +/** + * `url` property setter. + * + * @api protected + */ + +function setUrl(this: any, v: string): void { + debug('set `.url`: %o', v); + this._url = v; + proxy.call(this, this._proxyUri); +} diff --git a/packages/@ionic/cli/tsconfig.json b/packages/@ionic/cli/tsconfig.json index 6d50be6c3d..85d3dba781 100644 --- a/packages/@ionic/cli/tsconfig.json +++ b/packages/@ionic/cli/tsconfig.json @@ -10,7 +10,6 @@ "../../../types/leek.d.ts", "../../../types/ssh-config.d.ts", "../../../types/stream-combiner2.d.ts", - "../../../types/superagent-proxy.d.ts", "src/**/*.ts" ], "exclude": [ diff --git a/packages/@ionic/discover/CHANGELOG.md b/packages/@ionic/discover/CHANGELOG.md index 4894b9a6dd..51de1d984c 100644 --- a/packages/@ionic/discover/CHANGELOG.md +++ b/packages/@ionic/discover/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.1.8](https://github.com/ionic-team/ionic-cli/compare/@ionic/discover@3.1.7...@ionic/discover@3.1.8) (2023-12-19) + + +### Bug Fixes + +* **cli:** resolve vm2 security vulnerability ([#5070](https://github.com/ionic-team/ionic-cli/issues/5070)) ([4050419](https://github.com/ionic-team/ionic-cli/commit/4050419bef70fb92e58b0a83cd4b68b48090e596)) + + + + + +## [3.1.7](https://github.com/ionic-team/ionic-cli/compare/@ionic/discover@3.1.6...@ionic/discover@3.1.7) (2023-11-07) + +**Note:** Version bump only for package @ionic/discover + + + + + +## [3.1.6](https://github.com/ionic-team/ionic-cli/compare/@ionic/discover@3.1.5...@ionic/discover@3.1.6) (2023-03-29) + +**Note:** Version bump only for package @ionic/discover + + + + + ## [3.1.5](https://github.com/ionic-team/ionic-cli/compare/@ionic/discover@3.1.4...@ionic/discover@3.1.5) (2020-08-28) **Note:** Version bump only for package @ionic/discover diff --git a/packages/@ionic/discover/package.json b/packages/@ionic/discover/package.json index 7f0aa1c673..fc684c5114 100644 --- a/packages/@ionic/discover/package.json +++ b/packages/@ionic/discover/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/discover", - "version": "3.1.5", + "version": "3.1.8", "description": "Simple UDP based protocol for service discovery implemented in pure JS.", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -29,21 +29,21 @@ }, "dependencies": { "debug": "^4.0.0", - "netmask": "^1.0.6", + "netmask": "^2.0.2", "tslib": "^2.0.1", "ws": "^7.0.0" }, "devDependencies": { "@types/debug": "^4.1.1", "@types/jest": "^26.0.10", - "@types/netmask": "^1.0.30", - "@types/node": "~10.17.13", + "@types/netmask": "^2.0.5", + "@types/node": "~16.0.0", "@types/ws": "^7.2.0", "jest": "^26.4.2", "jest-cli": "^26.0.1", "lint-staged": "^10.0.2", "rimraf": "^3.0.0", "ts-jest": "~26.3.0", - "typescript": "~4.0.2" + "typescript": "~4.8.0" } } diff --git a/packages/@ionic/discover/src/comm.ts b/packages/@ionic/discover/src/comm.ts index a08c704434..5fb3af0776 100644 --- a/packages/@ionic/discover/src/comm.ts +++ b/packages/@ionic/discover/src/comm.ts @@ -1,7 +1,8 @@ -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as events from 'events'; import * as util from 'util'; -import * as WebSocket from 'ws'; +import WebSocket from 'ws'; + const debug = Debug('ionic:discover:comm'); @@ -95,7 +96,7 @@ export class CommServer extends events.EventEmitter { } return payload; - } catch (e) { + } catch (e: any) { debug(e); } } diff --git a/packages/@ionic/discover/src/publisher.ts b/packages/@ionic/discover/src/publisher.ts index ebdef1d5e5..d36f2b112d 100644 --- a/packages/@ionic/discover/src/publisher.ts +++ b/packages/@ionic/discover/src/publisher.ts @@ -1,4 +1,4 @@ -import * as Debug from 'debug'; +import { debug as Debug } from 'debug'; import * as dgram from 'dgram'; import * as events from 'events'; import { Netmask } from 'netmask'; @@ -118,7 +118,7 @@ export class Publisher extends events.EventEmitter implements IPublisherEventEmi }; } - protected getInterfaces(): Interface[] { + protected getInterfaces(): Interface[] | undefined { return prepareInterfaces(os.networkInterfaces()); } @@ -145,34 +145,35 @@ export class Publisher extends events.EventEmitter implements IPublisherEventEmi } }); } - } catch (e) { + } catch (e: any) { this.emit('error', e); } } } -export function prepareInterfaces(interfaces: { [index: string]: os.NetworkInterfaceInfo[] }): Interface[] { +export function prepareInterfaces(interfaces: NodeJS.Dict): Interface[] | undefined { const set = new Set(); - - return Object.keys(interfaces) - .map(key => interfaces[key]) - .reduce((prev, current) => prev.concat(current)) - .filter(iface => iface.family === 'IPv4') - .map(iface => { - return { - address: iface.address, - broadcast: computeBroadcastAddress(iface.address, iface.netmask), - }; - }) - .filter(iface => { - if (!set.has(iface.broadcast)) { - set.add(iface.broadcast); - - return true; - } - - return false; - }); + const values = Object.values(interfaces); + const flatValues = values.reduce((prev, current) => prev?.concat(current ? current : [])); + if (flatValues) { + return flatValues + .filter((iface: os.NetworkInterfaceInfo) => iface.family === 'IPv4') + .map((iface: os.NetworkInterfaceInfo) => { + return { + address: iface.address, + broadcast: computeBroadcastAddress(iface.address, iface.netmask), + }; + }) + .filter((iface: any) => { + if (!set.has(iface.broadcast)) { + set.add(iface.broadcast); + + return true; + } + + return false; + }); + } } export function newSilentPublisher(namespace: string, name: string, port: number): Publisher { diff --git a/packages/@ionic/lab/.gitignore b/packages/@ionic/lab/.gitignore deleted file mode 100644 index 796a78ed1b..0000000000 --- a/packages/@ionic/lab/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.stencil -www -bin/ionic-lab.* diff --git a/packages/@ionic/lab/.npmrc b/packages/@ionic/lab/.npmrc deleted file mode 100644 index 43c97e719a..0000000000 --- a/packages/@ionic/lab/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/packages/@ionic/lab/CHANGELOG.md b/packages/@ionic/lab/CHANGELOG.md deleted file mode 100644 index e4f55793ea..0000000000 --- a/packages/@ionic/lab/CHANGELOG.md +++ /dev/null @@ -1,625 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [3.2.15](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.14...@ionic/lab@3.2.15) (2022-10-06) - - -### Bug Fixes - -* **cli:** capacitor commands not working ([#4918](https://github.com/ionic-team/ionic-cli/issues/4918)) ([27b958b](https://github.com/ionic-team/ionic-cli/commit/27b958bdf22c37f962d705a1b8ba1fee78b59c42)) - - - - - -## [3.2.14](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.13...@ionic/lab@3.2.14) (2022-09-29) - - -### Bug Fixes - -* **cli:** capacitor config not being read from multi-project directories ([#4909](https://github.com/ionic-team/ionic-cli/issues/4909)) ([0ab4881](https://github.com/ionic-team/ionic-cli/commit/0ab4881736f0d3b967d12802cb9cd01ba24d2088)) - - - - - -## [3.2.13](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.12...@ionic/lab@3.2.13) (2022-06-16) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.12](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.11...@ionic/lab@3.2.12) (2022-05-09) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.11](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.10...@ionic/lab@3.2.11) (2022-03-04) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.10](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.9...@ionic/lab@3.2.10) (2020-12-10) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.9](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.8...@ionic/lab@3.2.9) (2020-09-29) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.8](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.7...@ionic/lab@3.2.8) (2020-09-24) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.7](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.6...@ionic/lab@3.2.7) (2020-09-02) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.6](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.5...@ionic/lab@3.2.6) (2020-08-29) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.5](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.4...@ionic/lab@3.2.5) (2020-08-28) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.4](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.3...@ionic/lab@3.2.4) (2020-08-27) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.3](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.2...@ionic/lab@3.2.3) (2020-08-27) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.1...@ionic/lab@3.2.2) (2020-08-26) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.2.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.2.0...@ionic/lab@3.2.1) (2020-08-25) - -**Note:** Version bump only for package @ionic/lab - - - - - -# [3.2.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.1.7...@ionic/lab@3.2.0) (2020-08-12) - - -### Features - -* **lab:** improvements to the lab design ([#4511](https://github.com/ionic-team/ionic-cli/issues/4511)) ([625fe7b](https://github.com/ionic-team/ionic-cli/commit/625fe7b894a47837c33dfe2b8363a491f57ecbda)) - - - - - -## [3.1.7](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.1.6...@ionic/lab@3.1.7) (2020-06-02) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.1.6](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.1.5...@ionic/lab@3.1.6) (2020-05-12) - - -### Bug Fixes - -* pin tslib to avoid "Cannot set property pathExists" error ([689e1f0](https://github.com/ionic-team/ionic-cli/commit/689e1f038b907356ef855a067a76d4822e7072a8)) - - - - - -## [3.1.5](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.1.4...@ionic/lab@3.1.5) (2020-05-06) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.1.4](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.1.3...@ionic/lab@3.1.4) (2020-04-29) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.1.3](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.1.2...@ionic/lab@3.1.3) (2020-03-30) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.1.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.1.1...@ionic/lab@3.1.2) (2020-03-09) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.1.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.1.0...@ionic/lab@3.1.1) (2020-03-03) - -**Note:** Version bump only for package @ionic/lab - - - - - -# 3.1.0 (2020-02-11) - - -### Features - -* **start:** add new list starter option ([#4315](https://github.com/ionic-team/ionic-cli/issues/4315)) ([1df44c1](https://github.com/ionic-team/ionic-cli/commit/1df44c1591f37b89f2b672857740edd6cb2aea67)) - - - - - -## [3.0.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.0.1...@ionic/lab@3.0.2) (2020-02-10) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [3.0.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@3.0.0...@ionic/lab@3.0.1) (2020-02-03) - -**Note:** Version bump only for package @ionic/lab - - - - - -# [3.0.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.19...@ionic/lab@3.0.0) (2020-01-25) - - -### chore - -* require Node 10 ([5a47874](https://github.com/ionic-team/ionic-cli/commit/5a478746c074207b6dc96aa8771f04a606deb1ef)) - - -### BREAKING CHANGES - -* A minimum of Node.js 10.3.0 is required. - - - - - -## [2.0.19](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.18...@ionic/lab@2.0.19) (2020-01-13) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.18](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.17...@ionic/lab@2.0.18) (2019-12-10) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.17](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.16...@ionic/lab@2.0.17) (2019-12-05) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.16](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.15...@ionic/lab@2.0.16) (2019-11-25) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.15](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.14...@ionic/lab@2.0.15) (2019-11-24) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.14](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.13...@ionic/lab@2.0.14) (2019-11-21) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.13](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.12...@ionic/lab@2.0.13) (2019-10-14) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.12](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.11...@ionic/lab@2.0.12) (2019-10-14) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.11](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.10...@ionic/lab@2.0.11) (2019-09-18) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.10](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.9...@ionic/lab@2.0.10) (2019-08-28) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.9](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.8...@ionic/lab@2.0.9) (2019-08-23) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.8](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.7...@ionic/lab@2.0.8) (2019-08-14) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.7](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.6...@ionic/lab@2.0.7) (2019-08-07) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.6](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.5...@ionic/lab@2.0.6) (2019-07-09) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.5](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.4...@ionic/lab@2.0.5) (2019-06-28) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.4](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.3...@ionic/lab@2.0.4) (2019-06-21) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.3](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.2...@ionic/lab@2.0.3) (2019-06-18) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.1...@ionic/lab@2.0.2) (2019-06-10) - -**Note:** Version bump only for package @ionic/lab - - - - - -## [2.0.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@2.0.0...@ionic/lab@2.0.1) (2019-06-05) - -**Note:** Version bump only for package @ionic/lab - - - - - -# [2.0.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.24...@ionic/lab@2.0.0) (2019-05-29) - - -### chore - -* require Node 8 ([5670e68](https://github.com/ionic-team/ionic-cli/commit/5670e68)) - - -### BREAKING CHANGES - -* A minimum of Node.js 8.9.4 is required. - - - - - - -## [1.0.24](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.23...@ionic/lab@1.0.24) (2019-03-12) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.23](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.22...@ionic/lab@1.0.23) (2019-03-06) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.22](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.21...@ionic/lab@1.0.22) (2019-02-27) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.21](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.20...@ionic/lab@1.0.21) (2019-02-15) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.20](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.19...@ionic/lab@1.0.20) (2019-01-29) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.19](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.18...@ionic/lab@1.0.19) (2019-01-23) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.18](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.17...@ionic/lab@1.0.18) (2019-01-08) - - -### Bug Fixes - -* **lab:** iframe now needs allow policy ([487b82a](https://github.com/ionic-team/ionic-cli/commit/487b82a)) - - - - - -## [1.0.17](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.16...@ionic/lab@1.0.17) (2019-01-07) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.16](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.15...@ionic/lab@1.0.16) (2018-12-19) - - -### Bug Fixes - -* **lab:** correct statusbar position ([#3766](https://github.com/ionic-team/ionic-cli/issues/3766)) ([b37fa03](https://github.com/ionic-team/ionic-cli/commit/b37fa03)) -* **lab:** default cursor for platform titles ([b215beb](https://github.com/ionic-team/ionic-cli/commit/b215beb)) -* **lab:** improve dev experience on smaller screens ([45a6c58](https://github.com/ionic-team/ionic-cli/commit/45a6c58)) -* **lab:** statusbar padding for all versions ([4fa13a3](https://github.com/ionic-team/ionic-cli/commit/4fa13a3)) - - - - - -## [1.0.15](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.14...@ionic/lab@1.0.15) (2018-11-27) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.14](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.13...@ionic/lab@1.0.14) (2018-11-20) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.13](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.12...@ionic/lab@1.0.13) (2018-11-04) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.12](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.11...@ionic/lab@1.0.12) (2018-10-31) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.11](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.10...@ionic/lab@1.0.11) (2018-10-05) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.10](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.9...@ionic/lab@1.0.10) (2018-10-03) - - -### Bug Fixes - -* **lab:** proper statusbar padding ([63b0eef](https://github.com/ionic-team/ionic-cli/commit/63b0eef)) -* **lab:** use correct query params for project type ([4801680](https://github.com/ionic-team/ionic-cli/commit/4801680)) -* **serve:** persist config for livereload ([27193e8](https://github.com/ionic-team/ionic-cli/commit/27193e8)) - - - - - -## [1.0.9](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.8...@ionic/lab@1.0.9) (2018-09-05) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.8](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.7...@ionic/lab@1.0.8) (2018-08-20) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.7](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.6...@ionic/lab@1.0.7) (2018-08-15) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.6](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.5...@ionic/lab@1.0.6) (2018-08-09) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.5](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.4...@ionic/lab@1.0.5) (2018-08-07) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.4](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.3...@ionic/lab@1.0.4) (2018-08-07) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.3](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.2...@ionic/lab@1.0.3) (2018-08-06) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.1...@ionic/lab@1.0.2) (2018-08-02) - - - - -**Note:** Version bump only for package @ionic/lab - - -## [1.0.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.0...@ionic/lab@1.0.1) (2018-07-30) - - - - -**Note:** Version bump only for package @ionic/lab - - -# [1.0.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/lab@1.0.0-rc.13...@ionic/lab@1.0.0) (2018-07-25) - - - - -**Note:** Version bump only for package @ionic/lab diff --git a/packages/@ionic/lab/LICENSE b/packages/@ionic/lab/LICENSE deleted file mode 100644 index 7c5808ced6..0000000000 --- a/packages/@ionic/lab/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 Drifty Co - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/@ionic/lab/README.md b/packages/@ionic/lab/README.md deleted file mode 100644 index d4f290bc38..0000000000 --- a/packages/@ionic/lab/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Ionic Lab - -Stand-alone Ionic Lab package, a utility used by `ionic serve --lab`. - -Once installed, invoke serve on your framework CLI of choice. Then, copy the -URL of that server and invoke the Ionic Lab CLI. For example: - -```bash -$ ionic-lab http://localhost:4200 -``` diff --git a/packages/@ionic/lab/bin/ionic-lab b/packages/@ionic/lab/bin/ionic-lab deleted file mode 100755 index 7548c3d0b0..0000000000 --- a/packages/@ionic/lab/bin/ionic-lab +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env node - -'use strict'; - -process.title = 'ionic-lab'; -process.on('unhandledRejection', function(r) { console.error(r); }); - -var semver = require('semver'); -var version = semver.parse(process.version); -var minversion = 'v10.3.0'; - -if (semver.lt(version, minversion)) { - var details = version.major === 6 - ? ' Node.js 6 reached end-of-life on 2019-04-30 and is no longer supported.' - : version.major === 8 - ? ' Node.js 8 reached end-of-life on 2019-12-31 and is no longer supported.' - : ''; - - process.stderr.write('ERR: Your Node.js version is ' + version.raw + '.' + details + ' Please update to the latest Node LTS version.\n'); - process.exit(1); -} - -var cli = require('../'); -cli.run(process.argv.slice(2), process.env); diff --git a/packages/@ionic/lab/jest.config.js b/packages/@ionic/lab/jest.config.js deleted file mode 100644 index eace8d6d9d..0000000000 --- a/packages/@ionic/lab/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../../jest.config.base'); diff --git a/packages/@ionic/lab/lint-staged.config.js b/packages/@ionic/lab/lint-staged.config.js deleted file mode 100644 index 5815a72f0d..0000000000 --- a/packages/@ionic/lab/lint-staged.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../../lint-staged.config.base'); diff --git a/packages/@ionic/lab/package.json b/packages/@ionic/lab/package.json deleted file mode 100644 index 52830fc72b..0000000000 --- a/packages/@ionic/lab/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "@ionic/lab", - "version": "3.2.15", - "description": "Ionic Lab utility for developing Ionic apps, used by Ionic CLI", - "bin": { - "ionic-lab": "./bin/ionic-lab" - }, - "main": "dist/index.js", - "types": "dist/index.d.ts", - "homepage": "https://ionicframework.com/", - "author": "Ionic Team (https://ionic.io)", - "license": "MIT", - "engines": { - "node": ">=10.3.0" - }, - "files": [ - "bin/", - "dist/", - "www/", - "LICENSE", - "README.md" - ], - "repository": { - "type": "git", - "url": "https://github.com/ionic-team/ionic-cli.git" - }, - "bugs": { - "url": "https://github.com/ionic-team/ionic-cli/issues" - }, - "scripts": { - "clean": "npm run clean.tsc", - "clean.tsc": "rimraf dist", - "clean.stencil": "rimraf www", - "lint": "true", - "build": "npm run build.tsc && npm run build.stencil", - "build.stencil": "npm run clean.stencil && stencil build", - "build.tsc": "npm run clean.tsc && tsc", - "watch": "npm run watch.tsc", - "watch.stencil": "npm run clean.stencil && stencil build --dev --watch --serve --address localhost --port 8900 --no-open", - "watch.tsc": "tsc -w --preserveWatchOutput", - "prepublishOnly": "npm run build" - }, - "dependencies": { - "@ionic/cli-framework": "5.1.3", - "@ionic/utils-fs": "3.1.6", - "chalk": "^4.0.0", - "express": "^4.16.2", - "tslib": "^2.0.1" - }, - "devDependencies": { - "@ionic-internal/ionic-ds": "^2.1.0", - "@stencil/core": "~1.8.5", - "@types/express": "4.17.13", - "@types/express-serve-static-core": "4.17.28", - "@types/node": "~10.17.13", - "lint-staged": "^10.0.2", - "rimraf": "^3.0.0", - "typescript": "~4.0.2" - } -} diff --git a/packages/@ionic/lab/src/index.ts b/packages/@ionic/lab/src/index.ts deleted file mode 100644 index 3bfa7686ba..0000000000 --- a/packages/@ionic/lab/src/index.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { Command, CommandLineInputs, CommandLineOptions, CommandMap, CommandMapDefault, Namespace, execute, validators } from '@ionic/cli-framework'; -import { readFile } from '@ionic/utils-fs'; -import * as chalk from 'chalk'; -import * as express from 'express'; -import * as http from 'http'; -import * as https from 'https'; -import * as path from 'path'; -import * as tls from 'tls'; - -const WWW_DIRECTORY = path.join(__dirname, '..', 'www'); - -class DefaultCommand extends Command { - async getMetadata() { - return { - name: 'default', - summary: '', - inputs: [ - { - name: 'url', - summary: 'The URL of the livereload server to use with lab', - validators: [validators.required, validators.url], - }, - ], - options: [ - { - name: 'host', - summary: 'HTTP host of Ionic Lab', - default: 'localhost', - }, - { - name: 'port', - summary: 'HTTP port of Ionic Lab', - default: '8200', - }, - { - name: 'project-type', - summary: 'Ionic project type', - }, - { - name: 'ssl', - summary: 'Host Ionic Lab with HTTPS', - type: Boolean, - }, - { - name: 'ssl-key', - summary: 'Path to SSL key', - }, - { - name: 'ssl-cert', - summary: 'Path to SSL certificate', - }, - { - name: 'app-name', - summary: 'App name to show in bottom left corner', - }, - { - name: 'app-version', - summary: 'App version to show in bottom left corner', - }, - ], - }; - } - - async run(inputs: CommandLineInputs, options: CommandLineOptions) { - const [ url ] = inputs; - const { host, port, ssl } = options; - - const protocol = ssl ? 'https' : 'http'; - const projectType = options['project-type']; - const name = options['app-name']; - const version = options['app-version']; - - const app = express(); - - app.use('/', express.static(WWW_DIRECTORY)); - - app.get('/api/app', (req, res) => { - res.json({ url, name, version, projectType }); - }); - - const server = ssl ? https.createServer(await this.collectSecureContextOptions(options), app) : http.createServer(app); - server.listen({ port, host }); - - const labUrl = `${protocol}://${host}:${port}`; - - server.on('listening', () => { - process.stdout.write( - 'Ionic Lab running!\n' + - `Lab: ${chalk.bold(labUrl)}\n` + - `App: ${chalk.bold(url)}\n` - ); - }); - } - - async collectSecureContextOptions(options: CommandLineOptions): Promise { - const sslKeyPath = options['ssl-key'] ? String(options['ssl-key']) : undefined; - const sslCertPath = options['ssl-cert'] ? String(options['ssl-cert']) : undefined; - - if (!sslKeyPath || !sslCertPath) { - throw new Error('SSL key and cert required for serving SSL'); - } - - const [ key, cert ] = await Promise.all([this.readPem(sslKeyPath), this.readPem(sslCertPath)]); - - return { key, cert }; - } - - async readPem(p: string): Promise { - try { - return await readFile(p, { encoding: 'utf8' }); - } catch (e) { - process.stderr.write(String(e.stack ? e.stack : e) + '\n'); - throw new Error(`Error encountered with ${p}`); - } - } -} - -class LabNamespace extends Namespace { - async getMetadata() { - return { - name: 'ionic-lab', - summary: '', - }; - } - - async getCommands(): Promise { - return new CommandMap([[CommandMapDefault, async () => new DefaultCommand(this)]]); - } -} - -const namespace = new LabNamespace(); - -export async function run(argv: string[], env: NodeJS.ProcessEnv) { - await execute({ namespace, argv, env }); -} diff --git a/packages/@ionic/lab/src/stencil/.gitignore b/packages/@ionic/lab/src/stencil/.gitignore deleted file mode 100644 index dd449725e1..0000000000 --- a/packages/@ionic/lab/src/stencil/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.md diff --git a/packages/@ionic/lab/src/stencil/assets/android-device-skin.png b/packages/@ionic/lab/src/stencil/assets/android-device-skin.png deleted file mode 100644 index d31f1cc511..0000000000 Binary files a/packages/@ionic/lab/src/stencil/assets/android-device-skin.png and /dev/null differ diff --git a/packages/@ionic/lab/src/stencil/assets/android-statusbar.png b/packages/@ionic/lab/src/stencil/assets/android-statusbar.png deleted file mode 100644 index b0d1f6f8f7..0000000000 Binary files a/packages/@ionic/lab/src/stencil/assets/android-statusbar.png and /dev/null differ diff --git a/packages/@ionic/lab/src/stencil/assets/app.css b/packages/@ionic/lab/src/stencil/assets/app.css deleted file mode 100644 index 620a151685..0000000000 --- a/packages/@ionic/lab/src/stencil/assets/app.css +++ /dev/null @@ -1,131 +0,0 @@ -html, -body { - height: 100%; -} - -body { - -webkit-font-smoothing: antialiased; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: normal; -} - -a { - color: var(--font-color); -} - -a:hover { - text-decoration: none; - color: var(--font-color); -} - -ionlab-app { - display: flex; - flex-direction: column; - height: 100%; -} - -header { - background: var(--section-bg); - height: 50px; - line-height: 50px; - font-size: var(--font-size-sm); - font-weight: bold; - color: var(--icon-color); -} - -header .icon { - display: inline-block; - vertical-align: middle; - margin-top: -3px; - font-size: 22px; - color: var(--icon-color); - cursor: pointer; -} - -#logo { - display: inline-block; - vertical-align: middle; - background: url('lab-logo.png'); - background-size: 100%; - background-repeat: no-repeat; - margin-top: -2px; - width: 64px; - height: 28px; - margin-left: 15px; -} - -#header-left { - float: left; -} - -#header-left .menu-icon { - margin-left: 13px; -} - -#header-right { - float: right; -} - -#header-right button { - display: inline-block; - height: 100%; - background: none; - border: none; - outline: none; - margin: 0; - padding: 0 10px; - cursor: pointer; - color: var(--icon-color); - font-weight: bold; -} - -#header-right .fullscreen-icon { - margin-left: 5px; -} - -main { - color: var(--font-color); - display: flex; - flex: 1; - overflow: hidden; -} - -footer { - width: 100%; - border-top: 1px solid rgba(0, 0, 0, 0.06); - background: var(--section-bg); - font-size: var(--font-size-sm); - color: var(--font-color); -} - -#footer-left { - float: left; - padding: 14px 0 14px 15px; -} - -#footer-right { - float: right; - padding: 14px 15px 14px 5px; -} - -#footer-right a { - font-family: var(--font-family-medium); - margin-left: 10px; -} - -#error-box { - width: 500px; - align-self: center; - justify-content: center; - text-align: center; - background: var(--section-bg); - padding: 25px; - margin: 100px 0; -} diff --git a/packages/@ionic/lab/src/stencil/assets/favicon.png b/packages/@ionic/lab/src/stencil/assets/favicon.png deleted file mode 100644 index 7469634d5a..0000000000 Binary files a/packages/@ionic/lab/src/stencil/assets/favicon.png and /dev/null differ diff --git a/packages/@ionic/lab/src/stencil/assets/fonts.css b/packages/@ionic/lab/src/stencil/assets/fonts.css deleted file mode 100644 index 570bf89e73..0000000000 --- a/packages/@ionic/lab/src/stencil/assets/fonts.css +++ /dev/null @@ -1,132 +0,0 @@ -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: normal; - font-weight: 400; - unicode-range: U+000-5FF; - src: local('Inter Regular'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-Regular.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-Regular.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: italic; - font-weight: 400; - unicode-range: U+000-5FF; - src: local('Inter Italic'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-Italic.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-Italic.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: normal; - font-weight: 500; - unicode-range: U+000-5FF; - src: local('Inter Medium'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-Medium.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-Medium.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: italic; - font-weight: 500; - unicode-range: U+000-5FF; - src: local('Inter Medium Italic'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-MediumItalic.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-MediumItalic.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: normal; - font-weight: 600; - unicode-range: U+000-5FF; - src: local('Inter SemiBold'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-SemiBold.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-SemiBold.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: italic; - font-weight: 600; - unicode-range: U+000-5FF; - src: local('Inter SemiBoldItalic'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-SemiBoldItalic.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-SemiBoldItalic.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: normal; - font-weight: 700; - unicode-range: U+000-5FF; - src: local('Inter Bold'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-Bold.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-Bold.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: italic; - font-weight: 700; - unicode-range: U+000-5FF; - src: local('Inter BoldItalic'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-BoldItalic.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-BoldItalic.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: normal; - font-weight: 800; - unicode-range: U+000-5FF; - src: local('Inter ExtraBold'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-ExtraBold.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-ExtraBold.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: italic; - font-weight: 800; - unicode-range: U+000-5FF; - src: local('Inter ExtraBoldItalic'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-ExtraBoldItalic.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-ExtraBoldItalic.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: normal; - font-weight: 900; - unicode-range: U+000-5FF; - src: local('Inter Black'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-Black.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-Black.woff') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-display: swap; - font-style: italic; - font-weight: 900; - unicode-range: U+000-5FF; - src: local('Inter BlackItalic'), - url('/assets/fonts/inter/Inter-variable-ASCII-subset.woff2') format('woff2-variations'), - url('/assets/fonts/inter/Inter-BlackItalic.woff2') format('woff2'), - url('/assets/fonts/inter/Inter-BlackItalic.woff') format('woff'); -} diff --git a/packages/@ionic/lab/src/stencil/assets/ios-statusbar.png b/packages/@ionic/lab/src/stencil/assets/ios-statusbar.png deleted file mode 100644 index 9626c8d4c7..0000000000 Binary files a/packages/@ionic/lab/src/stencil/assets/ios-statusbar.png and /dev/null differ diff --git a/packages/@ionic/lab/src/stencil/assets/iphone-device-skin.png b/packages/@ionic/lab/src/stencil/assets/iphone-device-skin.png deleted file mode 100644 index c2e4884328..0000000000 Binary files a/packages/@ionic/lab/src/stencil/assets/iphone-device-skin.png and /dev/null differ diff --git a/packages/@ionic/lab/src/stencil/assets/lab-logo.png b/packages/@ionic/lab/src/stencil/assets/lab-logo.png deleted file mode 100644 index 7e7b5ad49b..0000000000 Binary files a/packages/@ionic/lab/src/stencil/assets/lab-logo.png and /dev/null differ diff --git a/packages/@ionic/lab/src/stencil/assets/reboot.css b/packages/@ionic/lab/src/stencil/assets/reboot.css deleted file mode 100644 index 6a3c78a19e..0000000000 --- a/packages/@ionic/lab/src/stencil/assets/reboot.css +++ /dev/null @@ -1,281 +0,0 @@ -*, -*::before, -*::after { - box-sizing: border-box; -} - -html { - font-family: sans-serif; - line-height: 1.15; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; - -ms-overflow-style: scrollbar; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} - -article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section { - display: block; -} - -body { - margin: 0; - font-family: var(--font-family-base); - font-size: var(--font-size-base); - font-weight: var(--font-weight-base); - line-height: var(--line-height-base); - color: var(--body-color); - text-align: left; - background-color: var(--body-bg); -} - -[tabindex="-1"]:focus { - outline: 0 !important; -} - -hr { - box-sizing: content-box; - height: 0; - overflow: visible; -} - -h1, h2, h3, h4, h5, h6 { - margin-top: 0; - margin-bottom: var(--headings-margin-bottom); -} - -p { - margin-top: 0; - margin-bottom: var(--paragraph-margin-bottom); -} - -abbr[title], -abbr[data-original-title] { - text-decoration: underline; - text-decoration: underline dotted; - cursor: help; - border-bottom: 0; -} - -address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; -} - -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 1rem; -} - -ol ol, -ul ul, -ol ul, -ul ol { - margin-bottom: 0; -} - -blockquote { - margin: 0 0 1rem; -} - -dfn { - font-style: italic; -} - -b, -strong { - font-weight: bolder; -} - -small { - font-size: 80%; -} - -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} - -sub { bottom: -.25em; } -sup { top: -.5em; } - -a { - color: var(--link-color); - text-decoration: var(--link-decoration); - background-color: transparent; - -webkit-text-decoration-skip: objects; -} - -a:hover { - color: var(--link-hover-color); - text-decoration: var(--link-hover-decoration); -} - -pre, -code, -kbd, -samp { - font-family: monospace, monospace; - font-size: 1em; -} - -pre { - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - -ms-overflow-style: scrollbar; -} - -img { - vertical-align: middle; - border-style: none; -} - -svg:not(:root) { - overflow: hidden; -} - -a, -area, -button, -[role="button"], -input:not([type="range"]), -label, -select, -summary, -textarea { - touch-action: manipulation; -} - -label { - display: inline-block; - margin-bottom: .5rem; -} - -button { - border-radius: 0; -} - -button:focus { - outline: 1px dotted; - outline: 5px auto -webkit-focus-ring-color; -} - -input, -button, -select, -optgroup, -textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; -} - -button, -input { - overflow: visible; -} - -button, -select { - text-transform: none; -} - -button, -html [type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - padding: 0; - border-style: none; -} - -input[type="radio"], -input[type="checkbox"] { - box-sizing: border-box; - padding: 0; -} - - -input[type="date"], -input[type="time"], -input[type="datetime-local"], -input[type="month"] { - -webkit-appearance: listbox; -} - -textarea { - overflow: auto; - resize: vertical; -} - -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} - -legend { - display: block; - width: 100%; - max-width: 100%; - padding: 0; - margin-bottom: .5rem; - font-size: 1.5rem; - line-height: inherit; - color: inherit; - white-space: normal; -} - -progress { - vertical-align: baseline; -} - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -[type="search"] { - outline-offset: -2px; - -webkit-appearance: none; -} - -[type="search"]::-webkit-search-cancel-button, -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; -} - -output { - display: inline-block; -} - -summary { - display: list-item; -} - -template { - display: none; -} - -[hidden] { - display: none !important; -} diff --git a/packages/@ionic/lab/src/stencil/assets/variables.css b/packages/@ionic/lab/src/stencil/assets/variables.css deleted file mode 100644 index 5690f4e981..0000000000 --- a/packages/@ionic/lab/src/stencil/assets/variables.css +++ /dev/null @@ -1,41 +0,0 @@ -:root { - --font-family-sans-serif: 'Inter', 'Inter UI', Helvetica, Arial, sans-serif; - --font-family-base: var(--font-family-sans-serif); - --font-family-medium: 'Inter', 'Inter UI', Helvetica, Arial, sans-serif; - - --font-size-base: 1rem; - --font-size-lg: calc(var(--font-size-base) * 1.25); - --font-size-sm: calc(var(--font-size-base) * .875); - - --font-weight-light: 300; - --font-weight-normal: 400; - --font-weight-bold: 500; - - --font-weight-base: var(--font-weight-normal); - --line-height-base: 1.5; - - --body-bg: #242a31; - --body-color: #fff; - - --headings-margin-bottom: 1rem; - --paragraph-margin-bottom: 1rem; - - --link-color: #00f; - --link-decoration: none; - /* - --link-hover-color: darken($link-color, 15%); - */ - --link-hover-color: var(--link-color); - --link-hover-decoration: underline; - - --icon-color: #858d9b; - --main-bg: var(--body-bg); - --section-bg: #151a21; - --font-color: #a2a9b4; - - --frame-ratio: .488; /* width/height */ - --frame-default-width: 344px; - --frame-default-height: calc(var(--frame-default-width) / var(--frame-ratio)); - - --device-frame-width: 12px; -} diff --git a/packages/@ionic/lab/src/stencil/assets/wp-statusbar.png b/packages/@ionic/lab/src/stencil/assets/wp-statusbar.png deleted file mode 100644 index f6a647780f..0000000000 Binary files a/packages/@ionic/lab/src/stencil/assets/wp-statusbar.png and /dev/null differ diff --git a/packages/@ionic/lab/src/stencil/components.d.ts b/packages/@ionic/lab/src/stencil/components.d.ts deleted file mode 100644 index de4ee29cc3..0000000000 --- a/packages/@ionic/lab/src/stencil/components.d.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* eslint-disable */ -/* tslint:disable */ -/** - * This is an autogenerated file created by the Stencil compiler. - * It contains typing information for all components that exist in this project. - */ - - -import { HTMLStencilElement, JSXBase } from '@stencil/core/internal'; - - -export namespace Components { - interface IonlabApp {} - interface IonlabDeviceFrame { - 'icon': string; - 'platform': string; - 'platformName': string; - 'url': string; - } - interface IonlabPlatformDropdown { - 'activePlatforms': string[]; - } - interface IonlabPreview { - 'activeDevices': string[]; - 'projectType'?: string; - 'url'?: string; - } - interface IonlabSidebar { - 'visible': boolean; - } - interface SkFadingCircle {} -} - -declare global { - - - interface HTMLIonlabAppElement extends Components.IonlabApp, HTMLStencilElement {} - var HTMLIonlabAppElement: { - prototype: HTMLIonlabAppElement; - new (): HTMLIonlabAppElement; - }; - - interface HTMLIonlabDeviceFrameElement extends Components.IonlabDeviceFrame, HTMLStencilElement {} - var HTMLIonlabDeviceFrameElement: { - prototype: HTMLIonlabDeviceFrameElement; - new (): HTMLIonlabDeviceFrameElement; - }; - - interface HTMLIonlabPlatformDropdownElement extends Components.IonlabPlatformDropdown, HTMLStencilElement {} - var HTMLIonlabPlatformDropdownElement: { - prototype: HTMLIonlabPlatformDropdownElement; - new (): HTMLIonlabPlatformDropdownElement; - }; - - interface HTMLIonlabPreviewElement extends Components.IonlabPreview, HTMLStencilElement {} - var HTMLIonlabPreviewElement: { - prototype: HTMLIonlabPreviewElement; - new (): HTMLIonlabPreviewElement; - }; - - interface HTMLIonlabSidebarElement extends Components.IonlabSidebar, HTMLStencilElement {} - var HTMLIonlabSidebarElement: { - prototype: HTMLIonlabSidebarElement; - new (): HTMLIonlabSidebarElement; - }; - - interface HTMLSkFadingCircleElement extends Components.SkFadingCircle, HTMLStencilElement {} - var HTMLSkFadingCircleElement: { - prototype: HTMLSkFadingCircleElement; - new (): HTMLSkFadingCircleElement; - }; - interface HTMLElementTagNameMap { - 'ionlab-app': HTMLIonlabAppElement; - 'ionlab-device-frame': HTMLIonlabDeviceFrameElement; - 'ionlab-platform-dropdown': HTMLIonlabPlatformDropdownElement; - 'ionlab-preview': HTMLIonlabPreviewElement; - 'ionlab-sidebar': HTMLIonlabSidebarElement; - 'sk-fading-circle': HTMLSkFadingCircleElement; - } -} - -declare namespace LocalJSX { - interface IonlabApp {} - interface IonlabDeviceFrame { - 'icon'?: string; - 'platform'?: string; - 'platformName'?: string; - 'url'?: string; - } - interface IonlabPlatformDropdown { - 'activePlatforms'?: string[]; - 'onIonlabPlatformToggled'?: (event: CustomEvent) => void; - } - interface IonlabPreview { - 'activeDevices'?: string[]; - 'projectType'?: string; - 'url'?: string; - } - interface IonlabSidebar { - 'onIonlabSidebarCloseClicked'?: (event: CustomEvent) => void; - 'visible'?: boolean; - } - interface SkFadingCircle {} - - interface IntrinsicElements { - 'ionlab-app': IonlabApp; - 'ionlab-device-frame': IonlabDeviceFrame; - 'ionlab-platform-dropdown': IonlabPlatformDropdown; - 'ionlab-preview': IonlabPreview; - 'ionlab-sidebar': IonlabSidebar; - 'sk-fading-circle': SkFadingCircle; - } -} - -export { LocalJSX as JSX }; - - -declare module "@stencil/core" { - export namespace JSX { - interface IntrinsicElements { - 'ionlab-app': LocalJSX.IonlabApp & JSXBase.HTMLAttributes; - 'ionlab-device-frame': LocalJSX.IonlabDeviceFrame & JSXBase.HTMLAttributes; - 'ionlab-platform-dropdown': LocalJSX.IonlabPlatformDropdown & JSXBase.HTMLAttributes; - 'ionlab-preview': LocalJSX.IonlabPreview & JSXBase.HTMLAttributes; - 'ionlab-sidebar': LocalJSX.IonlabSidebar & JSXBase.HTMLAttributes; - 'sk-fading-circle': LocalJSX.SkFadingCircle & JSXBase.HTMLAttributes; - } - } -} - - diff --git a/packages/@ionic/lab/src/stencil/components/ionlab-app/ionlab-app.tsx b/packages/@ionic/lab/src/stencil/components/ionlab-app/ionlab-app.tsx deleted file mode 100644 index 1a338a05ff..0000000000 --- a/packages/@ionic/lab/src/stencil/components/ionlab-app/ionlab-app.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { Component, Listen, State, h } from '@stencil/core'; - -import { PLATFORM_IOS, PLATFORM_ANDROID } from '../../utils'; - -@Component({ - tag: 'ionlab-app', -}) -export class App { - @State() sidebarVisible: boolean = true; - @State() activeDevices: string[] = [PLATFORM_IOS, PLATFORM_ANDROID]; - @State() details: { url?: string; name?: string; version?: string; projectType?: string; } = {}; - session: string; - - componentWillLoad() { - this.loadAppDetails(); - this.loadLocalStorageState(); - } - - componentWillUpdate() { - this.saveLocalStorageState(); - } - - loadAppDetails() { - const self = this; - const req = new XMLHttpRequest(); - - req.addEventListener('load', function() { - try { - self.details = JSON.parse(this.responseText); - } catch (e) { - console.error('Error loading app details from Ionic Lab API!'); - console.error('Response was:', this.responseText); - } - }); - - req.addEventListener('error', (err) => { - console.error('Error loading app details from Ionic Lab API!'); - console.error('Error was:', err); - }); - - req.open('GET', '/api/app'); - req.send(); - } - - loadLocalStorageState() { - const storedPlatforms = localStorage.getItem('ionic-lab-platforms'); - - if (storedPlatforms) { - this.activeDevices = JSON.parse(storedPlatforms); - } - - const storedSidebarOpen = localStorage.getItem('ionic-lab-sidebar-open'); - - if (storedSidebarOpen) { - this.sidebarVisible = JSON.parse(storedSidebarOpen); - } - } - - saveLocalStorageState() { - localStorage.setItem('ionic-lab-platforms', JSON.stringify(this.activeDevices)); - localStorage.setItem('ionic-lab-sidebar-open', JSON.stringify(this.sidebarVisible)); - } - - @Listen('ionlabSidebarCloseClicked') - sidebarCloseClickedHander(event) { - this.sidebarVisible = false; - } - - @Listen('ionlabPlatformToggled') - ionlabPlatformToggledHandler(event) { - this.togglePlatform(event.detail); - } - - togglePlatform(platform: string) { - const idx = this.activeDevices.indexOf(platform); - const devices = [...this.activeDevices]; - - if (idx >= 0) { - devices.splice(idx, 1); - } else { - devices.push(platform); - } - - this.activeDevices = devices; - } - - render() { - return [ -
-
- this.sidebarVisible = !this.sidebarVisible } /> - -
- -
, -
- - -
, - - ]; - } -} diff --git a/packages/@ionic/lab/src/stencil/components/ionlab-device-frame/ionlab-device-frame.css b/packages/@ionic/lab/src/stencil/components/ionlab-device-frame/ionlab-device-frame.css deleted file mode 100644 index ddf171607e..0000000000 --- a/packages/@ionic/lab/src/stencil/components/ionlab-device-frame/ionlab-device-frame.css +++ /dev/null @@ -1,146 +0,0 @@ -ionlab-device-frame { - display: block; - margin: 0 20px; - vertical-align: middle; -} - -ionlab-device-frame h2 { - font-size: var(--font-size-base); - margin: 1em 0; - vertical-align: middle; - text-align: left; - font-weight: var(--font-weight-bold); - vertical-align: top; - color: #727a87; -} - -ionlab-device-frame h2 .icon { - display: inline-block; - margin-right: 7px; - font-size: var(--font-size-lg); -} - -ionlab-device-frame .frame-container { - position: relative; - width: var(--frame-width); - height: var(--frame-height); - border-radius: 3px; - box-shadow: 0 8px 24px rgba(0,0,0,.12), 0 2px 6px rgba(0,0,0,.16); - overflow: hidden; - - margin: 0; - - background-size: contain; - background-repeat: no-repeat; - - z-index: 1; -} - -ionlab-device-frame .frame-container .statusbar { - position: absolute; - top: 0; - width: var(--frame-width); - height: 20px; - background-size: 100%; - background-repeat: no-repeat; - background-position: center; - border-radius: 3px 3px 0 0; -} - -ionlab-device-frame .frame-container sk-fading-circle, -ionlab-device-frame .frame-container .load-error { - position: absolute; - top: 50%; - left: 50%; - -webkit-transform: translate(-50%, -50%); - -ms-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); -} - -ionlab-device-frame .frame-container .load-error { - background: var(--section-bg); - padding: 10px 20px; -} - -ionlab-device-frame .frame-container .load-error p { - margin: 0; -} - -ionlab-device-frame .frame-container iframe { - background: #000; - border: none; - width: 100%; - height: 100%; - - position: absolute; - - top: 0; - left: 0; - - margin: var(--device-frame-width); - - width: calc(100% - var(--device-frame-width) * 2); - height: calc(100% - var(--device-frame-width) * 2); - - border: none; - - overflow: hidden; - - -webkit-mask-image: -webkit-radial-gradient(white, black); - - z-index: 1; -} - -.statusbar-ios, -.statusbar-md { - display: none; -} - -#device-ios .statusbar-ios { - display: block; - fill: #090a0d; - left: 50%; - position: absolute; - top: 12px; - transform: translateX(-50%); - width: 165px; - z-index: 2; -} - -#device-android .statusbar-md { - display: block; - fill: hsla(0,0%,49%,.3); - padding: .5rem 2.2rem; - position: relative; - width: 100%; - z-index: 2; - top: 12px; -} - -#device-ios .frame-container { - background-image: url('/assets/android-device-skin.png'); - border-radius: 44px; -} - -#device-ios .frame-container iframe { - border-radius: 32px; -} - -#device-android .frame-container { - background-image: url('/assets/iphone-device-skin.png'); - border-radius: 54px; -} - -#device-android .frame-container iframe { - border-radius: 38px; -} - -@media only screen and (max-height: 850px) { - ionlab-device-frame h2 { - display: none; - } - - #device-ios .statusbar-ios { - top: 10px; - } -} diff --git a/packages/@ionic/lab/src/stencil/components/ionlab-device-frame/ionlab-device-frame.tsx b/packages/@ionic/lab/src/stencil/components/ionlab-device-frame/ionlab-device-frame.tsx deleted file mode 100644 index 4704ec4232..0000000000 --- a/packages/@ionic/lab/src/stencil/components/ionlab-device-frame/ionlab-device-frame.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Component, Prop, State, h } from '@stencil/core'; - -@Component({ - tag: 'ionlab-device-frame', - styleUrl: 'ionlab-device-frame.css', -}) -export class DeviceFrame { - @Prop() platform: string; - @Prop() platformName: string; - @Prop() url: string; - @Prop() icon: string; - @State() error: boolean = false; - @State() loaded: boolean = false; - - interval: number; - - componentDidLoad() { - this.interval = window.setInterval(() => { - this.error = this.url ? false : true; - }, 20000); - } - - loadedHandler(event) { - this.loaded = true; - this.error = false; - window.clearInterval(this.interval); - } - - hostData() { - return { - id: `device-${this.platform}`, - }; - } - - render() { - return [ -

{ this.platformName }

, -
-
- - - - - - - - -
- { !this.loaded && !this.error ? : null } - { this.error ?

Load Timeout

Still trying...

: null } -