@madebyseed/seed-cli-tools 2.3.2 → 3.0.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 (100) hide show
  1. package/lib/commands/build.d.ts +2 -0
  2. package/lib/commands/build.js +30 -29
  3. package/lib/commands/build.js.map +1 -0
  4. package/lib/commands/deploy.d.ts +2 -0
  5. package/lib/commands/deploy.js +47 -30
  6. package/lib/commands/deploy.js.map +1 -0
  7. package/lib/commands/pull.d.ts +2 -0
  8. package/lib/commands/pull.js +39 -29
  9. package/lib/commands/pull.js.map +1 -0
  10. package/lib/commands/watch.d.ts +2 -0
  11. package/lib/commands/watch.js +44 -40
  12. package/lib/commands/watch.js.map +1 -0
  13. package/lib/commands/zip.d.ts +2 -0
  14. package/lib/commands/zip.js +23 -25
  15. package/lib/commands/zip.js.map +1 -0
  16. package/lib/config.d.ts +3 -0
  17. package/lib/config.js +21 -28
  18. package/lib/config.js.map +1 -0
  19. package/lib/gulpfile.d.ts +1 -0
  20. package/lib/gulpfile.js +26 -33
  21. package/lib/gulpfile.js.map +1 -0
  22. package/lib/tasks/build-assets.d.ts +1 -0
  23. package/lib/tasks/build-assets.js +80 -72
  24. package/lib/tasks/build-assets.js.map +1 -0
  25. package/lib/tasks/build-css.d.ts +1 -0
  26. package/lib/tasks/build-css.js +88 -82
  27. package/lib/tasks/build-css.js.map +1 -0
  28. package/lib/tasks/build-js.d.ts +1 -0
  29. package/lib/tasks/build-js.js +78 -82
  30. package/lib/tasks/build-js.js.map +1 -0
  31. package/lib/tasks/build-svg.d.ts +1 -0
  32. package/lib/tasks/build-svg.js +51 -54
  33. package/lib/tasks/build-svg.js.map +1 -0
  34. package/lib/tasks/build-utils.d.ts +1 -0
  35. package/lib/tasks/build-utils.js +68 -49
  36. package/lib/tasks/build-utils.js.map +1 -0
  37. package/lib/tasks/includes/config.d.ts +71 -0
  38. package/lib/tasks/includes/config.js +118 -163
  39. package/lib/tasks/includes/config.js.map +1 -0
  40. package/lib/tasks/includes/messages.d.ts +19 -0
  41. package/lib/tasks/includes/messages.js +80 -72
  42. package/lib/tasks/includes/messages.js.map +1 -0
  43. package/lib/tasks/includes/utilities.d.ts +71 -0
  44. package/lib/tasks/includes/utilities.js +127 -141
  45. package/lib/tasks/includes/utilities.js.map +1 -0
  46. package/lib/tasks/shopify-cli.d.ts +1 -0
  47. package/lib/tasks/shopify-cli.js +107 -93
  48. package/lib/tasks/shopify-cli.js.map +1 -0
  49. package/lib/tasks/watchers.d.ts +1 -0
  50. package/lib/tasks/watchers.js +29 -29
  51. package/lib/tasks/watchers.js.map +1 -0
  52. package/lib/types.d.ts +53 -0
  53. package/lib/types.js +3 -0
  54. package/lib/types.js.map +1 -0
  55. package/lib/utils.d.ts +102 -0
  56. package/lib/utils.js +310 -189
  57. package/lib/utils.js.map +1 -0
  58. package/package.json +36 -16
  59. package/src/commands/build.ts +39 -0
  60. package/src/commands/deploy.ts +79 -0
  61. package/src/commands/pull.ts +58 -0
  62. package/src/commands/watch.ts +72 -0
  63. package/src/commands/zip.ts +28 -0
  64. package/src/config.ts +28 -0
  65. package/src/gulpfile.ts +134 -0
  66. package/src/tasks/build-assets.ts +135 -0
  67. package/src/tasks/build-css.ts +129 -0
  68. package/src/tasks/build-js.ts +108 -0
  69. package/src/tasks/build-svg.ts +92 -0
  70. package/src/tasks/build-utils.ts +137 -0
  71. package/src/tasks/includes/config.ts +224 -0
  72. package/src/tasks/includes/messages.ts +157 -0
  73. package/src/tasks/includes/utilities.ts +170 -0
  74. package/src/tasks/shopify-cli.ts +199 -0
  75. package/src/tasks/watchers.ts +50 -0
  76. package/src/types/declarations.d.ts +72 -0
  77. package/src/types.ts +84 -0
  78. package/src/utils.ts +444 -0
  79. package/tsconfig.json +14 -0
  80. package/tsconfig.tsbuildinfo +1 -0
  81. package/.babelrc +0 -3
  82. package/.eslintrc +0 -8
  83. /package/{src → src-legacy}/commands/build.js +0 -0
  84. /package/{src → src-legacy}/commands/deploy.js +0 -0
  85. /package/{src → src-legacy}/commands/pull.js +0 -0
  86. /package/{src → src-legacy}/commands/watch.js +0 -0
  87. /package/{src → src-legacy}/commands/zip.js +0 -0
  88. /package/{src → src-legacy}/config.js +0 -0
  89. /package/{src → src-legacy}/gulpfile.js +0 -0
  90. /package/{src → src-legacy}/tasks/build-assets.js +0 -0
  91. /package/{src → src-legacy}/tasks/build-css.js +0 -0
  92. /package/{src → src-legacy}/tasks/build-js.js +0 -0
  93. /package/{src → src-legacy}/tasks/build-svg.js +0 -0
  94. /package/{src → src-legacy}/tasks/build-utils.js +0 -0
  95. /package/{src → src-legacy}/tasks/includes/config.js +0 -0
  96. /package/{src → src-legacy}/tasks/includes/messages.js +0 -0
  97. /package/{src → src-legacy}/tasks/includes/utilities.js +0 -0
  98. /package/{src → src-legacy}/tasks/shopify-cli.js +0 -0
  99. /package/{src → src-legacy}/tasks/watchers.js +0 -0
  100. /package/{src → src-legacy}/utils.js +0 -0
