Skip to content
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
60 changes: 57 additions & 3 deletions packages/gatsby-transformer-json/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# gatsby-transformer-json

Parses JSON files. Currently it only handles stringified arrays but the
goal is to handle in a reasonable way many types of JSON data.
Parses JSON files. Supports arrays of objects and single objects.

## Install

Expand All @@ -18,6 +17,11 @@ plugins: [

## Parsing algorithm

You can choose to structure your data as arrays of objects in individual files
or as single objects spread across multiple files.

### Array of Objects

The algorithm for arrays is to convert each item in the array into
a node.

Expand All @@ -32,9 +36,59 @@ So if your project has a `letters.json` with `[{ "value": "a" }, { "value":
]
```

### Single Object

The algorithm for single JSON objects is to convert the object defined at the
root of the file into a node. The type of the node is based on the name of the
parent directory.

For example, lets say your project has a data layout like:

```
data/
letters/
a.json
b.json
c.json
```

Where each of `a.json`, `b.json` and `c.json` look like:

```javascript
{ 'value': 'a' }
```

```javascript
{ 'value': 'b' }
```

```javascript
{ 'value': 'c' }
```

Then the following three nodes would be created.

```javascript
[
{
value: 'a',
type: 'Letters',
},
{
value: 'b',
type: 'Letters',
},
{
value: 'c',
type: 'Letters',
},
]
```

## How to query

You'd be able to query your letters like:
Regardless of whether you choose to structure your data in arrays of objects or
single objects, you'd be able to query your letters like:

```graphql
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,56 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Process JSON nodes correctly correctly creates a node from JSON which is a single object 1`] = `
Array [
Array [
Object {
"blue": true,
"children": Array [],
"funny": "yup",
"id": "foo",
"internal": Object {
"contentDigest": "8838e569ae02d98806532310fb2a577a",
"type": "FooJson",
},
"parent": "whatever",
},
],
]
`;

exports[`Process JSON nodes correctly correctly creates a node from JSON which is a single object 2`] = `
Array [
Array [
Object {
"child": Object {
"blue": true,
"children": Array [],
"funny": "yup",
"id": "foo",
"internal": Object {
"contentDigest": "8838e569ae02d98806532310fb2a577a",
"type": "FooJson",
},
"parent": "whatever",
},
"parent": Object {
"children": Array [],
"content": "{\\"id\\":\\"foo\\",\\"blue\\":true,\\"funny\\":\\"yup\\"}",
"dir": "/tmp/foo/",
"id": "whatever",
"internal": Object {
"contentDigest": "whatever",
"mediaType": "application/json",
"name": "test",
},
"name": "nodeName",
"parent": "SOURCE",
},
},
],
]
`;

