From 3a6d45b50cb82a68f096b7597cafc4c45e268c0c Mon Sep 17 00:00:00 2001 From: Iain Campbell Date: Wed, 23 Jun 2021 17:17:50 -0400 Subject: [PATCH 1/3] refactor examples --- .../shopify-dev-renderer/components.ts | 19 ++++--- .../components/examples.ts | 53 +++++++++++++++++++ 2 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 scripts/typedoc/shopify-dev-renderer/components/examples.ts diff --git a/scripts/typedoc/shopify-dev-renderer/components.ts b/scripts/typedoc/shopify-dev-renderer/components.ts index 0b966b533..87e3a59fc 100644 --- a/scripts/typedoc/shopify-dev-renderer/components.ts +++ b/scripts/typedoc/shopify-dev-renderer/components.ts @@ -1,7 +1,7 @@ -import {resolve, extname} from 'path'; +import {resolve} from 'path'; import * as fs from 'fs'; -import type {Paths, Packages} from '../types'; +import type {Paths} from '../types'; import {createDependencyGraph} from '../utilities/dependency-graph'; @@ -14,11 +14,14 @@ import { strip, firstSentence, mkdir, - renderExamples, - findExamplesFor, } from './shared'; import type {Node, Visibility} from './shared'; +import { + findExamplesForComponent, + renderComponentExamplesForComponent, +} from './components/examples'; + export interface Content { title: string; frontMatterDescription: string; @@ -29,6 +32,7 @@ interface Options { subcomponentMap?: {[rootComponent: string]: string[]}; componentsToSkip?: string[]; generateReadmes?: boolean; + compileExamples?: boolean; visibility?: Visibility; } @@ -45,6 +49,7 @@ export async function components( componentsToSkip = [], generateReadmes = false, visibility = 'hidden', + compileExamples = false, } = options; const visibilityFrontMatter = visibilityToFrontMatterMap.get(visibility); @@ -94,9 +99,9 @@ export async function components( markdown += renderExampleImageFor(name, paths.shopifyDevAssets); // 2. Examples - const examples = findExamplesFor(name, paths.packages, '/components'); + const examples = findExamplesForComponent(name, paths.packages); if (examples.size > 0) { - markdown += renderExamples(examples); + markdown += renderComponentExamplesForComponent(examples); } // 3. Props table @@ -204,7 +209,7 @@ export async function components( }); } - indexContent.push(`
  • ${name}
  • `); + index += `
  • ${name}
  • `; }); index += [ diff --git a/scripts/typedoc/shopify-dev-renderer/components/examples.ts b/scripts/typedoc/shopify-dev-renderer/components/examples.ts new file mode 100644 index 000000000..d7654fb19 --- /dev/null +++ b/scripts/typedoc/shopify-dev-renderer/components/examples.ts @@ -0,0 +1,53 @@ +import * as fs from 'fs'; +import {resolve, extname} from 'path'; + +import type {Packages} from '../../types'; + +interface Example { + extension: string; + content: string; +} + +export function findExamplesForComponent(componentName: string, packages: Packages): Map { + const examples = new Map(); + + Object.keys(packages).forEach((packageName) => { + const packagePath = packages[packageName]; + const componentExamplesFolder = resolve(`${packagePath}/src/components/${componentName}/examples`); + + if (fs.existsSync(componentExamplesFolder)) { + fs.readdirSync(componentExamplesFolder).forEach((file) => { + examples.set(packageName, { + extension: extname(file).split('.').pop(), + content: fs.readFileSync(`${componentExamplesFolder}/${file}`, 'utf8'), + }) + }); + } + }); + + return examples; +} + +export function renderComponentExamplesForComponent(examples: Map): string { + let markdown = ''; + + if (examples.size > 1) { + const sections = [...examples.keys()].join(', '); + markdown += `{% sections "${sections}" %}\n\n`; + + [...examples.values()].forEach((example, index) => { + markdown += `{% highlight ${example.extension} %}{% raw %}\n`; + markdown += `${example.content}`; + markdown += '\n{% endraw %}{% endhighlight %}\n\n'; + if(index < examples.size-1){ + markdown += '\n\n----\n\n'; + } + }) + + markdown += '{% endsections %}\n\n'; + } else if (examples.size > 0) { + markdown += Object.values(examples).join('\n\n----\n\n'); + } + + return markdown; +} \ No newline at end of file From 7fb02a19b13159b10cecb36f564af3051c41982b Mon Sep 17 00:00:00 2001 From: Iain Campbell Date: Wed, 23 Jun 2021 17:18:28 -0400 Subject: [PATCH 2/3] Output compiled component examples using rollup --- package.json | 4 + scripts/generate-docs-admin.ts | 2 + .../compile-for-sandbox.ts | 95 ++++++++++ .../shopify-dev-renderer/components.ts | 29 ++- .../components/examples.ts | 2 + yarn.lock | 179 +++++++++++++++++- 6 files changed, 304 insertions(+), 7 deletions(-) create mode 100644 scripts/typedoc/shopify-dev-renderer/compile-for-sandbox.ts diff --git a/package.json b/package.json index 1370ef8b1..d05131127 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "devDependencies": { "@babel/node": "^7.8.7", "@microsoft/tsdoc": "^0.12.20", + "@rollup/plugin-virtual": "^2.0.3", "@sewing-kit/cli": "^0.2.0", "@sewing-kit/config": "^0.1.0", "@sewing-kit/eslint-plugin": "^0.0.14", @@ -45,9 +46,12 @@ "@types/uuid": "^8.3.0", "lerna": "^3.22.1", "markdown-table": "^2.0.0", + "node-fetch": "^2.6.1", "nodemon": "^2.0.4", "react": ">=17.0.0 <18.0.0", "resolve": "^1.17.0", + "rollup": "^2.52.2", + "rollup-plugin-jsx": "^1.0.3", "uuid": "^8.3.2" }, "resolutions": { diff --git a/scripts/generate-docs-admin.ts b/scripts/generate-docs-admin.ts index d6ed76c43..0269dd6c8 100644 --- a/scripts/generate-docs-admin.ts +++ b/scripts/generate-docs-admin.ts @@ -19,6 +19,8 @@ Each component has general guidelines for usage as well as additional informatio - 📱 denotes mobile specific information - 🖥 denotes desktop specific information `, +}, { + compileExamples: true, }); adminExtensionApi(paths, { diff --git a/scripts/typedoc/shopify-dev-renderer/compile-for-sandbox.ts b/scripts/typedoc/shopify-dev-renderer/compile-for-sandbox.ts new file mode 100644 index 000000000..263b59585 --- /dev/null +++ b/scripts/typedoc/shopify-dev-renderer/compile-for-sandbox.ts @@ -0,0 +1,95 @@ +// import * as rollup from 'rollup/dist/es/rollup.browser.js'; +import {rollup, ModuleFormat} from 'rollup'; +import virtual from '@rollup/plugin-virtual'; +import jsx from 'rollup-plugin-jsx' +import fetch from 'node-fetch'; + + +export async function compileForSandbox(inputCode: string): Promise { + // Using the virtual plugin to supply a string as input to rollup + const LOCAL_ID = 'INPUT'; + const virtualModules = { + [LOCAL_ID]: inputCode, + }; + + const inputOptions = { + input: LOCAL_ID, + plugins: [ + virtual(virtualModules), + urlResolve(), + jsx({factory: 'React.createElement'}), + ], + }; + + const outputOptions = { + format: 'esm' as ModuleFormat, + }; + + const bundle = await rollup(inputOptions); + const {output} = await bundle.generate(outputOptions); + + const outputCode = `${output[0].code}`; + return outputCode; +} + +const SKYPACK_URL = 'https://cdn.skypack.dev'; + +function urlResolve() { + return { + resolveId(source: string) { + const convertedSource = convertToSkypackSource(source); + + const url = parseURL( + isSkypackPath(convertedSource) ? `${SKYPACK_URL}${convertedSource}` : convertedSource, + ); + return url && isValidURL(url) ? url.href : null; + }, + async load(id: string) { + const url = parseURL(id); + const result = url && isValidURL(url) ? await loadURL(url) : null; + return result; + }, + }; +} + +function parseURL(source: string): URL | null { + try { + return new URL(source); + } catch (error) { + console.warn(error); + return null; + } +} + +function isValidURL(url: URL): boolean { + return url !== null && ['http:', 'https:'].indexOf(url.protocol) >= 0; +} + +async function loadURL(url: URL) { + switch (url.protocol) { + case 'http:': + case 'https:': + return fetch(url.href).then((res) => + res.status === 404 ? null : res.text(), + ); + default: + throw new Error(`Cannot load URL protocol: ${url.protocol}`); + } +} + +function isSkypackPath(path: string) { + return path.indexOf('/-/') === 0; +} + +function convertToSkypackSource(pkg: string) { + // remove after we ship 0.11.2 + const version = '@v0.11.2-alpha.0'; + + switch(pkg) { + case '@shopify/argo-admin': + case '@shopify/argo-admin-react': + return `${SKYPACK_URL}/${pkg}${version}`; + default: + return pkg; + } +} \ No newline at end of file diff --git a/scripts/typedoc/shopify-dev-renderer/components.ts b/scripts/typedoc/shopify-dev-renderer/components.ts index 87e3a59fc..e46cc69d4 100644 --- a/scripts/typedoc/shopify-dev-renderer/components.ts +++ b/scripts/typedoc/shopify-dev-renderer/components.ts @@ -32,6 +32,7 @@ interface Options { subcomponentMap?: {[rootComponent: string]: string[]}; componentsToSkip?: string[]; generateReadmes?: boolean; + /** Compile examples using Rollup and output alongside example files */ compileExamples?: boolean; visibility?: Visibility; } @@ -100,8 +101,34 @@ export async function components( // 2. Examples const examples = findExamplesForComponent(name, paths.packages); + if (examples.size > 0) { - markdown += renderComponentExamplesForComponent(examples); + if(compileExamples === true) { + const componentExampleFolder = `${componentDocsPath}/${filename}/examples`; + if (!fs.existsSync(componentExampleFolder)) { + fs.mkdirSync(componentExampleFolder, {recursive: true}); + } + + examples.forEach(async (example, key) => { + // todo: rollup JSX support + if(key === 'React') return; + + try { + const compiledContent = await compileForSandbox(example.content) + + fs.writeFile(`${componentExampleFolder}/${example.filename}`, compiledContent, function (err) { + if (err) throw err; + }); + + } catch(error) { + console.log(`error compiling ${name}/${key}`) + console.log(error) + } + + }) + } else { + markdown += renderComponentExamplesForComponent(examples); + } } // 3. Props table diff --git a/scripts/typedoc/shopify-dev-renderer/components/examples.ts b/scripts/typedoc/shopify-dev-renderer/components/examples.ts index d7654fb19..93766e0b0 100644 --- a/scripts/typedoc/shopify-dev-renderer/components/examples.ts +++ b/scripts/typedoc/shopify-dev-renderer/components/examples.ts @@ -4,6 +4,7 @@ import {resolve, extname} from 'path'; import type {Packages} from '../../types'; interface Example { + filename: string; extension: string; content: string; } @@ -18,6 +19,7 @@ export function findExamplesForComponent(componentName: string, packages: Packag if (fs.existsSync(componentExamplesFolder)) { fs.readdirSync(componentExamplesFolder).forEach((file) => { examples.set(packageName, { + filename: file, extension: extname(file).split('.').pop(), content: fs.readFileSync(`${componentExamplesFolder}/${file}`, 'utf8'), }) diff --git a/yarn.lock b/yarn.lock index a3a869b00..f72ff7cc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2199,6 +2199,11 @@ dependencies: "@remote-ui/rpc" "^1.2.2" +"@rollup/plugin-virtual@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-virtual/-/plugin-virtual-2.0.3.tgz#0afc88d75c1e1378ab290b8e9898d4edb5be0d74" + integrity sha512-pw6ziJcyjZtntQ//bkad9qXaBx665SgEL8C8KI5wO8G5iU5MPxvdWrQyVaAvjojGm9tJoS8M9Z/EEepbqieYmw== + "@sewing-kit/cli@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sewing-kit/cli/-/cli-0.2.0.tgz#d3301e70d2e12c5c6a12ccb7fe20fcea8bd60205" @@ -3163,6 +3168,11 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn@^5.2.1: + version "5.7.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" + integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== + acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" @@ -3232,6 +3242,11 @@ ajv@^7.0.2: require-from-string "^2.0.2" uri-js "^4.2.2" +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + ansi-align@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" @@ -3488,6 +3503,11 @@ ast-types-flow@0.0.7, ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= +ast-types@0.9.6: + version "0.9.6" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" + integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -3640,6 +3660,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base62@^1.1.0: + version "1.2.8" + resolved "https://registry.yarnpkg.com/base62/-/base62-1.2.8.tgz#1264cb0fb848d875792877479dbe8bae6bae3428" + integrity sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA== + base64-js@^1.0.2: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -4411,7 +4436,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.11.0, commander@^2.20.0, commander@~2.20.3: +commander@^2.11.0, commander@^2.20.0, commander@^2.5.0, commander@~2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -4426,6 +4451,21 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +commoner@^0.10.1: + version "0.10.8" + resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5" + integrity sha1-NPw2cs0kOT6LtH5wyqApOBH08sU= + dependencies: + commander "^2.5.0" + detective "^4.3.1" + glob "^5.0.15" + graceful-fs "^4.1.2" + iconv-lite "^0.4.5" + mkdirp "^0.5.0" + private "^0.1.6" + q "^1.1.2" + recast "^0.11.17" + compare-func@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" @@ -4992,6 +5032,11 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + del@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" @@ -5053,6 +5098,14 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.5.tgz#9d270aa7eaa5af0b72c4c9d9b814e7f4ce738b79" integrity sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw== +detective@^4.3.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" + integrity sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig== + dependencies: + acorn "^5.2.1" + defined "^1.0.0" + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -5648,11 +5701,26 @@ espree@^7.3.0, espree@^7.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" +esprima-fb@^15001.1.0-dev-harmony-fb: + version "15001.1.0-dev-harmony-fb" + resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz#30a947303c6b8d5e955bee2b99b1d233206a6901" + integrity sha1-MKlHMDxrjV6VW+4rmbHSMyBqaQE= + +esprima-fb@^15001.1001.0-dev-harmony-fb: + version "15001.1001.0-dev-harmony-fb" + resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" + integrity sha1-Q761fsJujPI3092LM+QlM1d/Jlk= + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esprima@~3.1.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= + esquery@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" @@ -5677,6 +5745,11 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== +estree-walker@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.2.1.tgz#bdafe8095383d8414d5dc2ecf4c9173b6db9412e" + integrity sha1-va/oCVOD2EFNXcLs9MkXO225QS4= + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -6167,7 +6240,7 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@^2.1.2, fsevents@~2.3.1: +fsevents@^2.1.2, fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -6346,6 +6419,17 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= +glob@^5.0.15: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -6778,7 +6862,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.5: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -7936,6 +8020,17 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jstransform@^11.0.3: + version "11.0.3" + resolved "https://registry.yarnpkg.com/jstransform/-/jstransform-11.0.3.tgz#09a78993e0ae4d4ef4487f6155a91f6190cb4223" + integrity sha1-CaeJk+CuTU70SH9hVakfYZDLQiM= + dependencies: + base62 "^1.1.0" + commoner "^0.10.1" + esprima-fb "^15001.1.0-dev-harmony-fb" + object-assign "^2.0.0" + source-map "^0.4.2" + jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz#8a9364e402448a3ce7f14d357738310d9248054f" @@ -7944,6 +8039,15 @@ jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3: array-includes "^3.0.3" object.assign "^4.1.0" +jsx-transform@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/jsx-transform/-/jsx-transform-2.4.1.tgz#996b6bc1331f32ba4b1a2a3cae40c35502f4b736" + integrity sha512-uFI3rgM5RxuC3LZ78MISnLrihp6x3Pyu+U7ZHOBiE26oQrLBbporxx1bMUcO86pCYSWXZUiB04G9TpONrcSMFQ== + dependencies: + esprima-fb "^15001.1001.0-dev-harmony-fb" + jstransform "^11.0.3" + through2 "^2.0.0" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -8237,6 +8341,13 @@ macos-release@^2.2.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac" integrity sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg== +magic-string@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.14.0.tgz#57224aef1701caeed273b17a39a956e72b172462" + integrity sha1-VyJK7xcByu7Sc7F6OalW5ysXJGI= + dependencies: + vlq "^0.2.1" + make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -8502,7 +8613,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -9027,6 +9138,11 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +object-assign@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" + integrity sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo= + object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -9722,6 +9838,11 @@ pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +private@^0.1.6, private@~0.1.5: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -9868,7 +9989,7 @@ pupa@^2.0.1: dependencies: escape-goat "^2.0.0" -q@^1.5.1: +q@^1.1.2, q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= @@ -10177,6 +10298,16 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +recast@^0.11.17: + version "0.11.23" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" + integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM= + dependencies: + ast-types "0.9.6" + esprima "~3.1.0" + private "~0.1.5" + source-map "~0.5.0" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -10467,6 +10598,30 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rollup-plugin-jsx@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-jsx/-/rollup-plugin-jsx-1.0.3.tgz#92793bca9e36f90fd00d991f6a0870e59defafad" + integrity sha1-knk7yp42+Q/QDZkfaghw5Z3vr60= + dependencies: + jsx-transform "^2.3.0" + magic-string "^0.14.0" + rollup-pluginutils "^1.3.1" + +rollup-pluginutils@^1.3.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz#1e156e778f94b7255bfa1b3d0178be8f5c552408" + integrity sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg= + dependencies: + estree-walker "^0.2.1" + minimatch "^3.0.2" + +rollup@^2.52.2: + version "2.52.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.52.2.tgz#a7e90d10ddae3e8472c2857bd9f44b09ef34a47a" + integrity sha512-4RlFC3k2BIHlUsJ9mGd8OO+9Lm2eDF5P7+6DNQOp5sx+7N/1tFM01kELfbxlMX3MxT6owvLB1ln4S3QvvQlbUA== + optionalDependencies: + fsevents "~2.3.2" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -10905,7 +11060,14 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== -source-map@^0.5.0, source-map@^0.5.6: +source-map@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + integrity sha1-66T12pwNyZneaAMti092FzZSA2s= + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -12013,6 +12175,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vlq@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" + integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== + vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" From 941a57898ca269c79727d57d06ac63f5c74d1f10 Mon Sep 17 00:00:00 2001 From: Iain Campbell Date: Fri, 25 Jun 2021 13:08:33 -0400 Subject: [PATCH 3/3] refactoring, skypack resolver --- scripts/generate-docs-admin.ts | 22 +++-- .../compile-for-sandbox.ts | 95 ------------------ .../{ => components}/components.ts | 53 ++++------ .../components/examples.ts | 55 ----------- .../shopify-dev-renderer/components/index.ts | 2 + .../utilities/compile-for-sandbox.ts | 99 +++++++++++++++++++ .../components/utilities/examples.ts | 59 +++++++++++ .../components/utilities/index.ts | 6 ++ .../utilities/skypack-url-resolve.ts | 73 ++++++++++++++ .../components/utilities/types.ts | 5 + 10 files changed, 278 insertions(+), 191 deletions(-) delete mode 100644 scripts/typedoc/shopify-dev-renderer/compile-for-sandbox.ts rename scripts/typedoc/shopify-dev-renderer/{ => components}/components.ts (86%) delete mode 100644 scripts/typedoc/shopify-dev-renderer/components/examples.ts create mode 100644 scripts/typedoc/shopify-dev-renderer/components/index.ts create mode 100644 scripts/typedoc/shopify-dev-renderer/components/utilities/compile-for-sandbox.ts create mode 100644 scripts/typedoc/shopify-dev-renderer/components/utilities/examples.ts create mode 100644 scripts/typedoc/shopify-dev-renderer/components/utilities/index.ts create mode 100644 scripts/typedoc/shopify-dev-renderer/components/utilities/skypack-url-resolve.ts create mode 100644 scripts/typedoc/shopify-dev-renderer/components/utilities/types.ts diff --git a/scripts/generate-docs-admin.ts b/scripts/generate-docs-admin.ts index 0269dd6c8..a31ea0757 100644 --- a/scripts/generate-docs-admin.ts +++ b/scripts/generate-docs-admin.ts @@ -6,22 +6,26 @@ const paths = { JavaScript: './packages/admin-ui-extensions', React: './packages/admin-ui-extensions-react', }, - outputRoot: '../shopify-dev/content/apps/app-extensions/ui-extensions', - shopifyDevUrl: '/apps/app-extensions/ui-extensions', + outputRoot: '../shopify-dev/content/api/admin-extensions', + shopifyDevUrl: '/api/admin-extensions', }; -components(paths, { - title: 'Components for Admin UI Extensions', - frontMatterDescription: 'A list of components for Admin UI Extensions.', - description: ` +components( + paths, + { + title: 'Components for Admin UI Extensions', + frontMatterDescription: 'A list of components for Admin UI Extensions.', + description: ` Each component has general guidelines for usage as well as additional information regarding the behavior on certain platforms. - 📱 denotes mobile specific information - 🖥 denotes desktop specific information `, -}, { - compileExamples: true, -}); + }, + { + compileExamples: true, + }, +); adminExtensionApi(paths, { componentsToSkip: ['ContainerApi', 'DataApi'], diff --git a/scripts/typedoc/shopify-dev-renderer/compile-for-sandbox.ts b/scripts/typedoc/shopify-dev-renderer/compile-for-sandbox.ts deleted file mode 100644 index 263b59585..000000000 --- a/scripts/typedoc/shopify-dev-renderer/compile-for-sandbox.ts +++ /dev/null @@ -1,95 +0,0 @@ -// import * as rollup from 'rollup/dist/es/rollup.browser.js'; -import {rollup, ModuleFormat} from 'rollup'; -import virtual from '@rollup/plugin-virtual'; -import jsx from 'rollup-plugin-jsx' -import fetch from 'node-fetch'; - - -export async function compileForSandbox(inputCode: string): Promise { - // Using the virtual plugin to supply a string as input to rollup - const LOCAL_ID = 'INPUT'; - const virtualModules = { - [LOCAL_ID]: inputCode, - }; - - const inputOptions = { - input: LOCAL_ID, - plugins: [ - virtual(virtualModules), - urlResolve(), - jsx({factory: 'React.createElement'}), - ], - }; - - const outputOptions = { - format: 'esm' as ModuleFormat, - }; - - const bundle = await rollup(inputOptions); - const {output} = await bundle.generate(outputOptions); - - const outputCode = `${output[0].code}`; - return outputCode; -} - -const SKYPACK_URL = 'https://cdn.skypack.dev'; - -function urlResolve() { - return { - resolveId(source: string) { - const convertedSource = convertToSkypackSource(source); - - const url = parseURL( - isSkypackPath(convertedSource) ? `${SKYPACK_URL}${convertedSource}` : convertedSource, - ); - return url && isValidURL(url) ? url.href : null; - }, - async load(id: string) { - const url = parseURL(id); - const result = url && isValidURL(url) ? await loadURL(url) : null; - return result; - }, - }; -} - -function parseURL(source: string): URL | null { - try { - return new URL(source); - } catch (error) { - console.warn(error); - return null; - } -} - -function isValidURL(url: URL): boolean { - return url !== null && ['http:', 'https:'].indexOf(url.protocol) >= 0; -} - -async function loadURL(url: URL) { - switch (url.protocol) { - case 'http:': - case 'https:': - return fetch(url.href).then((res) => - res.status === 404 ? null : res.text(), - ); - default: - throw new Error(`Cannot load URL protocol: ${url.protocol}`); - } -} - -function isSkypackPath(path: string) { - return path.indexOf('/-/') === 0; -} - -function convertToSkypackSource(pkg: string) { - // remove after we ship 0.11.2 - const version = '@v0.11.2-alpha.0'; - - switch(pkg) { - case '@shopify/argo-admin': - case '@shopify/argo-admin-react': - return `${SKYPACK_URL}/${pkg}${version}`; - default: - return pkg; - } -} \ No newline at end of file diff --git a/scripts/typedoc/shopify-dev-renderer/components.ts b/scripts/typedoc/shopify-dev-renderer/components/components.ts similarity index 86% rename from scripts/typedoc/shopify-dev-renderer/components.ts rename to scripts/typedoc/shopify-dev-renderer/components/components.ts index e46cc69d4..c23b21f16 100644 --- a/scripts/typedoc/shopify-dev-renderer/components.ts +++ b/scripts/typedoc/shopify-dev-renderer/components/components.ts @@ -1,9 +1,9 @@ -import {resolve} from 'path'; +import {resolve, extname} from 'path'; import * as fs from 'fs'; -import type {Paths} from '../types'; +import type {Paths} from '../../types'; -import {createDependencyGraph} from '../utilities/dependency-graph'; +import {createDependencyGraph} from '../../utilities/dependency-graph'; import { renderYamlFrontMatter, @@ -14,13 +14,15 @@ import { strip, firstSentence, mkdir, -} from './shared'; -import type {Node, Visibility} from './shared'; +} from '../shared'; +import type {Node, Visibility} from '../shared'; import { findExamplesForComponent, - renderComponentExamplesForComponent, -} from './components/examples'; + renderExamplesForComponent, + renderSandboxComponentExamples, + compileComponentExamples, +} from './utilities'; export interface Content { title: string; @@ -100,34 +102,21 @@ export async function components( markdown += renderExampleImageFor(name, paths.shopifyDevAssets); // 2. Examples - const examples = findExamplesForComponent(name, paths.packages); + const examples = findExamplesForComponent( + name, + paths.packages, + '/components', + ); if (examples.size > 0) { - if(compileExamples === true) { - const componentExampleFolder = `${componentDocsPath}/${filename}/examples`; - if (!fs.existsSync(componentExampleFolder)) { - fs.mkdirSync(componentExampleFolder, {recursive: true}); - } - - examples.forEach(async (example, key) => { - // todo: rollup JSX support - if(key === 'React') return; - - try { - const compiledContent = await compileForSandbox(example.content) + if (compileExamples === true) { + const examplesUrl = `/sandbox-examples/${filename}`; + const examplesPath = resolve(`../shopify-dev/public/${examplesUrl}`); - fs.writeFile(`${componentExampleFolder}/${example.filename}`, compiledContent, function (err) { - if (err) throw err; - }); - - } catch(error) { - console.log(`error compiling ${name}/${key}`) - console.log(error) - } - - }) + compileComponentExamples(examples, examplesPath); + markdown += renderSandboxComponentExamples(examples, examplesUrl); } else { - markdown += renderComponentExamplesForComponent(examples); + markdown += renderExamplesForComponent(examples); } } @@ -236,7 +225,7 @@ export async function components( }); } - index += `
  • ${name}
  • `; + indexContent.push(`
  • ${name}
  • `); }); index += [ diff --git a/scripts/typedoc/shopify-dev-renderer/components/examples.ts b/scripts/typedoc/shopify-dev-renderer/components/examples.ts deleted file mode 100644 index 93766e0b0..000000000 --- a/scripts/typedoc/shopify-dev-renderer/components/examples.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as fs from 'fs'; -import {resolve, extname} from 'path'; - -import type {Packages} from '../../types'; - -interface Example { - filename: string; - extension: string; - content: string; -} - -export function findExamplesForComponent(componentName: string, packages: Packages): Map { - const examples = new Map(); - - Object.keys(packages).forEach((packageName) => { - const packagePath = packages[packageName]; - const componentExamplesFolder = resolve(`${packagePath}/src/components/${componentName}/examples`); - - if (fs.existsSync(componentExamplesFolder)) { - fs.readdirSync(componentExamplesFolder).forEach((file) => { - examples.set(packageName, { - filename: file, - extension: extname(file).split('.').pop(), - content: fs.readFileSync(`${componentExamplesFolder}/${file}`, 'utf8'), - }) - }); - } - }); - - return examples; -} - -export function renderComponentExamplesForComponent(examples: Map): string { - let markdown = ''; - - if (examples.size > 1) { - const sections = [...examples.keys()].join(', '); - markdown += `{% sections "${sections}" %}\n\n`; - - [...examples.values()].forEach((example, index) => { - markdown += `{% highlight ${example.extension} %}{% raw %}\n`; - markdown += `${example.content}`; - markdown += '\n{% endraw %}{% endhighlight %}\n\n'; - if(index < examples.size-1){ - markdown += '\n\n----\n\n'; - } - }) - - markdown += '{% endsections %}\n\n'; - } else if (examples.size > 0) { - markdown += Object.values(examples).join('\n\n----\n\n'); - } - - return markdown; -} \ No newline at end of file diff --git a/scripts/typedoc/shopify-dev-renderer/components/index.ts b/scripts/typedoc/shopify-dev-renderer/components/index.ts new file mode 100644 index 000000000..692e4595e --- /dev/null +++ b/scripts/typedoc/shopify-dev-renderer/components/index.ts @@ -0,0 +1,2 @@ +export {components} from './components'; +export type {Content} from './components'; diff --git a/scripts/typedoc/shopify-dev-renderer/components/utilities/compile-for-sandbox.ts b/scripts/typedoc/shopify-dev-renderer/components/utilities/compile-for-sandbox.ts new file mode 100644 index 000000000..52ea8f5c1 --- /dev/null +++ b/scripts/typedoc/shopify-dev-renderer/components/utilities/compile-for-sandbox.ts @@ -0,0 +1,99 @@ +import * as fs from 'fs'; +import {resolve, extname} from 'path'; +import {rollup, ModuleFormat} from 'rollup'; +import virtual from '@rollup/plugin-virtual'; +import jsx from 'rollup-plugin-jsx'; + +import {skypackUrlResolve} from './skypack-url-resolve'; +import type {Example} from './types'; + +export function renderSandboxComponentExamples( + examples: Map, + compiledPath: string, +): string { + let markdown = ''; + + const compiledFilename = getCompiledFilename(examples); + + markdown += `{% codeblock extensions_sandbox, compiled: "${compiledPath}/${compiledFilename}" %}\n\n`; + + examples.forEach((example, key) => { + markdown += [ + `{% code ${example.extension}, title: "${key}" %}`, + `${example.content}`, + '{% endcode %}', + '\n', + ].join('\n'); + }); + + markdown += '{% endcodeblock %}\n'; + + return markdown; +} + +function getCompiledFilename(examples: Map): string { + // we assume the first item in paths.packages is the one we want to feed to the compiler + const key = [...examples.keys()][0]; + const example = examples.get(key); + return example.filename.replace(extname(example.filename), '.js'); +} + +export function compileComponentExamples( + examples: Map, + examplesPath: string, +) { + if (!fs.existsSync(examplesPath)) { + fs.mkdirSync(examplesPath, {recursive: true}); + } + + // we actually only need one compiled file per example + examples.forEach(async (example, key) => { + if (key === 'React') return; + + try { + const compiledContent = await compileForSandbox(example.content); + + const compiledFilename = example.filename.replace( + extname(example.filename), + '.js', + ); + fs.writeFile( + `${examplesPath}/${compiledFilename}`, + compiledContent, + function (err) { + if (err) throw err; + }, + ); + } catch (error) { + console.log(`error compiling ${example.filename}`); + console.log(error); + } + }); +} + +export async function compileForSandbox(inputCode: string): Promise { + const LOCAL_ID = 'INPUT'; + const virtualModules = { + [LOCAL_ID]: inputCode, + }; + + const inputOptions = { + input: LOCAL_ID, + plugins: [ + // The virtual plugin let us supply a string instead of a file as input + virtual(virtualModules), + skypackUrlResolve(), + jsx({factory: 'React.createElement'}), + ], + }; + + const outputOptions = { + format: 'esm' as ModuleFormat, + }; + + const bundle = await rollup(inputOptions); + const {output} = await bundle.generate(outputOptions); + + const outputCode = `${output[0].code}`; + return outputCode; +} diff --git a/scripts/typedoc/shopify-dev-renderer/components/utilities/examples.ts b/scripts/typedoc/shopify-dev-renderer/components/utilities/examples.ts new file mode 100644 index 000000000..51dd7bcdc --- /dev/null +++ b/scripts/typedoc/shopify-dev-renderer/components/utilities/examples.ts @@ -0,0 +1,59 @@ +import * as fs from 'fs'; +import {resolve, extname} from 'path'; + +import type {Packages} from '../../../types'; +import type {Example} from './types'; + +export function findExamplesForComponent( + componentName: string, + packages: Packages, + subPath: string, +): Map { + const examples = new Map(); + + Object.keys(packages).forEach((packageName) => { + const packagePath = packages[packageName]; + const componentExamplesFolder = resolve( + `${packagePath}/src${subPath}/${componentName}/examples`, + ); + + if (fs.existsSync(componentExamplesFolder)) { + fs.readdirSync(componentExamplesFolder).forEach((file) => { + examples.set(packageName, { + filename: file, + extension: extname(file).split('.').pop(), + content: fs.readFileSync( + `${componentExamplesFolder}/${file}`, + 'utf8', + ), + }); + }); + } + }); + + return examples; +} + +export function renderExamplesForComponent( + examples: Map, +): string { + if (examples.size === 0) { + return ''; + } + let markdown = ''; + + markdown += `{% codeblock %}\n\n`; + + examples.forEach((example, key) => { + markdown += [ + `{% code ${example.extension}, title: "${key}" %}{% raw %}`, + `${example.content}`, + '{% endraw %}{% endcode %}', + '\n', + ].join('\n'); + }); + + markdown += '{% endcodeblock %}\n\n'; + + return markdown; +} diff --git a/scripts/typedoc/shopify-dev-renderer/components/utilities/index.ts b/scripts/typedoc/shopify-dev-renderer/components/utilities/index.ts new file mode 100644 index 000000000..55c3585d4 --- /dev/null +++ b/scripts/typedoc/shopify-dev-renderer/components/utilities/index.ts @@ -0,0 +1,6 @@ +export {findExamplesForComponent, renderExamplesForComponent} from './examples'; + +export { + renderSandboxComponentExamples, + compileComponentExamples, +} from './compile-for-sandbox'; diff --git a/scripts/typedoc/shopify-dev-renderer/components/utilities/skypack-url-resolve.ts b/scripts/typedoc/shopify-dev-renderer/components/utilities/skypack-url-resolve.ts new file mode 100644 index 000000000..8f7bdb416 --- /dev/null +++ b/scripts/typedoc/shopify-dev-renderer/components/utilities/skypack-url-resolve.ts @@ -0,0 +1,73 @@ +import fetch from 'node-fetch'; + +const SKYPACK_URL = 'https://cdn.skypack.dev'; + +const pinnedUrls = new Map(); + +export function skypackUrlResolve() { + return { + async resolveId(source: string) { + const convertedSource = await convertToSkypackSource(source); + const maybeAbsoluteUrl = convertSkypackPathsToAbsoluteUrls( + convertedSource, + ); + const url = parseURL(maybeAbsoluteUrl); + return url && isValidURL(url) ? url.href : null; + }, + async load(id: string) { + const url = parseURL(id); + const result = url && isValidURL(url) ? await loadURL(url) : null; + return result; + }, + }; +} + +function parseURL(source: string): URL | null { + try { + return new URL(source); + } catch (error) { + return null; + } +} + +function isValidURL(url: URL): boolean { + return url !== null && ['http:', 'https:'].indexOf(url.protocol) >= 0; +} + +async function loadURL(url: URL) { + switch (url.protocol) { + case 'http:': + case 'https:': + return fetch(url.href).then((res) => + res.status === 404 ? null : res.text(), + ); + default: + throw new Error(`Cannot load URL protocol: ${url.protocol}`); + } +} + +/** Initial req to skypack returns a 'pinned' URL which loads faster on subsequent reqs */ +async function pinnedUrl(pkg: string) { + if (pinnedUrls.get(pkg) === undefined) { + const res = await fetch(`${SKYPACK_URL}/${pkg}`); + const importUrl = res.headers.get('x-import-url'); + pinnedUrls.set(pkg, `${SKYPACK_URL}${importUrl}`); + } + return pinnedUrls.get(pkg); +} + +async function convertToSkypackSource(pkg: string) { + switch (pkg) { + case '@shopify/admin-ui-extensions': + case '@shopify/admin-ui-extensions-react': + return pinnedUrl(`${pkg}`); + default: + return pkg; + } +} + +function convertSkypackPathsToAbsoluteUrls(path: string): string { + return path.startsWith('/-/') || path.startsWith('/new/') + ? `${SKYPACK_URL}${path}` + : path; +} diff --git a/scripts/typedoc/shopify-dev-renderer/components/utilities/types.ts b/scripts/typedoc/shopify-dev-renderer/components/utilities/types.ts new file mode 100644 index 000000000..e7dfd4df4 --- /dev/null +++ b/scripts/typedoc/shopify-dev-renderer/components/utilities/types.ts @@ -0,0 +1,5 @@ +export interface Example { + filename: string; + extension: string; + content: string; +}