Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: API auto-mentioning in docs (#910) #1305

Merged
merged 8 commits into from
May 19, 2020
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ typings/
*.log
.idea
.vscode
.history

# Mac finder artifacts
.DS_Store
Expand Down
6 changes: 3 additions & 3 deletions content/docs/api-reference/read.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ modelpkl = dvc.api.read(

## Description

This function wraps [`dvc.api.open()`](/doc/api-reference/open), for a simple
way to return the complete contents of a file tracked in a <abbr>DVC
project</abbr>. The file can be tracked by DVC or by Git.
This function wraps `dvc.api.open()`, for a simple way to return the complete
contents of a file tracked in a <abbr>DVC project</abbr>. The file can be
tracked by DVC or by Git.

> This is similar to the `dvc get` command in our CLI.

Expand Down
4 changes: 2 additions & 2 deletions content/docs/command-reference/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ DVC, by effectively replacing data files, models, directories with DVC-files
files when you browse a <abbr>DVC repository</abbr> on Git hosting (e.g.
Github), you just see the DVC-files. This makes it hard to navigate the project
to find <abbr>data artifacts</abbr> for use with `dvc get`, `dvc import`, or
[`dvc.api`](/doc/api-reference).
`dvc.api`.

`dvc list` prints a virtual view of a DVC repository, as if files and
directories [tracked by DVC](/doc/use-cases/versioning-data-and-model-files)
Expand All @@ -48,7 +48,7 @@ list files recursively.

Please note that `dvc list` doesn't check whether the listed data (tracked by
DVC) actually exists in remote storage, so it's not guaranteed whether it can be
accessed with `dvc get`, `dvc import`, or [`dvc.api`](/doc/api-reference)
accessed with `dvc get`, `dvc import`, or `dvc.api`

## Options

Expand Down
2 changes: 1 addition & 1 deletion content/docs/install/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
DVC can be used as a Python library, simply install it with a package manager
like `pip` or `conda`, and as a Python
[project requirement](https://pip.pypa.io/en/latest/user_guide/#requirements-files)
if needed. The [Python API](/doc/api-reference) module is `dvc.api`.
if needed. The Python API module is `dvc.api`.

## Advanced options

Expand Down
12 changes: 6 additions & 6 deletions content/docs/use-cases/data-registries.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ $ dvc push
## Using registries

The main methods to consume <abbr>data artifacts</abbr> from a **data registry**
are the `dvc import` and `dvc get` commands, as well as the
[`dvc.api`](/doc/api-reference) Python API.
are the `dvc import` and `dvc get` commands, as well as the `dvc.api` Python
API.

But first, you may want to explore the contents of a data DVC repo.

Expand Down Expand Up @@ -164,9 +164,9 @@ the project dependency metadata in the import stage (DVC-file).

### Programmatic reusability of DVC data

Our Python API, included with the `dvc` package installed with DVC, includes the
`open` function to load/stream data directly from external <abbr>DVC
projects</abbr>:
Our Python API (`dvc.api`), included with the `dvc` package installed with DVC,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I would actually prefer to link [Python API] manually for readability.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 036672d along with a couple more instances I found.

includes the `open` function to load/stream data directly from external
<abbr>DVC projects</abbr>:

```python
import dvc.api.open
Expand All @@ -182,7 +182,7 @@ with dvc.api.open(model_path, repo_url) as fd:
This opens `model.pkl` as a file descriptor. The example above tries to
illustrate a hardcoded ML model **deployment** method.

> Notice that the `dvc.api.get_url` and `dvc.api.read` functions are also
> Notice that the `dvc.api.get_url()` and `dvc.api.read()` functions are also
> available.

## Updating registries
Expand Down
4 changes: 4 additions & 0 deletions content/docs/user-guide/contributing/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ is installed when `yarn` runs (explained above).
- Markdown: Using `dvc <command>`, the docs engine will create a link to that
command automatically. (No need to use `[]()` explicitly to create them.)

- Markdown: Using `dvc.api.<api_method>()` or `dvc.api`, the docs engine will
create a link to that API method automatically. (No need to use `[]()`
explicitly to create them.)

- Markdown: Neither bullet lists nor each item's should be too long (3 sentence
paragraphs max.) Full sentence bullets should begin with a capital letter and
end in period `.` otherwise they can be all lower case and have no ending
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"@typescript-eslint/parser": "^2.27.0",
"autoprefixer": "^9.7.6",
"babel-eslint": "^10.1.0",
"babel-jest": "^25.2.6",
"babel-jest": "^26.0.1",
"babel-plugin-transform-define": "^2.0.0",
"babel-plugin-transform-object-assign": "^6.22.0",
"eslint": "^6.8.0",
Expand Down Expand Up @@ -137,7 +137,7 @@
"gatsby-transformer-sharp": "2.2.23",
"hast-util-select": "^4.0.0",
"husky": "^4.2.3",
"jest": "^25.2.7",
"jest": "^26.0.1",
"lint-staged": "^10.1.2",
"postcss-color-mod-function": "^3.0.3",
"postcss-custom-media": "^7.0.8",
Expand All @@ -149,9 +149,11 @@
"rehype-stringify": "^7.0.0",
"remark": "^12.0.0",
"remark-html": "^11.0.1",
"remark-parse": "^8.0.2",
"stylelint": "^13.3.0",
"stylelint-config-standard": "^20.0.0",
"typescript": "^3.8.3"
"typescript": "^3.8.3",
"unist-util-remove-position": "^2.0.1"
},
"husky": {
"hooks": {
Expand Down
35 changes: 35 additions & 0 deletions plugins/gatsby-remark-dvc-linker/apiLinker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint-env node */

const { createLinkNode } = require('./helpers')
const { getItemByPath } = require('../../src/utils/shared/sidebar')

const DVC_API_REGEXP = /dvc.api([a-z-._]*\(\)$)?/
const METHOD_REGEXP = /^[a-z-._]*\(\)$/
const API_ROOT = '/doc/api-reference/'

module.exports = astNode => {
const node = astNode[0]
const parent = astNode[2]

if (parent.type !== 'link' && DVC_API_REGEXP.test(node.value)) {
const parts = node.value.split('.')
let url

const isMethod = parts[2] && METHOD_REGEXP.test(parts[2])
const method = isMethod && parts[2].slice(0, -2)
const isRoot = parts[0] === 'dvc' && parts[1] === 'api' && !parts[2]

if (isRoot) {
url = `${API_ROOT}`
} else {
url = `${API_ROOT}${method}`
}

const isMethodPageExists = getItemByPath(url)
if (isMethodPageExists) {
createLinkNode(url, astNode)
}
}

return astNode
}
37 changes: 37 additions & 0 deletions plugins/gatsby-remark-dvc-linker/commandLinker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* eslint-env node */

const { createLinkNode } = require('./helpers')
const { getItemByPath } = require('../../src/utils/shared/sidebar')

const DVC_REGEXP = /dvc\s+[a-z][a-z-.]*/
const COMMAND_REGEXP = /^[a-z][a-z-]*$/
const COMMAND_ROOT = '/doc/command-reference/'

module.exports = astNode => {
const node = astNode[0]
const parent = astNode[2]

if (parent.type !== 'link' && DVC_REGEXP.test(node.value)) {
const parts = node.value.split(/\s+/)
let url

const hasThirdSegment = parts[2] && COMMAND_REGEXP.test(parts[2])
const isCommandPageExists = getItemByPath(`${COMMAND_ROOT}${parts[1]}`)
const isSubcommandPageExists =
isCommandPageExists &&
hasThirdSegment &&
getItemByPath(`${COMMAND_ROOT}${parts[1]}/${parts[2]}`)

if (isSubcommandPageExists) {
url = `${COMMAND_ROOT}${parts[1]}/${parts[2]}`
} else if (isCommandPageExists && hasThirdSegment) {
url = `${COMMAND_ROOT}${parts[1]}#${parts[2]}`
} else if (isCommandPageExists) {
url = `${COMMAND_ROOT}${parts[1]}`
}

createLinkNode(url, astNode)
}

return astNode
}
20 changes: 20 additions & 0 deletions plugins/gatsby-remark-dvc-linker/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const unified = require('unified')
const remarkHtml = require('remark-html')
const remarkParse = require('remark-parse')
const removePosition = require('unist-util-remove-position')

// We do not need to consider the position of the AST nodes
const buildAst = mdToBuild =>
removePosition(unified().use(remarkHtml).use(remarkParse).parse(mdToBuild))

const createLinkNode = (url, [node, index, parent]) =>
url &&
(parent.children[index] = {
type: 'link',
url,
title: null,
children: [node],
position: node.position
})

module.exports = { buildAst, createLinkNode }
46 changes: 11 additions & 35 deletions plugins/gatsby-remark-dvc-linker/index.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,19 @@
/* eslint-env node */

const flow = require('lodash/flow')
const constant = require('lodash/constant')
const visit = require('unist-util-visit')
const { getItemByPath } = require('../../src/utils/shared/sidebar')

const DVC_REGEXP = /dvc\s+[a-z][a-z-.]*/
const COMMAND_REGEXP = /^[a-z][a-z-]*$/
const COMMAND_ROOT = '/doc/command-reference/'
const apiLinker = require('./apiLinker')
const commandLinker = require('./commandLinker')

// Lifting up the AST visitor in order not to repeat the
// calculations times the amount of linkers we have
module.exports = ({ markdownAST }) => {
visit(markdownAST, 'inlineCode', function (node, index, parent) {
if (parent.type !== 'link' && DVC_REGEXP.test(node.value)) {
const parts = node.value.split(/\s+/)
let url

const hasThirdSegment = parts[2] && COMMAND_REGEXP.test(parts[2])
const isCommandPageExists = getItemByPath(`${COMMAND_ROOT}${parts[1]}`)
const isSubcommandPageExists =
isCommandPageExists &&
hasThirdSegment &&
getItemByPath(`${COMMAND_ROOT}${parts[1]}/${parts[2]}`)

if (isSubcommandPageExists) {
url = `${COMMAND_ROOT}${parts[1]}/${parts[2]}`
} else if (isCommandPageExists && hasThirdSegment) {
url = `${COMMAND_ROOT}${parts[1]}#${parts[2]}`
} else if (isCommandPageExists) {
url = `${COMMAND_ROOT}${parts[1]}`
}

if (url) {
parent.children[index] = {
type: 'link',
url: url,
children: [node],
position: node.position
}
}
}
})

visit(
markdownAST,
'inlineCode',
flow([Array, commandLinker, apiLinker, constant(undefined)])
)
return markdownAST
}
58 changes: 58 additions & 0 deletions plugins/gatsby-remark-dvc-linker/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const flow = require('lodash/flow')
const constant = require('lodash/constant')
const visit = require('unist-util-visit')

