Skip to content

Commit ba12f1f

Browse files
committed
Generate App Bridge Docs
1 parent ac5b34c commit ba12f1f

File tree

5 files changed

+179
-89
lines changed

5 files changed

+179
-89
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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 __filename = fileURLToPath(import.meta.url);
9+
const __dirname = path.dirname(__filename);
10+
11+
const API_VERSION = process.argv[2] || 'unstable';
12+
const rootPath = path.join(__dirname, '../../..');
13+
const docsRelativePath = 'docs/surfaces/admin';
14+
const srcRelativePath = 'src/surfaces/admin';
15+
const docsPath = path.join(rootPath, docsRelativePath);
16+
const srcPath = path.join(rootPath, srcRelativePath);
17+
const componentDefinitions = path.join(
18+
rootPath,
19+
'src/surfaces/admin/components.d.ts',
20+
);
21+
const copiedComponentDefinitions = path.join(srcPath, 'components.ts');
22+
const shopifyDevPath = path.join(rootPath, '../../../shopify-dev');
23+
24+
const replaceFileContent = async (filePath, searchValue, replaceValue) => {
25+
const content = await fs.readFile(filePath, 'utf8');
26+
// @ts-ignore -- TS should know this is a string but it doesn't
27+
const replacedContent = content.replaceAll(searchValue, replaceValue);
28+
await fs.writeFile(filePath, replacedContent);
29+
};
30+
31+
if (API_VERSION === 'unstable') {
32+
console.log(
33+
"Building docs for 'unstable' admin UI extensions API. You can add a calver version argument (e.g. 'yarn docs:admin 2023-07') to generate the docs for a stable version.",
34+
);
35+
} else {
36+
console.log(`Building docs for '${API_VERSION}' admin UI extensions API.`);
37+
console.log(
38+
"When generating docs for a stable version, 'unstable' docs are not regenerated. This avoids overwriting other unstable changes that are not included in this version.",
39+
);
40+
console.log(
41+
"If you need to update the 'unstable' version, run this command again without the '$API_VERSION' parameter.",
42+
);
43+
}
44+
45+
// Rename components.d.ts to components.ts so it can be picked up be the compiler
46+
await fs.copyFile(componentDefinitions, copiedComponentDefinitions);
47+
48+
// Replace references of 'globalThis.HTMLElement' to 'any'
49+
await replaceFileContent(
50+
copiedComponentDefinitions,
51+
/typeof globalThis\.HTMLElement/g,
52+
'any',
53+
);
54+
55+
childProcess.execSync(
56+
`yarn tsc --project ${docsRelativePath}/tsconfig.docs.json --moduleResolution node --target esNext --module CommonJS && yarn generate-docs --overridePath ./${docsRelativePath}/typeOverride.json --input ./${docsRelativePath}/reference ./${srcRelativePath} --typesInput ./${srcRelativePath} --output ./${docsRelativePath}/generated`,
57+
);
58+
childProcess.execSync(
59+
`yarn tsc ${docsRelativePath}/staticPages/*.doc.ts --moduleResolution node --target esNext --module CommonJS && yarn generate-docs --isLandingPage --input ./${docsRelativePath}/staticPages --output ./${docsRelativePath}/generated`,
60+
);
61+
62+
const docsDataPath = path.join(docsPath, 'generated/generated_docs_data.json');
63+
64+
// Clean up generated files
65+
const srcFiles = await fs.readdir(rootPath, {recursive: true});
66+
const docJsFiles = srcFiles.filter((file) => file.endsWith('.doc.js'));
67+
await Promise.all(docJsFiles.map((file) => fs.rm(path.join(rootPath, file))));
68+
await fs.rm(copiedComponentDefinitions);
69+
70+
// Make sure https://shopify.dev URLs are relative so they work in Spin.
71+
// See https://github.com/Shopify/generate-docs/issues/181
72+
await replaceFileContent(docsDataPath, 'https://shopify.dev', '');
73+
74+
// Replace 'unstable' with the exact API version in relative doc links
75+
await replaceFileContent(
76+
docsDataPath,
77+
'/docs/api/admin-extensions/unstable/',
78+
`/docs/api/admin-extensions/${API_VERSION}`,
79+
);
80+
81+
if (!existsSync(shopifyDevPath)) {
82+
console.log(
83+
`Not copying docs to shopify-dev because it was not found at ${shopifyDevPath}.`,
84+
);
85+
process.exit();
86+
}
87+
88+
// Ship the Admin Extensions Docs to shopify.dev
89+
await fs.mkdir(
90+
path.join(
91+
shopifyDevPath,
92+
'db/data/docs/templated_apis/admin_extensions',
93+
API_VERSION,
94+
),
95+
{recursive: true},
96+
);
97+
98+
await fs.cp(
99+
path.join(docsPath, 'generated'),
100+
path.join(
101+
shopifyDevPath,
102+
'db/data/docs/templated_apis/admin_extensions',
103+
API_VERSION,
104+
),
105+
{recursive: true},
106+
);
107+
108+
// Shim which could live in @shopify/generate-docs to generate App Bridge docs
109+
// In the future we could integrate this back into generate docs
110+
// Right now we are doing this the quick and dirty way.
111+
const docsData = await fs.readFile(docsDataPath, 'utf8');
112+
const appBridgeComponentDocsData = JSON.parse(docsData).filter(
113+
(doc) =>
114+
doc.category === 'Components' &&
115+
// The Admin UI Extension components like AdminLink and AdminBlock should be excluded
116+
!['Other'].includes(doc.subCategory),
117+
);
118+
119+
const appBridgeDocs = [];
120+
await Promise.all(
121+
appBridgeComponentDocsData.map(async (component) => {
122+
delete component.defaultExample;
123+
component.category = 'Experimental Components';
124+
125+
const componentDir = path.join(
126+
srcPath,
127+
'components',
128+
component.name,
129+
'examples',
130+
// Assumption that we only need one example for each component
131+
`basic-${component.name.toLowerCase()}.example.html`,
132+
);
133+
134+
try {
135+
const componentExample = (await fs.readFile(componentDir, 'utf8')).trim();
136+
137+
// eslint-disable-next-line require-atomic-updates
138+
component.defaultExample = {
139+
appBridge: componentExample,
140+
codeblock: {
141+
tabs: [{title: 'HTML', code: componentExample, language: 'html'}],
142+
},
143+
};
144+
} catch (error) {
145+
console.warn('No AppBridge example found for', component.name);
146+
}
147+
148+
appBridgeDocs.push(component);
149+
}),
150+
);
151+
152+
// Heart surgery to ensure Experimental Components are updated
153+
const existingAppBridgeDocsPath = path.join(
154+
shopifyDevPath,
155+
'db/data/docs/templated_apis/app_bridge/generated_docs_data.json',
156+
);
157+
const existingAppBridgeDocs = await fs.readFile(
158+
existingAppBridgeDocsPath,
159+
'utf-8',
160+
);
161+
162+
const updatedAppBridgeDocs = JSON.parse(existingAppBridgeDocs)
163+
.filter((existingDoc) => existingDoc.category !== 'Experimental Components')
164+
.concat(appBridgeDocs);
165+
166+
await fs.writeFile(
167+
existingAppBridgeDocsPath,
168+
JSON.stringify(updatedAppBridgeDocs, null, 2),
169+
);
170+
171+
console.log(
172+
'Admin Extensions Docs: https://shopify-dev.myshopify.io/docs/api/admin-extensions',
173+
);
174+
console.log(
175+
'App Bridge Docs: https://shopify-dev.myshopify.io/docs/api/app-bridge',
176+
);

packages/ui-extensions/docs/surfaces/admin/build-docs.sh

-88
This file was deleted.

packages/ui-extensions/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@shopify/ui-extensions",
33
"version": "2025.7.0",
44
"scripts": {
5-
"docs:admin": "bash ./docs/surfaces/admin/build-docs.sh",
5+
"docs:admin": "node ./docs/surfaces/admin/build-docs.mjs",
66
"gen-docs:admin": "bash ./docs/surfaces/admin/create-doc-files.sh \"admin\"",
77
"docs:checkout": "bash ./docs/surfaces/checkout/build-docs.sh",
88
"docs:point-of-sale": "bash ./docs/surfaces/point-of-sale/build-docs.sh",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<s-badge>Fufilled</s-badge>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<s-box>Hello world</s-box>

0 commit comments

Comments
 (0)