Skip to content

update generate docs to v1.1.1#7189

Draft
laurelthorburn wants to merge 6 commits intomainfrom
migrate-cli-to-gen-docs-v1
Draft

update generate docs to v1.1.1#7189
laurelthorburn wants to merge 6 commits intomainfrom
migrate-cli-to-gen-docs-v1

Conversation

@laurelthorburn
Copy link
Copy Markdown

@laurelthorburn laurelthorburn commented Apr 3, 2026

🎟️ Part of https://github.com/shop/issues-learn/issues/1466

🎩 Tophatting related PR: https://github.com/shop/world/pull/577553

Source of truth: docs-shopify.dev/type-descriptions.json — a JSON file mapping each interface name (e.g. appbuild) to
its canonical description text. This is a hand-maintained file extracted from the v1 docs.

What changed vs the original generate.ts:

  1. Added readFile import (line 3) — to read the JSON file
  2. writeCommandDocumentation (line 77) — generates .doc.ts files for the v1 pipeline
    - Before: description came from command.descriptionWithMarkdown ?? command.description ?? command.summary (oclif
    command object)
    - Now: reads type-descriptions.json first (line 88-89), uses that as primary source, falls back to oclif command if
    the type isn't in the JSON (line 90-91)
    - This ensures the v1 .doc.ts description matches the v2 JSDoc description
  3. writeCommandFlagInterface (line 134) — generates .interface.ts files with @publicdocs
    - Before: no JSDoc, no description, no @publicdocs tag
    - Now: reads the same type-descriptions.json (line 162-163), gets the description for this interface name (line 164),
    formats it as a JSDoc comment (lines 165-168), and adds @publicdocs (line 173)
    - This is what generate-docs v2 scans to produce generated_docs_data_v2.json

Data flow:

type-descriptions.json

├──> writeCommandDocumentation() ──> .doc.ts files ──> generate-docs v1 ──> generated_docs_data.json

└──> writeCommandFlagInterface() ──> .interface.ts files (with @publicdocs JSDoc)

└──> generate-docs v2 ──> generated_docs_data_v2.json

Both pipelines read descriptions from the same JSON file, so they produce matching output.

const typeDescriptions = JSON.parse(await readFile(typeDescriptionsPath)) as Record<string, string>
const description =
typeDescriptions[interfaceName] ?? command.descriptionWithMarkdown ?? command.description ?? command.summary ?? ''
const cleanDescription = description.replace(/`/g, '\\`')

Check failure

Code scanning / CodeQL

Incomplete string escaping or encoding High documentation

This does not escape backslash characters in the input.

Copilot Autofix

AI about 2 hours ago

In general, the proper fix is to avoid hand-rolled escaping with ad‑hoc string.replace calls and instead use a well-tested escaping routine that correctly handles all the meta-characters relevant to the target context (here, JavaScript template literals). That means making sure both backticks and backslashes (and optionally ${ sequences) are escaped consistently before embedding arbitrary text into a template literal delimited with backticks.

In this specific file, the problematic logic is on lines 92 and 94 where description and previewDescription are sanitized only by replacing backticks. The best fix that preserves existing functionality is to centralize escaping into a small helper function that escapes backslashes first, then backticks, and reuse it for both cleanDescription and cleanPreview. For example, define a function escapeForTemplateLiteral that does: s.replace(/\\/g, '\\\\').replace(//g, '\'). This ensures that any literal backslash in the source becomes \\ inside the template literal (so it is interpreted as a literal backslash), and backticks are escaped as before. Then replace the direct .replace(//g, '\') calls with calls to this helper. No behavior changes besides improved escaping, and no external dependencies are required.

Concretely:

  • In packages/cli/src/cli/commands/docs/generate.ts, above the use of description.replace(...), add a small local helper function, e.g. function escapeForTemplateLiteral(input: string): string { ... }.
  • Change line 92 to: const cleanDescription = escapeForTemplateLiteral(description)
  • Change line 94 to: const cleanPreview = escapeForTemplateLiteral(previewDescription).replace(/https:\/\/shopify\.dev/g, '')
    This keeps all existing logic, including stripping https://shopify.dev from the preview, while correctly handling backslashes and backticks for the generated template literals.
Suggested changeset 1
packages/cli/src/cli/commands/docs/generate.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/cli/src/cli/commands/docs/generate.ts b/packages/cli/src/cli/commands/docs/generate.ts
--- a/packages/cli/src/cli/commands/docs/generate.ts
+++ b/packages/cli/src/cli/commands/docs/generate.ts
@@ -73,6 +73,10 @@
   return {commandName, fileName, interfaceName, hasTopic, topic, hasFlags}
 }
 
+function escapeForTemplateLiteral(input: string): string {
+  return input.replace(/\\/g, '\\\\').replace(/`/g, '\\`')
+}
+
 // Generates the documentation for a command and writes it to a file (also a file with an example usage of the command)
 export async function writeCommandDocumentation(
   command: CommandWithMarkdown,
@@ -89,9 +93,9 @@
   const typeDescriptions = JSON.parse(await readFile(typeDescriptionsPath)) as Record<string, string>
   const description =
     typeDescriptions[interfaceName] ?? command.descriptionWithMarkdown ?? command.description ?? command.summary ?? ''
-  const cleanDescription = description.replace(/`/g, '\\`')
+  const cleanDescription = escapeForTemplateLiteral(description)
   const previewDescription = command.summary ?? description ?? ''
-  const cleanPreview = previewDescription.replace(/`/g, '\\`').replace(/https:\/\/shopify\.dev/g, '')
+  const cleanPreview = escapeForTemplateLiteral(previewDescription).replace(/https:\/\/shopify\.dev/g, '')
 
   const category = hasTopic && !generalTopics.includes(topic!) ? topic : 'general commands'
 
EOF
@@ -73,6 +73,10 @@
return {commandName, fileName, interfaceName, hasTopic, topic, hasFlags}
}

function escapeForTemplateLiteral(input: string): string {
return input.replace(/\\/g, '\\\\').replace(/`/g, '\\`')
}

// Generates the documentation for a command and writes it to a file (also a file with an example usage of the command)
export async function writeCommandDocumentation(
command: CommandWithMarkdown,
@@ -89,9 +93,9 @@
const typeDescriptions = JSON.parse(await readFile(typeDescriptionsPath)) as Record<string, string>
const description =
typeDescriptions[interfaceName] ?? command.descriptionWithMarkdown ?? command.description ?? command.summary ?? ''
const cleanDescription = description.replace(/`/g, '\\`')
const cleanDescription = escapeForTemplateLiteral(description)
const previewDescription = command.summary ?? description ?? ''
const cleanPreview = previewDescription.replace(/`/g, '\\`').replace(/https:\/\/shopify\.dev/g, '')
const cleanPreview = escapeForTemplateLiteral(previewDescription).replace(/https:\/\/shopify\.dev/g, '')

const category = hasTopic && !generalTopics.includes(topic!) ? topic : 'general commands'

Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants