Compare commits
No commits in common. "main" and "tylerdcooper-patch-1" have entirely different histories.
main
...
tylerdcoop
11 changed files with 34 additions and 183 deletions
3
.github/workflows/docs.yml
vendored
3
.github/workflows/docs.yml
vendored
|
|
@ -41,9 +41,6 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Export Block Images
|
|
||||||
run: npm run export:block-images
|
|
||||||
|
|
||||||
- name: Export Block Definitions to Markdown
|
- name: Export Block Definitions to Markdown
|
||||||
run: npm run docs:export
|
run: npm run docs:export
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,18 +20,9 @@ Node v22.x is expected, but other versions may work.
|
||||||
git clone https://github.com/adafruit/io-actions
|
git clone https://github.com/adafruit/io-actions
|
||||||
cd io-actions
|
cd io-actions
|
||||||
npm i
|
npm i
|
||||||
npm run export:block-images # run once, and whenever images need updating
|
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
Now 2 processes should be running:
|
|
||||||
- a builder process that:
|
|
||||||
- does a full build of the docs from the app files
|
|
||||||
- watches app files for changes and updates matching docs
|
|
||||||
- the docs dev server where you can see your changes rendered live as you save them
|
|
||||||
|
|
||||||
When you're done working simply press `CTRL + C` to terminate the processes.
|
|
||||||
|
|
||||||
### Exporting
|
### Exporting
|
||||||
|
|
||||||
Export a Blockly application:
|
Export a Blockly application:
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
|
|
||||||
import process from 'node:process'
|
|
||||||
import { exec, execSync, spawnSync, spawn } from 'node:child_process'
|
|
||||||
import { promisify } from 'node:util'
|
|
||||||
|
|
||||||
|
|
||||||
const execAsync = promisify(exec)
|
|
||||||
|
|
||||||
// run a clean documentation build, wait for it to complete
|
|
||||||
console.log("Building docs from scratch...")
|
|
||||||
spawnSync("node", ["export.js", "docs"], { stdio: 'inherit' })
|
|
||||||
|
|
||||||
// start the file watcher and incremental builder
|
|
||||||
console.log("Starting incremental builder and file watcher...")
|
|
||||||
const docBuilder = spawn("node", ["--watch-path=./app", "export.js", "docs-incremental"], { stdio: 'inherit' })
|
|
||||||
docBuilder.on('error', err => console.log('Builder Error:', err))
|
|
||||||
docBuilder.on('exit', code => console.log('Builder Exited', code === 0 ? "Cleanly" : `With Error Code ${code}`))
|
|
||||||
|
|
||||||
// start the Vitepress docs dev server
|
|
||||||
console.log("Starting Vitepress docs server...")
|
|
||||||
const docServer = spawn("npm", ["run", "docs:dev"], { stdio: 'inherit' })
|
|
||||||
docServer.on('error', err => console.log('Server Error:', err))
|
|
||||||
docServer.on('exit', code => console.log('Server Exited', code === 0 ? "Cleanly" : `With Error Code ${code}`))
|
|
||||||
|
|
||||||
const killAll = () => {
|
|
||||||
console.log('Shutting down...')
|
|
||||||
docBuilder.kill()
|
|
||||||
docServer.kill()
|
|
||||||
process.exit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// if either one exits, kill the other
|
|
||||||
console.log("Watching files for changes and servers for crashes")
|
|
||||||
docServer.on('exit', killAll)
|
|
||||||
docBuilder.on('exit', killAll)
|
|
||||||
|
|
@ -6,10 +6,6 @@ const REPO = 'https://github.com/adafruit/io-actions'
|
||||||
|
|
||||||
// https://vitepress.dev/reference/site-config
|
// https://vitepress.dev/reference/site-config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
vite: {
|
|
||||||
clearScreen: false
|
|
||||||
},
|
|
||||||
|
|
||||||
title: "IO Actions: Block Reference",
|
title: "IO Actions: Block Reference",
|
||||||
description: "Documentation for Adafruit IO's block-based Actions",
|
description: "Documentation for Adafruit IO's block-based Actions",
|
||||||
|
|
||||||
|
|
|
||||||
44
docs/test.md
44
docs/test.md
|
|
@ -1,44 +0,0 @@
|
||||||
# Markdown Tester
|
|
||||||
|
|
||||||
## Works
|
|
||||||
|
|
||||||
<span v-pre>
|
|
||||||
{{ span v-pre }}
|
|
||||||
|
|
||||||
::: warning
|
|
||||||
what a warning!
|
|
||||||
:::
|
|
||||||
</span>
|
|
||||||
|
|
||||||
```
|
|
||||||
{{ triple_backticks }}
|
|
||||||
```
|
|
||||||
|
|
||||||
```js
|
|
||||||
{{ js_triple_backticks }}
|
|
||||||
```
|
|
||||||
|
|
||||||
::: details Embedded in a panel
|
|
||||||
::: details multiple panels
|
|
||||||
::: details multiple panels
|
|
||||||
::: details multiple panels
|
|
||||||
a message!
|
|
||||||
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Doesn't Work
|
|
||||||
|
|
||||||
<span v-pre>
|
|
||||||
`{{ single_backticks }}`
|
|
||||||
</span>
|
|
||||||
|
|
||||||
## Experiment
|
|
||||||
|
|
||||||
::: v-pre
|
|
||||||
| Separator |
|
|
||||||
|-----------|
|
|
||||||
| <span v-pre>{{ in_span_table }}</span> |
|
|
||||||
| {{ in_span_table }} |
|
|
||||||
:::
|
|
||||||
|
|
||||||
64
export.js
64
export.js
|
|
@ -1,5 +1,5 @@
|
||||||
import { spawn, spawnSync } from 'node:child_process'
|
import { spawn, spawnSync } from 'node:child_process'
|
||||||
import { copyFileSync, cpSync, existsSync } from 'node:fs'
|
import { copyFileSync, cpSync } from 'node:fs'
|
||||||
|
|
||||||
import { cleanDir, write, totalBytesWritten } from "./export_util.js"
|
import { cleanDir, write, totalBytesWritten } from "./export_util.js"
|
||||||
import DefinitionSet from '#src/definitions/definition_set.js'
|
import DefinitionSet from '#src/definitions/definition_set.js'
|
||||||
|
|
@ -20,7 +20,6 @@ const
|
||||||
definitions = await DefinitionSet.load(),
|
definitions = await DefinitionSet.load(),
|
||||||
|
|
||||||
exporters = {
|
exporters = {
|
||||||
// Build the Blockly application itself
|
|
||||||
"app": async (destination="export") => {
|
"app": async (destination="export") => {
|
||||||
|
|
||||||
// clear the export directory
|
// clear the export directory
|
||||||
|
|
@ -35,13 +34,13 @@ const
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Build the documentation for the Blockly application
|
|
||||||
"docs": async () => {
|
"docs": async () => {
|
||||||
// TODO: check and warn if block images haven't been generated
|
// allow option to skip image generation
|
||||||
if(!existsSync("docs/block_images/action_root.png")) {
|
const skipImages = taskArgs.includes("skipImages")
|
||||||
console.log("Block images missing from docs/block_images!")
|
if(!skipImages) {
|
||||||
console.log("Run: `npm run export:block-images` before exporting the docs")
|
await exporters.blockImages()
|
||||||
process.exit(1)
|
cleanDir("docs/block_images")
|
||||||
|
cpSync("tmp/block_images/images", "docs/block_images", { recursive: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
await exporters.app("docs/blockly")
|
await exporters.app("docs/blockly")
|
||||||
|
|
@ -55,50 +54,31 @@ const
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Build the documentation but only touch files that have changed
|
"blockImages": async () => {
|
||||||
// This is good to pair with a process that watches files for changes
|
const destination = "tmp/block_images"
|
||||||
"docs-incremental": async () => {
|
cleanDir(destination)
|
||||||
await exportTo("docs", definitions, exportItem => {
|
cleanDir(`${destination}/images`)
|
||||||
exportItem.blockIndex("blocks/index.md")
|
|
||||||
exportItem.blockPages()
|
|
||||||
exportItem.sidebar("blocks/_blocks_sidebar.json")
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Create png images of all blocks by:
|
|
||||||
// - creating a temporary app with no toolbox and all blocks on the workspace
|
|
||||||
// - serving that application
|
|
||||||
// - driving a browser to it with Cypress
|
|
||||||
// - right clicking on each block and clicking "download image"
|
|
||||||
"blockImages": async (imageDestination="docs/block_images") => {
|
|
||||||
const tmpAppDestination = "tmp/block_images_app"
|
|
||||||
cleanDir(tmpAppDestination)
|
|
||||||
|
|
||||||
// export a special app with no toolbox, all blocks on workspace
|
// export a special app with no toolbox, all blocks on workspace
|
||||||
await exportTo(tmpAppDestination, definitions, exportItem => {
|
await exportTo(destination, definitions, exportItem => {
|
||||||
exportItem.workspaceAllBlocks("workspace.json")
|
exportItem.workspaceAllBlocks("workspace.json")
|
||||||
write(`${tmpAppDestination}/toolbox.json`, "null")
|
write(`${destination}/toolbox.json`, "null")
|
||||||
exportItem.blocks("blocks.json")
|
exportItem.blocks("blocks.json")
|
||||||
exportItem.script("blockly_app.js")
|
exportItem.script("blockly_app.js")
|
||||||
// TODO: make a DocumentExporter for generating html wrappers
|
// TODO: make a DocumentExporter for generating html wrappers
|
||||||
copyFileSync("src/exporters/document_templates/blockly_workspace.template.html", `${tmpAppDestination}/index.html`)
|
copyFileSync("src/exporters/document_templates/blockly_workspace.template.html", `${destination}/index.html`)
|
||||||
})
|
})
|
||||||
|
|
||||||
// serve the screenshot app
|
// serve it
|
||||||
console.log('Serving workspace for screenshots...')
|
console.log('Serving workspace for screenshots...')
|
||||||
const viteProcess = spawn("npx", ["vite", "serve", tmpAppDestination])
|
const viteProcess = spawn("npx", ["vite", "serve", destination])
|
||||||
|
|
||||||
// prepare the image location
|
|
||||||
cleanDir(imageDestination)
|
|
||||||
|
|
||||||
// extract the screenshots
|
// extract the screenshots
|
||||||
console.log('Generating screenshots...')
|
console.log('Generating screenshots...')
|
||||||
await spawnSync("npx", ["cypress", "run",
|
spawnSync("npx", ["cypress", "run",
|
||||||
"--config", `downloadsFolder=${imageDestination}`,
|
"--config", `downloadsFolder=${destination}/images`,
|
||||||
"--config-file", `cypress/cypress.config.js`,
|
"--config-file", `cypress/cypress.config.js`,
|
||||||
"--browser", "chromium",
|
])
|
||||||
"--spec", "cypress/e2e/block_images.cy.js",
|
|
||||||
], { stdio: 'inherit' })
|
|
||||||
console.log('Generation complete.')
|
console.log('Generation complete.')
|
||||||
|
|
||||||
// kill the server
|
// kill the server
|
||||||
|
|
@ -106,19 +86,16 @@ const
|
||||||
console.log("Vite failed to exit gracefully")
|
console.log("Vite failed to exit gracefully")
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Server closed.')
|
console.log('Server closed.')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
exporterNames = Object.keys(exporters)
|
exporterNames = Object.keys(exporters)
|
||||||
|
|
||||||
// Look up the requested exporter
|
|
||||||
if(!exporterNames.includes(toExport)) {
|
if(!exporterNames.includes(toExport)) {
|
||||||
console.error(`Export Error: No exporter found for: "${toExport}"\nValid exporters: "${exporterNames.join('", "')}"`)
|
console.error(`Export Error: No exporter found for: "${toExport}"\nValid exporters: "${exporterNames.join('", "')}"`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the export
|
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
console.log(`\nStarting Export: ${toExport}`)
|
console.log(`\nStarting Export: ${toExport}`)
|
||||||
console.log("=======================")
|
console.log("=======================")
|
||||||
|
|
@ -128,8 +105,7 @@ await exporter()
|
||||||
|
|
||||||
const elapsed = Date.now() - startTime
|
const elapsed = Date.now() - startTime
|
||||||
console.log("=======================")
|
console.log("=======================")
|
||||||
console.log(`🏁 Done (${elapsed}ms) 🏁`)
|
console.log(`🏁 Done. Wrote ${totalBytesWritten.toFixed(3)}k in ${elapsed}ms 🏁`)
|
||||||
|
|
||||||
|
|
||||||
// Bye!
|
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,17 @@
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node dev_server.js",
|
"start": "vite --force",
|
||||||
|
|
||||||
"test": "node --test",
|
"test": "node --test",
|
||||||
"test-watch": "node --test --watch",
|
"test-watch": "node --test --watch",
|
||||||
"test-snapshots": "node --test test/app/blocks/snapshots/block_snapshots_test.js",
|
"test-snapshots": "node --test test/app/blocks/snapshots/block_snapshots_test.js",
|
||||||
"test-update-snapshots": "node --test --test-update-snapshots test/app/blocks/snapshots/block_snapshots_test.js",
|
"test-update-snapshots": "node --test --test-update-snapshots test/app/blocks/snapshots/block_snapshots_test.js",
|
||||||
"lint": "eslint src/",
|
"lint": "eslint src/",
|
||||||
"lint-export": "eslint export/",
|
"lint-export": "eslint export/",
|
||||||
|
|
||||||
"export:app": "node export.js app",
|
"export:app": "node export.js app",
|
||||||
"export:block-images": "node export.js blockImages",
|
"build": "npm run export:app && vite build",
|
||||||
|
"build-all-branches": "node build_all_branches.js",
|
||||||
|
"preview": "npm run build && vite preview",
|
||||||
"docs:export": "node export.js docs",
|
"docs:export": "node export.js docs",
|
||||||
"docs:dev": "vitepress dev docs",
|
"docs:dev": "vitepress dev docs",
|
||||||
"docs:build": "vitepress build docs",
|
"docs:build": "vitepress build docs",
|
||||||
|
|
|
||||||
|
|
@ -194,8 +194,7 @@ BlockDefinition.parseRawDefinition = function(rawBlockDefinition, definitionPath
|
||||||
// take the first line of the description
|
// take the first line of the description
|
||||||
// blockDef.tooltip = blockDef.description.split("\n")[0]
|
// blockDef.tooltip = blockDef.description.split("\n")[0]
|
||||||
// take the first sentence of the description
|
// take the first sentence of the description
|
||||||
blockDef.tooltip = blockDef.description.split(/\.(\s|$)/)[0]
|
blockDef.tooltip = blockDef.description.split(/\.(\s|$)/)[0] + "."
|
||||||
if(!blockDef.tooltip.endsWith("?")) { blockDef.tooltip += "." }
|
|
||||||
blockDef.disabled = !!rawBlockDefinition.disabled
|
blockDef.disabled = !!rawBlockDefinition.disabled
|
||||||
blockDef.connections = rawBlockDefinition.connections
|
blockDef.connections = rawBlockDefinition.connections
|
||||||
blockDef.template = rawBlockDefinition.template
|
blockDef.template = rawBlockDefinition.template
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
import { mkdirSync, writeFileSync } from 'fs'
|
||||||
|
import { dirname } from 'path'
|
||||||
import { forEach, identity, pickBy } from 'lodash-es'
|
import { forEach, identity, pickBy } from 'lodash-es'
|
||||||
|
|
||||||
import { writeFileIfDifferent } from '#src/util.js'
|
|
||||||
import toBlockMarkdown from "#src/docs/render_block.js"
|
import toBlockMarkdown from "#src/docs/render_block.js"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -24,10 +25,10 @@ export default class BlockPageExporter {
|
||||||
forEach(this.definitionSet.blocks, blockDefinition => {
|
forEach(this.definitionSet.blocks, blockDefinition => {
|
||||||
const
|
const
|
||||||
docPath = options.filenameFunc(blockDefinition),
|
docPath = options.filenameFunc(blockDefinition),
|
||||||
fullPath = `${this.destination}/${docPath}`,
|
fullPath = `${this.destination}/${docPath}`
|
||||||
newContent = toBlockMarkdown(blockDefinition)
|
|
||||||
|
|
||||||
writeFileIfDifferent(fullPath, newContent)
|
mkdirSync(dirname(fullPath), { recursive: true })
|
||||||
|
writeFileSync(fullPath, toBlockMarkdown(blockDefinition))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,4 +41,3 @@ export default class BlockPageExporter {
|
||||||
this.export(exportOptions)
|
this.export(exportOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { find, forEach, isString, map, sortBy } from 'lodash-es'
|
import { writeFileSync } from 'fs'
|
||||||
|
import { find, forEach, isString, map } from 'lodash-es'
|
||||||
import { writeFileIfDifferent } from '#src/util.js'
|
|
||||||
|
|
||||||
|
|
||||||
export default class SidebarExporter {
|
export default class SidebarExporter {
|
||||||
|
|
@ -35,7 +34,7 @@ export default class SidebarExporter {
|
||||||
|
|
||||||
blockSidebar.items.push(uncategorizedCategory)
|
blockSidebar.items.push(uncategorizedCategory)
|
||||||
|
|
||||||
forEach(sortBy(this.definitionSet.blocks, 'type'), blockDefinition => {
|
forEach(this.definitionSet.blocks, blockDefinition => {
|
||||||
const
|
const
|
||||||
sidebarEntry = {
|
sidebarEntry = {
|
||||||
text: blockDefinition.name,
|
text: blockDefinition.name,
|
||||||
|
|
@ -70,7 +69,7 @@ export default class SidebarExporter {
|
||||||
? options.toFile
|
? options.toFile
|
||||||
: `_blocks_sidebar.json`
|
: `_blocks_sidebar.json`
|
||||||
|
|
||||||
writeFileIfDifferent(`${this.destination}/${filename}`, JSON.stringify(blockSidebar, null, 2))
|
writeFileSync(`${this.destination}/${filename}`, JSON.stringify(blockSidebar, null, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
exportToFile = (toFile=true) => {
|
exportToFile = (toFile=true) => {
|
||||||
|
|
|
||||||
27
src/util.js
27
src/util.js
|
|
@ -1,21 +1,6 @@
|
||||||
import { dirname } from 'path'
|
|
||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
|
|
||||||
import { createHash } from 'crypto'
|
|
||||||
|
|
||||||
import { map } from 'lodash-es'
|
import { map } from 'lodash-es'
|
||||||
|
|
||||||
|
|
||||||
const getStringHash = stringToHash => {
|
|
||||||
const hash = createHash('sha256')
|
|
||||||
hash.update(stringToHash)
|
|
||||||
return hash.digest('hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFileHash = filePath => {
|
|
||||||
if(!existsSync(filePath)) { return '' }
|
|
||||||
return getStringHash(readFileSync(filePath))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const
|
export const
|
||||||
niceTemplate = tplString => {
|
niceTemplate = tplString => {
|
||||||
const
|
const
|
||||||
|
|
@ -34,16 +19,4 @@ export const
|
||||||
// TODO: support niceties for markdown, double-newlines, escaping, etc
|
// TODO: support niceties for markdown, double-newlines, escaping, etc
|
||||||
|
|
||||||
return tplString
|
return tplString
|
||||||
},
|
|
||||||
|
|
||||||
writeFileIfDifferent = (filename, content) => {
|
|
||||||
const
|
|
||||||
fileHash = getFileHash(filename),
|
|
||||||
contentHash = getStringHash(content)
|
|
||||||
|
|
||||||
if(fileHash !== contentHash) {
|
|
||||||
console.log("writing", filename)
|
|
||||||
mkdirSync(dirname(filename), { recursive: true })
|
|
||||||
writeFileSync(filename, content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue