Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12,626 changes: 12,626 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

70 changes: 66 additions & 4 deletions packages/ui-extensions/docs/surfaces/admin/build-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import {
import {extractIconList} from './build-doc-extract-icons.mjs';

const EXTENSIONS_API_VERSION = process.argv[2] || 'unstable';
/** Folder name for admin_extensions when copying to shopify-dev. Defaults to EXTENSIONS_API_VERSION; for 2026-04 we use 2026-04-rc so docs land in the rc folder. */
const SHOPIFY_DEV_EXTENSIONS_FOLDER =
process.argv[3] ??
(EXTENSIONS_API_VERSION === '2026-04' ? '2026-04-rc' : null);

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Expand All @@ -33,6 +37,7 @@ const shopifyDevDBPath = path.join(
const shopifyDevExists = existsSync(shopifyDevPath);

const generatedDocsDataFile = 'generated_docs_data.json';
const generatedDocsDataV2File = 'generated_docs_data_v2.json';
const generatedStaticPagesFile = 'generated_static_pages.json';

const componentDefs = path.join(srcPath, 'components.d.ts');
Expand Down Expand Up @@ -254,6 +259,12 @@ const templates = {
const transformJson = async (filePath, isExtensions) => {
let jsonData = JSON.parse((await fs.readFile(filePath, 'utf8')).toString());

// V2 format is an object keyed by symbol name; transforms only apply to legacy array format
if (!Array.isArray(jsonData)) {
await fs.writeFile(filePath, JSON.stringify(jsonData, null, 2));
return;
}

const iconEntry = jsonData.find(
(entry) => entry.name === 'Icon' && entry.subSections,
);
Expand Down Expand Up @@ -447,13 +458,18 @@ const generateExtensionsDocs = async () => {
"You can add a calver version argument (e.g. 'yarn docs:admin 2023-07') to generate the docs for a stable version.",
);
}
if (SHOPIFY_DEV_EXTENSIONS_FOLDER) {
console.log(
`Shopify-dev admin_extensions folder will be: ${SHOPIFY_DEV_EXTENSIONS_FOLDER}`,
);
}

const outputDir = `${docsGeneratedRelativePath}/admin_extensions/${EXTENSIONS_API_VERSION}`;

const scripts = [
`yarn tsc --project ${docsRelativePath}/${tsconfigExtensions} --moduleResolution node --target esNext --module CommonJS`,
`yarn tsc --project ${docsRelativePath}/${tsconfigExtensions} --moduleResolution node --target esNext --module CommonJS --skipLibCheck`,
`yarn generate-docs --input ./${srcRelativePath} --typesInput ./${srcRelativePath} --output ./${outputDir}`,
`yarn tsc ${docsRelativePath}/staticPages/*.doc.ts --moduleResolution node --target esNext --module CommonJS`,
`yarn tsc ${docsRelativePath}/staticPages/*.doc.ts --moduleResolution node --target esNext --module CommonJS --skipLibCheck`,
`yarn generate-docs --isLandingPage --input ./${docsRelativePath}/staticPages --output ./${outputDir}`,
];

Expand All @@ -462,17 +478,31 @@ const generateExtensionsDocs = async () => {
outputDir,
rootPath,
generatedDocsDataFile,
generatedDocsDataV2File,
generatedStaticPagesFile,
transformJson: (filePath) => transformJson(filePath, true),
});

// Replace 'unstable' with the exact API version in relative doc links
const generatedDocsPathForVersion = path.join(rootPath, outputDir);
await replaceFileContent({
filePaths: path.join(outputDir, generatedDocsDataFile),
filePaths: path.join(generatedDocsPathForVersion, generatedDocsDataFile),
searchValue: '/docs/api/admin-extensions/unstable/',
replaceValue: `/docs/api/admin-extensions/${EXTENSIONS_API_VERSION}`,
});

// Generate generated_docs_data_v2.json for shopify-dev (same content as generated_docs_data.json)
const docsDataPath = path.join(
generatedDocsPathForVersion,
generatedDocsDataFile,
);
const docsDataV2Path = path.join(
generatedDocsPathForVersion,
generatedDocsDataV2File,
);
const docsDataContent = await fs.readFile(docsDataPath, 'utf8');
await fs.writeFile(docsDataV2Path, docsDataContent);

// Generate targets.json (extension targets + APIs + components mapping)
const targetsScriptPath = path.join(__dirname, 'build-docs-targets-json.mjs');
childProcess.execSync(`node ${targetsScriptPath}`, {
Expand All @@ -486,7 +516,7 @@ const generateAppBridgeDocs = async () => {

const outputDir = `${docsGeneratedRelativePath}/app_home`;
const scripts = [
`yarn tsc --project ${docsRelativePath}/${tsconfigAppBridge} --moduleResolution node --target esNext --module CommonJS`,
`yarn tsc --project ${docsRelativePath}/${tsconfigAppBridge} --moduleResolution node --target esNext --module CommonJS --skipLibCheck`,
`yarn generate-docs --input ./${srcRelativePath} --typesInput ./${srcRelativePath} --output ./${outputDir}`,
];

Expand All @@ -495,6 +525,7 @@ const generateAppBridgeDocs = async () => {
outputDir,
rootPath,
generatedDocsDataFile,
generatedDocsDataV2File,
transformJson: (filePath) => transformJson(filePath, false),
});
};
Expand Down Expand Up @@ -524,9 +555,14 @@ try {
'app_home',
);
const targetsJsonPath = path.join(adminExtensionsOutput, 'targets.json');
const generatedDocsDataV2Path = path.join(
adminExtensionsOutput,
generatedDocsDataV2File,
);
console.log('\nGenerated docs at:');
console.log(' Admin extensions:', adminExtensionsOutput);
console.log(' targets.json:', targetsJsonPath);
console.log(' generated_docs_data_v2.json:', generatedDocsDataV2Path);
console.log(' App Home:', appHomeOutput);

await copyGeneratedToShopifyDev({
Expand All @@ -535,6 +571,32 @@ try {
shopifyDevDBPath,
});

if (
SHOPIFY_DEV_EXTENSIONS_FOLDER &&
SHOPIFY_DEV_EXTENSIONS_FOLDER !== EXTENSIONS_API_VERSION &&
shopifyDevExists
) {
const adminExtSource = path.join(
shopifyDevDBPath,
'admin_extensions',
EXTENSIONS_API_VERSION,
);
const adminExtDest = path.join(
shopifyDevDBPath,
'admin_extensions',
SHOPIFY_DEV_EXTENSIONS_FOLDER,
);
if (existsSync(adminExtSource)) {
if (existsSync(adminExtDest)) {
await fs.rm(adminExtDest, {recursive: true});
}
await fs.rename(adminExtSource, adminExtDest);
console.log(
` Renamed admin_extensions/${EXTENSIONS_API_VERSION} → admin_extensions/${SHOPIFY_DEV_EXTENSIONS_FOLDER} in shopify-dev`,
);
}
}

await fs.cp(
path.join(docsPath, 'screenshots'),
path.join(
Expand Down
79 changes: 68 additions & 11 deletions packages/ui-extensions/docs/surfaces/build-doc-shared.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,61 @@ import readline from 'readline/promises';
import process, {stdin as input, stdout as output} from 'process';

export const resolveShopifyDevPath = async (rootPath) => {
const defaultPath = path.resolve(rootPath, '../../../shopify-dev');
if (existsSync(defaultPath)) return defaultPath;
const normalizeShopifyDevSourceRoot = (candidatePath) => {
if (!candidatePath) return null;

const absolutePath = path.resolve(candidatePath);

const sourceRootCandidates = [
absolutePath,
path.join(absolutePath, 'trees/root/src'),
];

for (const sourceRootPath of sourceRootCandidates) {
const zonePath = path.join(sourceRootPath, 'areas/platforms/shopify-dev');
if (existsSync(zonePath)) return sourceRootPath;
}

// Candidate might be the zone path itself (.../areas/platforms/shopify-dev)
if (absolutePath.endsWith(path.normalize('areas/platforms/shopify-dev'))) {
const sourceRootPath = path.resolve(absolutePath, '../../..');
if (existsSync(path.join(sourceRootPath, 'areas/platforms/shopify-dev'))) {
return sourceRootPath;
}
}

return null;
};

const candidatePaths = [
process.env.SHOPIFY_DEV_PATH,
process.env.WORLD_PATH,
process.env.HOME ? path.join(process.env.HOME, 'world') : null,
path.resolve(rootPath, '../../../world'),
path.resolve(rootPath, '../../../shopify-dev'),
].filter(Boolean);

for (const candidatePath of candidatePaths) {
const sourceRootPath = normalizeShopifyDevSourceRoot(candidatePath);
if (sourceRootPath) return sourceRootPath;
}

const defaultPath = path.resolve(rootPath, '../../../world');

const rl = readline.createInterface({input, output});
const answer = (
await rl.question(
`\n\x1b[33m⚠️ Shopify dev directory not found at:\x1b[0m \x1b[1m${defaultPath}\x1b[0m\n\x1b[32mPlease provide the absolute path to your "shopify-dev" repo:\x1b[0m `,
`\n\x1b[33m⚠️ Could not find a world/shopify-dev repo near:\x1b[0m \x1b[1m${defaultPath}\x1b[0m\n\x1b[32mPlease provide the absolute path to your world repo, shopify-dev repo, source root, or areas/platforms/shopify-dev folder:\x1b[0m `,
)
).trim();
rl.close();

const shopifyDevPath = answer ? path.resolve(answer) : '';
if (!shopifyDevPath || !existsSync(shopifyDevPath)) {
const shopifyDevPath = normalizeShopifyDevSourceRoot(answer);
if (!shopifyDevPath) {
throw new Error(
`\x1b[31m❌ Error: ${
shopifyDevPath
? `shopify-dev directory not found at ${shopifyDevPath}.`
answer
? `No shopify-dev zone found from ${path.resolve(answer)}.`
: 'Path cannot be empty.'
} Check the path and try again!\x1b[0m`,
);
Expand Down Expand Up @@ -51,10 +89,13 @@ export const generateFiles = async ({
outputDir,
rootPath,
generatedDocsDataFile,
generatedDocsDataV2File,
generatedStaticPagesFile,
transformJson,
}) => {
scripts.forEach((script) => childProcess.execSync(script, {stdio: 'pipe'}));
scripts.forEach((script) =>
childProcess.execSync(script, {stdio: 'inherit', cwd: rootPath}),
);

const srcFiles = await fs.readdir(rootPath, {recursive: true});
const builtFiles = srcFiles.filter((file) => file.endsWith('.ts'));
Expand All @@ -65,9 +106,25 @@ export const generateFiles = async ({
}),
);

const generatedFiles = [path.join(outputDir, generatedDocsDataFile)];
const outputPath = path.join(rootPath, outputDir);
const generatedDocsPath = path.join(outputPath, generatedDocsDataFile);
if (!existsSync(generatedDocsPath) && generatedDocsDataV2File) {
const v2Path = path.join(outputPath, generatedDocsDataV2File);
if (existsSync(v2Path)) {
await fs.copyFile(v2Path, generatedDocsPath);
}
}
if (!existsSync(generatedDocsPath)) {
throw new Error(
`Generated docs file not found at ${generatedDocsPath}. ` +
'The first tsc step may have failed (check output above). ' +
'Ensure the admin docs build uses --skipLibCheck for tsc to avoid @types/node errors.',
);
}

const generatedFiles = [generatedDocsPath];
if (generatedStaticPagesFile) {
generatedFiles.push(path.join(outputDir, generatedStaticPagesFile));
generatedFiles.push(path.join(outputPath, generatedStaticPagesFile));
}

// Make sure https://shopify.dev URLs are relative so they work in Spin.
Expand All @@ -79,7 +136,7 @@ export const generateFiles = async ({
});

if (transformJson) {
await transformJson(path.join(outputDir, generatedDocsDataFile));
await transformJson(path.join(outputPath, generatedDocsDataFile));
}
};

Expand Down
63 changes: 61 additions & 2 deletions packages/ui-extensions/docs/surfaces/point-of-sale/build-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import {
} from '../build-doc-shared.mjs';

const EXTENSIONS_API_VERSION = process.argv[2];
const SHOPIFY_DEV_EXTENSIONS_FOLDER =
process.argv[3] ??
(EXTENSIONS_API_VERSION === '2026-04' ? '2026-04-rc' : null);

if (!EXTENSIONS_API_VERSION) {
console.error('Error: API_VERSION is required.');
Expand All @@ -38,6 +41,7 @@ const shopifyDevDBPath = path.join(
);

const generatedDocsDataFile = 'generated_docs_data.json';
const generatedDocsDataV2File = 'generated_docs_data_v2.json';
const generatedStaticPagesFile = 'generated_static_pages.json';

const componentDefs = path.join(srcPath, 'components.d.ts');
Expand All @@ -48,6 +52,12 @@ const tsconfig = 'tsconfig.docs.json';
const transformJson = async (filePath) => {
let jsonData = JSON.parse((await fs.readFile(filePath, 'utf8')).toString());

// V2 format is keyed by symbol name; the legacy filter only applies to arrays.
if (!Array.isArray(jsonData)) {
await fs.writeFile(filePath, JSON.stringify(jsonData, null, 2));
return;
}

jsonData = jsonData.filter(Boolean);
await fs.writeFile(filePath, JSON.stringify(jsonData, null, 2));
};
Expand Down Expand Up @@ -117,12 +127,18 @@ const generateExtensionsDocs = async () => {
`Building Point of Sale UI Extensions docs for ${EXTENSIONS_API_VERSION} version`,
);

if (SHOPIFY_DEV_EXTENSIONS_FOLDER) {
console.log(
`Shopify-dev pos_ui_extensions folder will be: ${SHOPIFY_DEV_EXTENSIONS_FOLDER}`,
);
}

const outputDir = `${docsGeneratedRelativePath}/pos_ui_extensions/${EXTENSIONS_API_VERSION}`;

const scripts = [
`yarn tsc --project ${docsRelativePath}/${tsconfig} --moduleResolution node --target esNext --module CommonJS`,
`yarn tsc --project ${docsRelativePath}/${tsconfig} --moduleResolution node --target esNext --module CommonJS --skipLibCheck`,
`yarn generate-docs --overridePath ./${docsRelativePath}/typeOverride.json --input ./${docsRelativePath}/reference ./${srcRelativePath} --typesInput ./${srcRelativePath} --output ./${outputDir}`,
`yarn tsc ${docsRelativePath}/staticPages/*.doc.ts --moduleResolution node --target esNext --module CommonJS`,
`yarn tsc ${docsRelativePath}/staticPages/*.doc.ts --moduleResolution node --target esNext --module CommonJS --skipLibCheck`,
`yarn generate-docs --isLandingPage --input ./${docsRelativePath}/staticPages --output ./${outputDir}`,
];

Expand All @@ -131,10 +147,21 @@ const generateExtensionsDocs = async () => {
outputDir,
rootPath,
generatedDocsDataFile,
generatedDocsDataV2File,
generatedStaticPagesFile,
transformJson,
});

// Keep parity with admin docs output for downstream consumers that read v2.
const docsDataPath = path.join(rootPath, outputDir, generatedDocsDataFile);
const docsDataV2Path = path.join(
rootPath,
outputDir,
generatedDocsDataV2File,
);
const docsDataContent = await fs.readFile(docsDataPath, 'utf8');
await fs.writeFile(docsDataV2Path, docsDataContent);

// Update API version in relative doc links
await replaceFileContent({
filePaths: path.join(outputDir, generatedDocsDataFile),
Expand Down Expand Up @@ -179,16 +206,48 @@ try {
EXTENSIONS_API_VERSION,
);
const targetsJsonPath = path.join(posOutputDir, 'targets.json');
const generatedDocsDataV2Path = path.join(
posOutputDir,
generatedDocsDataV2File,
);
console.log('\nGenerated docs at:');
console.log(' POS UI extensions:', posOutputDir);
console.log(' targets.json:', targetsJsonPath);
console.log(' generated_docs_data_v2.json:', generatedDocsDataV2Path);

await copyGeneratedToShopifyDev({
generatedDocsPath,
shopifyDevPath,
shopifyDevDBPath,
});

if (
SHOPIFY_DEV_EXTENSIONS_FOLDER &&
SHOPIFY_DEV_EXTENSIONS_FOLDER !== EXTENSIONS_API_VERSION
) {
const posSource = path.join(
shopifyDevDBPath,
'pos_ui_extensions',
EXTENSIONS_API_VERSION,
);
const posDestination = path.join(
shopifyDevDBPath,
'pos_ui_extensions',
SHOPIFY_DEV_EXTENSIONS_FOLDER,
);

if (existsSync(posSource)) {
if (existsSync(posDestination)) {
await fs.rm(posDestination, {recursive: true});
}

await fs.rename(posSource, posDestination);
console.log(
` Renamed pos_ui_extensions/${EXTENSIONS_API_VERSION} → pos_ui_extensions/${SHOPIFY_DEV_EXTENSIONS_FOLDER} in shopify-dev`,
);
}
}

await cleanup();
} catch (error) {
console.error(error);
Expand Down
Loading
Loading