@shopify/cli-kit 3.29.0 → 3.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/admin.js +5 -11
- package/dist/api/admin.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/output.js +0 -2
- package/dist/output.js.map +1 -1
- package/dist/public/common/array.d.ts +1 -1
- package/dist/public/common/array.js.map +1 -1
- package/dist/public/node/base-command.js +0 -2
- package/dist/public/node/base-command.js.map +1 -1
- package/dist/public/node/checksum.d.ts +1 -1
- package/dist/public/node/checksum.js +1 -1
- package/dist/public/node/checksum.js.map +1 -1
- package/dist/public/node/cli.js.map +1 -1
- package/dist/public/node/dot-env.js.map +1 -1
- package/dist/public/node/error-handler.d.ts +5 -1
- package/dist/public/node/error-handler.js.map +1 -1
- package/dist/public/node/framework.js.map +1 -1
- package/dist/public/node/hooks/prerun.js.map +1 -1
- package/dist/public/node/node-package-manager.js.map +1 -1
- package/dist/public/node/ruby.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/ui.js +0 -11
- package/dist/ui.js.map +1 -1
- package/package.json +1 -1
- package/dist/log.d.ts +0 -31
- package/dist/log.js +0 -169
- package/dist/log.js.map +0 -1
package/dist/ui.js
CHANGED
|
@@ -5,18 +5,12 @@ import colors from './public/node/colors.js';
|
|
|
5
5
|
import { relative } from './path.js';
|
|
6
6
|
import { isTerminalInteractive } from './environment/local.js';
|
|
7
7
|
import { mapper as mapperUI, run as executorUI } from './ui/executor.js';
|
|
8
|
-
import { logToFile } from './log.js';
|
|
9
8
|
import { Listr as OriginalListr, ListrTaskState } from 'listr2';
|
|
10
9
|
import findProcess from 'find-process';
|
|
11
10
|
export function newListr(tasks, options) {
|
|
12
11
|
const listr = new OriginalListr(tasks, options);
|
|
13
12
|
listr.tasks.forEach((task) => {
|
|
14
13
|
const loggedSubtaskTitles = [];
|
|
15
|
-
task.subscribe((event) => {
|
|
16
|
-
if (event.type === 'TITLE' && typeof event.data === 'string') {
|
|
17
|
-
logToFile(event.data, 'INFO');
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
14
|
task.renderHook$.subscribe(() => {
|
|
21
15
|
if (task.hasSubtasks()) {
|
|
22
16
|
const activeSubtasks = task.subtasks.filter((subtask) => {
|
|
@@ -25,7 +19,6 @@ export function newListr(tasks, options) {
|
|
|
25
19
|
activeSubtasks.forEach((subtask) => {
|
|
26
20
|
if (subtask.title && !loggedSubtaskTitles.includes(subtask.title)) {
|
|
27
21
|
loggedSubtaskTitles.push(subtask.title);
|
|
28
|
-
logToFile(subtask.title, 'INFO');
|
|
29
22
|
}
|
|
30
23
|
});
|
|
31
24
|
}
|
|
@@ -72,13 +65,9 @@ ${token.json(questions)}
|
|
|
72
65
|
}
|
|
73
66
|
// eslint-disable-next-line no-await-in-loop
|
|
74
67
|
value[question.name] = await executorUI(question);
|
|
75
|
-
logPromptResults(question.message, value[question.name]);
|
|
76
68
|
}
|
|
77
69
|
return value;
|
|
78
70
|
};
|
|
79
|
-
function logPromptResults(questionName, answer) {
|
|
80
|
-
logToFile([questionName, answer].join(' '), 'INFO');
|
|
81
|
-
}
|
|
82
71
|
export async function nonEmptyDirectoryPrompt(directory) {
|
|
83
72
|
if (await exists(directory)) {
|
|
84
73
|
const options = [
|
package/dist/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import {CancelExecution, Abort, AbortSilent} from './error.js'\nimport {remove, exists} from './file.js'\nimport {info, completed, content, token, logUpdate, Message, Logger, stringifyMessage, debug} from './output.js'\nimport colors from './public/node/colors.js'\nimport {relative} from './path.js'\nimport {isTerminalInteractive} from './environment/local.js'\nimport {mapper as mapperUI, run as executorUI} from './ui/executor.js'\nimport {logToFile} from './log.js'\nimport {Listr as OriginalListr, ListrTask, ListrEvent, ListrTaskState, ListrBaseClassOptions} from 'listr2'\nimport findProcess from 'find-process'\n\nexport function newListr(tasks: ListrTask[], options?: object | ListrBaseClassOptions) {\n const listr = new OriginalListr(tasks, options)\n listr.tasks.forEach((task) => {\n const loggedSubtaskTitles: string[] = []\n task.subscribe((event: ListrEvent) => {\n if (event.type === 'TITLE' && typeof event.data === 'string') {\n logToFile(event.data, 'INFO')\n }\n })\n task.renderHook$.subscribe(() => {\n if (task.hasSubtasks()) {\n const activeSubtasks = task.subtasks.filter((subtask) => {\n return [ListrTaskState.PENDING, ListrTaskState.COMPLETED].includes(subtask.state as ListrTaskState)\n })\n activeSubtasks.forEach((subtask) => {\n if (subtask.title && !loggedSubtaskTitles.includes(subtask.title)) {\n loggedSubtaskTitles.push(subtask.title)\n logToFile(subtask.title, 'INFO')\n }\n })\n }\n })\n })\n return listr\n}\n\nexport type ListrTasks = ConstructorParameters<typeof OriginalListr>[0]\nexport type {ListrTaskWrapper, ListrDefaultRenderer, ListrTask} from 'listr2'\n\nexport interface PromptAnswer {\n name: string\n value: string\n}\nexport type FilterFunction = (answers: PromptAnswer[], input: string) => Promise<PromptAnswer[]>\n\ninterface BaseQuestion<TName extends string> {\n name: TName\n message: string\n preface?: string\n validate?: (value: string) => string | true\n default?: string\n result?: (value: string) => string | boolean\n choices?: QuestionChoiceType[]\n source?: (filter: FilterFunction) => FilterFunction\n}\n\ntype TextQuestion<TName extends string> = BaseQuestion<TName> & {\n type: 'input'\n // a default is required, otherwise we'd show a prompt like 'undefined'\n default: string\n}\n\ntype PasswordQuestion<TName extends string> = BaseQuestion<TName> & {\n type: 'password'\n}\n\ntype SelectableQuestion<TName extends string> = BaseQuestion<TName> & {\n type: 'select' | 'autocomplete'\n choices: QuestionChoiceType[]\n}\n\nexport type Question<TName extends string = string> =\n | TextQuestion<TName>\n | SelectableQuestion<TName>\n | PasswordQuestion<TName>\n\nexport interface QuestionChoiceType {\n name: string\n value: string\n group?: {name: string; order: number}\n}\n\nconst started = (content: Message, logger: Logger) => {\n const message = `${colors.yellow('❯')} ${stringifyMessage(content)}`\n info(message, logger)\n}\n\nconst failed = (content: Message, logger: Logger) => {\n const message = `${colors.red('✖')} ${stringifyMessage(content)}`\n info(message, logger)\n}\n\n/**\n * Performs a task with the title kept up to date and stdout available to the\n * task while it runs (there is no re-writing stdout while the task runs).\n */\nexport interface TaskOptions {\n title: string\n task: () => Promise<void | {successMessage: string}>\n}\nexport const task = async ({title, task}: TaskOptions) => {\n let success\n started(title, logUpdate)\n try {\n const result = await task()\n success = result?.successMessage || title\n } catch (err) {\n failed(title, logUpdate)\n logUpdate.done()\n throw err\n }\n completed(success, logUpdate)\n logUpdate.done()\n}\nexport const prompt = async <\n TName extends string & keyof TAnswers,\n TAnswers extends {[key in TName]: string} = {[key in TName]: string},\n>(\n questions: ReadonlyArray<Question<TName>>,\n): Promise<TAnswers> => {\n if (!isTerminalInteractive() && questions.length !== 0) {\n throw new Abort(content`\nThe CLI prompted in a non-interactive terminal with the following questions:\n${token.json(questions)}\n `)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mappedQuestions: any[] = questions.map(mapperUI)\n const value = {} as TAnswers\n for (const question of mappedQuestions) {\n if (question.preface) {\n info(question.preface)\n }\n\n // eslint-disable-next-line no-await-in-loop\n value[question.name as TName] = await executorUI(question)\n\n logPromptResults(question.message, value[question.name as TName])\n }\n return value\n}\n\nfunction logPromptResults(questionName: string, answer: string) {\n logToFile([questionName, answer].join(' '), 'INFO')\n}\n\nexport async function nonEmptyDirectoryPrompt(directory: string) {\n if (await exists(directory)) {\n const options = [\n {name: 'No, don’t delete the files', value: 'abort'},\n {name: 'Yes, delete the files', value: 'overwrite'},\n ]\n\n const relativeDirectory = relative(process.cwd(), directory)\n\n const questions: Question<'value'> = {\n type: 'select',\n name: 'value',\n message: `${relativeDirectory} is not an empty directory. Do you want to delete the existing files and continue?`,\n choices: options,\n }\n\n const choice = await prompt([questions])\n\n if (choice.value === 'abort') {\n throw new CancelExecution()\n }\n\n await remove(directory)\n }\n}\n\nexport async function terminateBlockingPortProcessPrompt(port: number, stepDescription?: string): Promise<boolean> {\n const stepDescriptionContent = stepDescription ?? 'current step'\n\n const processInfo = await findProcess('port', port)\n const formattedProcessName =\n processInfo && processInfo.length > 0 && processInfo[0]?.name\n ? ` ${content`${token.italic(`(${processInfo[0].name})`)}`.value}`\n : ''\n\n const options = [\n {name: 'Yes, terminate process in order to log in now', value: 'finish'},\n {name: `No, cancel command and try later`, value: 'cancel'},\n ]\n\n const choice = await prompt([\n {\n type: 'select',\n name: 'value',\n message: `${stepDescriptionContent} requires a port ${port} that's unavailable because it's running another process${formattedProcessName}. Terminate that process? `,\n choices: options,\n },\n ])\n return choice.value === 'finish'\n}\n\nexport const keypress = async () => {\n return new Promise((resolve, reject) => {\n const handler = (buffer: Buffer) => {\n process.stdin.setRawMode(false)\n process.stdin.pause()\n\n const bytes = Array.from(buffer)\n\n if (bytes.length && bytes[0] === 3) {\n debug('Canceled keypress, User pressed CTRL+C')\n reject(new AbortSilent())\n }\n process.nextTick(resolve)\n }\n\n process.stdin.resume()\n process.stdin.setRawMode(true)\n process.stdin.once('data', handler)\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import {CancelExecution, Abort, AbortSilent} from './error.js'\nimport {remove, exists} from './file.js'\nimport {info, completed, content, token, logUpdate, Message, Logger, stringifyMessage, debug} from './output.js'\nimport colors from './public/node/colors.js'\nimport {relative} from './path.js'\nimport {isTerminalInteractive} from './environment/local.js'\nimport {mapper as mapperUI, run as executorUI} from './ui/executor.js'\nimport {Listr as OriginalListr, ListrTask, ListrTaskState, ListrBaseClassOptions} from 'listr2'\nimport findProcess from 'find-process'\n\nexport function newListr(tasks: ListrTask[], options?: object | ListrBaseClassOptions) {\n const listr = new OriginalListr(tasks, options)\n listr.tasks.forEach((task) => {\n const loggedSubtaskTitles: string[] = []\n task.renderHook$.subscribe(() => {\n if (task.hasSubtasks()) {\n const activeSubtasks = task.subtasks.filter((subtask) => {\n return [ListrTaskState.PENDING, ListrTaskState.COMPLETED].includes(subtask.state as ListrTaskState)\n })\n activeSubtasks.forEach((subtask) => {\n if (subtask.title && !loggedSubtaskTitles.includes(subtask.title)) {\n loggedSubtaskTitles.push(subtask.title)\n }\n })\n }\n })\n })\n return listr\n}\n\nexport type ListrTasks = ConstructorParameters<typeof OriginalListr>[0]\nexport type {ListrTaskWrapper, ListrDefaultRenderer, ListrTask} from 'listr2'\n\nexport interface PromptAnswer {\n name: string\n value: string\n}\nexport type FilterFunction = (answers: PromptAnswer[], input: string) => Promise<PromptAnswer[]>\n\ninterface BaseQuestion<TName extends string> {\n name: TName\n message: string\n preface?: string\n validate?: (value: string) => string | true\n default?: string\n result?: (value: string) => string | boolean\n choices?: QuestionChoiceType[]\n source?: (filter: FilterFunction) => FilterFunction\n}\n\ntype TextQuestion<TName extends string> = BaseQuestion<TName> & {\n type: 'input'\n // a default is required, otherwise we'd show a prompt like 'undefined'\n default: string\n}\n\ntype PasswordQuestion<TName extends string> = BaseQuestion<TName> & {\n type: 'password'\n}\n\ntype SelectableQuestion<TName extends string> = BaseQuestion<TName> & {\n type: 'select' | 'autocomplete'\n choices: QuestionChoiceType[]\n}\n\nexport type Question<TName extends string = string> =\n | TextQuestion<TName>\n | SelectableQuestion<TName>\n | PasswordQuestion<TName>\n\nexport interface QuestionChoiceType {\n name: string\n value: string\n group?: {name: string; order: number}\n}\n\nconst started = (content: Message, logger: Logger) => {\n const message = `${colors.yellow('❯')} ${stringifyMessage(content)}`\n info(message, logger)\n}\n\nconst failed = (content: Message, logger: Logger) => {\n const message = `${colors.red('✖')} ${stringifyMessage(content)}`\n info(message, logger)\n}\n\n/**\n * Performs a task with the title kept up to date and stdout available to the\n * task while it runs (there is no re-writing stdout while the task runs).\n */\nexport interface TaskOptions {\n title: string\n task: () => Promise<void | {successMessage: string}>\n}\nexport const task = async ({title, task}: TaskOptions) => {\n let success\n started(title, logUpdate)\n try {\n const result = await task()\n success = result?.successMessage || title\n } catch (err) {\n failed(title, logUpdate)\n logUpdate.done()\n throw err\n }\n completed(success, logUpdate)\n logUpdate.done()\n}\nexport const prompt = async <\n TName extends string & keyof TAnswers,\n TAnswers extends {[key in TName]: string} = {[key in TName]: string},\n>(\n questions: ReadonlyArray<Question<TName>>,\n): Promise<TAnswers> => {\n if (!isTerminalInteractive() && questions.length !== 0) {\n throw new Abort(content`\nThe CLI prompted in a non-interactive terminal with the following questions:\n${token.json(questions)}\n `)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mappedQuestions: any[] = questions.map(mapperUI)\n const value = {} as TAnswers\n for (const question of mappedQuestions) {\n if (question.preface) {\n info(question.preface)\n }\n\n // eslint-disable-next-line no-await-in-loop\n value[question.name as TName] = await executorUI(question)\n }\n return value\n}\n\nexport async function nonEmptyDirectoryPrompt(directory: string) {\n if (await exists(directory)) {\n const options = [\n {name: 'No, don’t delete the files', value: 'abort'},\n {name: 'Yes, delete the files', value: 'overwrite'},\n ]\n\n const relativeDirectory = relative(process.cwd(), directory)\n\n const questions: Question<'value'> = {\n type: 'select',\n name: 'value',\n message: `${relativeDirectory} is not an empty directory. Do you want to delete the existing files and continue?`,\n choices: options,\n }\n\n const choice = await prompt([questions])\n\n if (choice.value === 'abort') {\n throw new CancelExecution()\n }\n\n await remove(directory)\n }\n}\n\nexport async function terminateBlockingPortProcessPrompt(port: number, stepDescription?: string): Promise<boolean> {\n const stepDescriptionContent = stepDescription ?? 'current step'\n\n const processInfo = await findProcess('port', port)\n const formattedProcessName =\n processInfo && processInfo.length > 0 && processInfo[0]?.name\n ? ` ${content`${token.italic(`(${processInfo[0].name})`)}`.value}`\n : ''\n\n const options = [\n {name: 'Yes, terminate process in order to log in now', value: 'finish'},\n {name: `No, cancel command and try later`, value: 'cancel'},\n ]\n\n const choice = await prompt([\n {\n type: 'select',\n name: 'value',\n message: `${stepDescriptionContent} requires a port ${port} that's unavailable because it's running another process${formattedProcessName}. Terminate that process? `,\n choices: options,\n },\n ])\n return choice.value === 'finish'\n}\n\nexport const keypress = async () => {\n return new Promise((resolve, reject) => {\n const handler = (buffer: Buffer) => {\n process.stdin.setRawMode(false)\n process.stdin.pause()\n\n const bytes = Array.from(buffer)\n\n if (bytes.length && bytes[0] === 3) {\n debug('Canceled keypress, User pressed CTRL+C')\n reject(new AbortSilent())\n }\n process.nextTick(resolve)\n }\n\n process.stdin.resume()\n process.stdin.setRawMode(true)\n process.stdin.once('data', handler)\n })\n}\n"]}
|
package/package.json
CHANGED
package/dist/log.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
|
-
import { Transform, TransformCallback, TransformOptions } from 'node:stream';
|
|
4
|
-
interface LinesTruncatorTransformerOptions {
|
|
5
|
-
fileSize: number;
|
|
6
|
-
maxFileSize?: number;
|
|
7
|
-
maxFileSizeToTruncate?: number;
|
|
8
|
-
}
|
|
9
|
-
export declare class LinesTruncatorTransformer extends Transform {
|
|
10
|
-
linesToRetain: string[];
|
|
11
|
-
lastLineCompleted: boolean;
|
|
12
|
-
contentSize: number;
|
|
13
|
-
options: LinesTruncatorTransformerOptions;
|
|
14
|
-
constructor(truncatorOptions: LinesTruncatorTransformerOptions, opts?: TransformOptions);
|
|
15
|
-
_transform(chunk: unknown, encoding: BufferEncoding, callback: TransformCallback): void;
|
|
16
|
-
_flush(callback: TransformCallback): void;
|
|
17
|
-
shouldTruncate(chunk: unknown): boolean;
|
|
18
|
-
truncate(chunk: unknown): void;
|
|
19
|
-
calculateNumLinesToRetain(): number;
|
|
20
|
-
completeLastLine(tokens: string[]): void;
|
|
21
|
-
}
|
|
22
|
-
export declare function initiateLogging(options?: {
|
|
23
|
-
logDir?: string;
|
|
24
|
-
override?: boolean;
|
|
25
|
-
}): Promise<void>;
|
|
26
|
-
export declare function closeLogging(): void;
|
|
27
|
-
export declare function logToFile(message: string, logLevel: string): void;
|
|
28
|
-
export declare function pageLogs({ lastCommand }: {
|
|
29
|
-
lastCommand: boolean;
|
|
30
|
-
}): Promise<void>;
|
|
31
|
-
export {};
|
package/dist/log.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { isUnitTest } from './environment/local.js';
|
|
2
|
-
import constants from './constants.js';
|
|
3
|
-
import { generateRandomUUID } from './id.js';
|
|
4
|
-
import { mkdirSync as fileMkdirSync, size as fileSize, touchSync as fileTouchSync, readSync as fileReadSync, } from './file.js';
|
|
5
|
-
import { join as pathJoin } from './path.js';
|
|
6
|
-
import { consoleLog, debug } from './output.js';
|
|
7
|
-
import { page } from './system.js';
|
|
8
|
-
import { promisify } from 'node:util';
|
|
9
|
-
import { Stream, Transform } from 'node:stream';
|
|
10
|
-
import { createWriteStream, createReadStream, unlinkSync } from 'node:fs';
|
|
11
|
-
import { EOL } from 'node:os';
|
|
12
|
-
const logFileName = 'shopify.cli.log';
|
|
13
|
-
const maxLogFileSize = 5 * 1024 * 1024;
|
|
14
|
-
const maxLogFileSizeToTruncate = 30 * 1024 * 1024;
|
|
15
|
-
let logFileStream;
|
|
16
|
-
let commandUuid;
|
|
17
|
-
let logFilePath;
|
|
18
|
-
export class LinesTruncatorTransformer extends Transform {
|
|
19
|
-
constructor(truncatorOptions, opts) {
|
|
20
|
-
super(opts);
|
|
21
|
-
this.linesToRetain = [];
|
|
22
|
-
this.lastLineCompleted = true;
|
|
23
|
-
this.contentSize = 0;
|
|
24
|
-
this.options = truncatorOptions;
|
|
25
|
-
}
|
|
26
|
-
_transform(chunk, encoding, callback) {
|
|
27
|
-
if (this.shouldTruncate(chunk)) {
|
|
28
|
-
this.truncate(chunk);
|
|
29
|
-
}
|
|
30
|
-
callback();
|
|
31
|
-
}
|
|
32
|
-
_flush(callback) {
|
|
33
|
-
this.push(this.linesToRetain.join(EOL));
|
|
34
|
-
callback();
|
|
35
|
-
}
|
|
36
|
-
shouldTruncate(chunk) {
|
|
37
|
-
this.contentSize += chunk.toString().length;
|
|
38
|
-
return this.options.fileSize - this.contentSize < (this.options.maxFileSizeToTruncate ?? maxLogFileSizeToTruncate);
|
|
39
|
-
}
|
|
40
|
-
truncate(chunk) {
|
|
41
|
-
const tokens = chunk.toString().split(EOL);
|
|
42
|
-
this.completeLastLine(tokens);
|
|
43
|
-
// last splitted token will be an empty string when last character is a breakline
|
|
44
|
-
this.lastLineCompleted = tokens[tokens.length - 1] === '';
|
|
45
|
-
if (this.lastLineCompleted) {
|
|
46
|
-
tokens.pop();
|
|
47
|
-
}
|
|
48
|
-
this.linesToRetain = this.linesToRetain.concat(tokens);
|
|
49
|
-
const numLinesToRetain = this.calculateNumLinesToRetain();
|
|
50
|
-
if (this.linesToRetain.length > numLinesToRetain) {
|
|
51
|
-
this.linesToRetain = this.linesToRetain.splice(this.linesToRetain.length - numLinesToRetain);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// Lines retained length average is used so the number of lines depends on the length of them
|
|
55
|
-
calculateNumLinesToRetain() {
|
|
56
|
-
return Math.floor((this.options.maxFileSize ?? maxLogFileSize) /
|
|
57
|
-
(this.linesToRetain.map((line) => line.length).reduce((l1, l2) => l1 + l2, 0) / this.linesToRetain.length));
|
|
58
|
-
}
|
|
59
|
-
completeLastLine(tokens) {
|
|
60
|
-
if (this.lastLineCompleted) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const remainingToken = tokens.shift() ?? '';
|
|
64
|
-
const incompleteToken = this.linesToRetain[this.linesToRetain.length - 1] ?? '';
|
|
65
|
-
this.linesToRetain[this.linesToRetain.length - 1] = incompleteToken.concat(remainingToken);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
export async function initiateLogging(options = {}) {
|
|
69
|
-
if (isUnitTest())
|
|
70
|
-
return;
|
|
71
|
-
commandUuid = generateRandomUUID();
|
|
72
|
-
logFilePath = getLogFilePath(options);
|
|
73
|
-
await truncateLogs(logFilePath);
|
|
74
|
-
logFileStream = createWriteStream(logFilePath, { flags: 'a' });
|
|
75
|
-
}
|
|
76
|
-
export function closeLogging() {
|
|
77
|
-
if (logFileExists()) {
|
|
78
|
-
logFileStream.end();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
// DO NOT USE THIS FUNCTION DIRECTLY under normal circumstances.
|
|
82
|
-
// It is exported purely for use in cases where output is already being logged
|
|
83
|
-
// to the terminal but is not reflected in the logfile, e.g. Listr output.
|
|
84
|
-
export function logToFile(message, logLevel) {
|
|
85
|
-
// If file logging hasn't been initiated, skip it
|
|
86
|
-
if (!logFileExists())
|
|
87
|
-
return;
|
|
88
|
-
const timestamp = new Date().toISOString();
|
|
89
|
-
const logContents = `[${timestamp} ${commandUuid} ${logLevel}]: ${message}\n`;
|
|
90
|
-
logFileStream.write(logContents);
|
|
91
|
-
}
|
|
92
|
-
export async function pageLogs({ lastCommand }) {
|
|
93
|
-
const logDir = constants.paths.directories.cache.path();
|
|
94
|
-
const logFile = pathJoin(logDir, logFileName);
|
|
95
|
-
// Ensure file exists in case they deleted it or something
|
|
96
|
-
fileTouchSync(logFile);
|
|
97
|
-
if (lastCommand) {
|
|
98
|
-
printLastCommand(logFile);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
await page(logFile);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function getLogFilePath(options = {}) {
|
|
105
|
-
if (!logFilePath || options.override) {
|
|
106
|
-
const logDir = options.logDir || constants.paths.directories.cache.path();
|
|
107
|
-
fileMkdirSync(logDir);
|
|
108
|
-
logFilePath = pathJoin(logDir, logFileName);
|
|
109
|
-
fileTouchSync(logFilePath);
|
|
110
|
-
}
|
|
111
|
-
return logFilePath;
|
|
112
|
-
}
|
|
113
|
-
// Shaves off older log lines if logs are over maxLogFileSize long.
|
|
114
|
-
async function truncateLogs(logFile) {
|
|
115
|
-
const size = await fileSize(logFile);
|
|
116
|
-
if (size < maxLogFileSize) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
debug(`Starting the truncation of the ${Math.floor(size / (1024 * 1024)).toLocaleString('en-US')}MB log file`);
|
|
120
|
-
const tmpLogFile = logFile.concat('.tmp');
|
|
121
|
-
const truncateLines = new LinesTruncatorTransformer({ fileSize: size });
|
|
122
|
-
const pipeline = promisify(Stream.pipeline);
|
|
123
|
-
await pipeline(createReadStream(logFile), truncateLines, createWriteStream(tmpLogFile));
|
|
124
|
-
await pipeline(createReadStream(tmpLogFile), createWriteStream(logFile));
|
|
125
|
-
unlinkSync(tmpLogFile);
|
|
126
|
-
debug('Finished log truncation process');
|
|
127
|
-
}
|
|
128
|
-
function logFileExists() {
|
|
129
|
-
return Boolean(logFileStream);
|
|
130
|
-
}
|
|
131
|
-
function printLastCommand(logFile) {
|
|
132
|
-
const contents = fileReadSync(logFile).split('\n');
|
|
133
|
-
const uuids = contents
|
|
134
|
-
.map(logfileLineUUID)
|
|
135
|
-
.filter((uuid) => uuid)
|
|
136
|
-
.reverse();
|
|
137
|
-
// 2nd unique UUID, because the currently running command will be the 1st
|
|
138
|
-
const relevantUuid = Array.from(new Set(uuids))[1];
|
|
139
|
-
if (relevantUuid) {
|
|
140
|
-
consoleLog(relevantLines(contents, relevantUuid).join('\n'));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function relevantLines(contents, relevantUuid) {
|
|
144
|
-
// We run through the file line by line, keeping track of the most recently
|
|
145
|
-
// encountered UUID.
|
|
146
|
-
//
|
|
147
|
-
// If the current line has a UUID, it's a new logged unit and should be
|
|
148
|
-
// considered. Otherwise, the line is related to the most recent UUID.
|
|
149
|
-
let mostRecentUuid = '';
|
|
150
|
-
return contents.filter((line) => {
|
|
151
|
-
const currentUuid = logfileLineUUID(line) || mostRecentUuid;
|
|
152
|
-
mostRecentUuid = currentUuid;
|
|
153
|
-
return currentUuid === relevantUuid;
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
function logfileLineUUID(line) {
|
|
157
|
-
// Log lines look like:
|
|
158
|
-
//
|
|
159
|
-
// timestamp UUID contents
|
|
160
|
-
// ===========================================================================================
|
|
161
|
-
// [2022-07-20T08:51:40.296Z 5288e1da-a06a-4f96-b1a6-e34fcdd7b416 DEBUG]: Running command logs
|
|
162
|
-
// ===========================================================================================
|
|
163
|
-
//
|
|
164
|
-
// There may be subsequent lines if the contents section is multi-line.
|
|
165
|
-
//
|
|
166
|
-
const match = line.match(/^\[\S+ ([0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}) [A-Z]+\]/);
|
|
167
|
-
return match && match[1];
|
|
168
|
-
}
|
|
169
|
-
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"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","sourcesContent":["import {isUnitTest} from './environment/local.js'\nimport constants from './constants.js'\nimport {generateRandomUUID} from './id.js'\nimport {\n mkdirSync as fileMkdirSync,\n size as fileSize,\n touchSync as fileTouchSync,\n readSync as fileReadSync,\n} from './file.js'\nimport {join as pathJoin} from './path.js'\nimport {consoleLog, debug} from './output.js'\nimport {page} from './system.js'\nimport {promisify} from 'node:util'\nimport {Stream, Transform, TransformCallback, TransformOptions} from 'node:stream'\nimport {WriteStream, createWriteStream, createReadStream, unlinkSync} from 'node:fs'\nimport {EOL} from 'node:os'\n\nconst logFileName = 'shopify.cli.log'\nconst maxLogFileSize = 5 * 1024 * 1024\nconst maxLogFileSizeToTruncate = 30 * 1024 * 1024\nlet logFileStream: WriteStream\nlet commandUuid: string\nlet logFilePath: string\n\ninterface LinesTruncatorTransformerOptions {\n fileSize: number\n maxFileSize?: number\n maxFileSizeToTruncate?: number\n}\nexport class LinesTruncatorTransformer extends Transform {\n linesToRetain: string[] = []\n lastLineCompleted = true\n contentSize = 0\n options: LinesTruncatorTransformerOptions\n\n constructor(truncatorOptions: LinesTruncatorTransformerOptions, opts?: TransformOptions) {\n super(opts)\n this.options = truncatorOptions\n }\n\n _transform(chunk: unknown, encoding: BufferEncoding, callback: TransformCallback): void {\n if (this.shouldTruncate(chunk)) {\n this.truncate(chunk)\n }\n callback()\n }\n\n _flush(callback: TransformCallback): void {\n this.push(this.linesToRetain.join(EOL))\n callback()\n }\n\n shouldTruncate(chunk: unknown): boolean {\n this.contentSize += (chunk as string).toString().length\n return this.options.fileSize - this.contentSize < (this.options.maxFileSizeToTruncate ?? maxLogFileSizeToTruncate)\n }\n\n truncate(chunk: unknown) {\n const tokens = (chunk as string).toString().split(EOL)\n\n this.completeLastLine(tokens)\n // last splitted token will be an empty string when last character is a breakline\n this.lastLineCompleted = tokens[tokens.length - 1] === ''\n if (this.lastLineCompleted) {\n tokens.pop()\n }\n this.linesToRetain = this.linesToRetain.concat(tokens)\n\n const numLinesToRetain = this.calculateNumLinesToRetain()\n if (this.linesToRetain.length > numLinesToRetain) {\n this.linesToRetain = this.linesToRetain.splice(this.linesToRetain.length - numLinesToRetain)\n }\n }\n\n // Lines retained length average is used so the number of lines depends on the length of them\n calculateNumLinesToRetain() {\n return Math.floor(\n (this.options.maxFileSize ?? maxLogFileSize) /\n (this.linesToRetain.map((line) => line.length).reduce((l1, l2) => l1 + l2, 0) / this.linesToRetain.length),\n )\n }\n\n completeLastLine(tokens: string[]) {\n if (this.lastLineCompleted) {\n return\n }\n\n const remainingToken = tokens.shift() ?? ''\n const incompleteToken = this.linesToRetain[this.linesToRetain.length - 1] ?? ''\n this.linesToRetain[this.linesToRetain.length - 1] = incompleteToken.concat(remainingToken)\n }\n}\n\nexport async function initiateLogging(options: {logDir?: string; override?: boolean} = {}) {\n if (isUnitTest()) return\n commandUuid = generateRandomUUID()\n logFilePath = getLogFilePath(options)\n await truncateLogs(logFilePath)\n logFileStream = createWriteStream(logFilePath, {flags: 'a'})\n}\n\nexport function closeLogging() {\n if (logFileExists()) {\n logFileStream.end()\n }\n}\n\n// DO NOT USE THIS FUNCTION DIRECTLY under normal circumstances.\n// It is exported purely for use in cases where output is already being logged\n// to the terminal but is not reflected in the logfile, e.g. Listr output.\nexport function logToFile(message: string, logLevel: string): void {\n // If file logging hasn't been initiated, skip it\n if (!logFileExists()) return\n const timestamp = new Date().toISOString()\n const logContents = `[${timestamp} ${commandUuid} ${logLevel}]: ${message}\\n`\n logFileStream.write(logContents)\n}\n\nexport async function pageLogs({lastCommand}: {lastCommand: boolean}) {\n const logDir = constants.paths.directories.cache.path()\n const logFile = pathJoin(logDir, logFileName)\n // Ensure file exists in case they deleted it or something\n fileTouchSync(logFile)\n if (lastCommand) {\n printLastCommand(logFile)\n } else {\n await page(logFile)\n }\n}\n\nfunction getLogFilePath(options: {logDir?: string; override?: boolean} = {}) {\n if (!logFilePath || options.override) {\n const logDir = options.logDir || constants.paths.directories.cache.path()\n fileMkdirSync(logDir)\n logFilePath = pathJoin(logDir, logFileName)\n fileTouchSync(logFilePath)\n }\n\n return logFilePath\n}\n\n// Shaves off older log lines if logs are over maxLogFileSize long.\nasync function truncateLogs(logFile: string) {\n const size = await fileSize(logFile)\n if (size < maxLogFileSize) {\n return\n }\n debug(`Starting the truncation of the ${Math.floor(size / (1024 * 1024)).toLocaleString('en-US')}MB log file`)\n const tmpLogFile = logFile.concat('.tmp')\n const truncateLines = new LinesTruncatorTransformer({fileSize: size})\n const pipeline = promisify(Stream.pipeline)\n await pipeline(createReadStream(logFile), truncateLines, createWriteStream(tmpLogFile))\n await pipeline(createReadStream(tmpLogFile), createWriteStream(logFile))\n unlinkSync(tmpLogFile)\n debug('Finished log truncation process')\n}\n\nfunction logFileExists(): boolean {\n return Boolean(logFileStream)\n}\n\nfunction printLastCommand(logFile: string): void {\n const contents = fileReadSync(logFile).split('\\n')\n const uuids = contents\n .map(logfileLineUUID)\n .filter((uuid) => uuid)\n .reverse()\n // 2nd unique UUID, because the currently running command will be the 1st\n const relevantUuid = Array.from(new Set(uuids))[1]\n if (relevantUuid) {\n consoleLog(relevantLines(contents, relevantUuid).join('\\n'))\n }\n}\n\nfunction relevantLines(contents: string[], relevantUuid: string): string[] {\n // We run through the file line by line, keeping track of the most recently\n // encountered UUID.\n //\n // If the current line has a UUID, it's a new logged unit and should be\n // considered. Otherwise, the line is related to the most recent UUID.\n let mostRecentUuid = ''\n return contents.filter((line: string) => {\n const currentUuid = logfileLineUUID(line) || mostRecentUuid\n mostRecentUuid = currentUuid\n return currentUuid === relevantUuid\n })\n}\n\nfunction logfileLineUUID(line: string): string | null {\n // Log lines look like:\n //\n // timestamp UUID contents\n // ===========================================================================================\n // [2022-07-20T08:51:40.296Z 5288e1da-a06a-4f96-b1a6-e34fcdd7b416 DEBUG]: Running command logs\n // ===========================================================================================\n //\n // There may be subsequent lines if the contents section is multi-line.\n //\n const match = line.match(/^\\[\\S+ ([0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}) [A-Z]+\\]/)\n return match && match[1]!\n}\n"]}
|