exports[`Process JSON nodes correctly correctly creates nodes from JSON which is an array of objects 1`] = `
Array [
Array [
Expand Down
21 changes: 21 additions & 0 deletions packages/gatsby-transformer-json/src/__tests__/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ describe(`Process JSON nodes correctly`, () => {
})
})

it(`correctly creates a node from JSON which is a single object`, async () => {
const data = { id: `foo`, blue: true, funny: `yup` }
node.content = JSON.stringify(data)
node.dir = `/tmp/foo/`

const createNode = jest.fn()
const createParentChildLink = jest.fn()
const boundActionCreators = { createNode, createParentChildLink }

await onCreateNode({
node,
loadNodeContent,
boundActionCreators,
}).then(() => {
expect(createNode.mock.calls).toMatchSnapshot()
expect(createParentChildLink.mock.calls).toMatchSnapshot()
expect(createNode).toHaveBeenCalledTimes(1)
expect(createParentChildLink).toHaveBeenCalledTimes(1)
})
})

it(`If the object has an id, it uses that as the id instead of an autogenerated one`, async () => {
const data = [
{ id: `foo`, blue: true, funny: `yup` },
Expand Down
58 changes: 32 additions & 26 deletions packages/gatsby-transformer-json/src/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,54 @@
const _ = require(`lodash`)
const crypto = require(`crypto`)
const path = require(`path`)

async function onCreateNode({ node, boundActionCreators, loadNodeContent }) {
const { createNode, createParentChildLink } = boundActionCreators

// We only care about JSON content.
if (node.internal.mediaType !== `application/json`) {
return
}

const content = await loadNodeContent(node)
const parsedContent = JSON.parse(content)

// TODO handle non-array data.
if (_.isArray(parsedContent)) {
const JSONArray = parsedContent.map((obj, i) => {
function transformObject(obj, id, type) {
const objStr = JSON.stringify(obj)
const contentDigest = crypto
.createHash(`md5`)
.update(objStr)
.digest(`hex`)

return {
const jsonNode = {
...obj,
id: obj.id ? obj.id : `${node.id} [${i}] >>> JSON`,
id,
children: [],
parent: node.id,
internal: {
contentDigest,
// TODO make choosing the "type" a lot smarter. This assumes
// the parent node is a file.
// PascalCase
type: _.upperFirst(_.camelCase(`${node.name} Json`)),
type,
},
}
})
createNode(jsonNode)
createParentChildLink({ parent: node, child: jsonNode })
}

_.each(JSONArray, j => {
createNode(j)
createParentChildLink({ parent: node, child: j })
})
const { createNode, createParentChildLink } = boundActionCreators

// We only care about JSON content.
if (node.internal.mediaType !== `application/json`) {
return
}

return
const content = await loadNodeContent(node)
const parsedContent = JSON.parse(content)

if (_.isArray(parsedContent)) {
parsedContent.forEach((obj, i) => {
transformObject(
obj,
obj.id ? obj.id : `${node.id} [${i}] >>> JSON`,
_.upperFirst(_.camelCase(`${node.name} Json`)),
)
})
}
else if (_.isPlainObject(parsedContent)) {
transformObject(
parsedContent,
parsedContent.id ? parsedContent.id : `${node.id} >>> JSON`,
_.upperFirst(_.camelCase(`${path.basename(node.dir)} Json`)),
)
}
}

exports.onCreateNode = onCreateNode
62 changes: 58 additions & 4 deletions packages/gatsby-transformer-yaml/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# gatsby-transformer-yaml

Parses YAML files. Currently it only handles YAML arrays but the
goal is to handle in a reasonable way many types of YAML data.
Parses YAML files. Supports arrays of objects and single objects.

## Install

Expand All @@ -18,8 +17,13 @@ plugins: [

## Parsing algorithm

You can choose to structure your data as arrays of objects in individual files
or as single objects spread across multiple files.

### Array of Objects

The algorithm for YAML arrays is to convert each item in the array into
a node.
a node. The type of the node is based on the filename.

So if your project has a `letters.yaml` which looks like:

Expand Down Expand Up @@ -48,9 +52,59 @@ Then the following three nodes would be created.
]
```

### Single Object

The algorithm for single YAML objects is to convert the object defined at the
root of the file into a node. The type of the node is based on the name of the
parent directory.

For example, lets say your project has a data layout like:

```
data/
letters/
a.yml
b.yml
c.yml
```

Where each of `a.yml`, `b.yml` and `c.yml` look like:

```yaml
value: a
```

```yaml
value: b
```

```yaml
value: c
```

Then the following three nodes would be created.

```javascript
[
{
value: 'a',
type: 'Letters',
},
{
value: 'b',
type: 'Letters',
},
{
value: 'c',
type: 'Letters',
},
]
```

## How to query

You'd be able to query your letters like:
Regardless of whether you choose to structure your data in arrays of objects or
single objects, you'd be able to query your letters like:

```graphql
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,57 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Process YAML nodes correctly correctly creates a node from JSON which is a single object 1`] = `
Array [
Array [
Object {
"blue": true,
"children": Array [],
"funny": "yup",
"id": "whatever >>> YAML",
"internal": Object {
"contentDigest": "73901821b17d5aa9dd6026181f73b64c",
"type": "BarYaml",
},
"parent": "whatever",
},
],
]
`;

exports[`Process YAML nodes correctly correctly creates a node from JSON which is a single object 2`] = `
Array [
Array [
Object {
"child": Object {
"blue": true,
"children": Array [],
"funny": "yup",
"id": "whatever >>> YAML",
"internal": Object {
"contentDigest": "73901821b17d5aa9dd6026181f73b64c",
"type": "BarYaml",
},
"parent": "whatever",
},
"parent": Object {
"children": Array [],
"content": "blue: true
funny: yup
",
"dir": "/tmp/bar/",
"id": "whatever",
"internal": Object {
"contentDigest": "whatever",
"mediaType": "text/yaml",
},
"name": "test",
"parent": "SOURCE",
},
},
],
]
`;

exports[`Process YAML nodes correctly correctly creates nodes from JSON which is an array of objects 1`] = `
Array [
Array [
Expand Down
Loading