@shopify/cli-kit 3.3.3 → 3.4.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/api/admin.js +20 -34
  3. package/dist/api/admin.js.map +1 -1
  4. package/dist/api/common.d.ts +11 -1
  5. package/dist/api/common.js +50 -4
  6. package/dist/api/common.js.map +1 -1
  7. package/dist/api/identity.js +3 -3
  8. package/dist/api/identity.js.map +1 -1
  9. package/dist/api/partners.d.ts +0 -5
  10. package/dist/api/partners.js +22 -42
  11. package/dist/api/partners.js.map +1 -1
  12. package/dist/constants.d.ts +1 -0
  13. package/dist/constants.js +1 -0
  14. package/dist/constants.js.map +1 -1
  15. package/dist/environment/local.d.ts +1 -0
  16. package/dist/environment/local.js +3 -0
  17. package/dist/environment/local.js.map +1 -1
  18. package/dist/environment/service.d.ts +2 -1
  19. package/dist/environment/service.js +16 -0
  20. package/dist/environment/service.js.map +1 -1
  21. package/dist/error.d.ts +5 -0
  22. package/dist/error.js +10 -4
  23. package/dist/error.js.map +1 -1
  24. package/dist/file.d.ts +2 -0
  25. package/dist/file.js +13 -1
  26. package/dist/file.js.map +1 -1
  27. package/dist/git.js +12 -4
  28. package/dist/git.js.map +1 -1
  29. package/dist/http/fetch.d.ts +9 -2
  30. package/dist/http/fetch.js +11 -2
  31. package/dist/http/fetch.js.map +1 -1
  32. package/dist/http/graphql.d.ts +15 -0
  33. package/dist/http/graphql.js +12 -0
  34. package/dist/http/graphql.js.map +1 -0
  35. package/dist/http.d.ts +24 -0
  36. package/dist/http.js +27 -0
  37. package/dist/http.js.map +1 -1
  38. package/dist/network/service.d.ts +1 -1
  39. package/dist/network/service.js.map +1 -1
  40. package/dist/node/base-command.d.ts +1 -0
  41. package/dist/node/base-command.js +10 -1
  42. package/dist/node/base-command.js.map +1 -1
  43. package/dist/node/cli.d.ts +0 -2
  44. package/dist/node/cli.js +0 -4
  45. package/dist/node/cli.js.map +1 -1
  46. package/dist/node/error-handler.d.ts +20 -0
  47. package/dist/node/error-handler.js +40 -3
  48. package/dist/node/error-handler.js.map +1 -1
  49. package/dist/node/hooks/init.d.ts +2 -0
  50. package/dist/node/hooks/init.js +7 -0
  51. package/dist/node/hooks/init.js.map +1 -0
  52. package/dist/node/hooks/postrun.js +4 -1
  53. package/dist/node/hooks/postrun.js.map +1 -1
  54. package/dist/node/hooks/prerun.js +2 -0
  55. package/dist/node/hooks/prerun.js.map +1 -1
  56. package/dist/node/ruby.d.ts +7 -2
  57. package/dist/node/ruby.js +22 -7
  58. package/dist/node/ruby.js.map +1 -1
  59. package/dist/output.d.ts +18 -11
  60. package/dist/output.js +96 -26
  61. package/dist/output.js.map +1 -1
  62. package/dist/session/exchange.js +2 -2
  63. package/dist/session/exchange.js.map +1 -1
  64. package/dist/session/validate.js +3 -0
  65. package/dist/session/validate.js.map +1 -1
  66. package/dist/session.js +7 -1
  67. package/dist/session.js.map +1 -1
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/dist/ui.d.ts +11 -0
  70. package/dist/ui.js +35 -5
  71. package/dist/ui.js.map +1 -1
  72. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"postrun.js","sourceRoot":"","sources":["../../../src/node/hooks/postrun.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAA;AAG9C,gGAAgG;AAChG,MAAM,CAAC,MAAM,QAAQ,GAAiB,KAAK,EAAE,QAAQ,EAAE,EAAE;IACvD,MAAM,WAAW,EAAE,CAAA;AACrB,CAAC,CAAA","sourcesContent":["import {reportEvent} from '../../analytics.js'\nimport {Hook} from '@oclif/core'\n\n// This hook is called after each successful command run. More info: https://oclif.io/docs/hooks\nexport const hookPost: Hook.Postrun = async (_options) => {\n await reportEvent()\n}\n"]}
1
+ {"version":3,"file":"postrun.js","sourceRoot":"","sources":["../../../src/node/hooks/postrun.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAC,KAAK,EAAC,MAAM,iBAAiB,CAAA;AAGrC,gGAAgG;AAChG,MAAM,CAAC,MAAM,QAAQ,GAAiB,KAAK,EAAE,OAAO,EAAE,EAAE;IACtD,MAAM,WAAW,EAAE,CAAA;IACnB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACvD,KAAK,CAAC,qBAAqB,OAAO,EAAE,CAAC,CAAA;AACvC,CAAC,CAAA","sourcesContent":["import {reportEvent} from '../../analytics.js'\nimport {debug} from '../../output.js'\nimport {Hook} from '@oclif/core'\n\n// This hook is called after each successful command run. More info: https://oclif.io/docs/hooks\nexport const hookPost: Hook.Postrun = async (options) => {\n await reportEvent()\n const command = options.Command?.id?.replace(/:/g, ' ')\n debug(`Completed command ${command}`)\n}\n"]}
@@ -1,9 +1,11 @@
1
1
  import { start } from '../../analytics.js';
2
+ import { debug } from '../../output.js';
2
3
  // This hook is called before each command run. More info: https://oclif.io/docs/hooks
3
4
  export const hook = async (options) => {
4
5
  const cmd = options.Command.aliases.length === 0 ? options.Command.id : options.Command.aliases[0];
5
6
  const command = cmd.replace(/:/g, ' ');
6
7
  const args = options.argv;
8
+ debug(`Running command ${command}`);
7
9
  start({ command, args });
8
10
  };
9
11
  //# sourceMappingURL=prerun.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prerun.js","sourceRoot":"","sources":["../../../src/node/hooks/prerun.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,oBAAoB,CAAA;AAGxC,sFAAsF;AACtF,MAAM,CAAC,MAAM,IAAI,GAAgB,KAAK,EAAE,OAAO,EAAE,EAAE;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAClG,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IACzB,KAAK,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC,CAAA;AACxB,CAAC,CAAA","sourcesContent":["import {start} from '../../analytics.js'\nimport {Hook} from '@oclif/core'\n\n// This hook is called before each command run. More info: https://oclif.io/docs/hooks\nexport const hook: Hook.Prerun = async (options) => {\n const cmd = options.Command.aliases.length === 0 ? options.Command.id : options.Command.aliases[0]\n const command = cmd.replace(/:/g, ' ')\n const args = options.argv\n start({command, args})\n}\n"]}
1
+ {"version":3,"file":"prerun.js","sourceRoot":"","sources":["../../../src/node/hooks/prerun.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAC,KAAK,EAAC,MAAM,iBAAiB,CAAA;AAGrC,sFAAsF;AACtF,MAAM,CAAC,MAAM,IAAI,GAAgB,KAAK,EAAE,OAAO,EAAE,EAAE;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAClG,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IACzB,KAAK,CAAC,mBAAmB,OAAO,EAAE,CAAC,CAAA;IACnC,KAAK,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC,CAAA;AACxB,CAAC,CAAA","sourcesContent":["import {start} from '../../analytics.js'\nimport {debug} from '../../output.js'\nimport {Hook} from '@oclif/core'\n\n// This hook is called before each command run. More info: https://oclif.io/docs/hooks\nexport const hook: Hook.Prerun = async (options) => {\n const cmd = options.Command.aliases.length === 0 ? options.Command.id : options.Command.aliases[0]\n const command = cmd.replace(/:/g, ' ')\n const args = options.argv\n debug(`Running command ${command}`)\n start({command, args})\n}\n"]}
@@ -1,15 +1,20 @@
1
1
  /// <reference types="node" />
2
2
  import { AdminSession } from '../session.js';
3
3
  import { Writable } from 'node:stream';
4
+ interface ExecCLI2Options {
5
+ adminSession?: AdminSession;
6
+ storefrontToken?: string;
7
+ directory?: string;
8
+ }
4
9
  /**
5
10
  * Execute CLI 2.0 commands.
6
11
  * Installs a version of RubyCLI as a vendor dependency in a hidden folder in the system.
7
12
  * User must have a valid ruby+bundler environment to run any command.
8
13
  *
9
14
  * @param args {string[]} List of argumets to execute. (ex: ['theme', 'pull'])
10
- * @param adminSession {AdminSession} Contains token and store to pass to CLI 2.0, which will be set as environment variables
15
+ * @param options {ExecCLI2Options}
11
16
  */
12
- export declare function execCLI2(args: string[], adminSession?: AdminSession): Promise<void>;
17
+ export declare function execCLI2(args: string[], { adminSession, storefrontToken, directory }?: ExecCLI2Options): Promise<void>;
13
18
  interface ExecThemeCheckCLIOptions {
14
19
  /** A list of directories in which theme-check should run */
15
20
  directories: string[];
package/dist/node/ruby.js CHANGED
@@ -18,18 +18,23 @@ const MinRubyGemVersion = '2.5.0';
18
18
  * User must have a valid ruby+bundler environment to run any command.
19
19
  *
20
20
  * @param args {string[]} List of argumets to execute. (ex: ['theme', 'pull'])
21
- * @param adminSession {AdminSession} Contains token and store to pass to CLI 2.0, which will be set as environment variables
21
+ * @param options {ExecCLI2Options}
22
22
  */
23
- export async function execCLI2(args, adminSession) {
23
+ export async function execCLI2(args, { adminSession, storefrontToken, directory } = {}) {
24
24
  await installCLIDependencies();
25
25
  const env = {
26
26
  ...process.env,
27
+ SHOPIFY_CLI_STOREFRONT_RENDERER_AUTH_TOKEN: storefrontToken,
27
28
  SHOPIFY_CLI_ADMIN_AUTH_TOKEN: adminSession?.token,
28
29
  SHOPIFY_CLI_STORE: adminSession?.storeFqdn,
30
+ // Bundler uses this Gemfile to understand which gems are available in the
31
+ // environment. We use this to specify our own Gemfile for CLI2, which exists
32
+ // outside the user's project directory.
33
+ BUNDLE_GEMFILE: join(shopifyCLIDirectory(), 'Gemfile'),
29
34
  };
30
35
  await system.exec('bundle', ['exec', 'shopify'].concat(args), {
31
36
  stdio: 'inherit',
32
- cwd: shopifyCLIDirectory(),
37
+ cwd: directory ?? process.cwd(),
33
38
  env,
34
39
  });
35
40
  }
@@ -106,10 +111,16 @@ async function installCLIDependencies() {
106
111
  {
107
112
  title: 'Installing theme dependencies',
108
113
  task: async () => {
114
+ const usingLocalCLI2 = Boolean(process.env.SHOPIFY_CLI_2_0_DIRECTORY);
109
115
  await validateRubyEnv();
110
- await createShopifyCLIWorkingDirectory();
111
- await createShopifyCLIGemfile();
112
- await bundleInstallShopifyCLI();
116
+ if (usingLocalCLI2) {
117
+ await bundleInstallLocalShopifyCLI();
118
+ }
119
+ else {
120
+ await createShopifyCLIWorkingDirectory();
121
+ await createShopifyCLIGemfile();
122
+ await bundleInstallShopifyCLI();
123
+ }
113
124
  },
114
125
  },
115
126
  ], { renderer });
@@ -170,6 +181,9 @@ async function createThemeCheckGemfile() {
170
181
  const gemPath = join(themeCheckDirectory(), 'Gemfile');
171
182
  await file.write(gemPath, `source 'https://rubygems.org'\ngem 'theme-check', '${ThemeCheckVersion}'`);
172
183
  }
184
+ async function bundleInstallLocalShopifyCLI() {
185
+ await system.exec('bundle', ['install'], { cwd: shopifyCLIDirectory() });
186
+ }
173
187
  async function bundleInstallShopifyCLI() {
174
188
  await system.exec('bundle', ['config', 'set', '--local', 'path', shopifyCLIDirectory()], { cwd: shopifyCLIDirectory() });
175
189
  await system.exec('bundle', ['install'], { cwd: shopifyCLIDirectory() });
@@ -179,7 +193,8 @@ async function bundleInstallThemeCheck() {
179
193
  await system.exec('bundle', ['install'], { cwd: themeCheckDirectory() });
180
194
  }
181
195
  function shopifyCLIDirectory() {
182
- return join(constants.paths.directories.cache.vendor.path(), 'ruby-cli', RubyCLIVersion);
196
+ return (process.env.SHOPIFY_CLI_2_0_DIRECTORY ??
197
+ join(constants.paths.directories.cache.vendor.path(), 'ruby-cli', RubyCLIVersion));
183
198
  }
184
199
  function themeCheckDirectory() {
185
200
  return join(constants.paths.directories.cache.vendor.path(), 'theme-check', ThemeCheckVersion);
@@ -1 +1 @@
1
- {"version":3,"file":"ruby.js","sourceRoot":"","sources":["../../src/node/ruby.ts"],"names":[],"mappings":"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","sourcesContent":["import * as file from '../file.js'\nimport * as ui from '../ui.js'\nimport * as system from '../system.js'\nimport {Abort} from '../error.js'\nimport {glob, join} from '../path.js'\nimport constants from '../constants.js'\nimport {coerce} from '../semver.js'\nimport {AdminSession} from '../session.js'\nimport {content, token} from '../output.js'\nimport {Writable} from 'node:stream'\n\nconst RubyCLIVersion = '2.20.1'\nconst ThemeCheckVersion = '1.10.3'\nconst MinBundlerVersion = '2.3.8'\nconst MinRubyVersion = '2.3.0'\nconst MinRubyGemVersion = '2.5.0'\n\n/**\n * Execute CLI 2.0 commands.\n * Installs a version of RubyCLI as a vendor dependency in a hidden folder in the system.\n * User must have a valid ruby+bundler environment to run any command.\n *\n * @param args {string[]} List of argumets to execute. (ex: ['theme', 'pull'])\n * @param adminSession {AdminSession} Contains token and store to pass to CLI 2.0, which will be set as environment variables\n */\nexport async function execCLI2(args: string[], adminSession?: AdminSession) {\n await installCLIDependencies()\n const env = {\n ...process.env,\n SHOPIFY_CLI_ADMIN_AUTH_TOKEN: adminSession?.token,\n SHOPIFY_CLI_STORE: adminSession?.storeFqdn,\n }\n\n await system.exec('bundle', ['exec', 'shopify'].concat(args), {\n stdio: 'inherit',\n cwd: shopifyCLIDirectory(),\n env,\n })\n}\n\ninterface ExecThemeCheckCLIOptions {\n /** A list of directories in which theme-check should run */\n directories: string[]\n /** Arguments to pass to the theme-check CLI */\n args?: string[]\n /** Writable to send standard output content through */\n stdout: Writable\n /** Writable to send standard error content through */\n stderr: Writable\n}\n\n/**\n * A function that installs (if needed) and runs the theme-check CLI.\n * @param options {ExecThemeCheckCLIOptions} Options to customize the execution of theme-check.\n * @returns {Promise<void>} A promise that resolves or rejects depending on the result of the underlying theme-check process.\n */\nexport async function execThemeCheckCLI({\n directories,\n args,\n stdout,\n stderr,\n}: ExecThemeCheckCLIOptions): Promise<void[]> {\n await installThemeCheckCLIDependencies(stdout)\n\n const processes = directories.map(async (directory): Promise<void> => {\n // Check that there are files aside from the extension TOML config file,\n // otherwise theme-check will return a false failure.\n const files = await glob(join(directory, '/**/*'))\n const fileCount = files.filter((file) => !file.match(/\\.toml$/)).length\n if (fileCount === 0) return\n\n const customStderr = new Writable({\n write(chunk, ...args) {\n // For some reason, theme-check reports this initial status line to stderr\n // See https://github.com/Shopify/theme-check/blob/1092737cfb58a73ca397ffb1371665dc55df2976/lib/theme_check/language_server/diagnostics_engine.rb#L31\n // which leads to https://github.com/Shopify/theme-check/blob/1092737cfb58a73ca397ffb1371665dc55df2976/lib/theme_check/language_server/io_messenger.rb#L65\n if (chunk.toString('ascii').match(/^Checking/)) {\n stdout.write(chunk, ...args)\n } else {\n stderr.write(chunk, ...args)\n }\n },\n })\n await system.exec('bundle', ['exec', 'theme-check'].concat([directory, ...(args || [])]), {\n stdout,\n stderr: customStderr,\n cwd: themeCheckDirectory(),\n })\n })\n return Promise.all(processes)\n}\n\n/**\n * Validate Ruby Enviroment\n * Install Theme Check CLI and its dependencies\n * Shows a loading message if it's the first time installing dependencies\n * or if we are installing a new version of Theme Check CLI\n */\nasync function installThemeCheckCLIDependencies(stdout: Writable) {\n const exists = await file.exists(themeCheckDirectory())\n\n if (!exists) stdout.write('Installing theme dependencies...')\n const list = ui.newListr(\n [\n {\n title: 'Installing theme dependencies',\n task: async () => {\n await validateRubyEnv()\n await createThemeCheckCLIWorkingDirectory()\n await createThemeCheckGemfile()\n await bundleInstallThemeCheck()\n },\n },\n ],\n {renderer: 'silent'},\n )\n await list.run()\n if (!exists) stdout.write('Installed theme dependencies!')\n}\n\n/**\n * Validate Ruby Enviroment\n * Install RubyCLI and its dependencies\n * Shows a loading spinner if it's the first time installing dependencies\n * or if we are installing a new version of RubyCLI\n */\nasync function installCLIDependencies() {\n const exists = await file.exists(shopifyCLIDirectory())\n const renderer = exists ? 'silent' : 'default'\n\n const list = ui.newListr(\n [\n {\n title: 'Installing theme dependencies',\n task: async () => {\n await validateRubyEnv()\n await createShopifyCLIWorkingDirectory()\n await createShopifyCLIGemfile()\n await bundleInstallShopifyCLI()\n },\n },\n ],\n {renderer},\n )\n await list.run()\n}\n\nasync function validateRubyEnv() {\n await validateRuby()\n await validateRubyGems()\n await validateBundler()\n}\n\nasync function validateRuby() {\n let version\n try {\n const stdout = await system.captureOutput('ruby', ['-v'])\n version = coerce(stdout)\n } catch {\n throw new Abort(\n 'Ruby environment not found',\n `Make sure you have Ruby installed on your system: ${\n content`${token.link('', 'https://www.ruby-lang.org/en/documentation/installation/')}`.value\n }`,\n )\n }\n\n const isValid = version?.compare(MinRubyVersion)\n if (isValid === -1 || isValid === undefined) {\n throw new Abort(\n `Ruby version ${content`${token.yellow(version!.raw)}`.value} is not supported`,\n `Make sure you have at least Ruby ${content`${token.yellow(MinRubyVersion)}`.value} installed on your system: ${\n content`${token.link('', 'https://www.ruby-lang.org/en/documentation/installation/')}`.value\n }`,\n )\n }\n}\n\nasync function validateRubyGems() {\n const stdout = await system.captureOutput('gem', ['-v'])\n const version = coerce(stdout)\n\n const isValid = version?.compare(MinRubyGemVersion)\n if (isValid === -1 || isValid === undefined) {\n throw new Abort(\n `RubyGems version ${content`${token.yellow(version!.raw)}`.value} is not supported`,\n `To update to the latest version of RubyGems, run ${\n content`${token.genericShellCommand('gem update --system')}`.value\n }`,\n )\n }\n}\n\nasync function validateBundler() {\n let version\n try {\n const stdout = await system.captureOutput('bundler', ['-v'])\n version = coerce(stdout)\n } catch {\n throw new Abort(\n 'Bundler not found',\n `To install the latest version of Bundler, run ${\n content`${token.genericShellCommand('gem install bundler')}`.value\n }`,\n )\n }\n\n const isValid = version?.compare(MinBundlerVersion)\n if (isValid === -1 || isValid === undefined) {\n throw new Abort(\n `Bundler version ${content`${token.yellow(version!.raw)}`.value} is not supported`,\n `To update to the latest version of Bundler, run ${\n content`${token.genericShellCommand('gem install bundler')}`.value\n }`,\n )\n }\n}\n\nfunction createShopifyCLIWorkingDirectory() {\n return file.mkdir(shopifyCLIDirectory())\n}\n\nfunction createThemeCheckCLIWorkingDirectory() {\n return file.mkdir(themeCheckDirectory())\n}\n\nasync function createShopifyCLIGemfile() {\n const gemPath = join(shopifyCLIDirectory(), 'Gemfile')\n await file.write(gemPath, `source 'https://rubygems.org'\\ngem 'shopify-cli', '${RubyCLIVersion}'`)\n}\n\nasync function createThemeCheckGemfile() {\n const gemPath = join(themeCheckDirectory(), 'Gemfile')\n await file.write(gemPath, `source 'https://rubygems.org'\\ngem 'theme-check', '${ThemeCheckVersion}'`)\n}\n\nasync function bundleInstallShopifyCLI() {\n await system.exec('bundle', ['config', 'set', '--local', 'path', shopifyCLIDirectory()], {cwd: shopifyCLIDirectory()})\n await system.exec('bundle', ['install'], {cwd: shopifyCLIDirectory()})\n}\n\nasync function bundleInstallThemeCheck() {\n await system.exec('bundle', ['config', 'set', '--local', 'path', themeCheckDirectory()], {cwd: themeCheckDirectory()})\n await system.exec('bundle', ['install'], {cwd: themeCheckDirectory()})\n}\n\nfunction shopifyCLIDirectory() {\n return join(constants.paths.directories.cache.vendor.path(), 'ruby-cli', RubyCLIVersion)\n}\n\nfunction themeCheckDirectory() {\n return join(constants.paths.directories.cache.vendor.path(), 'theme-check', ThemeCheckVersion)\n}\n\nexport async function version(): Promise<string | undefined> {\n const parseOutput = (version: string) => version.match(/ruby (\\d+\\.\\d+\\.\\d+)/)?.[1]\n return system\n .captureOutput('ruby', ['-v'])\n .then(parseOutput)\n .catch(() => undefined)\n}\n"]}
1
+ {"version":3,"file":"ruby.js","sourceRoot":"","sources":["../../src/node/ruby.ts"],"names":[],"mappings":"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","sourcesContent":["import * as file from '../file.js'\nimport * as ui from '../ui.js'\nimport * as system from '../system.js'\nimport {Abort} from '../error.js'\nimport {glob, join} from '../path.js'\nimport constants from '../constants.js'\nimport {coerce} from '../semver.js'\nimport {AdminSession} from '../session.js'\nimport {content, token} from '../output.js'\nimport {Writable} from 'node:stream'\n\nconst RubyCLIVersion = '2.20.1'\nconst ThemeCheckVersion = '1.10.3'\nconst MinBundlerVersion = '2.3.8'\nconst MinRubyVersion = '2.3.0'\nconst MinRubyGemVersion = '2.5.0'\n\ninterface ExecCLI2Options {\n // Contains token and store to pass to CLI 2.0, which will be set as environment variables\n adminSession?: AdminSession\n // Contains token for storefront access to pass to CLI 2.0 as environment variable\n storefrontToken?: string\n // Directory in which to execute the command. Otherwise the current directory will be used.\n directory?: string\n}\n/**\n * Execute CLI 2.0 commands.\n * Installs a version of RubyCLI as a vendor dependency in a hidden folder in the system.\n * User must have a valid ruby+bundler environment to run any command.\n *\n * @param args {string[]} List of argumets to execute. (ex: ['theme', 'pull'])\n * @param options {ExecCLI2Options}\n */\nexport async function execCLI2(args: string[], {adminSession, storefrontToken, directory}: ExecCLI2Options = {}) {\n await installCLIDependencies()\n const env = {\n ...process.env,\n SHOPIFY_CLI_STOREFRONT_RENDERER_AUTH_TOKEN: storefrontToken,\n SHOPIFY_CLI_ADMIN_AUTH_TOKEN: adminSession?.token,\n SHOPIFY_CLI_STORE: adminSession?.storeFqdn,\n // Bundler uses this Gemfile to understand which gems are available in the\n // environment. We use this to specify our own Gemfile for CLI2, which exists\n // outside the user's project directory.\n BUNDLE_GEMFILE: join(shopifyCLIDirectory(), 'Gemfile'),\n }\n\n await system.exec('bundle', ['exec', 'shopify'].concat(args), {\n stdio: 'inherit',\n cwd: directory ?? process.cwd(),\n env,\n })\n}\n\ninterface ExecThemeCheckCLIOptions {\n /** A list of directories in which theme-check should run */\n directories: string[]\n /** Arguments to pass to the theme-check CLI */\n args?: string[]\n /** Writable to send standard output content through */\n stdout: Writable\n /** Writable to send standard error content through */\n stderr: Writable\n}\n\n/**\n * A function that installs (if needed) and runs the theme-check CLI.\n * @param options {ExecThemeCheckCLIOptions} Options to customize the execution of theme-check.\n * @returns {Promise<void>} A promise that resolves or rejects depending on the result of the underlying theme-check process.\n */\nexport async function execThemeCheckCLI({\n directories,\n args,\n stdout,\n stderr,\n}: ExecThemeCheckCLIOptions): Promise<void[]> {\n await installThemeCheckCLIDependencies(stdout)\n\n const processes = directories.map(async (directory): Promise<void> => {\n // Check that there are files aside from the extension TOML config file,\n // otherwise theme-check will return a false failure.\n const files = await glob(join(directory, '/**/*'))\n const fileCount = files.filter((file) => !file.match(/\\.toml$/)).length\n if (fileCount === 0) return\n\n const customStderr = new Writable({\n write(chunk, ...args) {\n // For some reason, theme-check reports this initial status line to stderr\n // See https://github.com/Shopify/theme-check/blob/1092737cfb58a73ca397ffb1371665dc55df2976/lib/theme_check/language_server/diagnostics_engine.rb#L31\n // which leads to https://github.com/Shopify/theme-check/blob/1092737cfb58a73ca397ffb1371665dc55df2976/lib/theme_check/language_server/io_messenger.rb#L65\n if (chunk.toString('ascii').match(/^Checking/)) {\n stdout.write(chunk, ...args)\n } else {\n stderr.write(chunk, ...args)\n }\n },\n })\n await system.exec('bundle', ['exec', 'theme-check'].concat([directory, ...(args || [])]), {\n stdout,\n stderr: customStderr,\n cwd: themeCheckDirectory(),\n })\n })\n return Promise.all(processes)\n}\n\n/**\n * Validate Ruby Enviroment\n * Install Theme Check CLI and its dependencies\n * Shows a loading message if it's the first time installing dependencies\n * or if we are installing a new version of Theme Check CLI\n */\nasync function installThemeCheckCLIDependencies(stdout: Writable) {\n const exists = await file.exists(themeCheckDirectory())\n\n if (!exists) stdout.write('Installing theme dependencies...')\n const list = ui.newListr(\n [\n {\n title: 'Installing theme dependencies',\n task: async () => {\n await validateRubyEnv()\n await createThemeCheckCLIWorkingDirectory()\n await createThemeCheckGemfile()\n await bundleInstallThemeCheck()\n },\n },\n ],\n {renderer: 'silent'},\n )\n await list.run()\n if (!exists) stdout.write('Installed theme dependencies!')\n}\n\n/**\n * Validate Ruby Enviroment\n * Install RubyCLI and its dependencies\n * Shows a loading spinner if it's the first time installing dependencies\n * or if we are installing a new version of RubyCLI\n */\nasync function installCLIDependencies() {\n const exists = await file.exists(shopifyCLIDirectory())\n const renderer = exists ? 'silent' : 'default'\n\n const list = ui.newListr(\n [\n {\n title: 'Installing theme dependencies',\n task: async () => {\n const usingLocalCLI2 = Boolean(process.env.SHOPIFY_CLI_2_0_DIRECTORY)\n await validateRubyEnv()\n if (usingLocalCLI2) {\n await bundleInstallLocalShopifyCLI()\n } else {\n await createShopifyCLIWorkingDirectory()\n await createShopifyCLIGemfile()\n await bundleInstallShopifyCLI()\n }\n },\n },\n ],\n {renderer},\n )\n await list.run()\n}\n\nasync function validateRubyEnv() {\n await validateRuby()\n await validateRubyGems()\n await validateBundler()\n}\n\nasync function validateRuby() {\n let version\n try {\n const stdout = await system.captureOutput('ruby', ['-v'])\n version = coerce(stdout)\n } catch {\n throw new Abort(\n 'Ruby environment not found',\n `Make sure you have Ruby installed on your system: ${\n content`${token.link('', 'https://www.ruby-lang.org/en/documentation/installation/')}`.value\n }`,\n )\n }\n\n const isValid = version?.compare(MinRubyVersion)\n if (isValid === -1 || isValid === undefined) {\n throw new Abort(\n `Ruby version ${content`${token.yellow(version!.raw)}`.value} is not supported`,\n `Make sure you have at least Ruby ${content`${token.yellow(MinRubyVersion)}`.value} installed on your system: ${\n content`${token.link('', 'https://www.ruby-lang.org/en/documentation/installation/')}`.value\n }`,\n )\n }\n}\n\nasync function validateRubyGems() {\n const stdout = await system.captureOutput('gem', ['-v'])\n const version = coerce(stdout)\n\n const isValid = version?.compare(MinRubyGemVersion)\n if (isValid === -1 || isValid === undefined) {\n throw new Abort(\n `RubyGems version ${content`${token.yellow(version!.raw)}`.value} is not supported`,\n `To update to the latest version of RubyGems, run ${\n content`${token.genericShellCommand('gem update --system')}`.value\n }`,\n )\n }\n}\n\nasync function validateBundler() {\n let version\n try {\n const stdout = await system.captureOutput('bundler', ['-v'])\n version = coerce(stdout)\n } catch {\n throw new Abort(\n 'Bundler not found',\n `To install the latest version of Bundler, run ${\n content`${token.genericShellCommand('gem install bundler')}`.value\n }`,\n )\n }\n\n const isValid = version?.compare(MinBundlerVersion)\n if (isValid === -1 || isValid === undefined) {\n throw new Abort(\n `Bundler version ${content`${token.yellow(version!.raw)}`.value} is not supported`,\n `To update to the latest version of Bundler, run ${\n content`${token.genericShellCommand('gem install bundler')}`.value\n }`,\n )\n }\n}\n\nfunction createShopifyCLIWorkingDirectory() {\n return file.mkdir(shopifyCLIDirectory())\n}\n\nfunction createThemeCheckCLIWorkingDirectory() {\n return file.mkdir(themeCheckDirectory())\n}\n\nasync function createShopifyCLIGemfile() {\n const gemPath = join(shopifyCLIDirectory(), 'Gemfile')\n await file.write(gemPath, `source 'https://rubygems.org'\\ngem 'shopify-cli', '${RubyCLIVersion}'`)\n}\n\nasync function createThemeCheckGemfile() {\n const gemPath = join(themeCheckDirectory(), 'Gemfile')\n await file.write(gemPath, `source 'https://rubygems.org'\\ngem 'theme-check', '${ThemeCheckVersion}'`)\n}\n\nasync function bundleInstallLocalShopifyCLI() {\n await system.exec('bundle', ['install'], {cwd: shopifyCLIDirectory()})\n}\n\nasync function bundleInstallShopifyCLI() {\n await system.exec('bundle', ['config', 'set', '--local', 'path', shopifyCLIDirectory()], {cwd: shopifyCLIDirectory()})\n await system.exec('bundle', ['install'], {cwd: shopifyCLIDirectory()})\n}\n\nasync function bundleInstallThemeCheck() {\n await system.exec('bundle', ['config', 'set', '--local', 'path', themeCheckDirectory()], {cwd: themeCheckDirectory()})\n await system.exec('bundle', ['install'], {cwd: themeCheckDirectory()})\n}\n\nfunction shopifyCLIDirectory() {\n return (\n process.env.SHOPIFY_CLI_2_0_DIRECTORY ??\n join(constants.paths.directories.cache.vendor.path(), 'ruby-cli', RubyCLIVersion)\n )\n}\n\nfunction themeCheckDirectory() {\n return join(constants.paths.directories.cache.vendor.path(), 'theme-check', ThemeCheckVersion)\n}\n\nexport async function version(): Promise<string | undefined> {\n const parseOutput = (version: string) => version.match(/ruby (\\d+\\.\\d+\\.\\d+)/)?.[1]\n return system\n .captureOutput('ruby', ['-v'])\n .then(parseOutput)\n .catch(() => undefined)\n}\n"]}
package/dist/output.d.ts CHANGED
@@ -3,9 +3,9 @@ import { Fatal } from './error.js';
3
3
  import { PackageManager } from './node/node-package-manager.js';
4
4
  import { AbortSignal } from 'abort-controller';
5
5
  import { Writable } from 'node:stream';
6
- export declare function initiateLogging({ logDir, filename, }: {
6
+ export { default as logUpdate } from 'log-update';
7
+ export declare function initiateLogging(options?: {
7
8
  logDir?: string;
8
- filename?: string;
9
9
  }): void;
10
10
  declare enum ContentTokenType {
11
11
  Raw = 0,
@@ -25,6 +25,7 @@ declare enum ContentTokenType {
25
25
  interface ContentMetadata {
26
26
  link?: string;
27
27
  }
28
+ export declare type Logger = (message: string) => void;
28
29
  declare class ContentToken {
29
30
  type: ContentTokenType;
30
31
  value: Message;
@@ -68,40 +69,45 @@ export declare let collectedLogs: {
68
69
  };
69
70
  export declare const clearCollectedLogs: () => void;
70
71
  /**
71
- * Ouputs information to the user. This is akin to "console.log"
72
+ * Ouputs information to the user.
72
73
  * Info messages don't get additional formatting.
73
74
  * Note: Info messages are sent through the standard output.
74
75
  * @param content {string} The content to be output to the user.
76
+ * @param logger {Function} The logging function to use to output to the user.
75
77
  */
76
- export declare const info: (content: Message) => void;
78
+ export declare const info: (content: Message, logger?: Logger) => void;
77
79
  /**
78
80
  * Outputs a success message to the user.
79
- * Success message receive a special formatting to make them stand out in the console.
81
+ * Success messages receive a special formatting to make them stand out in the console.
80
82
  * Note: Success messages are sent through the standard output.
81
83
  * @param content {string} The content to be output to the user.
84
+ * @param logger {Function} The logging function to use to output to the user.
82
85
  */
83
- export declare const success: (content: Message) => void;
86
+ export declare const success: (content: Message, logger?: Logger) => void;
84
87
  /**
85
88
  * Outputs a completed message to the user.
86
89
  * Completed message receive a special formatting to make them stand out in the console.
87
90
  * Note: Completed messages are sent through the standard output.
88
91
  * @param content {string} The content to be output to the user.
92
+ * @param logger {Function} The logging function to use to output to the user.
89
93
  */
90
- export declare const completed: (content: Message) => void;
94
+ export declare const completed: (content: Message, logger?: Logger) => void;
91
95
  /**
92
96
  * Ouputs debug information to the user. By default these output is hidden unless the user calls the CLI with --verbose.
93
97
  * Debug messages don't get additional formatting.
94
98
  * Note: Debug messages are sent through the standard output.
95
99
  * @param content {string} The content to be output to the user.
100
+ * @param logger {Function} The logging function to use to output to the user.
96
101
  */
97
- export declare const debug: (content: Message) => void;
102
+ export declare const debug: (content: Message, logger?: Logger) => void;
98
103
  /**
99
104
  * Outputs a warning message to the user.
100
105
  * Warning messages receive a special formatting to make them stand out in the console.
101
106
  * Note: Warning messages are sent through the standard output.
102
107
  * @param content {string} The content to be output to the user.
108
+ * @param logger {Function} The logging function to use to output to the user.
103
109
  */
104
- export declare const warn: (content: Message) => void;
110
+ export declare const warn: (content: Message, logger?: Logger) => void;
105
111
  /**
106
112
  * Prints a new line in the terminal.
107
113
  */
@@ -138,7 +144,9 @@ export declare function logFileExists(): boolean;
138
144
  export declare function logToFile(message: string, logLevel: string): void;
139
145
  export declare function unstyled(message: string): string;
140
146
  export declare function shouldDisplayColors(): boolean;
141
- export declare function pageLogs(): Promise<void>;
147
+ export declare function pageLogs({ lastCommand }: {
148
+ lastCommand: boolean;
149
+ }): Promise<void>;
142
150
  /**
143
151
  *
144
152
  * @param packageManager {PackageManager} The package manager that is being used.
@@ -146,4 +154,3 @@ export declare function pageLogs(): Promise<void>;
146
154
  * @returns {te}
147
155
  */
148
156
  export declare function getOutputUpdateCLIReminder(packageManager: PackageManager, version: string): string;
149
- export {};
package/dist/output.js CHANGED
@@ -1,8 +1,9 @@
1
1
  /* eslint-disable no-console */
2
- import { Bug } from './error.js';
2
+ import { Bug, cleanSingleStackTracePath } from './error.js';
3
3
  import { isUnitTest, isVerbose } from './environment/local.js';
4
4
  import constants from './constants.js';
5
- import { append as fileAppend, mkdirSync as fileMkdirSync, readSync as fileReadSync, sizeSync as fileSizeSync, writeSync as fileWriteSync, touchSync as fileTouchSync, } from './file.js';
5
+ import { generateRandomUUID } from './id.js';
6
+ import { mkdirSync as fileMkdirSync, readSync as fileReadSync, sizeSync as fileSizeSync, writeSync as fileWriteSync, touchSync as fileTouchSync, } from './file.js';
6
7
  import { join as pathJoin, relativize as relativizePath } from './path.js';
7
8
  import { page } from './system.js';
8
9
  import { colors } from './node/colors.js';
@@ -14,16 +15,25 @@ import { AbortController } from 'abort-controller';
14
15
  import cjs from 'color-json';
15
16
  import stripAnsi from 'strip-ansi';
16
17
  import { Writable } from 'node:stream';
17
- let logFile;
18
- export function initiateLogging({ logDir = constants.paths.directories.cache.path(), filename = 'shopify.log', }) {
18
+ import { createWriteStream } from 'node:fs';
19
+ export { default as logUpdate } from 'log-update';
20
+ const logFileName = 'shopify.cli.log';
21
+ let logFileStream;
22
+ let commandUuid;
23
+ export function initiateLogging(options = {}) {
24
+ if (isUnitTest())
25
+ return;
26
+ const logDir = options.logDir || constants.paths.directories.cache.path();
27
+ commandUuid = generateRandomUUID();
19
28
  fileMkdirSync(logDir);
20
- logFile = pathJoin(logDir, filename);
29
+ const logFile = pathJoin(logDir, logFileName);
21
30
  fileTouchSync(logFile);
22
- truncateLogs();
31
+ truncateLogs(logFile);
32
+ logFileStream = createWriteStream(logFile, { flags: 'a' });
23
33
  }
24
34
  // Shaves off the first 10,000 log lines (circa 1MB) if logs are over 5MB long.
25
35
  // Rescues in case the file hasn't been created yet.
26
- function truncateLogs() {
36
+ function truncateLogs(logFile) {
27
37
  try {
28
38
  if (fileSizeSync(logFile) > 5 * 1024 * 1024) {
29
39
  const contents = fileReadSync(logFile);
@@ -259,61 +269,69 @@ export const clearCollectedLogs = () => {
259
269
  collectedLogs = {};
260
270
  };
261
271
  /**
262
- * Ouputs information to the user. This is akin to "console.log"
272
+ * Ouputs information to the user.
263
273
  * Info messages don't get additional formatting.
264
274
  * Note: Info messages are sent through the standard output.
265
275
  * @param content {string} The content to be output to the user.
276
+ * @param logger {Function} The logging function to use to output to the user.
266
277
  */
267
- export const info = (content) => {
278
+ export const info = (content, logger = consoleLog) => {
279
+ const message = stringifyMessage(content);
268
280
  if (isUnitTest())
269
281
  collectLog('info', content);
270
- message(content, 'info');
282
+ outputWhereAppropriate('info', logger, message);
271
283
  };
272
284
  /**
273
285
  * Outputs a success message to the user.
274
- * Success message receive a special formatting to make them stand out in the console.
286
+ * Success messages receive a special formatting to make them stand out in the console.
275
287
  * Note: Success messages are sent through the standard output.
276
288
  * @param content {string} The content to be output to the user.
289
+ * @param logger {Function} The logging function to use to output to the user.
277
290
  */
278
- export const success = (content) => {
291
+ export const success = (content, logger = consoleLog) => {
279
292
  const message = colors.bold(`✅ Success! ${stringifyMessage(content)}.`);
280
293
  if (isUnitTest())
281
294
  collectLog('success', content);
282
- outputWhereAppropriate('info', consoleLog, message);
295
+ outputWhereAppropriate('info', logger, message);
283
296
  };
284
297
  /**
285
298
  * Outputs a completed message to the user.
286
299
  * Completed message receive a special formatting to make them stand out in the console.
287
300
  * Note: Completed messages are sent through the standard output.
288
301
  * @param content {string} The content to be output to the user.
302
+ * @param logger {Function} The logging function to use to output to the user.
289
303
  */
290
- export const completed = (content) => {
304
+ export const completed = (content, logger = consoleLog) => {
291
305
  const message = `${colors.green('✔')} ${stringifyMessage(content)}`;
292
306
  if (isUnitTest())
293
307
  collectLog('completed', content);
294
- outputWhereAppropriate('info', consoleLog, message);
308
+ outputWhereAppropriate('info', logger, message);
295
309
  };
296
310
  /**
297
311
  * Ouputs debug information to the user. By default these output is hidden unless the user calls the CLI with --verbose.
298
312
  * Debug messages don't get additional formatting.
299
313
  * Note: Debug messages are sent through the standard output.
300
314
  * @param content {string} The content to be output to the user.
315
+ * @param logger {Function} The logging function to use to output to the user.
301
316
  */
302
- export const debug = (content) => {
317
+ export const debug = (content, logger = consoleLog) => {
303
318
  if (isUnitTest())
304
319
  collectLog('debug', content);
305
- message(colors.gray(stringifyMessage(content)), 'debug');
320
+ const message = colors.gray(stringifyMessage(content));
321
+ outputWhereAppropriate('debug', logger, message);
306
322
  };
307
323
  /**
308
324
  * Outputs a warning message to the user.
309
325
  * Warning messages receive a special formatting to make them stand out in the console.
310
326
  * Note: Warning messages are sent through the standard output.
311
327
  * @param content {string} The content to be output to the user.
328
+ * @param logger {Function} The logging function to use to output to the user.
312
329
  */
313
- export const warn = (content) => {
330
+ export const warn = (content, logger = consoleWarn) => {
314
331
  if (isUnitTest())
315
332
  collectLog('warn', content);
316
- consoleWarn(colors.yellow(stringifyMessage(content)));
333
+ const message = colors.yellow(stringifyMessage(content));
334
+ outputWhereAppropriate('warn', logger, message);
317
335
  };
318
336
  /**
319
337
  * Prints a new line in the terminal.
@@ -349,7 +367,11 @@ export const error = async (content) => {
349
367
  outputString += `${padding}${line}\n`;
350
368
  }
351
369
  }
352
- let stack = await new StackTracey(content).withSourcesAsync();
370
+ let stack = new StackTracey(content);
371
+ stack.items.forEach((item) => {
372
+ item.file = cleanSingleStackTracePath(item.file);
373
+ });
374
+ stack = await stack.withSourcesAsync();
353
375
  stack = stack
354
376
  .filter((entry) => {
355
377
  return !entry.file.includes('@oclif/core');
@@ -465,14 +487,14 @@ function consoleError(message) {
465
487
  function consoleWarn(message) {
466
488
  console.warn(withOrWithoutStyle(message));
467
489
  }
468
- function outputWhereAppropriate(logLevel, logFunc, message) {
490
+ function outputWhereAppropriate(logLevel, logger, message) {
469
491
  if (shouldOutput(logLevel)) {
470
- logFunc(message);
492
+ logger(message);
471
493
  }
472
494
  logToFile(message, logLevel.toUpperCase());
473
495
  }
474
496
  export function logFileExists() {
475
- return Boolean(logFile);
497
+ return Boolean(logFileStream);
476
498
  }
477
499
  // DO NOT USE THIS FUNCTION DIRECTLY under normal circumstances.
478
500
  // It is exported purely for use in cases where output is already being logged
@@ -482,7 +504,8 @@ export function logToFile(message, logLevel) {
482
504
  if (!logFileExists())
483
505
  return;
484
506
  const timestamp = new Date().toISOString();
485
- fileAppend(logFile, `[${timestamp} ${logLevel}]: ${message}\n`);
507
+ const logContents = `[${timestamp} ${commandUuid} ${logLevel}]: ${message}\n`;
508
+ logFileStream.write(logContents);
486
509
  }
487
510
  function withOrWithoutStyle(message) {
488
511
  if (shouldDisplayColors()) {
@@ -498,8 +521,55 @@ export function unstyled(message) {
498
521
  export function shouldDisplayColors() {
499
522
  return Boolean(process.stdout.isTTY || process.env.FORCE_COLOR);
500
523
  }
501
- export async function pageLogs() {
502
- await page(logFile);
524
+ export async function pageLogs({ lastCommand }) {
525
+ const logDir = constants.paths.directories.cache.path();
526
+ const logFile = pathJoin(logDir, logFileName);
527
+ // Ensure file exists in case they deleted it or something
528
+ fileTouchSync(logFile);
529
+ if (lastCommand) {
530
+ printLastCommand(logFile);
531
+ }
532
+ else {
533
+ await page(logFile);
534
+ }
535
+ }
536
+ function printLastCommand(logFile) {
537
+ const contents = fileReadSync(logFile).split('\n');
538
+ const uuids = contents
539
+ .map(logfileLineUUID)
540
+ .filter((uuid) => uuid)
541
+ .reverse();
542
+ // 2nd unique UUID, because the currently running command will be the 1st
543
+ const relevantUuid = Array.from(new Set(uuids))[1];
544
+ if (relevantUuid) {
545
+ consoleLog(relevantLines(contents, relevantUuid).join('\n'));
546
+ }
547
+ }
548
+ function relevantLines(contents, relevantUuid) {
549
+ // We run through the file line by line, keeping track of the most recently
550
+ // encountered UUID.
551
+ //
552
+ // If the current line has a UUID, it's a new logged unit and should be
553
+ // considered. Otherwise, the line is related to the most recent UUID.
554
+ let mostRecentUuid = '';
555
+ return contents.filter((line) => {
556
+ const currentUuid = logfileLineUUID(line) || mostRecentUuid;
557
+ mostRecentUuid = currentUuid;
558
+ return currentUuid === relevantUuid;
559
+ });
560
+ }
561
+ function logfileLineUUID(line) {
562
+ // Log lines look like:
563
+ //
564
+ // timestamp UUID contents
565
+ // ===========================================================================================
566
+ // [2022-07-20T08:51:40.296Z 5288e1da-a06a-4f96-b1a6-e34fcdd7b416 DEBUG]: Running command logs
567
+ // ===========================================================================================
568
+ //
569
+ // There may be subsequent lines if the contents section is multi-line.
570
+ //
571
+ const match = line.match(/^\[\S+ ([0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}) [A-Z]+\]/);
572
+ return match && match[1];
503
573
  }
504
574
  /**
505
575
  *