diff --git a/.gitignore b/.gitignore index 38efa6caa2..d0ba1dbc90 100644 --- a/.gitignore +++ b/.gitignore @@ -17,9 +17,6 @@ lib-cov # Coverage directory used by tools like istanbul coverage -# nyc test coverage -.nyc_output - # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt diff --git a/package.json b/package.json index 426f1a9649..6eb4265682 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "dev": "node server.js", - "dev:debug": "node --inspect server.js", + "debug": "node --inspect-brk server.js", "build": "next build", "test": "jest", "start": "NODE_ENV=production node server.js", diff --git a/pages/doc.js b/pages/doc.js index 340ddd1f91..96184ba4e0 100644 --- a/pages/doc.js +++ b/pages/doc.js @@ -60,7 +60,7 @@ export default function Documentation({ item, headings, markdown, errorCode }) { apiKey: '755929839e113a981f481601c4f52082', indexName: 'dvc', inputSelector: '#doc-search', - debug: false // Set debug to true if you want to inspect the dropdown + debug: false // Set to `true` if you want to inspect the dropdown }) } } catch (ReferenceError) { @@ -81,7 +81,7 @@ export default function Documentation({ item, headings, markdown, errorCode }) { return () => Router.events.off('routeChangeComplete', handleRouteChange) }, []) - const githubLink = `https://github.com/iterative/dvc.org/blob/master${source}` + const githubLink = `https://github.com/iterative/dvc.org/blob/master/public${source}` return ( diff --git a/public/static/docs/command-reference/checkout.md b/public/static/docs/command-reference/checkout.md index 0f807ef1be..829f1b1602 100644 --- a/public/static/docs/command-reference/checkout.md +++ b/public/static/docs/command-reference/checkout.md @@ -6,7 +6,7 @@ DVC-files. ## Synopsis ```usage -usage: dvc checkout [-h] [-q | -v] [-d] [-f] [-R] +usage: dvc checkout [-h] [-q | -v] [-d] [-R] [-f] [--relink] [targets [targets ...]] positional arguments: @@ -16,46 +16,38 @@ positional arguments: ## Description -[DVC-files](/doc/user-guide/dvc-file-format) in a project specify -which instance of each data file or directory should be used, with the checksums -saved in the `outs` field. The `dvc checkout` command updates the workspace data -to match with the cached files that correspond to those checksums. - -Using an SCM like Git, the DVC-files are kept under version control. At a given -branch or tag of the SCM repository, the DVC-files will contain checksums for -the corresponding data files kept in the cache. After an SCM command like -`git checkout` is run, the DVC-files will change to the state at the specified -branch or commit or tag. Afterwards, the `dvc checkout` command is required in -order to synchronize the data files with the currently checked out DVC-files. - -This command must be executed after `git checkout` since Git doesn't track files -that are under DVC control. For convenience a Git hook is available, simply by -running `dvc install`, that will automate running `dvc checkout` after -`git checkout`. See `dvc install` for more information. - -The execution of `dvc checkout` does: - -- Scan the `outs` entries in DVC-files to compare with the currently checked out - data files. The scanned DVC-files is limited by the listed `targets` (if any) - on the command line. And if the `--with-deps` option is specified, it scans - backward from the given `targets` in the corresponding - [pipeline](/doc/command-reference/pipeline). - -- For any data files where the checksum doesn't match their DVC-file entry, the - data file is restored from the cache. The link strategy used (`reflink`, - `hardlink`, `symlink`, or `copy`) depends on the OS and the configured value - for `cache.type` – See `dvc config cache`. - -Note that this command by default tries NOT to copy files between the cache and -the workspace, using reflinks instead when supported by the file system. (Refer -to +[DVC-files](/doc/user-guide/dvc-file-format) act as pointers to specific version +of data files or directories under DVC control. This command synchronizes the +workspace data with the versions specified in the current DVC-files. + +`dvc checkout` is useful, for example, when using Git in the +project, after `git clone`, `git checkout`, or any other operation +that changes the DVC-files in the workspace. + +💡 For convenience, a Git hook is available to automate running `dvc checkout` +after `git checkout`. Use `dvc install` to install it. + +The execution of `dvc checkout` does the following: + +- Scans the DVC-files to compare against the data files or directories in the + workspace. DVC knows which data (outputs) match + because their checksums are saved in the `outs` fields inside the DVC-files. + Scanning is limited to the given `targets` (if any). See also options + `--with-deps` and `--recursive` below. + +- Missing data files or directories, or those that don't match with any + DVC-file, are restored from the cache. See options `--force` and + `--relink`. + +By default, this command tries not to copy files between the cache and the +workspace, using reflinks instead, when supported by the file system. (Refer to [File link types](/doc/user-guide/large-dataset-optimization#file-link-types-for-the-dvc-cache).) The next linking strategy default value is `copy` though, so unless other file link types are manually configured in `cache.type` (using `dvc config`), files will be copied. Keep in mind that having file copies doesn't present much of a negative impact unless the project uses very large data (several GBs or more). -But leveraging file links is crucial for large files where checking out a 50Gb -by copying file might take a few minutes for example, whereas with links, +But leveraging file links is crucial with large files, for example when checking +out a 50Gb file by copying might take a few minutes whereas, with links, restoring any file size will be almost instantaneous. > When linking files takes longer than expected (10 seconds for any one file) @@ -63,13 +55,10 @@ restoring any file size will be almost instantaneous. > the faster link types available. These warnings can be turned off setting the > `cache.slow_link_warning` config option to `false` with `dvc config cache`. -The output of `dvc checkout` does not list which data files were restored. It -does report removed files and files that DVC was unable to restore because -they're missing from the cache. - This command will fail to checkout files that are missing from the cache. In -such a case, `dvc checkout` prints a warning message. Any files that can be -checked out without error will be restored. +such a case, `dvc checkout` prints a warning message. It also lists removed +files. Any files that can be checked out without error will be restored without +being reported individually. There are two methods to restore a file missing from the cache, depending on the situation. In some cases a pipeline must be reproduced (using `dvc repro`) to @@ -94,6 +83,12 @@ be pulled from remote storage using `dvc pull`. remove files that don't match those DVC-file references or are missing from cache. (They are not "committed", in DVC terms.) +- `--relink` - ensures the file linking strategy (`reflink`, `hardlink`, + `symlink`, or `copy`) for all data in the workspace is consistent with the + project's [`cache.type`](/doc/command-reference/config#cache). This is + achieved by restoring **all data files or a directories** referenced in + current DVC-files (regardless of whether they match a current DVC-file). + - `-h`, `--help` - shows the help message and exit. - `-q`, `--quiet` - do not write anything to standard output. Exit with 0 if no @@ -210,18 +205,21 @@ do `dvc fetch` + `dvc checkout`. ## Automating `dvc checkout` -We have the data files (managed by DVC) lined up with the other files (managed -by Git). This required us to remember to run `dvc checkout`, and of course we -won't always remember to do so. Wouldn't it be nice to automate this? +We want the data files or directories (managed by DVC) to match with the other +files (managed by Git e.g. source code). This requires us to remember running +`dvc checkout` when needed, and of course we won't always remember to do so. +Wouldn't it be nice to automate this? -Let's run this command: +Let's try this: ```dvc $ dvc install ``` -This installs Git hooks to automate running `dvc checkout` (or `dvc status`) -when needed. Then we can checkout the master branch again: +`dvc install` installs Git hooks to automate common operations, including +running `dvc checkout` when needed. + +We can then checkout the master branch again: ```dvc $ git checkout bigrams @@ -233,6 +231,6 @@ $ md5 model.pkl MD5 (model.pkl) = 3863d0e317dee0a55c4e59d2ec0eef33 ``` -Previously this took two steps, `git checkout` followed by `dvc checkout`. We -can now skip the second one, which is automatically executed for us. The -workspace is automatically synchronized accordingly. +Previously this took two commands, `git checkout` followed by `dvc checkout`. We +can now skip the second one, which is automatically run for us. The workspace is +automatically synchronized accordingly. diff --git a/public/static/docs/command-reference/config.md b/public/static/docs/command-reference/config.md index 02285e9ab4..59ee19a932 100644 --- a/public/static/docs/command-reference/config.md +++ b/public/static/docs/command-reference/config.md @@ -139,6 +139,10 @@ for more details.) This section contains the following options: [File link types](/doc/user-guide/large-dataset-optimization#file-link-types-for-the-dvc-cache) for a full explanation of each one. + To apply changes to this option in the workspace, by restoring all file + links/copies from cache, please use `dvc checkout --relink`. See + [checkout options](/doc/command-reference/checkout#options) for more details. + - `cache.slow_link_warning` - used to turn off the warnings about having a slow cache link type. These warnings are thrown by `dvc pull` and `dvc checkout` when linking files takes longer than usual, to remind them that there are diff --git a/public/static/docs/command-reference/remote/index.md b/public/static/docs/command-reference/remote/index.md index 9856561927..7e018cd6e7 100644 --- a/public/static/docs/command-reference/remote/index.md +++ b/public/static/docs/command-reference/remote/index.md @@ -95,13 +95,13 @@ url = /path/to/remote remote = myremote ``` -## Example: Add Amazon S3 remote and modify its region +## Example: Add a default Amazon S3 remote and modify its region > 💡 Before adding an S3 remote, be sure to > [Create a Bucket](https://docs.aws.amazon.com/AmazonS3/latest/gsg/CreatingABucket.html). ```dvc -$ dvc remote add mynewremote s3://mybucket/myproject +$ dvc remote add -d mynewremote s3://mybucket/myproject $ dvc remote modify mynewremote region us-east-2 ``` diff --git a/public/static/docs/command-reference/repro.md b/public/static/docs/command-reference/repro.md index a990c2b76c..729645abbd 100644 --- a/public/static/docs/command-reference/repro.md +++ b/public/static/docs/command-reference/repro.md @@ -45,6 +45,38 @@ files, intermediate or final results. It saves all the data files, intermediate or final results into the DVC cache (unless `--no-commit` option is specified), and updates stage files with the new checksum information. +### Parallel stage execution + +Currently, `dvc repro` is not able to parallelize stage execution automatically. +If you need to do this, you can launch `dvc repro` multiple times manually. For +example, let's say a pipeline graph looks something like this: + +``` +$ dvc pipeline show --ascii result.py ++--------+ +--------+ +| A1.dvc | | B1.dvc | ++--------+ +--------+ + * * + * * + * * ++--------+ +--------+ +| A2.dvc | | B2.dvc | ++--------+ +--------+ + * * + ** ** + * * + +------------+ + | result.dvc | + +------------+ +``` + +This pipeline consists of two parallel branches (`A` and `B`), and the final +"result" stage, where the branches merge. To reproduce both branches at the same +time, you could run `dvc repro A2.dvc` and `dvc repro B2.dvc` at the same time +(e.g. in separate terminals). After both finish successfully, you can then run +`dvc repro result.dvc`: DVC will know that both branches are already up-to-date +and only execute the final stage. + ## Options - `-f`, `--force` - reproduce a pipeline, regenerating its results, even if no diff --git a/public/static/docs/sidebar.json b/public/static/docs/sidebar.json index c92333fbb3..c253134e83 100644 --- a/public/static/docs/sidebar.json +++ b/public/static/docs/sidebar.json @@ -30,7 +30,6 @@ ] }, { - "label": "Install", "slug": "install", "source": "install/index.md", "children": [ @@ -140,7 +139,7 @@ { "label": "Contributing", "slug": "contributing", - "source": "contributing/index.md", + "source": false, "children": [ { "label": "DVC Core Project", @@ -356,8 +355,8 @@ ] }, { - "label": "Understanding DVC", "slug": "understanding-dvc", + "label": "Understanding DVC", "source": false, "children": [ "collaboration-issues", diff --git a/public/static/docs/understanding-dvc/related-technologies.md b/public/static/docs/understanding-dvc/related-technologies.md index 2dd2cb7c57..bf5dffa5bf 100644 --- a/public/static/docs/understanding-dvc/related-technologies.md +++ b/public/static/docs/understanding-dvc/related-technologies.md @@ -100,11 +100,11 @@ http://studio.ml/ - Git-annex is a datafile-centric system whereas DVC is focused on providing a workflow for machine learning and reproducible experiments. When a DVC or Git-annex repository is cloned via `git clone`, data files won't be copied to - the local machine as file contents are stored in separate + the local machine, as file contents are stored in separate [remotes](/doc/command-reference/remote). With DVC, - [DVC-files](/doc/user-guide/dvc-file-format) (that provide the reproducible - workflow) are always included in the Git repository and hence can be recreated - locally with minimal effort. + [DVC-files](/doc/user-guide/dvc-file-format), which provide the reproducible + workflow, are always included in the Git repository. Hence, they can be + executed locally with minimal effort. - DVC is not fundamentally bound to Git, and users have the option of changing the repository format. diff --git a/public/static/docs/user-guide/contributing/docs.md b/public/static/docs/user-guide/contributing/docs.md index 28ac6cd4c1..496ecdc5e5 100644 --- a/public/static/docs/user-guide/contributing/docs.md +++ b/public/static/docs/user-guide/contributing/docs.md @@ -88,8 +88,8 @@ documentation files automatically. ### Debugging -The `yarn dev:debug` script runs the local development server with Node's -[`--inspect` option](https://nodejs.org/en/docs/guides/debugging-getting-started/#command-line-options) +The `yarn debug` script runs the local development server with `node`'s +[`--inspect-brk` option](https://nodejs.org/en/docs/guides/debugging-getting-started/#command-line-options) in order for debuggers to connect to it (on the default port, 9229). > For example, use this launch configuration in **Visual Studio Code**: @@ -100,7 +100,7 @@ in order for debuggers to connect to it (on the default port, 9229). > "request": "launch", > "name": "Launch via Yarn", > "runtimeExecutable": "yarn", -> "runtimeArgs": ["dev:debug"], +> "runtimeArgs": ["debug"], > "port": 9229 > } > ``` diff --git a/public/static/docs/user-guide/contributing/index.md b/public/static/docs/user-guide/contributing/index.md deleted file mode 100644 index cb1f5bc1fb..0000000000 --- a/public/static/docs/user-guide/contributing/index.md +++ /dev/null @@ -1,35 +0,0 @@ -# Contributing - -## Contributing to DVC - -We welcome [contributions](/doc/user-guide/contributing/core) to -[DVC](https://github.com/iterative/dvc) by the community. - -- [How to report a problem](/doc/user-guide/contributing/core#how-to-report-a-problem) - -- [Submitting changes](/doc/user-guide/contributing/core#submitting-changes) - -- [Development environment](/doc/user-guide/contributing/core#development-environment) - -- [Running tests](/doc/user-guide/contributing/core#running-tests) - -- [Testing remotes](/doc/user-guide/contributing/core#testing-remotes) - -- [Code style guidelines (for Python)](/doc/user-guide/contributing/core#code-style-guidelines-for-python) - -- [Commit message format guidelines](/doc/user-guide/contributing/core#commit-message-format-guidelines) - -## Contributing Docs - -We welcome any contributions to our documentation repository, -[dvc.org](https://github.com/iterative/dvc.org). Contributions can be updates to -the documentation content, or (rare) changes to the JS engine we use to run the -website. - -- [Structure of the project](/doc/user-guide/contributing/docs#structure-of-the-project) - -- [Submitting changes](/doc/user-guide/contributing/docs#submitting-changes) - -- [Development environment](/doc/user-guide/contributing/docs#development-environment) - -- [Doc style guidelines and tips (for JavaScript and Markdown)](/doc/user-guide/contributing/docs#doc-style-guidelines-and-tips-for-java-script-and-markdown) diff --git a/public/static/docs/user-guide/dvc-files-and-directories.md b/public/static/docs/user-guide/dvc-files-and-directories.md index 272426ace6..e971e23df8 100644 --- a/public/static/docs/user-guide/dvc-files-and-directories.md +++ b/public/static/docs/user-guide/dvc-files-and-directories.md @@ -41,6 +41,12 @@ operation: - `.dvc/lock`: Lock file for the entire DVC project +- `.dvc/tmp`: Directory for miscellaneous temporary files + +- `.dvc/tmp/rwlock`: JSON file that contains read and write locks for specific + dependencies and outputs, to allow safely running multiple DVC commands in + parallel. + ## Structure of cache directory There are two ways in which the data is stored in cache: As a diff --git a/public/static/docs/user-guide/large-dataset-optimization.md b/public/static/docs/user-guide/large-dataset-optimization.md index 5c29ed0f09..b88a94a96f 100644 --- a/public/static/docs/user-guide/large-dataset-optimization.md +++ b/public/static/docs/user-guide/large-dataset-optimization.md @@ -38,11 +38,11 @@ Symbolic links, and Reflinks in more recent systems. While reflinks bring all the benefits and none of the worries, they're not commonly supported in most platforms yet. Hard/soft links optimize **speed** and **space** in the file system, but may break your workflow since updating hard/sym-linked files tracked -by DVC in the workspace causes cache corruption. These 2 link types -thus require using cache **protected mode** (see the `cache.protected` config -option in `dvc config cache`). Finally, a 4th "linking" option is to actually -copy files from/to the cache, which is safe but inefficient – especially for -large files (several GBs or more). +by DVC in the workspace causes cache corruption. These +2 link types thus require using cache **protected mode** (see the +`cache.protected` config option in `dvc config cache`). Finally, a 4th "linking" +option is to actually copy files from/to the cache, which is safe but +inefficient – especially for large files (several GBs or more). > Some versions of Windows (e.g. Windows Server 2012+ and Windows 10 Enterprise) > support hard or soft links on the @@ -92,9 +92,9 @@ efficiency: 4. **`copy`**: An inefficient "linking" strategy, yet supported on all file systems. Using `copy` means there will be no file links, but that the tracked - files will be duplicated as copies existing in both the cache and workspace. - Suitable for scenarios with relatively small data files, where copying them - is not a storage performance concern. + files will be duplicated as copies existing in both the cache and + workspace. Suitable for scenarios with relatively small data + files, where copying them is not a storage performance concern. > DVC avoids `symlink` and `hardlink` types by default to protect user from > accidental cache corruption. Refer to the @@ -120,6 +120,10 @@ file link types. Please refer to the [Update a Tracked File](/doc/user-guide/updating-tracked-files) on how to manage tracked files under these cache configurations. +To make sure that the data files in the workspace are consistent with the +project's `cache.type` option, you may use `dvc checkout --relink`. +See `dvc checkout` for more information. + --- > \***copy-on-write links or "reflinks"** are a relatively new way to link files diff --git a/server.js b/server.js index 8ac859e014..8f94111f0d 100644 --- a/server.js +++ b/server.js @@ -1,9 +1,13 @@ /* eslint-env node */ -// This file doesn't go through babel or webpack transformation. Make sure the -// syntax and sources this file requires are compatible with the current Node.js -// version you are running. (See https://github.com/zeit/next.js/issues/1245 for -// discussions on universal Webpack vs universal Babel.) +/* + * Custom server (with custom routes) See + * https://nextjs.org/docs/advanced-features/custom-server + * + * NOTE: This file doesn't go through babel or webpack. Make sure the syntax and + * sources this file requires are compatible with the current node version you + * are running. + */ const { createServer } = require('http') const next = require('next') @@ -21,7 +25,7 @@ app.prepare().then(() => { const { pathname, query } = parsedUrl /* - * Special URL redirects. + * HTTP redirects * NOTE: The order of the redirects is important. */ if ( @@ -107,22 +111,21 @@ app.prepare().then(() => { res.end() } else if (/^\/doc(\/.*)?$/.test(pathname)) { /* - * Special docs engine handler + * Docs Engine handler */ - // Force 404 response for any inexistent /doc item. + // Force 404 response code for any inexistent /doc item. if (!getItemByPath(pathname)) { res.statusCode = 404 } - // Fire up docs engine! + // Custom route for all docs app.render(req, res, '/doc', query) } else { // Regular Next.js handler handle(req, res, parsedUrl) } }).listen(port, err => { - // Invokes `createServer` server. if (err) throw err console.info(`> Ready on localhost:${port}`) }) diff --git a/src/Documentation/Markdown/Markdown.js b/src/Documentation/Markdown/Markdown.js index 7a708cd1be..4f9619c6e2 100644 --- a/src/Documentation/Markdown/Markdown.js +++ b/src/Documentation/Markdown/Markdown.js @@ -156,9 +156,9 @@ export default class Markdown extends React.PureComponent { } isInsideCodeBlock = elem => { - for (; elem && elem !== document; elem = elem.parentNode) { - if (elem.tagName === 'PRE') return true - if (elem.tagName === 'ARTICLE') return false + for (let el = elem; el && el !== document; el = el.parentNode) { + if (el.tagName === 'PRE') return true + if (el.tagName === 'ARTICLE') return false } return false } diff --git a/src/styles.js b/src/styles.js index ef89ddebf8..d4500df152 100644 --- a/src/styles.js +++ b/src/styles.js @@ -8,25 +8,25 @@ export const global = ` font-weight: 400; text-rendering: optimizeLegibility !important; } - + @-moz-document url-prefix() { body { font-weight: lighter !important; } } - + body { padding: 0px; font-family: BrandonGrotesque, Tahoma, Arial; font-weight: normal; -webkit-font-smoothing: antialiased; line-height: 1.5; - + // IE flex min-height fix https://stackoverflow.com/a/40491316 display: flex; flex-direction: column; } - + *:focus { outline: 0; } @@ -51,13 +51,14 @@ export const sizes = { sizes.phablet = Math.floor((sizes.tablet + sizes.phone) / 2) -export const media = Object.keys(sizes).reduce((accumulator, label) => { - accumulator[label] = (...args) => css` - @media (max-width: ${sizes[label]}px) { - ${css(...args)}; - } - ` - return accumulator +export const media = Object.keys(sizes).reduce((acc, s) => { + return Object.assign(acc, { + [s]: (...args) => css` + @media (max-width: ${sizes[s]}px) { + ${css(...args)}; + } + ` + }) }, {}) export const container = css` diff --git a/src/utils/sidebar.js b/src/utils/sidebar.js index 1f2fb16a6d..0135d87975 100644 --- a/src/utils/sidebar.js +++ b/src/utils/sidebar.js @@ -1,11 +1,7 @@ /* eslint-env node */ - -const startCase = require('lodash.startcase') -const sidebar = require('../../public/static/docs/sidebar.json') - /* - We will use this helper to normalize sidebar structure and create - all of the resurces we need to prevent future recalculations. + These helpers normalize sidebar structure and create all the resources needed. + This prevents future recalculations. Target structure example: @@ -22,11 +18,26 @@ const sidebar = require('../../public/static/docs/sidebar.json') } */ +const startCase = require('lodash.startcase') +const sidebar = require('../../public/static/docs/sidebar.json') + const PATH_ROOT = '/doc/' const FILE_ROOT = '/static/docs/' const FILE_EXTENSION = '.md' -// Inner helpers +function validateRawItem({ slug, source, children }) { + const isSourceDisabled = source === false + + if (!slug) { + throw Error("'slug' field is required in objects in sidebar.json") + } + + if (isSourceDisabled && (!children || !children.length)) { + throw Error( + "If you set 'source' to false, you had to add at least one child" + ) + } +} function findItem(data, targetPath) { if (data.length) { @@ -45,42 +56,22 @@ function findItem(data, targetPath) { } } -function findChildWithSource(item) { - return item.source ? item : findChildWithSource(item.children[0]) -} - function findPrevItemWithSource(data, item) { - if (item.source) { + if (item && item.source) { return item - } else if (item.prev) { + } else if (item && item.prev) { const prevItem = findItem(data, item.prev) return findPrevItemWithSource(data, prevItem) } } -function validateRawItem({ slug, source, children }) { - const isSourceDisabled = source === false - - if (!slug) { - throw Error("'slug' field is required in objects in sidebar.json") - } +function normalizeItem({ rawItem, parentPath, resultRef, prevRef }) { + validateRawItem(rawItem) - if (isSourceDisabled && (!children || !children.length)) { - throw Error( - "If you set 'source' to false, you had to add at least one child" - ) - } -} + const { label, slug, source, tutorials } = rawItem -// Normalization - -function normalizeItem({ item, parentPath, resultRef, prevRef }) { - validateRawItem(item) - - const { label, slug, source, tutorials } = item - - // If prev item doesn't have source we need to recirsively search for it + // If prev item doesn't have source we need to search for it const prevItemWithSource = prevRef && findPrevItemWithSource(resultRef, prevRef) @@ -111,9 +102,9 @@ function normalizeSidebar({ data.forEach(rawItem => { const isShortcut = typeof rawItem === 'string' - const item = isShortcut ? { slug: rawItem } : rawItem + rawItem = isShortcut ? { slug: rawItem } : rawItem const normalizedItem = normalizeItem({ - item, + rawItem, parentPath, resultRef, prevRef @@ -123,10 +114,10 @@ function normalizeSidebar({ prevRef.next = normalizedItem.path } - if (item.children) { + if (rawItem.children) { normalizedItem.children = normalizeSidebar({ - data: item.children, - parentPath: `${parentPath}${item.slug}/`, + data: rawItem.children, + parentPath: `${parentPath}${rawItem.slug}/`, parentResultRef: resultRef, startingPrevRef: normalizedItem }) @@ -142,13 +133,19 @@ function normalizeSidebar({ return currentResult } +function findChildWithSource(item) { + return item.source ? item : findChildWithSource(item.children[0]) +} + +/* + * Exports + */ + const normalizedSidebar = normalizeSidebar({ data: sidebar, parentPath: '' }) -// Exports - function getItemByPath(path) { const normalizedPath = path.replace(/\/$/, '') const isRoot = normalizedPath === PATH_ROOT.slice(0, -1) @@ -156,7 +153,9 @@ function getItemByPath(path) { ? normalizedSidebar[0] : findItem(normalizedSidebar, normalizedPath) - return item && findChildWithSource(item) + if (!item) return false + + return findChildWithSource(item) } function getParentsListFromPath(path) { diff --git a/src/utils/sidebar.test.js b/src/utils/sidebar.test.js index e66f692f31..c8f977de3e 100644 --- a/src/utils/sidebar.test.js +++ b/src/utils/sidebar.test.js @@ -336,20 +336,31 @@ describe('SidebarMenu/helper', () => { expect(getItemByPath('/doc')).toEqual(result) }) - it('Returns first child with source for sourceless parents', () => { + // eslint-disable-next-line max-len + it('Returns first child with source for all parents with source:false', () => { const rawData = [ { - slug: 'item-name', + slug: 'item', source: false, children: [ - { slug: 'nested-item', source: false, children: ['subnested-item'] } + { + slug: 'nested', + source: false, + children: [ + { + slug: 'subnested', + source: false, + children: ['leaf-item'] + } + ] + } ] } ] const result = { - label: 'Subnested Item', - path: '/doc/item-name/nested-item/subnested-item', - source: '/static/docs/item-name/nested-item/subnested-item.md', + label: 'Leaf Item', + path: '/doc/item/nested/subnested/leaf-item', + source: '/static/docs/item/nested/subnested/leaf-item.md', tutorials: {}, prev: undefined, next: undefined @@ -358,7 +369,9 @@ describe('SidebarMenu/helper', () => { jest.doMock('../../public/static/docs/sidebar.json', () => rawData) const { getItemByPath } = require('./sidebar') - expect(getItemByPath('/doc/item-name')).toEqual(result) + expect(getItemByPath('/doc/item')).toEqual(result) + expect(getItemByPath('/doc/item/nested')).toEqual(result) + expect(getItemByPath('/doc/item/nested/subnested')).toEqual(result) }) })