shopify-accelerate-app 1.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 (47) hide show
  1. package/@types/metafields.ts +9 -0
  2. package/@types/sections.ts +1769 -0
  3. package/@types/settings.ts +3 -0
  4. package/@types/shopify.ts +1871 -0
  5. package/@types/types.d.ts +17 -0
  6. package/README.md +2 -0
  7. package/package.json +111 -0
  8. package/shopify-accelerate-app.ts +394 -0
  9. package/shopify.graphql +48866 -0
  10. package/src/esbuild/esbuild.ts +246 -0
  11. package/src/scaffold-theme/build-theme.ts +24 -0
  12. package/src/scaffold-theme/generate-asset-files.ts +48 -0
  13. package/src/scaffold-theme/generate-base-types.ts +23 -0
  14. package/src/scaffold-theme/generate-block-files.ts +194 -0
  15. package/src/scaffold-theme/generate-blocks-types.ts +242 -0
  16. package/src/scaffold-theme/generate-config-files.ts +34 -0
  17. package/src/scaffold-theme/generate-liquid-files.ts +995 -0
  18. package/src/scaffold-theme/generate-schema-locales.ts +195 -0
  19. package/src/scaffold-theme/generate-schema-variables.ts +380 -0
  20. package/src/scaffold-theme/generate-section-files.ts +303 -0
  21. package/src/scaffold-theme/generate-section-preset-files.ts +296 -0
  22. package/src/scaffold-theme/generate-section-types.ts +339 -0
  23. package/src/scaffold-theme/generate-setting-types.ts +123 -0
  24. package/src/scaffold-theme/generate-settings-file.ts +103 -0
  25. package/src/scaffold-theme/parse-files.ts +466 -0
  26. package/src/scaffold-theme/parse-locales.ts +98 -0
  27. package/src/shopify-cli/pull.ts +103 -0
  28. package/src/tailwind/postcss.config.js +8 -0
  29. package/src/tailwind/tailwind-watch.ts +133 -0
  30. package/src/tailwind/tailwind.config.js +261 -0
  31. package/src/telemetry/telemetry.ts +84 -0
  32. package/src/templates/.env.template +12 -0
  33. package/src/templates/shopify.theme.toml +9 -0
  34. package/src/utils/capitalize.ts +3 -0
  35. package/src/utils/delay.ts +3 -0
  36. package/src/utils/fs.ts +87 -0
  37. package/src/utils/is-object.ts +3 -0
  38. package/src/utils/json.ts +8 -0
  39. package/src/utils/to-camel-case.ts +6 -0
  40. package/src/utils/to-kebab-case.ts +6 -0
  41. package/src/utils/to-pascal-case.ts +12 -0
  42. package/src/utils/to-snake-case.ts +45 -0
  43. package/src/validate-cli-options.ts +281 -0
  44. package/src/watch-headless/watch-headless.ts +81 -0
  45. package/src/watch-theme/watch-theme.ts +84 -0
  46. package/tsconfig.json +82 -0
  47. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,246 @@
1
+ import { dtsPlugin } from "esbuild-plugin-d.ts";
2
+ import path from "path";
3
+ import { config, root_dir } from "../../shopify-accelerate-app";
4
+ import { isBlockTs, isSectionTs } from "../scaffold-theme/parse-files";
5
+
6
+ const { build } = require("esbuild");
7
+
8
+ // import watch from "node-watch";
9
+ const watch = require("node-watch");
10
+ const fs = require("fs");
11
+
12
+ const runEsBuild = () => {
13
+ const entryPoints = [];
14
+ if (fs.existsSync(path.join(root_dir, "assets", "theme.ts"))) {
15
+ entryPoints.push(path.join(root_dir, "assets", "theme.ts"));
16
+ }
17
+
18
+ if (fs.existsSync(path.join(root_dir, "assets", "editor.ts"))) {
19
+ entryPoints.push(path.join(root_dir, "assets", "editor.ts"));
20
+ }
21
+ if (!entryPoints.length) {
22
+ console.log("No JS/TS entry files found.");
23
+ return;
24
+ }
25
+ build({
26
+ entryPoints: entryPoints,
27
+ metafile: true,
28
+ target: "es2020",
29
+ // sourcemap: "external",
30
+ treeShaking: true,
31
+ bundle: true,
32
+ // outfile: "./assets/theme.js",
33
+ outdir: path.join(root_dir, "assets"),
34
+ minify: false,
35
+ ignoreAnnotations: true,
36
+ packages: "external",
37
+
38
+ format: "esm",
39
+ legalComments: "none",
40
+ keepNames: true,
41
+ plugins: [dtsPlugin({})],
42
+
43
+ // splitting: true,
44
+ })
45
+ .then((e) => {
46
+ console.log("theme.js - bundled");
47
+ const content = fs.readFileSync(path.join(root_dir, "assets", "editor.js"), {
48
+ encoding: "utf-8",
49
+ });
50
+ fs.writeFileSync(
51
+ path.join(root_dir, "assets", "editor.js"),
52
+ `${content}\n // random_comment `
53
+ );
54
+ const content2 = fs.readFileSync(path.join(root_dir, "assets", "theme.js"), {
55
+ encoding: "utf-8",
56
+ });
57
+ fs.writeFileSync(
58
+ path.join(root_dir, "assets", "theme.js"),
59
+ `${content2}\n // random_comment `
60
+ );
61
+ })
62
+ .catch((error) => {
63
+ console.error(error);
64
+ // eslint-disable-next-line no-process-exit
65
+ });
66
+ };
67
+
68
+ const runSectionJsEsbuild = (entryFile) => {
69
+ build({
70
+ entryPoints: [entryFile],
71
+ metafile: true,
72
+ target: "es2020",
73
+ // sourcemap: "external",
74
+ treeShaking: true,
75
+ // bundle: true,
76
+ outfile: path.join(
77
+ root_dir,
78
+ config.theme_path,
79
+ "assets",
80
+ `__section--${entryFile
81
+ .split(/[\\/]/gi)
82
+ .at(-1)
83
+ .replace(/\.(ts)x?$/gi, ".js")}`
84
+ ),
85
+ // outdir: path.join(root_dir, config.theme_path, "assets"),
86
+ minify: false,
87
+ ignoreAnnotations: true,
88
+ packages: "external",
89
+ format: "esm",
90
+ legalComments: "none",
91
+ keepNames: true,
92
+ plugins: [dtsPlugin({})],
93
+
94
+ // splitting: true,
95
+ })
96
+ .then((e) => {
97
+ console.log(
98
+ `__section--${entryFile
99
+ .split(/[\\/]/gi)
100
+ .at(-1)
101
+ .replace(/\.(ts)x?$/gi, ".js")}- bundled`
102
+ );
103
+ })
104
+ .catch((error) => {
105
+ console.error(error);
106
+ // eslint-disable-next-line no-process-exit
107
+ });
108
+ };
109
+
110
+ const runBlockJsEsbuild = (entryFile) => {
111
+ build({
112
+ entryPoints: [entryFile],
113
+ metafile: true,
114
+ target: "es2020",
115
+ // sourcemap: "external",
116
+ treeShaking: true,
117
+ // bundle: true,
118
+ outfile: path.join(
119
+ root_dir,
120
+ config.theme_path,
121
+ "assets",
122
+ `__block--${entryFile
123
+ .split(/[\\/]/gi)
124
+ .at(-1)
125
+ .replace(/\.(ts)x?$/gi, ".js")}`
126
+ ),
127
+ // outdir: path.join(root_dir, config.theme_path, "assets"),
128
+ minify: false,
129
+ ignoreAnnotations: true,
130
+ packages: "external",
131
+ format: "esm",
132
+ legalComments: "none",
133
+ keepNames: true,
134
+ plugins: [dtsPlugin({})],
135
+
136
+ // splitting: true,
137
+ })
138
+ .then((e) => {
139
+ console.log(
140
+ `__block--${entryFile
141
+ .split(/[\\/]/gi)
142
+ .at(-1)
143
+ .replace(/\.(ts)x?$/gi, ".js")} - bundled`
144
+ );
145
+ })
146
+ .catch((error) => {
147
+ console.error(error);
148
+ // eslint-disable-next-line no-process-exit
149
+ });
150
+ };
151
+
152
+ export const runEsbuild = () => {
153
+ let running = false;
154
+ watch(root_dir, { recursive: true }, async (event, name) => {
155
+ if (running || event === "remove") return;
156
+ if (!name.match(/\.(ts)x?$/) || /schema\.ts$/gi.test(name)) return;
157
+ running = true;
158
+
159
+ if (isSectionTs(name)) {
160
+ const filename = name.split(/[\\/]/gi).at(-1);
161
+
162
+ const section = Object.values(config.sources.sectionSchemas).find((section) =>
163
+ section.path.includes(name.replace(filename, ""))
164
+ );
165
+
166
+ if (section && !section.disabled) {
167
+ try {
168
+ runSectionJsEsbuild(name);
169
+ } catch (err) {
170
+ console.log(err);
171
+ }
172
+ }
173
+ running = false;
174
+ return;
175
+ }
176
+
177
+ if (isBlockTs(name)) {
178
+ const filename = name.split(/[\\/]/gi).at(-1);
179
+
180
+ const block = Object.values(config.sources.blockSchemas).find((block) =>
181
+ block.path.includes(name.replace(filename, ""))
182
+ );
183
+
184
+ if (block && !block.disabled) {
185
+ try {
186
+ runBlockJsEsbuild(name);
187
+ } catch (err) {
188
+ console.log(err);
189
+ }
190
+ }
191
+ running = false;
192
+ return;
193
+ }
194
+
195
+ try {
196
+ runEsBuild();
197
+ } catch (err) {
198
+ console.log(err);
199
+ }
200
+ running = false;
201
+ });
202
+
203
+ config.sources.sectionsJs.forEach((name) => {
204
+ running = true;
205
+
206
+ if (isSectionTs(name)) {
207
+ const filename = name.split(/[\\/]/gi).at(-1);
208
+
209
+ const section = Object.values(config.sources.sectionSchemas).find((section) =>
210
+ section.path.includes(name.replace(filename, ""))
211
+ );
212
+
213
+ if (section && !section.disabled) {
214
+ try {
215
+ runSectionJsEsbuild(name);
216
+ } catch (err) {
217
+ console.log(err);
218
+ }
219
+ }
220
+ running = false;
221
+ return;
222
+ }
223
+ });
224
+
225
+ config.sources.blocksJs.forEach((name) => {
226
+ running = true;
227
+ if (isBlockTs(name)) {
228
+ const filename = name.split(/[\\/]/gi).at(-1);
229
+
230
+ const block = Object.values(config.sources.blockSchemas).find((block) =>
231
+ block.path.includes(name.replace(filename, ""))
232
+ );
233
+
234
+ if (block && !block.disabled) {
235
+ try {
236
+ runBlockJsEsbuild(name);
237
+ } catch (err) {
238
+ console.log(err);
239
+ }
240
+ }
241
+ running = false;
242
+ return;
243
+ }
244
+ });
245
+ runEsBuild();
246
+ };
@@ -0,0 +1,24 @@
1
+ import { generateAssetFiles } from "./generate-asset-files";
2
+ import { generateBaseTypes } from "./generate-base-types";
3
+ import { generateBlocksTypes } from "./generate-blocks-types";
4
+ import { generateLiquidFiles } from "./generate-liquid-files";
5
+ import { generateSchemaLocales } from "./generate-schema-locales";
6
+ import { generateSchemaVariables } from "./generate-schema-variables";
7
+ import { generateSectionsTypes } from "./generate-section-types";
8
+ import { generateSettingTypes } from "./generate-setting-types";
9
+ import { getSources, getTargets } from "./parse-files";
10
+ import { parseLocales } from "./parse-locales";
11
+
12
+ export const buildTheme = () => {
13
+ generateBaseTypes();
14
+ getSources();
15
+ getTargets();
16
+ parseLocales();
17
+ generateSchemaVariables();
18
+ generateSchemaLocales();
19
+ generateSectionsTypes();
20
+ generateBlocksTypes();
21
+ generateSettingTypes();
22
+ generateLiquidFiles();
23
+ generateAssetFiles();
24
+ };
@@ -0,0 +1,48 @@
1
+ import chalk from "chalk";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { config, root_dir } from "../../shopify-accelerate-app";
5
+ import { deleteFile, writeCompareFile, writeOnlyNew } from "../utils/fs";
6
+
7
+ export const generateAssetFiles = () => {
8
+ const { theme_path, sources, targets, delete_external_assets, ignore_assets } = config;
9
+
10
+ sources.assets.forEach((file) => {
11
+ const fileName = file.split(/[\\/]/gi).at(-1);
12
+ const targetPath = path.join(process.cwd(), theme_path, "assets", fileName);
13
+
14
+ const rawContent = fs.readFileSync(file, { encoding: "utf-8" });
15
+
16
+ if (ignore_assets?.includes(targetPath.split(/[/\\]/)?.at(-1))) {
17
+ console.log(
18
+ `[${chalk.gray(new Date().toLocaleTimeString())}]: ${chalk.greenBright(
19
+ `Ignored: ${targetPath.replace(process.cwd(), "")}`
20
+ )}`
21
+ );
22
+ writeOnlyNew(targetPath, rawContent);
23
+ } else {
24
+ writeCompareFile(targetPath, rawContent);
25
+ }
26
+ });
27
+
28
+ if (delete_external_assets) {
29
+ targets.assets.forEach((file) => {
30
+ const fileName = file.split(/[\\/]/gi).at(-1);
31
+ const targetFile = sources.assets.find((sourcePath) =>
32
+ sourcePath.split(/[\\/]/gi).at(-1).includes(fileName)
33
+ );
34
+
35
+ if (
36
+ /^replo/gi.test(fileName) ||
37
+ /^pandectes/gi.test(fileName) ||
38
+ /^locksmith/gi.test(fileName) ||
39
+ /^shogun/gi.test(fileName)
40
+ ) {
41
+ return;
42
+ }
43
+ if (!targetFile) {
44
+ deleteFile(path.join(process.cwd(), file));
45
+ }
46
+ });
47
+ }
48
+ };
@@ -0,0 +1,23 @@
1
+ import path from "path";
2
+ import { config } from "../../shopify-accelerate-app";
3
+ import { readFile, writeCompareFile, writeOnlyNew } from "../utils/fs";
4
+
5
+ export const generateBaseTypes = () => {
6
+ const { folders, package_types } = config;
7
+ writeCompareFile(
8
+ path.join(folders.types, "shopify.ts"),
9
+ readFile(path.join(package_types, "shopify.ts"))
10
+ );
11
+ writeOnlyNew(
12
+ path.join(folders.types, "settings.ts"),
13
+ readFile(path.join(package_types, "settings.ts"))
14
+ );
15
+ writeOnlyNew(
16
+ path.join(folders.types, "sections.ts"),
17
+ readFile(path.join(package_types, "sections.ts"))
18
+ );
19
+ writeOnlyNew(
20
+ path.join(folders.types, "metafields.ts"),
21
+ readFile(path.join(package_types, "metafields.ts"))
22
+ );
23
+ };
@@ -0,0 +1,194 @@
1
+ import { ShopifyBlock } from "../../@types/shopify";
2
+ import { config } from "../../shopify-accelerate-app";
3
+ import { toLocaleFriendlySnakeCase } from "../utils/to-snake-case";
4
+
5
+ export const generateBlockFiles = ({
6
+ name,
7
+ disabled,
8
+ path,
9
+ folder,
10
+ ...section
11
+ }: ShopifyBlock & { path: string; folder: string }) => {
12
+ const sectionName = toLocaleFriendlySnakeCase(name);
13
+ const { sources, disabled_locales } = config;
14
+ const localeDuplicates = sources.locale_duplicates;
15
+ let paragraphCount = 1;
16
+ let headerCount = 1;
17
+
18
+ const localizedSection = {
19
+ name: disabled_locales || sectionName?.length <= 25 ? name : `t:blocks.${sectionName}.name`,
20
+ ...section,
21
+ settings: section?.settings?.map((setting) => {
22
+ const settingsBase = `t:blocks.${sectionName}.settings`;
23
+ if (setting.type === "paragraph") {
24
+ return {
25
+ ...setting,
26
+ content:
27
+ "content" in setting
28
+ ? disabled_locales && !setting.content.includes(" ") && setting.content.length < 500
29
+ ? setting.content
30
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.content)]?.length > 1
31
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.content)}`
32
+ : `${settingsBase}.paragraph__${paragraphCount++}.content`
33
+ : undefined,
34
+ };
35
+ }
36
+ if (setting.type === "header") {
37
+ return {
38
+ ...setting,
39
+ content:
40
+ "content" in setting
41
+ ? disabled_locales && !setting.content.includes(" ") && setting.content.length < 500
42
+ ? setting.content
43
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.content)]?.length > 1
44
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.content)}`
45
+ : `${settingsBase}.header__${headerCount++}.content`
46
+ : undefined,
47
+ };
48
+ }
49
+ return {
50
+ ...setting,
51
+ label:
52
+ "label" in setting
53
+ ? disabled_locales
54
+ ? setting.label
55
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.label)]?.length > 1
56
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.label)}`
57
+ : `${settingsBase}.${setting.id}.label`
58
+ : undefined,
59
+ info:
60
+ "info" in setting
61
+ ? disabled_locales && setting.info.length < 500
62
+ ? setting.info
63
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.info)]?.length > 1
64
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.info)}`
65
+ : `${settingsBase}.${setting.id}.info`
66
+ : undefined,
67
+ placeholder:
68
+ "placeholder" in setting && typeof setting.placeholder === "string"
69
+ ? disabled_locales
70
+ ? setting.placeholder
71
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.placeholder)]?.length > 1
72
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.placeholder)}`
73
+ : `${settingsBase}.${setting.id}.placeholder`
74
+ : undefined,
75
+ options:
76
+ "options" in setting
77
+ ? disabled_locales
78
+ ? setting.options
79
+ : setting.options.map((option, index) => ({
80
+ ...option,
81
+ label:
82
+ localeDuplicates[toLocaleFriendlySnakeCase(option.label)]?.length > 1
83
+ ? `t:all.${toLocaleFriendlySnakeCase(option.label)}`
84
+ : `${settingsBase}.${setting.id}.options__${index + 1}.label`,
85
+ }))
86
+ : undefined,
87
+ };
88
+ }),
89
+ blocks: section.blocks?.map(({ name, ...block }) => {
90
+ let paragraphCount = 1;
91
+ let headerCount = 1;
92
+
93
+ if (block.type === "@app") return { name, ...block };
94
+ if (block.type === "@theme") return { name, ...block };
95
+
96
+ return {
97
+ name:
98
+ disabled_locales || name?.length <= 25
99
+ ? name
100
+ : `t:blocks.${sectionName}.blocks.${toLocaleFriendlySnakeCase(name)}.name`,
101
+ ...block,
102
+ settings: block?.settings?.map((setting) => {
103
+ const settingsBase = `t:blocks.${sectionName}.blocks.${toLocaleFriendlySnakeCase(
104
+ name
105
+ )}.settings`;
106
+
107
+ if (setting.type === "paragraph") {
108
+ return {
109
+ ...setting,
110
+ content:
111
+ "content" in setting
112
+ ? disabled_locales &&
113
+ !setting.content.includes(" ") &&
114
+ setting.content.length < 500
115
+ ? setting.content
116
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.content)]?.length > 1
117
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.content)}`
118
+ : `${settingsBase}.paragraph__${paragraphCount++}.content`
119
+ : undefined,
120
+ };
121
+ }
122
+ if (setting.type === "header") {
123
+ return {
124
+ ...setting,
125
+ content:
126
+ "content" in setting
127
+ ? disabled_locales &&
128
+ !setting.content.includes(" ") &&
129
+ setting.content.length < 500
130
+ ? setting.content
131
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.content)]?.length > 1
132
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.content)}`
133
+ : `${settingsBase}.header__${headerCount++}.content`
134
+ : undefined,
135
+ };
136
+ }
137
+ return {
138
+ ...setting,
139
+ label:
140
+ "label" in setting
141
+ ? disabled_locales
142
+ ? setting.label
143
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.label)]?.length > 1
144
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.label)}`
145
+ : `${settingsBase}.${setting.id}.label`
146
+ : undefined,
147
+ info:
148
+ "info" in setting
149
+ ? disabled_locales && setting.info.length < 500
150
+ ? setting.info
151
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.info)]?.length > 1
152
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.info)}`
153
+ : `${settingsBase}.${setting.id}.info`
154
+ : undefined,
155
+ placeholder:
156
+ "placeholder" in setting && typeof setting.placeholder === "string"
157
+ ? disabled_locales
158
+ ? setting.placeholder
159
+ : localeDuplicates[toLocaleFriendlySnakeCase(setting.placeholder)]?.length > 1
160
+ ? `t:all.${toLocaleFriendlySnakeCase(setting.placeholder)}`
161
+ : `${settingsBase}.${setting.id}.placeholder`
162
+ : undefined,
163
+ options:
164
+ "options" in setting
165
+ ? disabled_locales
166
+ ? setting.options
167
+ : setting.options.map((option, index) => ({
168
+ ...option,
169
+ label:
170
+ localeDuplicates[toLocaleFriendlySnakeCase(option.label)]?.length > 1
171
+ ? `t:all.${toLocaleFriendlySnakeCase(option.label)}`
172
+ : `${settingsBase}.${setting.id}.options__${index + 1}.label`,
173
+ }))
174
+ : undefined,
175
+ };
176
+ }),
177
+ };
178
+ }),
179
+ presets: section.presets?.map(({ name, ...preset }) => {
180
+ return {
181
+ name:
182
+ disabled_locales || name?.length <= 25
183
+ ? name
184
+ : `t:blocks.${sectionName}.presets.${toLocaleFriendlySnakeCase(name)}.name`,
185
+ ...preset,
186
+ };
187
+ }),
188
+ };
189
+
190
+ return `{% schema %}
191
+ ${JSON.stringify(localizedSection, undefined, 2)}
192
+ {% endschema %}
193
+ `;
194
+ };