From 744c9a2df1d7e6fee94f2a6a49f471e8e83b69d7 Mon Sep 17 00:00:00 2001 From: William Riley Date: Tue, 13 Jul 2021 16:23:35 -0500 Subject: [PATCH 01/16] feat(cli): [STENCIL-33] Writing and Reading the Ionic config file --- src/cli/ionic-config.ts | 45 ++++++++++++++++++++++++++++++++++++ src/cli/telemetry/helpers.ts | 25 ++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/cli/ionic-config.ts create mode 100644 src/cli/telemetry/helpers.ts diff --git a/src/cli/ionic-config.ts b/src/cli/ionic-config.ts new file mode 100644 index 00000000000..4a38b518131 --- /dev/null +++ b/src/cli/ionic-config.ts @@ -0,0 +1,45 @@ +import { readJSON, writeJSON, mkdirp } from '@ionic/utils-fs'; + +import * as os from 'os'; +import * as path from 'path'; + +import { uuidv4 } from './telemetry/helpers'; + +export const CONFIG_FILE = 'config.json'; +export const DEFAULT_CONFIG_DIRECTORY = path.resolve(os.homedir(), '.ionic', CONFIG_FILE); + +export interface SystemConfig { + "telemetry"?: boolean, + "telemetry.stencil"?: boolean, + "tokens.telemetry"?: string, +} + +export async function readConfig(): Promise { + try { + return await readJSON(DEFAULT_CONFIG_DIRECTORY); + } catch (e) { + if (e.code !== 'ENOENT') { + throw e; + } + + const config: SystemConfig = { + "tokens.telemetry": uuidv4(), + "telemetry": true, + "telemetry.stencil": true, + }; + + await writeConfig(config); + + return config; + } +} + +export async function writeConfig(config: SystemConfig): Promise { + await mkdirp(path.dirname(DEFAULT_CONFIG_DIRECTORY)); + await writeJSON(DEFAULT_CONFIG_DIRECTORY, config, { spaces: '\t' }); +} + +export async function updateConfig(newOptions: SystemConfig): Promise { + const config = await readConfig(); + await writeConfig(Object.assign(config, newOptions)); +} \ No newline at end of file diff --git a/src/cli/telemetry/helpers.ts b/src/cli/telemetry/helpers.ts new file mode 100644 index 00000000000..a81ccc5a5b7 --- /dev/null +++ b/src/cli/telemetry/helpers.ts @@ -0,0 +1,25 @@ +import { TERMINAL_INFO } from '@ionic/utils-terminal'; + +export const tryFn = async Promise, R>( + fn: T, + ...args: any[] +): Promise => { + try { + return await fn(...args); + } catch { + // ignore + } + + return null; +}; + +export const isInteractive = (): boolean => TERMINAL_INFO.tty && !TERMINAL_INFO.ci; + +export function uuidv4(): string { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { + const r = (Math.random() * 16) | 0; + const v = c == 'x' ? r : (r & 0x3) | 0x8; + + return v.toString(16); + }); +} \ No newline at end of file From 95b1c912c90df2cd185897eaabf8a55d144a0d03 Mon Sep 17 00:00:00 2001 From: William Riley Date: Wed, 14 Jul 2021 12:18:05 -0500 Subject: [PATCH 02/16] Adding feedback from Ryan and Lars --- package-lock.json | 2 +- src/cli/ionic-config.ts | 27 ++++++------ src/cli/telemetry/helpers.ts | 22 +++++++++- src/cli/telemetry/test/helpers.spec.ts | 32 ++++++++++++++ src/cli/test/ionic-config.spec.ts | 58 ++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 src/cli/telemetry/test/helpers.spec.ts create mode 100644 src/cli/test/ionic-config.spec.ts diff --git a/package-lock.json b/package-lock.json index 44eb6ea412b..30d9e8bcd4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@stencil/core", - "version": "2.6.0", + "version": "2.7.0-0", "license": "MIT", "bin": { "stencil": "bin/stencil" diff --git a/src/cli/ionic-config.ts b/src/cli/ionic-config.ts index 4a38b518131..b00060f8412 100644 --- a/src/cli/ionic-config.ts +++ b/src/cli/ionic-config.ts @@ -1,30 +1,28 @@ -import { readJSON, writeJSON, mkdirp } from '@ionic/utils-fs'; - +import fs from 'fs-extra'; import * as os from 'os'; import * as path from 'path'; import { uuidv4 } from './telemetry/helpers'; -export const CONFIG_FILE = 'config.json'; +const CONFIG_FILE = 'config.json'; export const DEFAULT_CONFIG_DIRECTORY = path.resolve(os.homedir(), '.ionic', CONFIG_FILE); -export interface SystemConfig { - "telemetry"?: boolean, +export interface TelemetryConfig { "telemetry.stencil"?: boolean, "tokens.telemetry"?: string, } -export async function readConfig(): Promise { +export async function readConfig(): Promise { try { - return await readJSON(DEFAULT_CONFIG_DIRECTORY); + + return await fs.readJson(DEFAULT_CONFIG_DIRECTORY); } catch (e) { if (e.code !== 'ENOENT') { throw e; } - const config: SystemConfig = { + const config: TelemetryConfig = { "tokens.telemetry": uuidv4(), - "telemetry": true, "telemetry.stencil": true, }; @@ -34,12 +32,15 @@ export async function readConfig(): Promise { } } -export async function writeConfig(config: SystemConfig): Promise { - await mkdirp(path.dirname(DEFAULT_CONFIG_DIRECTORY)); - await writeJSON(DEFAULT_CONFIG_DIRECTORY, config, { spaces: '\t' }); +export async function writeConfig(config: TelemetryConfig): Promise { + await fs.mkdirp(path.dirname(DEFAULT_CONFIG_DIRECTORY)).then(async () => { + return await fs.writeJSON(DEFAULT_CONFIG_DIRECTORY, config, { spaces: '\t' }); + }).catch(() => { + console.debug(`Couldn't write to ${DEFAULT_CONFIG_DIRECTORY}. `) + }); } -export async function updateConfig(newOptions: SystemConfig): Promise { +export async function updateConfig(newOptions: TelemetryConfig): Promise { const config = await readConfig(); await writeConfig(Object.assign(config, newOptions)); } \ No newline at end of file diff --git a/src/cli/telemetry/helpers.ts b/src/cli/telemetry/helpers.ts index a81ccc5a5b7..1aa685f7374 100644 --- a/src/cli/telemetry/helpers.ts +++ b/src/cli/telemetry/helpers.ts @@ -1,4 +1,23 @@ -import { TERMINAL_INFO } from '@ionic/utils-terminal'; +interface TerminalInfo { + /** + * Whether this is in CI or not. + */ + readonly ci: boolean; + /** + * Path to the user's shell program. + */ + readonly shell: string; + /** + * Whether the terminal is an interactive TTY or not. + */ + readonly tty: boolean; + /** + * Whether this is a Windows shell or not. + */ + readonly windows: boolean; +} + +declare const TERMINAL_INFO: TerminalInfo; export const tryFn = async Promise, R>( fn: T, @@ -15,6 +34,7 @@ export const tryFn = async Promise, R>( export const isInteractive = (): boolean => TERMINAL_INFO.tty && !TERMINAL_INFO.ci; +// Plucked from https://github.com/ionic-team/capacitor/blob/b893a57aaaf3a16e13db9c33037a12f1a5ac92e0/cli/src/util/uuid.ts export function uuidv4(): string { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { const r = (Math.random() * 16) | 0; diff --git a/src/cli/telemetry/test/helpers.spec.ts b/src/cli/telemetry/test/helpers.spec.ts new file mode 100644 index 00000000000..1a25af44297 --- /dev/null +++ b/src/cli/telemetry/test/helpers.spec.ts @@ -0,0 +1,32 @@ +import { tryFn, uuidv4 } from '../helpers'; + +describe('uuidv4', () => { + + it('should output a UUID', async () => { + const pattern = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i); + const uuid = uuidv4() + expect(typeof uuid).toBe("string") + expect(!!uuid.match(pattern)).toBe(true) + }); + +}); + +describe('tryFn', () => { + + it('should handle failures correctly', async () => { + const result = await tryFn(async () => { + throw new Error("Uh oh!") + }); + + expect(result).toBe(null) + }); + + it('should handle success correctly', async () => { + const result = await tryFn(async () => { + return true + }) + + expect(result).toBe(true) + }); + +}); diff --git a/src/cli/test/ionic-config.spec.ts b/src/cli/test/ionic-config.spec.ts new file mode 100644 index 00000000000..d4f8ec1f405 --- /dev/null +++ b/src/cli/test/ionic-config.spec.ts @@ -0,0 +1,58 @@ +import fs from 'fs-extra'; +import { readConfig, writeConfig, updateConfig, DEFAULT_CONFIG_DIRECTORY } from '../ionic-config'; + +describe('readConfig', () => { + + it('should create a file if it does not exist', async () => { + if (await fs.pathExists(DEFAULT_CONFIG_DIRECTORY)) { + fs.rmSync(DEFAULT_CONFIG_DIRECTORY); + } + + expect(await fs.pathExists(DEFAULT_CONFIG_DIRECTORY)).toBe(false) + + const config = await readConfig(); + + expect(typeof config).toBe("object"); + expect(Object.keys(config).join()).toBe("tokens.telemetry,telemetry.stencil"); + }); + + it('should read a file if it exists', async () => { + await writeConfig({"telemetry.stencil": true, "tokens.telemetry": "12345"}) + + expect(await fs.pathExists(DEFAULT_CONFIG_DIRECTORY)).toBe(true) + + const config = await readConfig(); + + expect(typeof config).toBe("object"); + expect(Object.keys(config).join()).toBe("telemetry.stencil,tokens.telemetry"); + expect(config['telemetry.stencil']).toBe(true); + expect(config['tokens.telemetry']).toBe("12345"); + }); +}); + +describe('updateConfig', () => { + + it('should edit a file', async () => { + await writeConfig({ "telemetry.stencil": true, "tokens.telemetry": "12345" }) + + expect(await fs.pathExists(DEFAULT_CONFIG_DIRECTORY)).toBe(true) + + const configPre = await readConfig(); + + expect(typeof configPre).toBe("object"); + expect(Object.keys(configPre).join()).toBe("telemetry.stencil,tokens.telemetry"); + expect(configPre['telemetry.stencil']).toBe(true); + expect(configPre['tokens.telemetry']).toBe("12345"); + + await updateConfig({ "telemetry.stencil": false, "tokens.telemetry": "67890" }); + + const configPost = await readConfig(); + + expect(typeof configPost).toBe("object"); + // Should keep the previous order + expect(Object.keys(configPost).join()).toBe("telemetry.stencil,tokens.telemetry"); + expect(configPost['telemetry.stencil']).toBe(false); + expect(configPost['tokens.telemetry']).toBe("67890"); + }); + +}); From 28706453d502b8942b3a6832fbead9bbe7485821 Mon Sep 17 00:00:00 2001 From: "William M. Riley" Date: Thu, 15 Jul 2021 09:42:10 -0500 Subject: [PATCH 03/16] Update src/cli/ionic-config.ts Co-authored-by: Ryan Waskiewicz --- src/cli/ionic-config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/ionic-config.ts b/src/cli/ionic-config.ts index b00060f8412..a41b104da14 100644 --- a/src/cli/ionic-config.ts +++ b/src/cli/ionic-config.ts @@ -36,11 +36,11 @@ export async function writeConfig(config: TelemetryConfig): Promise { await fs.mkdirp(path.dirname(DEFAULT_CONFIG_DIRECTORY)).then(async () => { return await fs.writeJSON(DEFAULT_CONFIG_DIRECTORY, config, { spaces: '\t' }); }).catch(() => { - console.debug(`Couldn't write to ${DEFAULT_CONFIG_DIRECTORY}. `) + console.error(`Stencil Telemetry: couldn't write configuration file to ${DEFAULT_CONFIG_DIRECTORY} - ${error}.`) }); } export async function updateConfig(newOptions: TelemetryConfig): Promise { const config = await readConfig(); await writeConfig(Object.assign(config, newOptions)); -} \ No newline at end of file +} From f9c26955af2e53d8dfad69d067241b5c80a57050 Mon Sep 17 00:00:00 2001 From: "William M. Riley" Date: Thu, 15 Jul 2021 10:26:15 -0500 Subject: [PATCH 04/16] Update src/cli/telemetry/test/helpers.spec.ts Co-authored-by: Ryan Waskiewicz --- src/cli/telemetry/test/helpers.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cli/telemetry/test/helpers.spec.ts b/src/cli/telemetry/test/helpers.spec.ts index 1a25af44297..bcd6c955d24 100644 --- a/src/cli/telemetry/test/helpers.spec.ts +++ b/src/cli/telemetry/test/helpers.spec.ts @@ -28,5 +28,11 @@ describe('tryFn', () => { expect(result).toBe(true) }); + it('handles returning false correctly', async () => { + const result = await tryFn(async () => { + return false; + }) + expect(result).toBe(false); + }); }); From 552241949a93b1bf31a26673558bb90733ca5e59 Mon Sep 17 00:00:00 2001 From: William Riley Date: Thu, 15 Jul 2021 13:11:16 -0500 Subject: [PATCH 05/16] Incorporating feedback from Ryan --- src/cli/ionic-config.ts | 9 +++++---- src/cli/telemetry/test/helpers.spec.ts | 1 - src/cli/test/ionic-config.spec.ts | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/cli/ionic-config.ts b/src/cli/ionic-config.ts index a41b104da14..9dd4a3b2d05 100644 --- a/src/cli/ionic-config.ts +++ b/src/cli/ionic-config.ts @@ -33,11 +33,12 @@ export async function readConfig(): Promise { } export async function writeConfig(config: TelemetryConfig): Promise { - await fs.mkdirp(path.dirname(DEFAULT_CONFIG_DIRECTORY)).then(async () => { - return await fs.writeJSON(DEFAULT_CONFIG_DIRECTORY, config, { spaces: '\t' }); - }).catch(() => { + try { + await fs.mkdirp(path.dirname(DEFAULT_CONFIG_DIRECTORY)) + await fs.writeJSON(DEFAULT_CONFIG_DIRECTORY, config, { spaces: '\t' }); + } catch (error) { console.error(`Stencil Telemetry: couldn't write configuration file to ${DEFAULT_CONFIG_DIRECTORY} - ${error}.`) - }); + }; } export async function updateConfig(newOptions: TelemetryConfig): Promise { diff --git a/src/cli/telemetry/test/helpers.spec.ts b/src/cli/telemetry/test/helpers.spec.ts index bcd6c955d24..8b97f9e466f 100644 --- a/src/cli/telemetry/test/helpers.spec.ts +++ b/src/cli/telemetry/test/helpers.spec.ts @@ -5,7 +5,6 @@ describe('uuidv4', () => { it('should output a UUID', async () => { const pattern = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i); const uuid = uuidv4() - expect(typeof uuid).toBe("string") expect(!!uuid.match(pattern)).toBe(true) }); diff --git a/src/cli/test/ionic-config.spec.ts b/src/cli/test/ionic-config.spec.ts index d4f8ec1f405..11b83e4d04f 100644 --- a/src/cli/test/ionic-config.spec.ts +++ b/src/cli/test/ionic-config.spec.ts @@ -12,7 +12,6 @@ describe('readConfig', () => { const config = await readConfig(); - expect(typeof config).toBe("object"); expect(Object.keys(config).join()).toBe("tokens.telemetry,telemetry.stencil"); }); @@ -23,7 +22,6 @@ describe('readConfig', () => { const config = await readConfig(); - expect(typeof config).toBe("object"); expect(Object.keys(config).join()).toBe("telemetry.stencil,tokens.telemetry"); expect(config['telemetry.stencil']).toBe(true); expect(config['tokens.telemetry']).toBe("12345"); From 74b8c402955fa112df23dfe7d85f5b366a072d85 Mon Sep 17 00:00:00 2001 From: William Riley Date: Thu, 15 Jul 2021 13:31:29 -0500 Subject: [PATCH 06/16] Continue to remove external Deps --- src/cli/ionic-config.ts | 11 +++++------ src/cli/telemetry/helpers.ts | 7 +++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/cli/ionic-config.ts b/src/cli/ionic-config.ts index 9dd4a3b2d05..5b230b75313 100644 --- a/src/cli/ionic-config.ts +++ b/src/cli/ionic-config.ts @@ -1,8 +1,8 @@ -import fs from 'fs-extra'; +import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { uuidv4 } from './telemetry/helpers'; +import { readJson, uuidv4 } from './telemetry/helpers'; const CONFIG_FILE = 'config.json'; export const DEFAULT_CONFIG_DIRECTORY = path.resolve(os.homedir(), '.ionic', CONFIG_FILE); @@ -14,8 +14,7 @@ export interface TelemetryConfig { export async function readConfig(): Promise { try { - - return await fs.readJson(DEFAULT_CONFIG_DIRECTORY); + return await readJson(DEFAULT_CONFIG_DIRECTORY); } catch (e) { if (e.code !== 'ENOENT') { throw e; @@ -34,8 +33,8 @@ export async function readConfig(): Promise { export async function writeConfig(config: TelemetryConfig): Promise { try { - await fs.mkdirp(path.dirname(DEFAULT_CONFIG_DIRECTORY)) - await fs.writeJSON(DEFAULT_CONFIG_DIRECTORY, config, { spaces: '\t' }); + await fs.promises.mkdir(path.dirname(DEFAULT_CONFIG_DIRECTORY), { recursive: true }); + await fs.promises.writeFile(DEFAULT_CONFIG_DIRECTORY, JSON.stringify(config)) } catch (error) { console.error(`Stencil Telemetry: couldn't write configuration file to ${DEFAULT_CONFIG_DIRECTORY} - ${error}.`) }; diff --git a/src/cli/telemetry/helpers.ts b/src/cli/telemetry/helpers.ts index 1aa685f7374..38ad3383bed 100644 --- a/src/cli/telemetry/helpers.ts +++ b/src/cli/telemetry/helpers.ts @@ -1,3 +1,5 @@ +import fs from "fs"; + interface TerminalInfo { /** * Whether this is in CI or not. @@ -42,4 +44,9 @@ export function uuidv4(): string { return v.toString(16); }); +} + +export async function readJson(path) { + const rawdata = await fs.readFileSync(path); + return JSON.parse(rawdata.toString()); } \ No newline at end of file From 614b0e918bfedd38cef0e2b5ee666c450786bddd Mon Sep 17 00:00:00 2001 From: William Riley Date: Thu, 15 Jul 2021 13:37:56 -0500 Subject: [PATCH 07/16] Fixing missed type --- src/cli/telemetry/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/telemetry/helpers.ts b/src/cli/telemetry/helpers.ts index 38ad3383bed..4fc7500e6b7 100644 --- a/src/cli/telemetry/helpers.ts +++ b/src/cli/telemetry/helpers.ts @@ -46,7 +46,7 @@ export function uuidv4(): string { }); } -export async function readJson(path) { +export async function readJson(path: string) { const rawdata = await fs.readFileSync(path); return JSON.parse(rawdata.toString()); } \ No newline at end of file From c2271df538594211508750004270270f30b3d9f9 Mon Sep 17 00:00:00 2001 From: "William M. Riley" Date: Thu, 15 Jul 2021 14:14:19 -0500 Subject: [PATCH 08/16] Update src/cli/telemetry/test/helpers.spec.ts Co-authored-by: Ryan Waskiewicz --- src/cli/telemetry/test/helpers.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cli/telemetry/test/helpers.spec.ts b/src/cli/telemetry/test/helpers.spec.ts index 8b97f9e466f..d6dd2dec041 100644 --- a/src/cli/telemetry/test/helpers.spec.ts +++ b/src/cli/telemetry/test/helpers.spec.ts @@ -27,6 +27,7 @@ describe('tryFn', () => { expect(result).toBe(true) }); + it('handles returning false correctly', async () => { const result = await tryFn(async () => { return false; From 866151c41d34e1565a1895c30f2cbffb28317c2a Mon Sep 17 00:00:00 2001 From: "William M. Riley" Date: Thu, 15 Jul 2021 14:14:56 -0500 Subject: [PATCH 09/16] Update src/cli/telemetry/helpers.ts Co-authored-by: Lars Mikkelsen --- src/cli/telemetry/helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/telemetry/helpers.ts b/src/cli/telemetry/helpers.ts index 4fc7500e6b7..ca8ededeee4 100644 --- a/src/cli/telemetry/helpers.ts +++ b/src/cli/telemetry/helpers.ts @@ -47,6 +47,6 @@ export function uuidv4(): string { } export async function readJson(path: string) { - const rawdata = await fs.readFileSync(path); + const rawdata = await fs.promises.readFileSync(path); return JSON.parse(rawdata.toString()); -} \ No newline at end of file +} From 71bbea0a619c76829cfdb41ef0678b41d9787174 Mon Sep 17 00:00:00 2001 From: William Riley Date: Thu, 15 Jul 2021 14:46:03 -0500 Subject: [PATCH 10/16] Adding tests for isInteractive --- src/cli/telemetry/helpers.ts | 4 ++-- src/cli/telemetry/test/helpers.spec.ts | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/cli/telemetry/helpers.ts b/src/cli/telemetry/helpers.ts index ca8ededeee4..10aa9425f23 100644 --- a/src/cli/telemetry/helpers.ts +++ b/src/cli/telemetry/helpers.ts @@ -19,7 +19,7 @@ interface TerminalInfo { readonly windows: boolean; } -declare const TERMINAL_INFO: TerminalInfo; +export declare const TERMINAL_INFO: TerminalInfo; export const tryFn = async Promise, R>( fn: T, @@ -47,6 +47,6 @@ export function uuidv4(): string { } export async function readJson(path: string) { - const rawdata = await fs.promises.readFileSync(path); + const rawdata = await fs.promises.readFile(path); return JSON.parse(rawdata.toString()); } diff --git a/src/cli/telemetry/test/helpers.spec.ts b/src/cli/telemetry/test/helpers.spec.ts index d6dd2dec041..a8c6c873f8e 100644 --- a/src/cli/telemetry/test/helpers.spec.ts +++ b/src/cli/telemetry/test/helpers.spec.ts @@ -1,4 +1,4 @@ -import { tryFn, uuidv4 } from '../helpers'; +import { isInteractive, TERMINAL_INFO, tryFn, uuidv4 } from '../helpers'; describe('uuidv4', () => { @@ -10,6 +10,27 @@ describe('uuidv4', () => { }); +describe('isInteractive', () => { + it('should return false when tty is false', async () => { + TERMINAL_INFO = { ci: true, shell: true, tty: false, windows: true }; + const result = isInteractive(); + expect(result).toBe(false) + }); + + it('should return false when ci is true', async () => { + TERMINAL_INFO = { ci: true, shell: true, tty: true, windows: true }; + const result = isInteractive(); + expect(result).toBe(false) + }); + + it('should return true when tty is true and ci is false', async () => { + TERMINAL_INFO = { ci: false, shell: true, tty: true, windows: true }; + const result = isInteractive(); + expect(result).toBe(true) + }); +}); + + describe('tryFn', () => { it('should handle failures correctly', async () => { From 2dd693fe410e04d1c1fa95d60b0e455441992067 Mon Sep 17 00:00:00 2001 From: William Riley Date: Fri, 16 Jul 2021 16:48:08 -0500 Subject: [PATCH 11/16] Switching to internal sys calls instead of using packages. Adding singleton to help facilitate accessing features during the CLI's lifecycle. --- src/cli/ionic-config.ts | 29 ++--- src/cli/run.ts | 12 ++ src/cli/state/stencil-cli-config.ts | 107 ++++++++++++++++++ src/cli/state/test/stencil-cli-config.spec.ts | 44 +++++++ src/cli/telemetry/helpers.ts | 6 +- src/cli/test/ionic-config.spec.ts | 35 ++++-- src/compiler/sys/stencil-sys.ts | 6 + src/compiler/sys/tests/stencil-sys.spec.ts | 5 + src/declarations/stencil-public-compiler.ts | 1 + src/sys/deno/deno-sys.ts | 3 + src/sys/node/node-sys.ts | 7 ++ src/testing/testing-sys.ts | 1 + 12 files changed, 229 insertions(+), 27 deletions(-) create mode 100644 src/cli/state/stencil-cli-config.ts create mode 100644 src/cli/state/test/stencil-cli-config.spec.ts diff --git a/src/cli/ionic-config.ts b/src/cli/ionic-config.ts index 5b230b75313..8a2d1ff66fd 100644 --- a/src/cli/ionic-config.ts +++ b/src/cli/ionic-config.ts @@ -1,11 +1,10 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; - +import { getCompilerSystem } from './state/stencil-cli-config'; import { readJson, uuidv4 } from './telemetry/helpers'; const CONFIG_FILE = 'config.json'; -export const DEFAULT_CONFIG_DIRECTORY = path.resolve(os.homedir(), '.ionic', CONFIG_FILE); +export const DEFAULT_CONFIG_DIRECTORY = (file: boolean = false) => { + return getCompilerSystem().resolvePath(`${getCompilerSystem().homeDir()}/.ionic${file ? "/" + CONFIG_FILE : ""}`); +} export interface TelemetryConfig { "telemetry.stencil"?: boolean, @@ -13,30 +12,26 @@ export interface TelemetryConfig { } export async function readConfig(): Promise { - try { - return await readJson(DEFAULT_CONFIG_DIRECTORY); - } catch (e) { - if (e.code !== 'ENOENT') { - throw e; - } + let config: TelemetryConfig = await readJson(DEFAULT_CONFIG_DIRECTORY(true)); - const config: TelemetryConfig = { + if (!config) { + config = { "tokens.telemetry": uuidv4(), "telemetry.stencil": true, }; await writeConfig(config); - - return config; } + + return config; } export async function writeConfig(config: TelemetryConfig): Promise { try { - await fs.promises.mkdir(path.dirname(DEFAULT_CONFIG_DIRECTORY), { recursive: true }); - await fs.promises.writeFile(DEFAULT_CONFIG_DIRECTORY, JSON.stringify(config)) + await getCompilerSystem().createDir(DEFAULT_CONFIG_DIRECTORY(), { recursive: true }); + await getCompilerSystem().writeFile(DEFAULT_CONFIG_DIRECTORY(true), JSON.stringify(config)) } catch (error) { - console.error(`Stencil Telemetry: couldn't write configuration file to ${DEFAULT_CONFIG_DIRECTORY} - ${error}.`) + console.error(`Stencil Telemetry: couldn't write configuration file to ${DEFAULT_CONFIG_DIRECTORY(true)} - ${error}.`) }; } diff --git a/src/cli/run.ts b/src/cli/run.ts index 530e38adc7d..6c18ca16746 100644 --- a/src/cli/run.ts +++ b/src/cli/run.ts @@ -13,10 +13,14 @@ import { taskInfo } from './task-info'; import { taskPrerender } from './task-prerender'; import { taskServe } from './task-serve'; import { taskTest } from './task-test'; +import { initializeStencilCLIConfig } from './state/stencil-cli-config'; export const run = async (init: CliInitOptions) => { const { args, logger, sys } = init; + // Initialize the singleton so we can use this throughout the lifecycle of the CLI. + const stencilCLIConfig = initializeStencilCLIConfig({ args, logger, sys}); + try { const flags = parseFlags(args, sys); const task = flags.task; @@ -32,6 +36,12 @@ export const run = async (init: CliInitOptions) => { sys.applyGlobalPatch(sys.getCurrentDirectory()); } + // Update singleton with modifications + stencilCLIConfig.logger = logger; + stencilCLIConfig.task = task; + stencilCLIConfig.sys = sys; + stencilCLIConfig.flags = flags; + if (task === 'help' || flags.help) { taskHelp(sys, logger); return; @@ -50,12 +60,14 @@ export const run = async (init: CliInitOptions) => { logger, dependencies: dependencies as any, }); + if (hasError(ensureDepsResults.diagnostics)) { logger.printDiagnostics(ensureDepsResults.diagnostics); return sys.exit(1); } const coreCompiler = await loadCoreCompiler(sys); + stencilCLIConfig.coreCompiler = coreCompiler; if (task === 'version' || flags.version) { console.log(coreCompiler.version); diff --git a/src/cli/state/stencil-cli-config.ts b/src/cli/state/stencil-cli-config.ts new file mode 100644 index 00000000000..b3fc9529cbe --- /dev/null +++ b/src/cli/state/stencil-cli-config.ts @@ -0,0 +1,107 @@ +import type { Logger, CompilerSystem, ConfigFlags } from "../../declarations"; +export type CoreCompiler = typeof import('@stencil/core/compiler'); + +export interface StencilCLIConfigArgs { + task?: string; + args: string[]; + logger: Logger; + sys: CompilerSystem; + flags?: ConfigFlags; + coreCompiler?: CoreCompiler +} + +export default class StencilCLIConfig { + + static instance: StencilCLIConfig; + + private _args: string[]; + private _logger: Logger; + private _sys: CompilerSystem; + private _flags: ConfigFlags; + private _task: string; + private _coreCompiler: CoreCompiler; + + private constructor (options: StencilCLIConfigArgs) { + this._args = options?.args || []; + this._logger = options?.logger; + this._sys = options?.sys; + } + + public static getInstance(options?: StencilCLIConfigArgs): StencilCLIConfig { + if (!StencilCLIConfig.instance && !!options) { + StencilCLIConfig.instance = new StencilCLIConfig(options); + } + + return StencilCLIConfig.instance; + } + + public get logger() { + return this._logger + } + public set logger(logger: Logger) { + this._logger = logger; + } + + + public get sys() { + return this._sys + } + public set sys(sys: CompilerSystem) { + this._sys = sys; + } + + + public get args() { + return this._args + } + public set args(args: string[]) { + this._args = args; + } + + + public get task() { + return this._task + } + public set task(task: string) { + this._task = task; + } + + + public get flags() { + return this._flags + } + public set flags(flags: ConfigFlags) { + this._flags = flags; + } + + + public get coreCompiler() { + return this._coreCompiler + } + public set coreCompiler(coreCompiler: CoreCompiler) { + this._coreCompiler = coreCompiler; + } +} + +export function initializeStencilCLIConfig(options: StencilCLIConfigArgs): StencilCLIConfig { + return StencilCLIConfig.getInstance(options); +} + +export function getStencilCLIConfig(): StencilCLIConfig { + return StencilCLIConfig.getInstance(); +} + +export function getCompilerSystem(): CompilerSystem { + const stencilCLIConfig = getStencilCLIConfig(); + return !!stencilCLIConfig && stencilCLIConfig?.sys +} + +export function getLogger(): Logger { + const stencilCLIConfig = getStencilCLIConfig(); + return !!stencilCLIConfig && stencilCLIConfig?.logger +} + +export function getCoreCompiler(): CoreCompiler { + const stencilCLIConfig = getStencilCLIConfig(); + return !!stencilCLIConfig && stencilCLIConfig?.coreCompiler +} \ No newline at end of file diff --git a/src/cli/state/test/stencil-cli-config.spec.ts b/src/cli/state/test/stencil-cli-config.spec.ts new file mode 100644 index 00000000000..88865e6e52b --- /dev/null +++ b/src/cli/state/test/stencil-cli-config.spec.ts @@ -0,0 +1,44 @@ +import { createLogger } from '../../../compiler/sys/logger/console-logger'; +import { createSystem } from '../../../compiler/sys/stencil-sys'; +import StencilCLIConfig, { getCompilerSystem, getStencilCLIConfig, initializeStencilCLIConfig, getLogger, getCoreCompiler } from '../stencil-cli-config'; + +describe('StencilCLIConfig', () => { + const config = initializeStencilCLIConfig({ + sys: createSystem(), + args: [], + logger: createLogger() + }) + + it('should behave as a singleton', async () => { + const config2 = initializeStencilCLIConfig({ + sys: createSystem(), + args: [], + logger: createLogger() + }) + + expect(config2).toBe(config); + + const config3 = getStencilCLIConfig(); + + expect(config3).toBe(config); + }); + + it('allows updating any item', async () => { + config.args = ["nice", "awesome"]; + expect(config.args).toEqual(["nice", "awesome"]); + }); + + + it('getCompilerSystem should return a segment of the singleton', async () => { + expect(config.sys).toBe(getCompilerSystem()); + }); + + it('getLogger should return a segment of the singleton', async () => { + expect(config.logger).toBe(getLogger()); + }); + + it('getCoreCompiler should return a segment of the singleton', async () => { + expect(config.coreCompiler).toBe(getCoreCompiler()); + }); + +}); diff --git a/src/cli/telemetry/helpers.ts b/src/cli/telemetry/helpers.ts index 10aa9425f23..8715631a5f8 100644 --- a/src/cli/telemetry/helpers.ts +++ b/src/cli/telemetry/helpers.ts @@ -1,4 +1,4 @@ -import fs from "fs"; +import { getCompilerSystem } from '../state/stencil-cli-config' interface TerminalInfo { /** @@ -47,6 +47,6 @@ export function uuidv4(): string { } export async function readJson(path: string) { - const rawdata = await fs.promises.readFile(path); - return JSON.parse(rawdata.toString()); + const file = await getCompilerSystem()?.readFile(path); + return !!file && JSON.parse(file); } diff --git a/src/cli/test/ionic-config.spec.ts b/src/cli/test/ionic-config.spec.ts index 11b83e4d04f..214e9520195 100644 --- a/src/cli/test/ionic-config.spec.ts +++ b/src/cli/test/ionic-config.spec.ts @@ -1,14 +1,26 @@ -import fs from 'fs-extra'; +import { mockLogger } from '@stencil/core/testing'; +import { getCompilerSystem, initializeStencilCLIConfig } from '../state/stencil-cli-config'; import { readConfig, writeConfig, updateConfig, DEFAULT_CONFIG_DIRECTORY } from '../ionic-config'; +import { createSystem } from '../../compiler/sys/stencil-sys'; describe('readConfig', () => { + initializeStencilCLIConfig({ + sys: createSystem(), + logger: mockLogger(), + args: [] + }); + it('should create a file if it does not exist', async () => { - if (await fs.pathExists(DEFAULT_CONFIG_DIRECTORY)) { - fs.rmSync(DEFAULT_CONFIG_DIRECTORY); + let result = await getCompilerSystem().stat(DEFAULT_CONFIG_DIRECTORY(true)); + + if (result.isFile) { + await getCompilerSystem().removeFile(DEFAULT_CONFIG_DIRECTORY(true)) } - expect(await fs.pathExists(DEFAULT_CONFIG_DIRECTORY)).toBe(false) + result = await getCompilerSystem().stat(DEFAULT_CONFIG_DIRECTORY(true)) + + expect(result.isFile).toBe(false) const config = await readConfig(); @@ -16,9 +28,11 @@ describe('readConfig', () => { }); it('should read a file if it exists', async () => { - await writeConfig({"telemetry.stencil": true, "tokens.telemetry": "12345"}) + await writeConfig({ "telemetry.stencil": true, "tokens.telemetry": "12345" }) + + let result = await getCompilerSystem().stat(DEFAULT_CONFIG_DIRECTORY(true)); - expect(await fs.pathExists(DEFAULT_CONFIG_DIRECTORY)).toBe(true) + expect(result.isFile).toBe(true) const config = await readConfig(); @@ -29,11 +43,18 @@ describe('readConfig', () => { }); describe('updateConfig', () => { + initializeStencilCLIConfig({ + sys: createSystem(), + logger: mockLogger(), + args: [] + }); it('should edit a file', async () => { await writeConfig({ "telemetry.stencil": true, "tokens.telemetry": "12345" }) - expect(await fs.pathExists(DEFAULT_CONFIG_DIRECTORY)).toBe(true) + let result = await getCompilerSystem().stat(DEFAULT_CONFIG_DIRECTORY(true)); + + expect(result.isFile).toBe(true) const configPre = await readConfig(); diff --git a/src/compiler/sys/stencil-sys.ts b/src/compiler/sys/stencil-sys.ts index a98af811d38..81b2c7101ef 100644 --- a/src/compiler/sys/stencil-sys.ts +++ b/src/compiler/sys/stencil-sys.ts @@ -16,6 +16,7 @@ import type { } from '../../declarations'; import platformPath from 'path-browserify'; import { basename, dirname, join } from 'path'; +import * as os from 'os' import { buildEvents } from '../events'; import { createLogger } from './logger/console-logger'; import { createWebWorkerMainController } from './worker/web-worker-main'; @@ -74,6 +75,10 @@ export const createSystem = (c?: { logger?: Logger }) => { return true; }; + const homeDir = () => { + return os.homedir(); + } + const createDirSync = (p: string, opts?: CompilerSystemCreateDirectoryOptions) => { p = normalize(p); const results: CompilerSystemCreateDirectoryResults = { @@ -546,6 +551,7 @@ export const createSystem = (c?: { logger?: Logger }) => { copyFile, createDir, createDirSync, + homeDir, destroy, encodeToBase64, exit: async exitCode => logger.warn(`exit ${exitCode}`), diff --git a/src/compiler/sys/tests/stencil-sys.spec.ts b/src/compiler/sys/tests/stencil-sys.spec.ts index 2b7ce85346b..330b71981d0 100644 --- a/src/compiler/sys/tests/stencil-sys.spec.ts +++ b/src/compiler/sys/tests/stencil-sys.spec.ts @@ -135,6 +135,11 @@ describe('stencil system', () => { expect(newABCStat.isDirectory).toBe(true); }); + it('get home directory', async () => { + const homedir = sys.homeDir(); + expect(typeof homedir).toBe("string") + }) + it('rename directory, with files/subfolders', async () => { await sys.createDir('/x/y/z', { recursive: true }); await sys.createDir('/x/y/y1-dir', { recursive: true }); diff --git a/src/declarations/stencil-public-compiler.ts b/src/declarations/stencil-public-compiler.ts index 9c2f9fcc932..2cc21973f79 100644 --- a/src/declarations/stencil-public-compiler.ts +++ b/src/declarations/stencil-public-compiler.ts @@ -902,6 +902,7 @@ export interface CompilerSystem { * SYNC! Does not throw. */ createDirSync(p: string, opts?: CompilerSystemCreateDirectoryOptions): CompilerSystemCreateDirectoryResults; + homeDir(): string; /** * Each plaform as a different way to dynamically import modules. */ diff --git a/src/sys/deno/deno-sys.ts b/src/sys/deno/deno-sys.ts index 00b7238acf5..ae0d4b2ee71 100644 --- a/src/sys/deno/deno-sys.ts +++ b/src/sys/deno/deno-sys.ts @@ -124,6 +124,9 @@ export function createDenoSys(c: { Deno?: any } = {}) { } return results; }, + homeDir() { + return deno.env.get("HOME") + }, createDirSync(p, opts) { const results: CompilerSystemCreateDirectoryResults = { basename: basename(p), diff --git a/src/sys/node/node-sys.ts b/src/sys/node/node-sys.ts index 99fabf53256..ea7cd768f21 100644 --- a/src/sys/node/node-sys.ts +++ b/src/sys/node/node-sys.ts @@ -17,6 +17,7 @@ import { NodeLazyRequire } from './node-lazy-require'; import { NodeResolveModule } from './node-resolve-module'; import { NodeWorkerController } from './node-worker-controller'; import path from 'path'; +import * as os from 'os'; import type TypeScript from 'typescript'; export function createNodeSys(c: { process?: any } = {}) { @@ -270,6 +271,12 @@ export function createNodeSys(c: { process?: any } = {}) { } catch (e) {} return undefined; }, + homeDir() { + try { + return os.homedir() + } catch (e) { } + return undefined; + }, realpath(p) { return new Promise(resolve => { fs.realpath(p, 'utf8', (e, data) => { diff --git a/src/testing/testing-sys.ts b/src/testing/testing-sys.ts index 7b7da828cfa..0a4c54d0c6c 100644 --- a/src/testing/testing-sys.ts +++ b/src/testing/testing-sys.ts @@ -36,6 +36,7 @@ export const createTestingSystem = () => { sys.access = wrapRead(sys.access); sys.accessSync = wrapRead(sys.accessSync); + sys.homeDir = wrapRead(sys.homeDir); sys.readFile = wrapRead(sys.readFile); sys.readFileSync = wrapRead(sys.readFileSync); sys.readDir = wrapRead(sys.readDir); From 13994a6b22b03571a01b18a2d7e0a208fc8860bc Mon Sep 17 00:00:00 2001 From: "William M. Riley" Date: Wed, 21 Jul 2021 09:21:36 -0500 Subject: [PATCH 12/16] Update src/cli/telemetry/test/helpers.spec.ts Co-authored-by: Ryan Waskiewicz --- src/cli/telemetry/test/helpers.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/telemetry/test/helpers.spec.ts b/src/cli/telemetry/test/helpers.spec.ts index a8c6c873f8e..d363f293021 100644 --- a/src/cli/telemetry/test/helpers.spec.ts +++ b/src/cli/telemetry/test/helpers.spec.ts @@ -2,7 +2,7 @@ import { isInteractive, TERMINAL_INFO, tryFn, uuidv4 } from '../helpers'; describe('uuidv4', () => { - it('should output a UUID', async () => { + it('outputs a UUID', async () => { const pattern = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i); const uuid = uuidv4() expect(!!uuid.match(pattern)).toBe(true) From 8a40ef6645b27bdd1e7eb0cc4d0fdc4755a338a3 Mon Sep 17 00:00:00 2001 From: William Riley Date: Wed, 21 Jul 2021 09:45:50 -0500 Subject: [PATCH 13/16] Feedback from Ryan --- src/cli/ionic-config.ts | 45 +++-- src/cli/state/stencil-cli-config.ts | 163 +++++++++--------- src/cli/state/test/stencil-cli-config.spec.ts | 82 ++++----- src/cli/telemetry/helpers.ts | 73 ++++---- src/cli/telemetry/test/helpers.spec.ts | 86 +++++---- src/cli/test/ionic-config.spec.ts | 96 +++++------ 6 files changed, 267 insertions(+), 278 deletions(-) diff --git a/src/cli/ionic-config.ts b/src/cli/ionic-config.ts index 8a2d1ff66fd..e8ecd9a5b85 100644 --- a/src/cli/ionic-config.ts +++ b/src/cli/ionic-config.ts @@ -1,41 +1,40 @@ import { getCompilerSystem } from './state/stencil-cli-config'; import { readJson, uuidv4 } from './telemetry/helpers'; -const CONFIG_FILE = 'config.json'; -export const DEFAULT_CONFIG_DIRECTORY = (file: boolean = false) => { - return getCompilerSystem().resolvePath(`${getCompilerSystem().homeDir()}/.ionic${file ? "/" + CONFIG_FILE : ""}`); -} +export const DEFAULT_CONFIG = (file: boolean = false) => { + return getCompilerSystem().resolvePath(`${getCompilerSystem().homeDir()}/.ionic${file ? '/config.json' : ''}`); +}; export interface TelemetryConfig { - "telemetry.stencil"?: boolean, - "tokens.telemetry"?: string, + 'telemetry.stencil'?: boolean; + 'tokens.telemetry'?: string; } export async function readConfig(): Promise { - let config: TelemetryConfig = await readJson(DEFAULT_CONFIG_DIRECTORY(true)); + let config: TelemetryConfig = await readJson(DEFAULT_CONFIG(true)); - if (!config) { - config = { - "tokens.telemetry": uuidv4(), - "telemetry.stencil": true, - }; + if (!config) { + config = { + 'tokens.telemetry': uuidv4(), + 'telemetry.stencil': true, + }; - await writeConfig(config); - } + await writeConfig(config); + } - return config; + return config; } export async function writeConfig(config: TelemetryConfig): Promise { - try { - await getCompilerSystem().createDir(DEFAULT_CONFIG_DIRECTORY(), { recursive: true }); - await getCompilerSystem().writeFile(DEFAULT_CONFIG_DIRECTORY(true), JSON.stringify(config)) - } catch (error) { - console.error(`Stencil Telemetry: couldn't write configuration file to ${DEFAULT_CONFIG_DIRECTORY(true)} - ${error}.`) - }; + try { + await getCompilerSystem().createDir(DEFAULT_CONFIG(), { recursive: true }); + await getCompilerSystem().writeFile(DEFAULT_CONFIG(true), JSON.stringify(config)); + } catch (error) { + console.error(`Stencil Telemetry: couldn't write configuration file to ${DEFAULT_CONFIG(true)} - ${error}.`); + } } export async function updateConfig(newOptions: TelemetryConfig): Promise { - const config = await readConfig(); - await writeConfig(Object.assign(config, newOptions)); + const config = await readConfig(); + await writeConfig(Object.assign(config, newOptions)); } diff --git a/src/cli/state/stencil-cli-config.ts b/src/cli/state/stencil-cli-config.ts index b3fc9529cbe..8faaf947952 100644 --- a/src/cli/state/stencil-cli-config.ts +++ b/src/cli/state/stencil-cli-config.ts @@ -1,107 +1,98 @@ -import type { Logger, CompilerSystem, ConfigFlags } from "../../declarations"; +import type { Logger, CompilerSystem, ConfigFlags } from '../../declarations'; export type CoreCompiler = typeof import('@stencil/core/compiler'); export interface StencilCLIConfigArgs { - task?: string; - args: string[]; - logger: Logger; - sys: CompilerSystem; - flags?: ConfigFlags; - coreCompiler?: CoreCompiler + task?: string; + args: string[]; + logger: Logger; + sys: CompilerSystem; + flags?: ConfigFlags; + coreCompiler?: CoreCompiler; } export default class StencilCLIConfig { - - static instance: StencilCLIConfig; - - private _args: string[]; - private _logger: Logger; - private _sys: CompilerSystem; - private _flags: ConfigFlags; - private _task: string; - private _coreCompiler: CoreCompiler; - - private constructor (options: StencilCLIConfigArgs) { - this._args = options?.args || []; - this._logger = options?.logger; - this._sys = options?.sys; - } - - public static getInstance(options?: StencilCLIConfigArgs): StencilCLIConfig { - if (!StencilCLIConfig.instance && !!options) { - StencilCLIConfig.instance = new StencilCLIConfig(options); - } - - return StencilCLIConfig.instance; - } - - public get logger() { - return this._logger - } - public set logger(logger: Logger) { - this._logger = logger; - } - - - public get sys() { - return this._sys - } - public set sys(sys: CompilerSystem) { - this._sys = sys; - } - - - public get args() { - return this._args - } - public set args(args: string[]) { - this._args = args; - } - - - public get task() { - return this._task - } - public set task(task: string) { - this._task = task; - } - - - public get flags() { - return this._flags - } - public set flags(flags: ConfigFlags) { - this._flags = flags; - } - - - public get coreCompiler() { - return this._coreCompiler - } - public set coreCompiler(coreCompiler: CoreCompiler) { - this._coreCompiler = coreCompiler; - } + static instance: StencilCLIConfig; + + private _args: string[]; + private _logger: Logger; + private _sys: CompilerSystem; + private _flags: ConfigFlags | undefined; + private _task: string | undefined; + private _coreCompiler: CoreCompiler | undefined; + + private constructor(options: StencilCLIConfigArgs) { + this._args = options?.args || []; + this._logger = options?.logger; + this._sys = options?.sys; + } + + public static getInstance(options?: StencilCLIConfigArgs): StencilCLIConfig { + if (!StencilCLIConfig.instance) { + StencilCLIConfig.instance = new StencilCLIConfig(options); + } + + return StencilCLIConfig.instance; + } + + public get logger() { + return this._logger; + } + public set logger(logger: Logger) { + this._logger = logger; + } + + public get sys() { + return this._sys; + } + public set sys(sys: CompilerSystem) { + this._sys = sys; + } + + public get args() { + return this._args; + } + public set args(args: string[]) { + this._args = args; + } + + public get task() { + return this._task; + } + public set task(task: string) { + this._task = task; + } + + public get flags() { + return this._flags; + } + public set flags(flags: ConfigFlags) { + this._flags = flags; + } + + public get coreCompiler() { + return this._coreCompiler; + } + public set coreCompiler(coreCompiler: CoreCompiler) { + this._coreCompiler = coreCompiler; + } } export function initializeStencilCLIConfig(options: StencilCLIConfigArgs): StencilCLIConfig { - return StencilCLIConfig.getInstance(options); + return StencilCLIConfig.getInstance(options); } export function getStencilCLIConfig(): StencilCLIConfig { - return StencilCLIConfig.getInstance(); + return StencilCLIConfig.getInstance(); } export function getCompilerSystem(): CompilerSystem { - const stencilCLIConfig = getStencilCLIConfig(); - return !!stencilCLIConfig && stencilCLIConfig?.sys + return getStencilCLIConfig().sys; } export function getLogger(): Logger { - const stencilCLIConfig = getStencilCLIConfig(); - return !!stencilCLIConfig && stencilCLIConfig?.logger + return getStencilCLIConfig().logger; } export function getCoreCompiler(): CoreCompiler { - const stencilCLIConfig = getStencilCLIConfig(); - return !!stencilCLIConfig && stencilCLIConfig?.coreCompiler -} \ No newline at end of file + return getStencilCLIConfig().coreCompiler; +} diff --git a/src/cli/state/test/stencil-cli-config.spec.ts b/src/cli/state/test/stencil-cli-config.spec.ts index 88865e6e52b..ec63563854f 100644 --- a/src/cli/state/test/stencil-cli-config.spec.ts +++ b/src/cli/state/test/stencil-cli-config.spec.ts @@ -1,44 +1,48 @@ import { createLogger } from '../../../compiler/sys/logger/console-logger'; import { createSystem } from '../../../compiler/sys/stencil-sys'; -import StencilCLIConfig, { getCompilerSystem, getStencilCLIConfig, initializeStencilCLIConfig, getLogger, getCoreCompiler } from '../stencil-cli-config'; +import { + getCompilerSystem, + getStencilCLIConfig, + initializeStencilCLIConfig, + getLogger, + getCoreCompiler, +} from '../stencil-cli-config'; describe('StencilCLIConfig', () => { - const config = initializeStencilCLIConfig({ - sys: createSystem(), - args: [], - logger: createLogger() - }) - - it('should behave as a singleton', async () => { - const config2 = initializeStencilCLIConfig({ - sys: createSystem(), - args: [], - logger: createLogger() - }) - - expect(config2).toBe(config); - - const config3 = getStencilCLIConfig(); - - expect(config3).toBe(config); - }); - - it('allows updating any item', async () => { - config.args = ["nice", "awesome"]; - expect(config.args).toEqual(["nice", "awesome"]); - }); - - - it('getCompilerSystem should return a segment of the singleton', async () => { - expect(config.sys).toBe(getCompilerSystem()); - }); - - it('getLogger should return a segment of the singleton', async () => { - expect(config.logger).toBe(getLogger()); - }); - - it('getCoreCompiler should return a segment of the singleton', async () => { - expect(config.coreCompiler).toBe(getCoreCompiler()); - }); - + const config = initializeStencilCLIConfig({ + sys: createSystem(), + args: [], + logger: createLogger(), + }); + + it('should behave as a singleton', () => { + const config2 = initializeStencilCLIConfig({ + sys: createSystem(), + args: [], + logger: createLogger(), + }); + + expect(config2).toBe(config); + + const config3 = getStencilCLIConfig(); + + expect(config3).toBe(config); + }); + + it('allows updating any item', () => { + config.args = ['nice', 'awesome']; + expect(config.args).toEqual(['nice', 'awesome']); + }); + + it('getCompilerSystem should return a segment of the singleton', () => { + expect(config.sys).toBe(getCompilerSystem()); + }); + + it('getLogger should return a segment of the singleton', () => { + expect(config.logger).toBe(getLogger()); + }); + + it('getCoreCompiler should return a segment of the singleton', () => { + expect(config.coreCompiler).toBe(getCoreCompiler()); + }); }); diff --git a/src/cli/telemetry/helpers.ts b/src/cli/telemetry/helpers.ts index 8715631a5f8..76f6a112a4e 100644 --- a/src/cli/telemetry/helpers.ts +++ b/src/cli/telemetry/helpers.ts @@ -1,52 +1,51 @@ -import { getCompilerSystem } from '../state/stencil-cli-config' +import { getCompilerSystem } from '../state/stencil-cli-config'; interface TerminalInfo { - /** - * Whether this is in CI or not. - */ - readonly ci: boolean; - /** - * Path to the user's shell program. - */ - readonly shell: string; - /** - * Whether the terminal is an interactive TTY or not. - */ - readonly tty: boolean; - /** - * Whether this is a Windows shell or not. - */ - readonly windows: boolean; + /** + * Whether this is in CI or not. + */ + readonly ci: boolean; + /** + * Whether the terminal is an interactive TTY or not. + */ + readonly tty: boolean; } export declare const TERMINAL_INFO: TerminalInfo; -export const tryFn = async Promise, R>( - fn: T, - ...args: any[] -): Promise => { - try { - return await fn(...args); - } catch { - // ignore - } - - return null; -}; +export const tryFn = async Promise, R>(fn: T, ...args: any[]): Promise => { + try { + return await fn(...args); + } catch { + // ignore + } -export const isInteractive = (): boolean => TERMINAL_INFO.tty && !TERMINAL_INFO.ci; + return null; +}; +export const isInteractive = (object?: TerminalInfo): boolean => { + const terminalInfo = + object || + Object.freeze({ + tty: process.stdout.isTTY ? true : false, + ci: + ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter(v => !!process.env[v]) + .length > 0 || process.argv.includes('--ci'), + }); + + return terminalInfo.tty && !terminalInfo.ci; +}; // Plucked from https://github.com/ionic-team/capacitor/blob/b893a57aaaf3a16e13db9c33037a12f1a5ac92e0/cli/src/util/uuid.ts export function uuidv4(): string { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { - const r = (Math.random() * 16) | 0; - const v = c == 'x' ? r : (r & 0x3) | 0x8; + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { + const r = (Math.random() * 16) | 0; + const v = c == 'x' ? r : (r & 0x3) | 0x8; - return v.toString(16); - }); + return v.toString(16); + }); } export async function readJson(path: string) { - const file = await getCompilerSystem()?.readFile(path); - return !!file && JSON.parse(file); + const file = await getCompilerSystem().readFile(path); + return !!file && JSON.parse(file); } diff --git a/src/cli/telemetry/test/helpers.spec.ts b/src/cli/telemetry/test/helpers.spec.ts index d363f293021..b4bb65e9f85 100644 --- a/src/cli/telemetry/test/helpers.spec.ts +++ b/src/cli/telemetry/test/helpers.spec.ts @@ -1,59 +1,57 @@ import { isInteractive, TERMINAL_INFO, tryFn, uuidv4 } from '../helpers'; describe('uuidv4', () => { - - it('outputs a UUID', async () => { - const pattern = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i); - const uuid = uuidv4() - expect(!!uuid.match(pattern)).toBe(true) - }); - + it('outputs a UUID', () => { + const pattern = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i); + const uuid = uuidv4(); + expect(!!uuid.match(pattern)).toBe(true); + }); }); describe('isInteractive', () => { - it('should return false when tty is false', async () => { - TERMINAL_INFO = { ci: true, shell: true, tty: false, windows: true }; - const result = isInteractive(); - expect(result).toBe(false) - }); - - it('should return false when ci is true', async () => { - TERMINAL_INFO = { ci: true, shell: true, tty: true, windows: true }; - const result = isInteractive(); - expect(result).toBe(false) - }); - - it('should return true when tty is true and ci is false', async () => { - TERMINAL_INFO = { ci: false, shell: true, tty: true, windows: true }; - const result = isInteractive(); - expect(result).toBe(true) - }); + it('returns false by default', () => { + const result = isInteractive(); + expect(result).toBe(false); + }); + + it('returns false when tty is false', () => { + const result = isInteractive({ ci: true, tty: false }); + expect(result).toBe(false); + }); + + it('returns false when ci is true', () => { + const result = isInteractive({ ci: true, tty: true }); + expect(result).toBe(false); + }); + + it('returns true when tty is true and ci is false', () => { + const result = isInteractive({ ci: false, tty: true }); + expect(result).toBe(true); + }); }); - describe('tryFn', () => { + it('handles failures correctly', async () => { + const result = await tryFn(async () => { + throw new Error('Uh oh!'); + }); - it('should handle failures correctly', async () => { - const result = await tryFn(async () => { - throw new Error("Uh oh!") - }); - - expect(result).toBe(null) - }); + expect(result).toBe(null); + }); - it('should handle success correctly', async () => { - const result = await tryFn(async () => { - return true - }) + it('handles success correctly', async () => { + const result = await tryFn(async () => { + return true; + }); - expect(result).toBe(true) - }); + expect(result).toBe(true); + }); - it('handles returning false correctly', async () => { - const result = await tryFn(async () => { - return false; - }) + it('handles returning false correctly', async () => { + const result = await tryFn(async () => { + return false; + }); - expect(result).toBe(false); - }); + expect(result).toBe(false); + }); }); diff --git a/src/cli/test/ionic-config.spec.ts b/src/cli/test/ionic-config.spec.ts index 214e9520195..9726c92a7b2 100644 --- a/src/cli/test/ionic-config.spec.ts +++ b/src/cli/test/ionic-config.spec.ts @@ -1,77 +1,75 @@ import { mockLogger } from '@stencil/core/testing'; import { getCompilerSystem, initializeStencilCLIConfig } from '../state/stencil-cli-config'; -import { readConfig, writeConfig, updateConfig, DEFAULT_CONFIG_DIRECTORY } from '../ionic-config'; +import { readConfig, writeConfig, updateConfig, DEFAULT_CONFIG } from '../ionic-config'; import { createSystem } from '../../compiler/sys/stencil-sys'; describe('readConfig', () => { + initializeStencilCLIConfig({ + sys: createSystem(), + logger: mockLogger(), + args: [], + }); - initializeStencilCLIConfig({ - sys: createSystem(), - logger: mockLogger(), - args: [] - }); + it('should create a file if it does not exist', async () => { + let result = await getCompilerSystem().stat(DEFAULT_CONFIG(true)); - it('should create a file if it does not exist', async () => { - let result = await getCompilerSystem().stat(DEFAULT_CONFIG_DIRECTORY(true)); + if (result.isFile) { + await getCompilerSystem().removeFile(DEFAULT_CONFIG(true)); + } - if (result.isFile) { - await getCompilerSystem().removeFile(DEFAULT_CONFIG_DIRECTORY(true)) - } + result = await getCompilerSystem().stat(DEFAULT_CONFIG(true)); - result = await getCompilerSystem().stat(DEFAULT_CONFIG_DIRECTORY(true)) + expect(result.isFile).toBe(false); - expect(result.isFile).toBe(false) + const config = await readConfig(); - const config = await readConfig(); + expect(Object.keys(config).join()).toBe('tokens.telemetry,telemetry.stencil'); + }); - expect(Object.keys(config).join()).toBe("tokens.telemetry,telemetry.stencil"); - }); + it('should read a file if it exists', async () => { + await writeConfig({ 'telemetry.stencil': true, 'tokens.telemetry': '12345' }); - it('should read a file if it exists', async () => { - await writeConfig({ "telemetry.stencil": true, "tokens.telemetry": "12345" }) + let result = await getCompilerSystem().stat(DEFAULT_CONFIG(true)); - let result = await getCompilerSystem().stat(DEFAULT_CONFIG_DIRECTORY(true)); + expect(result.isFile).toBe(true); - expect(result.isFile).toBe(true) + const config = await readConfig(); - const config = await readConfig(); - - expect(Object.keys(config).join()).toBe("telemetry.stencil,tokens.telemetry"); - expect(config['telemetry.stencil']).toBe(true); - expect(config['tokens.telemetry']).toBe("12345"); - }); + expect(Object.keys(config).join()).toBe('telemetry.stencil,tokens.telemetry'); + expect(config['telemetry.stencil']).toBe(true); + expect(config['tokens.telemetry']).toBe('12345'); + }); }); describe('updateConfig', () => { - initializeStencilCLIConfig({ - sys: createSystem(), - logger: mockLogger(), - args: [] - }); - - it('should edit a file', async () => { - await writeConfig({ "telemetry.stencil": true, "tokens.telemetry": "12345" }) + initializeStencilCLIConfig({ + sys: createSystem(), + logger: mockLogger(), + args: [], + }); - let result = await getCompilerSystem().stat(DEFAULT_CONFIG_DIRECTORY(true)); + it('should edit a file', async () => { + await writeConfig({ 'telemetry.stencil': true, 'tokens.telemetry': '12345' }); - expect(result.isFile).toBe(true) + let result = await getCompilerSystem().stat(DEFAULT_CONFIG(true)); - const configPre = await readConfig(); + expect(result.isFile).toBe(true); - expect(typeof configPre).toBe("object"); - expect(Object.keys(configPre).join()).toBe("telemetry.stencil,tokens.telemetry"); - expect(configPre['telemetry.stencil']).toBe(true); - expect(configPre['tokens.telemetry']).toBe("12345"); + const configPre = await readConfig(); - await updateConfig({ "telemetry.stencil": false, "tokens.telemetry": "67890" }); + expect(typeof configPre).toBe('object'); + expect(Object.keys(configPre).join()).toBe('telemetry.stencil,tokens.telemetry'); + expect(configPre['telemetry.stencil']).toBe(true); + expect(configPre['tokens.telemetry']).toBe('12345'); - const configPost = await readConfig(); + await updateConfig({ 'telemetry.stencil': false, 'tokens.telemetry': '67890' }); - expect(typeof configPost).toBe("object"); - // Should keep the previous order - expect(Object.keys(configPost).join()).toBe("telemetry.stencil,tokens.telemetry"); - expect(configPost['telemetry.stencil']).toBe(false); - expect(configPost['tokens.telemetry']).toBe("67890"); - }); + const configPost = await readConfig(); + expect(typeof configPost).toBe('object'); + // Should keep the previous order + expect(Object.keys(configPost).join()).toBe('telemetry.stencil,tokens.telemetry'); + expect(configPost['telemetry.stencil']).toBe(false); + expect(configPost['tokens.telemetry']).toBe('67890'); + }); }); From 2582e3a9a2275d89322c11e29a125f1784204905 Mon Sep 17 00:00:00 2001 From: William Riley Date: Wed, 21 Jul 2021 10:08:54 -0500 Subject: [PATCH 14/16] Feedback from Ryan --- src/cli/ionic-config.ts | 16 +++++++++------- src/cli/test/ionic-config.spec.ts | 12 ++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/cli/ionic-config.ts b/src/cli/ionic-config.ts index e8ecd9a5b85..2b2dadd18f7 100644 --- a/src/cli/ionic-config.ts +++ b/src/cli/ionic-config.ts @@ -1,9 +1,11 @@ import { getCompilerSystem } from './state/stencil-cli-config'; import { readJson, uuidv4 } from './telemetry/helpers'; -export const DEFAULT_CONFIG = (file: boolean = false) => { - return getCompilerSystem().resolvePath(`${getCompilerSystem().homeDir()}/.ionic${file ? '/config.json' : ''}`); -}; +export const default_config = () => + getCompilerSystem().resolvePath(`${getCompilerSystem().homeDir()}/.ionic/config.json`); + +export const default_config_directory = () => + getCompilerSystem().resolvePath(`${getCompilerSystem().homeDir()}/.ionic`); export interface TelemetryConfig { 'telemetry.stencil'?: boolean; @@ -11,7 +13,7 @@ export interface TelemetryConfig { } export async function readConfig(): Promise { - let config: TelemetryConfig = await readJson(DEFAULT_CONFIG(true)); + let config: TelemetryConfig = await readJson(default_config()); if (!config) { config = { @@ -27,10 +29,10 @@ export async function readConfig(): Promise { export async function writeConfig(config: TelemetryConfig): Promise { try { - await getCompilerSystem().createDir(DEFAULT_CONFIG(), { recursive: true }); - await getCompilerSystem().writeFile(DEFAULT_CONFIG(true), JSON.stringify(config)); + await getCompilerSystem().createDir(default_config_directory(), { recursive: true }); + await getCompilerSystem().writeFile(default_config(), JSON.stringify(config)); } catch (error) { - console.error(`Stencil Telemetry: couldn't write configuration file to ${DEFAULT_CONFIG(true)} - ${error}.`); + console.error(`Stencil Telemetry: couldn't write configuration file to ${default_config()} - ${error}.`); } } diff --git a/src/cli/test/ionic-config.spec.ts b/src/cli/test/ionic-config.spec.ts index 9726c92a7b2..842acf5975b 100644 --- a/src/cli/test/ionic-config.spec.ts +++ b/src/cli/test/ionic-config.spec.ts @@ -1,6 +1,6 @@ import { mockLogger } from '@stencil/core/testing'; import { getCompilerSystem, initializeStencilCLIConfig } from '../state/stencil-cli-config'; -import { readConfig, writeConfig, updateConfig, DEFAULT_CONFIG } from '../ionic-config'; +import { readConfig, writeConfig, updateConfig, default_config } from '../ionic-config'; import { createSystem } from '../../compiler/sys/stencil-sys'; describe('readConfig', () => { @@ -11,13 +11,13 @@ describe('readConfig', () => { }); it('should create a file if it does not exist', async () => { - let result = await getCompilerSystem().stat(DEFAULT_CONFIG(true)); + let result = await getCompilerSystem().stat(default_config()); if (result.isFile) { - await getCompilerSystem().removeFile(DEFAULT_CONFIG(true)); + await getCompilerSystem().removeFile(default_config()); } - result = await getCompilerSystem().stat(DEFAULT_CONFIG(true)); + result = await getCompilerSystem().stat(default_config()); expect(result.isFile).toBe(false); @@ -29,7 +29,7 @@ describe('readConfig', () => { it('should read a file if it exists', async () => { await writeConfig({ 'telemetry.stencil': true, 'tokens.telemetry': '12345' }); - let result = await getCompilerSystem().stat(DEFAULT_CONFIG(true)); + let result = await getCompilerSystem().stat(default_config()); expect(result.isFile).toBe(true); @@ -51,7 +51,7 @@ describe('updateConfig', () => { it('should edit a file', async () => { await writeConfig({ 'telemetry.stencil': true, 'tokens.telemetry': '12345' }); - let result = await getCompilerSystem().stat(DEFAULT_CONFIG(true)); + let result = await getCompilerSystem().stat(default_config()); expect(result.isFile).toBe(true); From e5c0313c18722f1cae33dd2383655379bc81a696 Mon Sep 17 00:00:00 2001 From: William Riley Date: Wed, 21 Jul 2021 10:18:21 -0500 Subject: [PATCH 15/16] Feedback from Ryan --- src/cli/ionic-config.ts | 13 ++++++------- src/cli/test/ionic-config.spec.ts | 12 ++++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/cli/ionic-config.ts b/src/cli/ionic-config.ts index 2b2dadd18f7..3a7b8769759 100644 --- a/src/cli/ionic-config.ts +++ b/src/cli/ionic-config.ts @@ -1,11 +1,10 @@ import { getCompilerSystem } from './state/stencil-cli-config'; import { readJson, uuidv4 } from './telemetry/helpers'; -export const default_config = () => +export const defaultConfig = () => getCompilerSystem().resolvePath(`${getCompilerSystem().homeDir()}/.ionic/config.json`); -export const default_config_directory = () => - getCompilerSystem().resolvePath(`${getCompilerSystem().homeDir()}/.ionic`); +export const defaultConfigDirectory = () => getCompilerSystem().resolvePath(`${getCompilerSystem().homeDir()}/.ionic`); export interface TelemetryConfig { 'telemetry.stencil'?: boolean; @@ -13,7 +12,7 @@ export interface TelemetryConfig { } export async function readConfig(): Promise { - let config: TelemetryConfig = await readJson(default_config()); + let config: TelemetryConfig = await readJson(defaultConfig()); if (!config) { config = { @@ -29,10 +28,10 @@ export async function readConfig(): Promise { export async function writeConfig(config: TelemetryConfig): Promise { try { - await getCompilerSystem().createDir(default_config_directory(), { recursive: true }); - await getCompilerSystem().writeFile(default_config(), JSON.stringify(config)); + await getCompilerSystem().createDir(defaultConfigDirectory(), { recursive: true }); + await getCompilerSystem().writeFile(defaultConfig(), JSON.stringify(config)); } catch (error) { - console.error(`Stencil Telemetry: couldn't write configuration file to ${default_config()} - ${error}.`); + console.error(`Stencil Telemetry: couldn't write configuration file to ${defaultConfig()} - ${error}.`); } } diff --git a/src/cli/test/ionic-config.spec.ts b/src/cli/test/ionic-config.spec.ts index 842acf5975b..c1d8f5a9d7f 100644 --- a/src/cli/test/ionic-config.spec.ts +++ b/src/cli/test/ionic-config.spec.ts @@ -1,6 +1,6 @@ import { mockLogger } from '@stencil/core/testing'; import { getCompilerSystem, initializeStencilCLIConfig } from '../state/stencil-cli-config'; -import { readConfig, writeConfig, updateConfig, default_config } from '../ionic-config'; +import { readConfig, writeConfig, updateConfig, defaultConfig } from '../ionic-config'; import { createSystem } from '../../compiler/sys/stencil-sys'; describe('readConfig', () => { @@ -11,13 +11,13 @@ describe('readConfig', () => { }); it('should create a file if it does not exist', async () => { - let result = await getCompilerSystem().stat(default_config()); + let result = await getCompilerSystem().stat(defaultConfig()); if (result.isFile) { - await getCompilerSystem().removeFile(default_config()); + await getCompilerSystem().removeFile(defaultConfig()); } - result = await getCompilerSystem().stat(default_config()); + result = await getCompilerSystem().stat(defaultConfig()); expect(result.isFile).toBe(false); @@ -29,7 +29,7 @@ describe('readConfig', () => { it('should read a file if it exists', async () => { await writeConfig({ 'telemetry.stencil': true, 'tokens.telemetry': '12345' }); - let result = await getCompilerSystem().stat(default_config()); + let result = await getCompilerSystem().stat(defaultConfig()); expect(result.isFile).toBe(true); @@ -51,7 +51,7 @@ describe('updateConfig', () => { it('should edit a file', async () => { await writeConfig({ 'telemetry.stencil': true, 'tokens.telemetry': '12345' }); - let result = await getCompilerSystem().stat(default_config()); + let result = await getCompilerSystem().stat(defaultConfig()); expect(result.isFile).toBe(true); From f29642dfe260439b894ac51ee10323b2eb1c00cd Mon Sep 17 00:00:00 2001 From: William Riley Date: Thu, 22 Jul 2021 13:51:57 -0500 Subject: [PATCH 16/16] Updating the process calls to route through the abstration. --- src/cli/telemetry/helpers.ts | 10 ++++++---- src/cli/telemetry/test/helpers.spec.ts | 9 +++++++++ src/compiler/sys/stencil-sys.ts | 15 +++++++++++++-- src/declarations/stencil-public-compiler.ts | 4 ++++ src/sys/deno/deno-sys.ts | 5 ++++- src/sys/node/node-sys.ts | 7 +++++-- 6 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/cli/telemetry/helpers.ts b/src/cli/telemetry/helpers.ts index 76f6a112a4e..b15cfc852f7 100644 --- a/src/cli/telemetry/helpers.ts +++ b/src/cli/telemetry/helpers.ts @@ -1,4 +1,4 @@ -import { getCompilerSystem } from '../state/stencil-cli-config'; +import { getCompilerSystem, getStencilCLIConfig } from '../state/stencil-cli-config'; interface TerminalInfo { /** @@ -27,14 +27,16 @@ export const isInteractive = (object?: TerminalInfo): boolean => { const terminalInfo = object || Object.freeze({ - tty: process.stdout.isTTY ? true : false, + tty: getCompilerSystem().isTTY() ? true : false, ci: - ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter(v => !!process.env[v]) - .length > 0 || process.argv.includes('--ci'), + ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter( + v => !!getCompilerSystem().getEnvironmentVar(v), + ).length > 0 || !!getStencilCLIConfig()?.flags?.ci, }); return terminalInfo.tty && !terminalInfo.ci; }; + // Plucked from https://github.com/ionic-team/capacitor/blob/b893a57aaaf3a16e13db9c33037a12f1a5ac92e0/cli/src/util/uuid.ts export function uuidv4(): string { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { diff --git a/src/cli/telemetry/test/helpers.spec.ts b/src/cli/telemetry/test/helpers.spec.ts index b4bb65e9f85..70a8c05e965 100644 --- a/src/cli/telemetry/test/helpers.spec.ts +++ b/src/cli/telemetry/test/helpers.spec.ts @@ -1,4 +1,7 @@ +import { initializeStencilCLIConfig } from '../../state/stencil-cli-config'; import { isInteractive, TERMINAL_INFO, tryFn, uuidv4 } from '../helpers'; +import { createSystem } from '../../../compiler/sys/stencil-sys'; +import { mockLogger } from '@stencil/core/testing'; describe('uuidv4', () => { it('outputs a UUID', () => { @@ -9,6 +12,12 @@ describe('uuidv4', () => { }); describe('isInteractive', () => { + initializeStencilCLIConfig({ + sys: createSystem(), + logger: mockLogger(), + args: [], + }); + it('returns false by default', () => { const result = isInteractive(); expect(result).toBe(false); diff --git a/src/compiler/sys/stencil-sys.ts b/src/compiler/sys/stencil-sys.ts index 81b2c7101ef..60806a909c0 100644 --- a/src/compiler/sys/stencil-sys.ts +++ b/src/compiler/sys/stencil-sys.ts @@ -16,7 +16,8 @@ import type { } from '../../declarations'; import platformPath from 'path-browserify'; import { basename, dirname, join } from 'path'; -import * as os from 'os' +import * as process from 'process'; +import * as os from 'os'; import { buildEvents } from '../events'; import { createLogger } from './logger/console-logger'; import { createWebWorkerMainController } from './worker/web-worker-main'; @@ -75,9 +76,13 @@ export const createSystem = (c?: { logger?: Logger }) => { return true; }; + const isTTY = (): boolean => { + return !!process?.stdout?.isTTY; + }; + const homeDir = () => { return os.homedir(); - } + }; const createDirSync = (p: string, opts?: CompilerSystemCreateDirectoryOptions) => { p = normalize(p); @@ -528,6 +533,10 @@ export const createSystem = (c?: { logger?: Logger }) => { return results; }; + const getEnvironmentVar = (key: string) => { + return process?.env[key]; + }; + const getLocalModulePath = (opts: { rootDir: string; moduleId: string; path: string }) => join(opts.rootDir, 'node_modules', opts.moduleId, opts.path); @@ -552,6 +561,8 @@ export const createSystem = (c?: { logger?: Logger }) => { createDir, createDirSync, homeDir, + isTTY, + getEnvironmentVar, destroy, encodeToBase64, exit: async exitCode => logger.warn(`exit ${exitCode}`), diff --git a/src/declarations/stencil-public-compiler.ts b/src/declarations/stencil-public-compiler.ts index 2cc21973f79..da803ab50e5 100644 --- a/src/declarations/stencil-public-compiler.ts +++ b/src/declarations/stencil-public-compiler.ts @@ -903,6 +903,10 @@ export interface CompilerSystem { */ createDirSync(p: string, opts?: CompilerSystemCreateDirectoryOptions): CompilerSystemCreateDirectoryResults; homeDir(): string; + /** + * Used to determine if the current context of the terminal is TTY. + */ + isTTY(): boolean; /** * Each plaform as a different way to dynamically import modules. */ diff --git a/src/sys/deno/deno-sys.ts b/src/sys/deno/deno-sys.ts index ae0d4b2ee71..4259829da87 100644 --- a/src/sys/deno/deno-sys.ts +++ b/src/sys/deno/deno-sys.ts @@ -124,8 +124,11 @@ export function createDenoSys(c: { Deno?: any } = {}) { } return results; }, + isTTY() { + return !!deno?.isatty(deno?.stdout?.rid); + }, homeDir() { - return deno.env.get("HOME") + return deno.env.get('HOME'); }, createDirSync(p, opts) { const results: CompilerSystemCreateDirectoryResults = { diff --git a/src/sys/node/node-sys.ts b/src/sys/node/node-sys.ts index ea7cd768f21..a5ef96c5cb6 100644 --- a/src/sys/node/node-sys.ts +++ b/src/sys/node/node-sys.ts @@ -243,6 +243,9 @@ export function createNodeSys(c: { process?: any } = {}) { }); }); }, + isTTY() { + return !!process?.stdout?.isTTY; + }, readDirSync(p) { try { return fs.readdirSync(p).map(f => { @@ -273,8 +276,8 @@ export function createNodeSys(c: { process?: any } = {}) { }, homeDir() { try { - return os.homedir() - } catch (e) { } + return os.homedir(); + } catch (e) {} return undefined; }, realpath(p) {