Skip to content

Commit c4489e6

Browse files
authored
Throw an error if a 'data' Liquid tag fails (#25730)
1 parent 0f97070 commit c4489e6

8 files changed

Lines changed: 100 additions & 10 deletions

File tree

lib/liquid-tags/data.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { TokenizationError } from 'liquidjs'
22

3+
import { THROW_ON_EMPTY, DataReferenceError } from './error-handling.js'
4+
35
const Syntax = /([a-z0-9/\\_.\-[\]]+)/i
46
const SyntaxHelp = "Syntax Error in 'data' - Valid syntax: data [path]"
57

@@ -14,7 +16,14 @@ export default {
1416

1517
async render(scope) {
1618
const value = await this.liquid.evalValue(`site.data.${this.path}`, scope)
17-
if (typeof value !== 'string') return value
19+
if (typeof value !== 'string') {
20+
const message = `Can't find the key 'site.data.${this.path}' in the scope.`
21+
if (THROW_ON_EMPTY) {
22+
throw new DataReferenceError(message)
23+
}
24+
console.warn(message)
25+
return
26+
}
1827
return this.liquid.parseAndRender(value, scope.environments)
1928
},
2029
}

lib/liquid-tags/error-handling.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// If 'THROW_ON_EMPTY' is set and it's value is '0' or 'false' it becomes
2+
// false. Or true if it's 'true' or '1'.
3+
export const THROW_ON_EMPTY = Boolean(
4+
process.env.THROW_ON_EMPTY
5+
? JSON.parse(process.env.THROW_ON_EMPTY)
6+
: JSON.parse(process.env.CI || process.env.NODE_ENV !== 'production')
7+
)
8+
9+
export class DataReferenceError extends Error {}
10+
export class IndentedDataReferenceError extends DataReferenceError {}

lib/liquid-tags/indented-data-reference.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import assert from 'assert'
22

3-
// If 'THROW_ON_EMPTY' is set and it's value is '0' or 'false' it becomes
4-
// false. Or true if it's 'true' or '1'.
5-
const THROW_ON_EMPTY = Boolean(
6-
process.env.THROW_ON_EMPTY
7-
? JSON.parse(process.env.THROW_ON_EMPTY)
8-
: JSON.parse(process.env.CI || process.env.NODE_ENV !== 'production')
9-
)
10-
11-
class IndentedDataReferenceError extends Error {}
3+
import { THROW_ON_EMPTY, IndentedDataReferenceError } from './error-handling.js'
124

135
// This class supports a tag that expects two parameters, a data reference and `spaces=NUMBER`:
146
//
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
title: Sample page
3+
versions: '*'
4+
---
5+
6+
{% data foo.bar.tipu %}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
title: Good sample page
3+
versions: '*'
4+
---
5+
6+
{% data variables.foo %}

tests/unit/liquid-tags/data.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { expect } from '@jest/globals'
2+
import path from 'path'
3+
import Page from '../../../lib/page.js'
4+
import nonEnterpriseDefaultVersion from '../../../lib/non-enterprise-default-version.js'
5+
import { fileURLToPath } from 'url'
6+
7+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
8+
9+
describe('data tag', () => {
10+
it('should render fine if data is found', async () => {
11+
const page = await Page.init({
12+
relativePath: 'liquid-tags/good-data-variable.md',
13+
basePath: path.join(__dirname, '../../fixtures'),
14+
languageCode: 'en',
15+
})
16+
const context = {
17+
currentVersion: nonEnterpriseDefaultVersion,
18+
currentLanguage: 'en',
19+
currentPath: '/en/liquid-tags/good-data-variable',
20+
}
21+
const rendered = await page.render(
22+
Object.assign(
23+
{
24+
site: {
25+
data: {
26+
variables: {
27+
foo: 'Foo',
28+
},
29+
},
30+
},
31+
},
32+
context
33+
)
34+
)
35+
// The test fixture contains:
36+
// {% data variables.foo %}
37+
// which we control the value of here in the test.
38+
expect(rendered.includes('Foo')).toBeTruthy()
39+
})
40+
it('should throw if the data tag is used with something unrecognized', async () => {
41+
const page = await Page.init({
42+
relativePath: 'liquid-tags/bad-data-variable.md',
43+
basePath: path.join(__dirname, '../../fixtures'),
44+
languageCode: 'en',
45+
})
46+
const context = {
47+
currentPath: '/en/liquid-tags/bad-data-variable',
48+
}
49+
await expect(page.render(context)).rejects.toThrow(
50+
"Can't find the key 'site.data.foo.bar.tipu' in the scope., line:2, col:1"
51+
)
52+
})
53+
})

tests/unit/page.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import readJsonFile from '../../lib/read-json-file.js'
88
import { allVersions } from '../../lib/all-versions.js'
99
import enterpriseServerReleases, { latest } from '../../lib/enterprise-server-releases.js'
1010
import nonEnterpriseDefaultVersion from '../../lib/non-enterprise-default-version.js'
11+
import loadSiteData from '../../lib/site-data.js'
1112

1213
const __dirname = path.dirname(fileURLToPath(import.meta.url))
1314
const prerenderedObjects = readJsonFile('./lib/graphql/static/prerendered-objects.json')
@@ -80,6 +81,8 @@ describe('Page class', () => {
8081
})
8182

8283
describe('page.render(context)', () => {
84+
const siteData = loadSiteData()
85+
8386
test('rewrites links to include the current language prefix and version', async () => {
8487
const page = await Page.init(opts)
8588
const context = {
@@ -88,6 +91,7 @@ describe('Page class', () => {
8891
currentPath:
8992
'/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches',
9093
currentLanguage: 'en',
94+
site: siteData.en.site,
9195
}
9296
const rendered = await page.render(context)
9397
const $ = cheerio.load(rendered)
@@ -126,6 +130,7 @@ describe('Page class', () => {
126130
currentVersion: `enterprise-server@${enterpriseServerReleases.latest}`,
127131
currentPath: '/en/page-with-deprecated-enterprise-links',
128132
currentLanguage: 'en',
133+
site: siteData.en.site,
129134
}
130135
const rendered = await page.render(context)
131136
// That page only contains exactly 2 links. And we can know
@@ -161,6 +166,7 @@ describe('Page class', () => {
161166
currentPath:
162167
'/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches',
163168
currentLanguage: 'en',
169+
site: siteData.en.site,
164170
}
165171
// This is needed because unit tests are weird. The page.render()
166172
// method is dependent on module global cache.
@@ -192,6 +198,7 @@ describe('Page class', () => {
192198
currentVersion: `enterprise-server@${enterpriseServerReleases.latest}`,
193199
currentPath: `/en/enterprise-server@${enterpriseServerReleases.latest}/admin/enterprise-management/migrating-from-github-enterprise-1110x-to-2123`,
194200
currentLanguage: 'en',
201+
site: siteData.en.site,
195202
}
196203
const rendered = await page.render(context)
197204
const $ = cheerio.load(rendered)
@@ -219,6 +226,7 @@ describe('Page class', () => {
219226
currentVersion: nonEnterpriseDefaultVersion,
220227
currentPath: `/en/${nonEnterpriseDefaultVersion}/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches`,
221228
currentLanguage: 'en',
229+
site: siteData.en.site,
222230
}
223231
const rendered = await page.render(context)
224232
const $ = cheerio.load(rendered)
@@ -239,6 +247,7 @@ describe('Page class', () => {
239247
currentVersion: `enterprise-server@${enterpriseServerReleases.latest}`,
240248
currentLanguage: 'en',
241249
enterpriseServerVersions,
250+
site: siteData.en.site,
242251
}
243252
context.currentPath = `/${context.currentLanguage}/${context.currentVersion}/${page.relativePath}`
244253
let rendered = await page.render(context)
@@ -282,6 +291,7 @@ describe('Page class', () => {
282291
const context = {
283292
currentVersion: 'enterprise-server@3.0',
284293
currentLanguage: 'en',
294+
site: siteData.en.site,
285295
}
286296
await expect(() => {
287297
return page.render(context)

tests/unit/toc-links.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ import { jest } from '@jest/globals'
22
import { loadPages } from '../../lib/page-data.js'
33
import renderContent from '../../lib/render-content/index.js'
44
import { allVersionKeys } from '../../lib/all-versions.js'
5+
import loadSiteData from '../../lib/site-data.js'
56

67
describe('toc links', () => {
78
jest.setTimeout(3 * 60 * 1000)
89

10+
const siteData = loadSiteData()
11+
912
test('every toc link works without redirects', async () => {
1013
const pages = await loadPages()
1114

@@ -27,6 +30,7 @@ describe('toc links', () => {
2730
redirects: {},
2831
currentLanguage: 'en',
2932
currentVersion: pageVersion,
33+
site: siteData.en.site,
3034
}
3135

3236
// ensure all toc pages can render

0 commit comments

Comments
 (0)