@swell/cli 2.9.7 → 2.9.8

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.
@@ -14,6 +14,7 @@ export declare abstract class AppCommand extends SwellCommand {
14
14
  static baseFlags: {
15
15
  'app-path': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
16
16
  };
17
+ static requiresSwellConfig: boolean;
17
18
  appPath: string;
18
19
  swellConfig: LocalApp;
19
20
  init(): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import * as path from 'node:path';
3
- import { default as swellConfig } from './lib/app-config.js';
3
+ import { default as swellConfig, swellConfigFileExists, } from './lib/app-config.js';
4
+ import style from './lib/style.js';
4
5
  import { SwellCommand } from './swell-command.js';
5
6
  /**
6
7
  * A base class for Swell CLI commands that do not require an app to be saved
@@ -20,6 +21,8 @@ export class AppCommand extends SwellCommand {
20
21
  description: 'Path to your app directory',
21
22
  }),
22
23
  };
24
+ // set to false for commands that may run before an app directory exists (pull)
25
+ static requiresSwellConfig = true;
23
26
  // the local path to the app directory
24
27
  appPath = '';
25
28
  // local app configuration, what developers define
@@ -35,8 +38,17 @@ export class AppCommand extends SwellCommand {
35
38
  // ensure the base flags are available
36
39
  klass.flags = { ...klass.flags, ...AppCommand.baseFlags };
37
40
  const { flags } = await this.parse(klass);
41
+ const targetDir = flags['app-path']
42
+ ? path.resolve(flags['app-path'])
43
+ : process.cwd();
44
+ // swell.json is only ever looked up in this exact directory
45
+ // Searching parent directories (the previous behavior) can silently pick up an unrelated app's config
46
+ if (klass.requiresSwellConfig &&
47
+ !(await swellConfigFileExists(path.join(targetDir, 'swell.json')))) {
48
+ this.error(`No swell.json found in ${style.path(targetDir)}. Run this command from your app's root directory, or pass --app-path.`);
49
+ }
38
50
  // if the user passed an app path, use it: reload the app config
39
- this.swellConfig = await swellConfig(flags['app-path']);
51
+ this.swellConfig = await swellConfig(targetDir);
40
52
  this.appPath = path.resolve(this.swellConfig.swDirPath);
41
53
  }
42
54
  }
@@ -4,6 +4,7 @@ export default class AppPull extends PushAppCommand {
4
4
  appId: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
5
5
  targetPath: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
6
  };
7
+ static requiresSwellConfig: boolean;
7
8
  static description: string;
8
9
  static examples: string[];
9
10
  static flags: {
@@ -11,6 +11,7 @@ export default class AppPull extends PushAppCommand {
11
11
  description: 'path to download app files into',
12
12
  }),
13
13
  };
14
+ static requiresSwellConfig = false;
14
15
  static description = `Pull all app files, a specific file, or a specific configuration
15
16
  type from an app in your store's test environment to your local machine.
16
17
 
@@ -209,12 +209,11 @@ export class CreateAppCommand extends SwellCommand {
209
209
  await fs.writeFile(configFilePath, content, 'utf8');
210
210
  }
211
211
  async createAppConfigFolders(swellConfig) {
212
+ const configPath = path.dirname(swellConfig.path);
212
213
  for (const type of getAllConfigPaths(swellConfig.get('type'))) {
213
214
  // Create a config folder
214
215
  // eslint-disable-next-line no-await-in-loop
215
- await execAsync(`mkdir -p ${type}`, {
216
- cwd: path.dirname(swellConfig.path),
217
- });
216
+ await fs.mkdir(path.join(configPath, type), { recursive: true });
218
217
  }
219
218
  }
220
219
  async createFrontendApp(swellConfig, flags, directCreate, kind) {
@@ -300,8 +299,8 @@ export class CreateAppCommand extends SwellCommand {
300
299
  }
301
300
  spinner.start(`Creating ${projectType?.name} frontend app (this may take a while)...`);
302
301
  try {
303
- await execAsync(`mkdir -p frontend`, {
304
- cwd: configPath,
302
+ await fs.mkdir(path.join(configPath, 'frontend'), {
303
+ recursive: true,
305
304
  });
306
305
  // SWELL_LOCAL_TEMPLATE_PATH overrides the scaffold for local dev — copies
307
306
  // the directory at the path into frontend/ instead of running the install
@@ -417,16 +416,14 @@ export class CreateAppCommand extends SwellCommand {
417
416
  this.log();
418
417
  spinner.start(`Creating theme template...`);
419
418
  try {
420
- await execAsync(`mkdir -p theme`, {
421
- cwd: configPath,
422
- });
419
+ await fs.mkdir(path.join(configPath, 'theme'), { recursive: true });
423
420
  for (const themeConfig of defaultThemeConfigs.results) {
424
421
  const filePath = themeConfig.file_path.replace(/^frontend\/theme-template\//, '');
425
422
  const fileContent = themeConfig.file_data;
426
423
  const isJson = filePath.endsWith('.json');
427
424
  // eslint-disable-next-line no-await-in-loop
428
- await execAsync(`mkdir -p ${path.dirname(filePath)}`, {
429
- cwd: configPath,
425
+ await fs.mkdir(path.join(configPath, path.dirname(filePath)), {
426
+ recursive: true,
430
427
  });
431
428
  // eslint-disable-next-line no-await-in-loop
432
429
  await (isJson
@@ -2,7 +2,7 @@
2
2
  /// <reference types="node" resolution-mode="require"/>
3
3
  import { AppConfig } from './app-config.js';
4
4
  export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
5
- export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, } from './paths.js';
5
+ export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, toPosixPath, } from './paths.js';
6
6
  export declare const PUSH_CONCURRENCY = 3;
7
7
  /**
8
8
  * An app as defined by the Swell API. This is different than an app
@@ -5,7 +5,7 @@ import * as path from 'node:path';
5
5
  import { detectPackageManager, transformCommand } from '../package-manager.js';
6
6
  import { AppConfig } from './app-config.js';
7
7
  export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
8
- export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, } from './paths.js';
8
+ export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, toPosixPath, } from './paths.js';
9
9
  export const PUSH_CONCURRENCY = 3;
10
10
  export var SwellJsonFields;
11
11
  (function (SwellJsonFields) {
@@ -1,5 +1,6 @@
1
1
  import { type GlobbyFilterFunction } from 'globby';
2
2
  import { ConfigType } from './index.js';
3
+ export declare function toPosixPath(inputPath: string): string;
3
4
  export declare function globAllFilesByPath(appPath: string, dirPath?: string): Promise<string[]>;
4
5
  export declare function getGlobIgnorePathsChecker(appPath: string): Promise<GlobbyFilterFunction>;
5
6
  export declare function allConfigDirsPaths(appPath: string): Generator<{
@@ -27,11 +27,15 @@ function globOptions(appPath, options) {
27
27
  ],
28
28
  };
29
29
  }
30
+ // use forward slashes to support Windows
31
+ export function toPosixPath(inputPath) {
32
+ return inputPath.split(path.sep).join('/');
33
+ }
30
34
  function globFilesSync(pattern, appPath, dirPath = '.', options = {}) {
31
- return globbySync(`${dirPath}${path.sep}${pattern}`, globOptions(appPath, options));
35
+ return globbySync(`${toPosixPath(dirPath)}/${pattern}`, globOptions(appPath, options));
32
36
  }
33
37
  function globFiles(pattern, appPath, dirPath = '.', options = {}) {
34
- return globby(`${dirPath}${path.sep}${pattern}`, globOptions(appPath, options));
38
+ return globby(`${toPosixPath(dirPath)}/${pattern}`, globOptions(appPath, options));
35
39
  }
36
40
  export function globAllFilesByPath(appPath, dirPath) {
37
41
  return globFiles('**', appPath, dirPath);
@@ -6,7 +6,7 @@ import * as path from 'node:path';
6
6
  import Stream from 'node:stream';
7
7
  import ora from 'ora';
8
8
  import { default as swellConfig } from './lib/app-config.js';
9
- import { ConfigType, getFrontendProjectValidValues, allConfigFilesInDir, CUSTOM_FRAMEWORK_SLUG, appConfigFromFile, filePathExistsAsync, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, getProjectCommands, globAllFilesByPath, hashString, isPathDirectory, } from './lib/apps/index.js';
9
+ import { ConfigType, getFrontendProjectValidValues, allConfigFilesInDir, CUSTOM_FRAMEWORK_SLUG, appConfigFromFile, filePathExistsAsync, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, getProjectCommands, globAllFilesByPath, hashString, isPathDirectory, toPosixPath, } from './lib/apps/index.js';
10
10
  import { getGlobIgnorePathsChecker } from './lib/apps/paths.js';
11
11
  import { slugFromApp } from './lib/apps/slug.js';
12
12
  import { default as localConfig } from './lib/config.js';
@@ -32,14 +32,12 @@ export class PushAppCommand extends RemoteAppCommand {
32
32
  if (!filename) {
33
33
  return;
34
34
  }
35
- if (this.watchingIgnoreFilter && this.watchingIgnoreFilter(filename)) {
35
+ const configFile = toPosixPath(filename);
36
+ if (this.watchingIgnoreFilter && this.watchingIgnoreFilter(configFile)) {
36
37
  return;
37
38
  }
38
- const configFile = filename;
39
- const pathParsed = path.parse(configFile);
40
- const fileDirs = pathParsed.dir.split(path.sep);
41
39
  // only the first directory is relevant for us
42
- const configDir = fileDirs[0];
40
+ const configDir = configFile.split('/')[0];
43
41
  // we only want to watch files in the root of the app directory
44
42
  // and ignore build files etc
45
43
  // TODO: renaming a folder doesn't seem to work
@@ -433,7 +431,7 @@ export class PushAppCommand extends RemoteAppCommand {
433
431
  async getFrontendDeploymentHash() {
434
432
  const localConfigs = await this.getLocalAppConfigs();
435
433
  let frontendHashes = localConfigs
436
- .filter((config) => config.filePath?.startsWith(`frontend${path.sep}`))
434
+ .filter((config) => config.filePath?.startsWith('frontend/'))
437
435
  .map((config) => config.hash)
438
436
  .sort()
439
437
  .join('|');
@@ -675,16 +673,18 @@ export class PushAppCommand extends RemoteAppCommand {
675
673
  this.log(`View your storefront at ${style.link(this.storefrontFrontendUrl(currentStore, storefront, branchId))}.`);
676
674
  }
677
675
  async pushFile(relativePath) {
678
- const basePath = relativePath.split(path.sep)[0];
676
+ const relativePathPosix = toPosixPath(relativePath);
677
+ const basePath = relativePathPosix.split('/')[0];
679
678
  const configType = getConfigTypeFromPath(basePath) || ConfigType.FILE;
680
- const config = await appConfigFromFile(relativePath, configType, this.appPath);
679
+ const config = await appConfigFromFile(relativePathPosix, configType, this.appPath);
681
680
  await this.pushRemoteFile(config);
682
681
  }
683
682
  async pushFilePath(relativePath, force) {
684
683
  const topDir = relativePath.split(path.sep)[0];
685
684
  const configType = getConfigTypeFromPath(topDir) || ConfigType.FILE;
686
685
  const configTypeKey = getConfigTypeKeyFromValue(configType) || ConfigType.FILE;
687
- const exAppConfigs = (this.app?.configs || []).filter((config) => config.filePath?.startsWith(`${relativePath}/`));
686
+ const relativePathPosix = toPosixPath(relativePath);
687
+ const exAppConfigs = (this.app?.configs || []).filter((config) => config.filePath?.startsWith(`${relativePathPosix}/`));
688
688
  const promises = [];
689
689
  for (const { configFile } of allConfigFilesInDir(this.appPath, relativePath, configTypeKey)) {
690
690
  promises.push(appConfigFromFile(configFile, configType, this.appPath));
@@ -708,6 +708,7 @@
708
708
  "pluginType": "core",
709
709
  "strict": true,
710
710
  "enableJsonFlag": false,
711
+ "requiresSwellConfig": true,
711
712
  "delayOrientation": false,
712
713
  "orientation": {
713
714
  "env": "test"
@@ -851,6 +852,7 @@
851
852
  "strict": true,
852
853
  "summary": "Show information about your Swell app.",
853
854
  "enableJsonFlag": false,
855
+ "requiresSwellConfig": true,
854
856
  "delayOrientation": false,
855
857
  "isESM": true,
856
858
  "relativePath": [
@@ -1101,6 +1103,7 @@
1101
1103
  "strict": true,
1102
1104
  "summary": "Install an existing app in another store environment.",
1103
1105
  "enableJsonFlag": false,
1106
+ "requiresSwellConfig": true,
1104
1107
  "delayOrientation": false,
1105
1108
  "orientation": {
1106
1109
  "env": "test"
@@ -1157,6 +1160,7 @@
1157
1160
  "pluginType": "core",
1158
1161
  "strict": true,
1159
1162
  "summary": "Pull app files from Swell to your local machine.",
1163
+ "requiresSwellConfig": false,
1160
1164
  "orientation": {
1161
1165
  "env": "test"
1162
1166
  },
@@ -2298,6 +2302,7 @@
2298
2302
  "strict": true,
2299
2303
  "summary": "Create tests scaffolding for your Swell app.",
2300
2304
  "enableJsonFlag": false,
2305
+ "requiresSwellConfig": true,
2301
2306
  "helpMeta": {
2302
2307
  "usageDirect": "[-y]"
2303
2308
  },
@@ -3326,6 +3331,7 @@
3326
3331
  "pluginName": "@swell/cli",
3327
3332
  "pluginType": "core",
3328
3333
  "summary": "Pull theme files from Swell to your local machine.",
3334
+ "requiresSwellConfig": false,
3329
3335
  "orientation": {
3330
3336
  "env": "test"
3331
3337
  },
@@ -3588,5 +3594,5 @@
3588
3594
  ]
3589
3595
  }
3590
3596
  },
3591
- "version": "2.9.7"
3597
+ "version": "2.9.8"
3592
3598
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swell/cli",
3
- "version": "2.9.7",
3
+ "version": "2.9.8",
4
4
  "type": "module",
5
5
  "description": "Swell's command line interface/utility",
6
6
  "keywords": [