package/src/types.ts ADDED
@@ -0,0 +1,84 @@
1
+ import { SpawnSyncReturns } from "child_process";
2
+ import type { ChildProcess } from "child_process";
3
+
4
+ export interface Config {
5
+ gulpFile: string;
6
+ gulp: string;
7
+ themeRoot: string;
8
+ seedConfig: string;
9
+ seedConfigDelimiter: string;
10
+ }
11
+
12
+ export interface ShopifyCLI {
13
+ login: (store: string) => SpawnSyncReturns<Buffer>;
14
+ serve: (store: string, options?: Record<string, unknown>) => ChildProcess;
15
+ pull: (
16
+ store: string,
17
+ themeId?: string | undefined,
18
+ dir?: string,
19
+ nodelete?: boolean,
20
+ ) => ChildProcess;
21
+ push: (
22
+ store: string,
23
+ themeId?: string | undefined,
24
+ nodelete?: boolean,
25
+ ) => ChildProcess;
26
+ package: () => ChildProcess;
27
+ list: (
28
+ store: string,
29
+ options?: { role?: string; json?: boolean },
30
+ ) => ChildProcess;
31
+ duplicate: (
32
+ store: string,
33
+ themeId: string,
34
+ name?: string,
35
+ json?: boolean,
36
+ ) => ChildProcess;
37
+ }
38
+
39
+ export interface SeedConfig {
40
+ store?: string;
41
+ stores?: {
42
+ [key: string]: {
43
+ domain: string;
44
+ themes: {
45
+ [key: string]: string;
46
+ };
47
+ };
48
+ };
49
+ themes?: {
50
+ [key: string]: string;
51
+ };
52
+ __developmentThemeId?: string;
53
+ }
54
+
55
+ export interface SeedProjectConfig {
56
+ // Store Configuration
57
+ stores: {
58
+ [key: string]: {
59
+ domain: string; // mystore.myshopify.com
60
+ password?: string; // store password if needed
61
+ };
62
+ };
63
+
64
+ pull: {
65
+ ignoreFiles: string[];
66
+ };
67
+
68
+ // Build Configuration
69
+ build: {
70
+ js: {
71
+ type: "legacy" | "bundle";
72
+ };
73
+ };
74
+ }
75
+
76
+ // Re-export build config types
77
+ export type {
78
+ BuildConfig,
79
+ PathConfig,
80
+ TmpPathConfig,
81
+ DistPathConfig,
82
+ RootsConfig,
83
+ PluginsConfig,
84
+ } from "./tasks/includes/config";
package/src/utils.ts ADDED
@@ -0,0 +1,444 @@
1
+ import { red, yellow } from "chalk";
2
+ import figures from "figures";
3
+ import spawn from "cross-spawn";
4
+ import type { ChildProcess } from "child_process";
5
+ import { join } from "path";
6
+ import { readFileSync, writeFileSync, existsSync } from "fs";
7
+ import config from "./config";
8
+ import { SeedConfig, ShopifyCLI, SeedProjectConfig } from "./types";
9
+
10
+ /**
11
+ * Get seed.project.json from project root dir
12
+ *
13
+ * @param {string} themeRoot - the path to theme root dir
14
+ * @returns {SeedProjectConfig | null} - project config object or null if not found
15
+ */
16
+ export function getProjectConfig(themeRoot: string): SeedProjectConfig | null {
17
+ console.log(require(join(themeRoot, "seed.project.json")));
18
+
19
+ return require(join(themeRoot, "seed.project.json"));
20
+ }
21
+
22
+ /**
23
+ * Get seed.config.js from project root dir
24
+ *
25
+ * @param {string} themeRoot - the path to theme root dir
26
+ * @returns {SeedConfig} - seed config object
27
+ */
28
+ export function getSeedConfig(themeRoot: string): SeedConfig {
29
+ return require(join(themeRoot, "seed.config.js"));
30
+ }
31
+
32
+ /**
33
+ * Get store name from seed.project.json or seed.config.js
34
+ *
35
+ * @param {string} themeRoot - the path to theme root dir
36
+ * @param {string} [store] - optional store name, defaults to "main"
37
+ * @returns {string | undefined} - store domain or undefined if not found
38
+ */
39
+ export function getStoreName(
40
+ themeRoot: string,
41
+ store?: string,
42
+ ): string | undefined {
43
+ // Default to "main" if no store specified
44
+ const storeKey = store || "main";
45
+
46
+ // Try to get from project config first
47
+ const projectConfig = getProjectConfig(themeRoot);
48
+ if (projectConfig?.stores?.[storeKey]) {
49
+ return projectConfig.stores[storeKey].domain;
50
+ }
51
+
52
+ // Fallback to legacy config
53
+ const legacyConfig = getSeedConfig(themeRoot);
54
+ return store && legacyConfig.stores?.[store]
55
+ ? legacyConfig.stores[store].domain
56
+ : legacyConfig.store;
57
+ }
58
+
59
+ /**
60
+ * Get store password from seed.project.json
61
+ *
62
+ * @param {string} themeRoot - the path to theme root dir
63
+ * @param {string} [store] - optional store name, defaults to "main"
64
+ * @returns {string | undefined} - store password or undefined if not found
65
+ */
66
+ export function getStorePassword(
67
+ themeRoot: string,
68
+ store?: string,
69
+ ): string | undefined {
70
+ const storeKey = store || "main";
71
+ const projectConfig = getProjectConfig(themeRoot);
72
+ return projectConfig?.stores?.[storeKey]?.password;
73
+ }
74
+
75
+ /**
76
+ * Wrappers for Shopify CLI commands
77
+ *
78
+ * @summary a set of utility functions used to wrap shopify's CLI
79
+ * @namespace seed-cli.shopifyCLI
80
+ * @memberof seed-cli
81
+ */
82
+ export const shopifyCLI: ShopifyCLI = {
83
+ /**
84
+ * shopify login
85
+ *
86
+ * @memberof seed-cli.shopifyCLI
87
+ * @param {string} store - store's myshopify domain name
88
+ * @returns {object} - spawnSync object or, node's <ChildProcess> object if async
89
+ */
90
+ login: (store: string) => {
91
+ const args = ["switch", "--store", store];
92
+ const options = {
93
+ env: process.env,
94
+ stdio: "inherit" as const,
95
+ shell: true,
96
+ };
97
+
98
+ return spawn.sync("shopify", args, options);
99
+ },
100
+
101
+ /**
102
+ * shopify theme serve
103
+ *
104
+ * @memberof seed-cli.shopifyCLI
105
+ * @param {string} store - store's myshopify domain name
106
+ * @param {object} options - node spawn options
107
+ * @returns {object} - node's ChildProcess
108
+ */
109
+ serve: (store: string, options = {}) => {
110
+ return spawn("shopify theme", ["dev", "--store", store, "--path", "dist"], {
111
+ env: process.env,
112
+ stdio: "inherit" as const,
113
+ shell: true,
114
+ ...options,
115
+ });
116
+ },
117
+
118
+ /**
119
+ * shopify theme pull
120
+ *
121
+ * @memberof seed-cli.shopifyCLI
122
+ * @param {string} store - store's myshopify domain name
123
+ * @param {string} themeId - shopify theme id to pull from
124
+ * @param {string} dir - directory to pull into
125
+ * @param {boolean} [nodelete = true] - run command with --no-delete flag
126
+ * @returns {object} - node's ChildProcess
127
+ */
128
+ pull: (store: string, themeId?: string, dir = "./src", nodelete = true) => {
129
+ let args = ["pull", "--store", store];
130
+ if (themeId) args = args.concat(["--theme", themeId, "--path", dir]);
131
+
132
+ if (nodelete) args.push("--nodelete");
133
+
134
+ return spawn("shopify theme", args, {
135
+ env: process.env,
136
+ stdio: "inherit" as const,
137
+ shell: true,
138
+ });
139
+ },
140
+
141
+ /**
142
+ * shopify theme push
143
+ *
144
+ * @memberof seed-cli.shopifyCLI
145
+ * @param {string} store - store's myshopify domain name
146
+ * @param {string} themeId - shopify theme id to push into
147
+ * @param {boolean} [nodelete = true] - run command with --no-delete flag
148
+ * @returns {ChildProcess} - node's ChildProcess
149
+ */
150
+ push: (store: string, themeId?: string, nodelete = true) => {
151
+ let args = ["push", "--store", store, "--path", "dist"];
152
+ if (themeId) args = args.concat(["--theme", themeId]);
153
+
154
+ if (nodelete) args.push("--nodelete");
155
+
156
+ return spawn("shopify theme", args, {
157
+ env: process.env,
158
+ stdio: "inherit" as const,
159
+ shell: true,
160
+ });
161
+ },
162
+
163
+ /**
164
+ * shopify theme package
165
+ *
166
+ * @memberof seed-cli.shopifyCLI
167
+ * @returns {ChildProcess} - node's ChildProcess
168
+ */
169
+ package: () => {
170
+ return spawn("shopify theme", ["package", "--path", "dist"], {
171
+ env: process.env,
172
+ stdio: "inherit" as const,
173
+ shell: true,
174
+ });
175
+ },
176
+
177
+ /**
178
+ * shopify theme list
179
+ *
180
+ * @memberof seed-cli.shopifyCLI
181
+ * @param {string} store - store's myshopify domain name
182
+ * @param {object} options - additional options (role, json, etc.)
183
+ * @returns {ChildProcess} - node's ChildProcess
184
+ */
185
+ list: (store: string, options: { role?: string; json?: boolean } = {}) => {
186
+ let args = ["list", "--store", store];
187
+
188
+ if (options.role) args = args.concat(["--role", options.role]);
189
+ if (options.json) args.push("--json");
190
+
191
+ return spawn("shopify theme", args, {
192
+ env: process.env,
193
+ stdio: options.json ? "pipe" : ("inherit" as const),
194
+ shell: true,
195
+ });
196
+ },
197
+
198
+ /**
199
+ * shopify theme duplicate
200
+ *
201
+ * @memberof seed-cli.shopifyCLI
202
+ * @param {string} store - store's myshopify domain name
203
+ * @param {string} themeId - theme ID to duplicate
204
+ * @param {string} [name] - optional name for the duplicated theme
205
+ * @param {boolean} [json] - return JSON output
206
+ * @returns {ChildProcess} - node's ChildProcess
207
+ */
208
+ duplicate: (store: string, themeId: string, name?: string, json = false) => {
209
+ let args = ["duplicate", "--store", store, "--theme", themeId, "--force"];
210
+
211
+ if (name) args = args.concat(["--name", name]);
212
+ if (json) args.push("--json");
213
+
214
+ return spawn("shopify theme", args, {
215
+ env: process.env,
216
+ stdio: json ? "pipe" : ("inherit" as const),
217
+ shell: true,
218
+ });
219
+ },
220
+ };
221
+
222
+ /**
223
+ * Get the live theme ID from a store
224
+ */
225
+ export async function getLiveThemeId(store: string): Promise<string> {
226
+ return new Promise((resolve, reject) => {
227
+ const listProcess = shopifyCLI.list(store, { role: "live", json: true });
228
+ let output = "";
229
+ let errorOutput = "";
230
+
231
+ listProcess.stdout?.on("data", (data: Buffer) => {
232
+ output += data.toString();
233
+ });
234
+
235
+ listProcess.stderr?.on("data", (data: Buffer) => {
236
+ errorOutput += data.toString();
237
+ });
238
+
239
+ listProcess.on("close", (code: number) => {
240
+ if (code !== 0) {
241
+ reject(new Error(`Failed to get live theme: ${errorOutput}`));
242
+ return;
243
+ }
244
+
245
+ try {
246
+ const response = JSON.parse(output);
247
+ const liveTheme = response.themes?.find(
248
+ (theme: any) => theme.role === "live",
249
+ );
250
+
251
+ if (!liveTheme) {
252
+ reject(new Error("No live theme found"));
253
+ return;
254
+ }
255
+
256
+ resolve(liveTheme.id.toString());
257
+ } catch (error) {
258
+ reject(new Error(`Failed to parse theme list response: ${error}`));
259
+ }
260
+ });
261
+ });
262
+ }
263
+
264
+ /**
265
+ * Duplicate a theme and return the new theme ID
266
+ */
267
+ export async function duplicateTheme(
268
+ store: string,
269
+ sourceThemeId: string,
270
+ name?: string,
271
+ ): Promise<string> {
272
+ return new Promise((resolve, reject) => {
273
+ const duplicateProcess = shopifyCLI.duplicate(
274
+ store,
275
+ sourceThemeId,
276
+ name,
277
+ true,
278
+ );
279
+ let output = "";
280
+ let errorOutput = "";
281
+
282
+ duplicateProcess.stdout?.on("data", (data: Buffer) => {
283
+ output += data.toString();
284
+ });
285
+
286
+ duplicateProcess.stderr?.on("data", (data: Buffer) => {
287
+ errorOutput += data.toString();
288
+ });
289
+
290
+ duplicateProcess.on("close", (code: number) => {
291
+ if (code !== 0) {
292
+ reject(new Error(`Failed to duplicate theme: ${errorOutput}`));
293
+ return;
294
+ }
295
+
296
+ try {
297
+ const response = JSON.parse(output);
298
+ resolve(response.theme.id.toString());
299
+ } catch (error) {
300
+ reject(new Error(`Failed to parse duplicate response: ${error}`));
301
+ }
302
+ });
303
+ });
304
+ }
305
+
306
+ /**
307
+ * Duplicate a theme from live theme
308
+ */
309
+ export async function duplicateFromLive(
310
+ store: string,
311
+ name?: string,
312
+ ): Promise<string> {
313
+ const liveThemeId = await getLiveThemeId(store);
314
+ const newThemeId = await duplicateTheme(store, liveThemeId, name);
315
+ return newThemeId;
316
+ }
317
+
318
+ /**
319
+ * Get theme ID from seed.config.js
320
+ *
321
+ * @param {string} themeRoot - the path to theme root dir
322
+ * @param {string} environment - theme name/environment
323
+ * @param {string} [store] - optional store name
324
+ * @returns {string} themeID
325
+ */
326
+ export function getThemeID(
327
+ themeRoot: string,
328
+ environment: string,
329
+ store?: string,
330
+ ): string | undefined {
331
+ const config = getSeedConfig(themeRoot);
332
+ return store && config.stores?.[store]
333
+ ? config.stores[store].themes[environment]
334
+ : config.themes?.[environment];
335
+ }
336
+
337
+ /**
338
+ * Get dev theme ID from seed.config.js
339
+ *
340
+ * @param {string} themeRoot - the path to theme root dir
341
+ * @returns {string} - development store theme ID
342
+ */
343
+ export function getDevThemeID(themeRoot: string): string | undefined {
344
+ return getSeedConfig(themeRoot).__developmentThemeId;
345
+ }
346
+
347
+ /**
348
+ * Given a string, finds the first themeID and returns it, if no themeID found
349
+ * returns false.
350
+ *
351
+ * @param {string} string - string to extract theme id from
352
+ * @return {string|boolean} - themeID or false if not found
353
+ */
354
+ export function extractThemeId(string: string): string | false {
355
+ const regex = /\d{12}/;
356
+ const match = string.match(regex);
357
+ return match && match.length ? match[0] : false;
358
+ }
359
+
360
+ /**
361
+ * Logs out to terminal the output of a piped child process
362
+ *
363
+ * @param {child_process} serveProcess shopify cli serve spawned child process
364
+ */
365
+ export function logChildProcessOutput(process: ChildProcess): void {
366
+ process.stdout?.on("data", (data: Buffer | string) => {
367
+ console.log(data.toString());
368
+ });
369
+ }
370
+
371
+ /**
372
+ * Writes the development theme ID as a key to seed config file
373
+ *
374
+ * @param {string} themeID - development theme ID
375
+ */
376
+ export function setDevThemeID(themeID: string): void {
377
+ const data = readFileSync(config.seedConfig, { encoding: "utf8" }).split(
378
+ config.seedConfigDelimiter,
379
+ );
380
+ data[1] = `\n __developmentThemeId: '${themeID}'` + "\n};";
381
+ writeFileSync(config.seedConfig, data.join(config.seedConfigDelimiter));
382
+ }
383
+
384
+ /**
385
+ * Logs into seed config shopify store while logging useful messages
386
+ * and handling errors.
387
+ *
388
+ * @param {string} [store] - optional store name, defaults to "main"
389
+ * @returns {boolean} whether login was successful or not
390
+ */
391
+ export function login(store?: string): boolean {
392
+ const domain = getStoreName(config.themeRoot, store);
393
+
394
+ if (!domain) {
395
+ storeErrorMessage(store);
396
+ return false;
397
+ }
398
+
399
+ console.log(`Logging into ${yellow(domain)} with Shopify CLI...`);
400
+ const loginProcess = shopifyCLI.login(domain);
401
+ if (loginProcess.error) {
402
+ console.log("");
403
+ console.log(
404
+ red(
405
+ ` ${figures.cross} Failed logging into ${domain}, are you sure you're using the correct account?`,
406
+ ),
407
+ );
408
+ console.log("");
409
+ return false;
410
+ }
411
+
412
+ return true;
413
+ }
414
+
415
+ /**
416
+ * Outputs error message if there's no store field in seed config
417
+ *
418
+ * @param {string} [store] - optional store name that was not found
419
+ */
420
+ export function storeErrorMessage(store?: string): void {
421
+ if (store) {
422
+ console.log("");
423
+ console.log(
424
+ red(
425
+ ` The store ${store} does not exist in seed.project.json or seed.config.js`,
426
+ ),
427
+ );
428
+ console.log(
429
+ " Check to see if you spelled the store correctly in your config files and when running the command.",
430
+ );
431
+ console.log("");
432
+ } else {
433
+ console.log("");
434
+ console.log(
435
+ red(
436
+ ` ${figures.cross} No 'main' store found in seed.project.json or seed.config.js`,
437
+ ),
438
+ );
439
+ console.log(
440
+ " Add a 'main' store to your seed.project.json or specify which store to use with --store <store>",
441
+ );
442
+ console.log("");
443
+ }
444
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./lib",
5
+ "rootDir": "./src",
6
+ "composite": true,
7
+ "declaration": true,
8
+ "sourceMap": true,
9
+ "esModuleInterop": true,
10
+ "strict": true
11
+ },
12
+ "include": ["./**/*.ts"],
13
+ "exclude": ["node_modules", "lib"]
14
+ }