const gatsbyRemarkDvcLinker = require('.')
const apiLinker = require('./apiLinker')
const commandLinker = require('./commandLinker')

const { buildAst } = require('./helpers')

describe('gatsby-remark-dvc-linker', () => {
api = {
inlineCode: '`dvc.api.get_url()`',
url: '[`dvc.api.get_url()`](/doc/api-reference/get_url)'
}

apiRoot = {
inlineCode: '`dvc.api`',
url: '[`dvc.api`](/doc/api-reference/)'
}

command = {
inlineCode: '`dvc get`',
url: '[`dvc get`](/doc/command-reference/get)'
}

it('composes apiLinker and commandLinker', () => {
const ast = buildAst(`${api.inlineCode} ${command.inlineCode}`)
gatsbyRemarkDvcLinker({ markdownAST: ast })
expect(ast).toEqual(buildAst(`${api.url} ${command.url}`))
})

describe('apiLinker', () => {
it('transforms API reference to a link', () => {
const ast = buildAst(api.inlineCode)
visit(ast, 'inlineCode', flow([Array, apiLinker, constant(undefined)]))
expect(ast).toEqual(buildAst(api.url))
})

it('transforms root API reference to a link', () => {
const ast = buildAst(apiRoot.inlineCode)
visit(ast, 'inlineCode', flow([Array, apiLinker, constant(undefined)]))
expect(ast).toEqual(buildAst(apiRoot.url))
})
})

describe('commandLinker', () => {
it('transforms command reference to a link', () => {
const ast = buildAst(command.inlineCode)
visit(
ast,
'inlineCode',
flow([Array, commandLinker, constant(undefined)])
)
expect(ast).toEqual(buildAst(command.url))
})
})
})
Loading