Skip to content

Commit 79048ab

Browse files
committed
support doc generate
1 parent fb35d1f commit 79048ab

File tree

12 files changed

+376
-117
lines changed

12 files changed

+376
-117
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/* eslint-disable no-undef, no-console */
2+
import childProcess from 'child_process';
3+
import fs from 'fs/promises';
4+
import {existsSync} from 'fs';
5+
import path from 'path';
6+
import {fileURLToPath} from 'url';
7+
8+
const EXTENSIONS_API_VERSION = process.argv[2] || 'unstable';
9+
10+
const __filename = fileURLToPath(import.meta.url);
11+
const __dirname = path.dirname(__filename);
12+
13+
const rootPath = path.join(__dirname, '../../..');
14+
const docsRelativePath = 'docs/surfaces/customer-account';
15+
const docsGeneratedRelativePath = 'docs/surfaces/customer-account/generated';
16+
const srcRelativePath = 'src/surfaces/customer-account';
17+
const docsPath = path.join(rootPath, docsRelativePath);
18+
const srcPath = path.join(rootPath, srcRelativePath);
19+
const generatedDocsPath = path.join(docsPath, 'generated');
20+
const shopifyDevPath = path.join(rootPath, '../../../shopify-dev');
21+
const shopifyDevDBPath = path.join(
22+
shopifyDevPath,
23+
'db/data/docs/templated_apis',
24+
);
25+
26+
const shopifyDevExists = existsSync(shopifyDevPath);
27+
28+
const generatedDocsDataFile = 'generated_docs_data.json';
29+
const generatedStaticPagesFile = 'generated_static_pages.json';
30+
31+
const componentDefs = path.join(srcPath, 'components.d.ts');
32+
const tempComponentDefs = path.join(srcPath, 'components.ts');
33+
34+
const replaceFileContent = async (filePaths, searchValue, replaceValue) => {
35+
const files = Array.isArray(filePaths) ? filePaths : [filePaths];
36+
for (const filePath of files) {
37+
const content = await fs.readFile(filePath, 'utf8');
38+
// @ts-ignore -- TS should know this is a string but it doesn't
39+
const replacedContent = content.replaceAll(searchValue, replaceValue);
40+
await fs.writeFile(filePath, replacedContent);
41+
}
42+
};
43+
44+
const decodeHTML = (str) => {
45+
return str
46+
.replace(/&/g, '&')
47+
.replace(/&lt;/g, '<')
48+
.replace(/&gt;/g, '>')
49+
.replace(/&quot;/g, '"')
50+
.replace(/&#039;/g, "'");
51+
};
52+
53+
const htmlWrapper = (htmlString, layout) => {
54+
return `<!DOCTYPE html><html><head><style>html, body {height:100%} body {box-sizing: border-box; margin: 0; padding:0.5rem; ${layout}}</style><script src="https://cdn.shopify.com/shopifycloud/app-bridge-ui-experimental.js"></script></head><body>${decodeHTML(
55+
htmlString,
56+
)}</body></html>`;
57+
};
58+
59+
const templates = {
60+
default: (htmlString) =>
61+
htmlWrapper(htmlString, 'display: grid; place-items: center; gap: 0.5rem;'),
62+
wrapped: (htmlString) =>
63+
htmlWrapper(
64+
`<div>${htmlString}</div>`,
65+
'display: grid; place-items: center; gap: 0.5rem;',
66+
),
67+
inline: (htmlString) =>
68+
htmlWrapper(
69+
htmlString,
70+
'display: flex; justify-content: center; align-items: center; gap: 0.5rem;',
71+
),
72+
section: (htmlString) =>
73+
htmlWrapper(
74+
`<s-section padding="none">${htmlString}</s-section>`,
75+
'display: grid; place-items: center; background: #F1F1F1',
76+
),
77+
page: (htmlString) =>
78+
htmlWrapper(
79+
htmlString,
80+
'display: grid; place-items: center; background: #F1F1F1;',
81+
),
82+
none: (htmlString) => htmlWrapper(htmlString, 'padding: 0'),
83+
};
84+
85+
const transformJson = async (filePath) => {
86+
const jsonData = JSON.parse((await fs.readFile(filePath, 'utf8')).toString());
87+
88+
jsonData.forEach((entry) => {
89+
// Temporary to ensure that isOptional is added to all members
90+
if (entry.definitions && entry.isVisualComponent) {
91+
entry.definitions.forEach((definition) => {
92+
if (definition.typeDefinitions) {
93+
Object.values(definition.typeDefinitions).forEach((typeDef) => {
94+
if (typeDef.members && Array.isArray(typeDef.members)) {
95+
typeDef.members
96+
.sort((first, second) => first.name.localeCompare(second.name))
97+
.forEach((member) => {
98+
// eslint-disable-next-line no-prototype-builtins
99+
if (member.hasOwnProperty('isOptional')) return;
100+
member.isOptional = true;
101+
});
102+
}
103+
});
104+
}
105+
});
106+
}
107+
108+
if (entry.defaultExample?.codeblock?.tabs) {
109+
const newTabs = [];
110+
entry.defaultExample.codeblock.tabs.forEach((tab) => {
111+
if (tab.language !== 'preview') {
112+
newTabs.push(tab);
113+
return;
114+
}
115+
116+
if (tab.layout && !(tab.layout in templates)) {
117+
console.warn(
118+
`${entry.name} has a layout of ${tab.layout} which is not a valid template.`,
119+
);
120+
}
121+
122+
const previewHTML =
123+
tab.layout && tab.layout in templates
124+
? templates[tab.layout](tab.code)
125+
: templates.default(tab.code);
126+
127+
newTabs.push(
128+
{code: tab.code, language: 'html'},
129+
{code: previewHTML, language: 'preview'},
130+
);
131+
});
132+
133+
entry.defaultExample.codeblock.tabs = newTabs;
134+
}
135+
});
136+
137+
await fs.writeFile(filePath, JSON.stringify(jsonData, null, 2));
138+
};
139+
140+
const generateFiles = async (tsconfig, outputDir) => {
141+
const scripts = [
142+
`yarn tsc --project ${docsRelativePath}/${tsconfig} --moduleResolution node --target esNext --module CommonJS`,
143+
`yarn generate-docs --input ./${srcRelativePath} --typesInput ./${srcRelativePath} --output ./${outputDir}`,
144+
];
145+
146+
scripts.push(
147+
`yarn tsc ${docsRelativePath}/staticPages/*.doc.ts --moduleResolution node --target esNext --module CommonJS`,
148+
`yarn generate-docs --isLandingPage --input ./${docsRelativePath}/staticPages --output ./${outputDir}`,
149+
);
150+
151+
scripts.forEach((script) => childProcess.execSync(script, {stdio: 'pipe'}));
152+
153+
const srcFiles = await fs.readdir(rootPath, {recursive: true});
154+
const builtFiles = srcFiles.filter((file) => file.endsWith('.ts'));
155+
await Promise.all(
156+
builtFiles.map((file) => {
157+
const jsFilePath = path.join(rootPath, file.replace('.ts', '.js'));
158+
return existsSync(jsFilePath) ? fs.rm(jsFilePath) : Promise.resolve();
159+
}),
160+
);
161+
162+
const generatedFiles = [path.join(outputDir, generatedDocsDataFile)];
163+
generatedFiles.push(path.join(outputDir, generatedStaticPagesFile));
164+
165+
// Make sure https://shopify.dev URLs are relative so they work in Spin.
166+
// See https://github.com/Shopify/generate-docs/issues/181
167+
await replaceFileContent(generatedFiles, 'https://shopify.dev', '');
168+
169+
// @ts-ignore
170+
await transformJson(path.join(outputDir, generatedDocsDataFile));
171+
};
172+
173+
const copyGeneratedToShopifyDev = async () => {
174+
if (!shopifyDevExists) {
175+
console.log(
176+
`Not copying docs to shopify-dev because it was not found at ${shopifyDevPath}.`,
177+
);
178+
process.exit();
179+
}
180+
181+
await fs.cp(generatedDocsPath, shopifyDevDBPath, {recursive: true});
182+
};
183+
184+
const generateExtensionsDocs = async () => {
185+
console.log(
186+
`Building Customer Account UI Extensions docs for ${EXTENSIONS_API_VERSION} version`,
187+
);
188+
189+
if (EXTENSIONS_API_VERSION === 'unstable') {
190+
console.log(
191+
"You can add a calver version argument (e.g. 'yarn docs:customer-account 2024-07') to generate the docs for a stable version.",
192+
);
193+
}
194+
195+
const extensionsOutputDir = `${docsGeneratedRelativePath}/customer-account/${EXTENSIONS_API_VERSION}`;
196+
197+
await generateFiles('tsconfig.docs.json', extensionsOutputDir);
198+
199+
// Replace 'unstable' with the exact API version in relative doc links
200+
await replaceFileContent(
201+
path.join(extensionsOutputDir, generatedDocsDataFile),
202+
'/docs/api/customer-account/unstable/',
203+
`/docs/api/customer-account/${EXTENSIONS_API_VERSION}`,
204+
);
205+
206+
await fs.cp(
207+
path.join(docsPath, 'screenshots'),
208+
path.join(
209+
shopifyDevPath,
210+
'app/assets/images/templated-apis-screenshots/customer-account',
211+
EXTENSIONS_API_VERSION,
212+
),
213+
{recursive: true},
214+
);
215+
};
216+
217+
try {
218+
if (existsSync(generatedDocsPath)) {
219+
await fs.rm(generatedDocsPath, {recursive: true});
220+
}
221+
await fs.copyFile(componentDefs, tempComponentDefs);
222+
await replaceFileContent(
223+
tempComponentDefs,
224+
/typeof globalThis\.HTMLElement/g,
225+
'any',
226+
);
227+
await generateExtensionsDocs();
228+
await copyGeneratedToShopifyDev();
229+
230+
await fs.rm(tempComponentDefs);
231+
} catch (error) {
232+
console.error(error);
233+
console.log(error.stdout.toString());
234+
console.log(error.stderr.toString());
235+
process.exit(1);
236+
}

Diff for: packages/ui-extensions/docs/surfaces/customer-account/tsconfig.docs.json

+1-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,5 @@
22
"compilerOptions": {
33
"rootDir": "/"
44
},
5-
"include": [
6-
"./**/*.doc.ts",
7-
"../../../src/surfaces/customer-account/**/*.doc.ts"
8-
],
9-
"exclude": []
5+
"include": ["./**/*.doc.ts", "../../../src/surfaces/customer-account/**/*.doc.ts"]
106
}

Diff for: packages/ui-extensions/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"docs:admin": "node ./docs/surfaces/admin/build-docs.mjs",
66
"docs:checkout": "bash ./docs/surfaces/checkout/build-docs.sh",
77
"docs:point-of-sale": "bash ./docs/surfaces/point-of-sale/build-docs.sh",
8-
"docs:customer-account": "bash ./docs/surfaces/customer-account/build-docs.sh"
8+
"docs:customer-account": "node ./docs/surfaces/customer-account/build-docs.mjs"
99
},
1010
"main": "index.js",
1111
"module": "index.mjs",

Diff for: packages/ui-extensions/src/shared.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ export type ApiVersion =
99
| '2024-04'
1010
| '2024-07'
1111
| '2024-10'
12-
| 'unstable';
12+
| '2025-01'
13+
| '2025-04'
14+
| 'unstable'
15+
| '2025-07';
1316

1417
/**
1518
* The capabilities an extension has access to.
@@ -858,7 +861,10 @@ export type StorefrontApiVersion =
858861
| '2024-04'
859862
| '2024-07'
860863
| '2024-10'
861-
| 'unstable';
864+
| '2025-01'
865+
| '2025-04'
866+
| 'unstable'
867+
| '2025-07';
862868

863869
export interface Country {
864870
/**

Diff for: packages/ui-extensions/src/surfaces/checkout.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {}

0 commit comments

Comments
 (0)