-
Notifications
You must be signed in to change notification settings - Fork 22
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
Space around class_list #418
base: main
Are you sure you want to change the base?
Changes from 4 commits
120e0fd
5a96df0
c976d9b
9f611e1
6e6394d
5af3b5d
b3c9033
95be3c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { expect, describe, it } from 'vitest'; | ||
import { SpaceAfterClassList } from '.'; | ||
import { runLiquidCheck, highlightedOffenses } from '../../test'; | ||
|
||
describe('Module: SpaceAfterClassList', () => { | ||
it('allows the happy path', async () => { | ||
const file = `<div class="{{ block.settings | class_list }}"> | ||
content | ||
</div> | ||
`; | ||
|
||
const offenses = await runLiquidCheck(SpaceAfterClassList, file); | ||
|
||
expect(offenses).to.have.length(0); | ||
}); | ||
|
||
it('should report an offense when missing a space', async () => { | ||
const file = `<div class="{{ block.settings | class_list }}other-class"> | ||
content | ||
</div> | ||
`; | ||
|
||
const offenses = await runLiquidCheck(SpaceAfterClassList, file); | ||
|
||
expect(offenses).to.have.length(1); | ||
expect(offenses[0].message).toEqual( | ||
`Missing a space after using the class_list filter: 'block.settings | class_list }}other-class'`, | ||
); | ||
|
||
const highlights = highlightedOffenses({ 'file.liquid': file }, offenses); | ||
expect(highlights).toEqual(['o']); | ||
}); | ||
|
||
it('supports classes on new lines', async () => { | ||
const file = `<div class=" | ||
{{ block.settings | class_list }}other-class"> | ||
content | ||
</div> | ||
`; | ||
|
||
const offenses = await runLiquidCheck(SpaceAfterClassList, file); | ||
|
||
expect(offenses).to.have.length(1); | ||
expect(offenses[0].message).toEqual( | ||
`Missing a space after using the class_list filter: 'block.settings | class_list }}other-class'`, | ||
); | ||
|
||
const highlights = highlightedOffenses({ 'file.liquid': file }, offenses); | ||
expect(highlights).toEqual(['o']); | ||
}); | ||
|
||
it('supports arbitrary whitespace', async () => { | ||
const file = `<div class="{{block.settings| class_list}}other-class"> | ||
content | ||
</div> | ||
`; | ||
|
||
const offenses = await runLiquidCheck(SpaceAfterClassList, file); | ||
|
||
expect(offenses).to.have.length(1); | ||
expect(offenses[0].message).toEqual( | ||
`Missing a space after using the class_list filter: 'block.settings| class_list}}other-class'`, | ||
); | ||
|
||
const highlights = highlightedOffenses({ 'file.liquid': file }, offenses); | ||
expect(highlights).toEqual(['o']); | ||
}); | ||
|
||
it('catches between multiple class_list filters', async () => { | ||
const file = `<div class=" | ||
{{ block.settings.spacing | class_list }}oh-no | ||
{{ block.settings.typography | class_list }}"> | ||
content | ||
</div> | ||
`; | ||
|
||
const offenses = await runLiquidCheck(SpaceAfterClassList, file); | ||
expect(offenses).to.have.length(1); | ||
|
||
expect(offenses[0].message).toEqual( | ||
`Missing a space after using the class_list filter: 'block.settings.spacing | class_list }}oh-no'`, | ||
); | ||
|
||
const highlights = highlightedOffenses({ 'file.liquid': file }, offenses); | ||
expect(highlights).toEqual(['o']); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { NodeTypes } from '@shopify/liquid-html-parser'; | ||
import { LiquidCheckDefinition, Severity, SourceCodeType } from '../../types'; | ||
|
||
export const SpaceAfterClassList: LiquidCheckDefinition = { | ||
meta: { | ||
code: 'SpaceAfterClassList', | ||
aliases: ['SpaceAfterClassList'], | ||
name: 'Space After Class List', | ||
docs: { | ||
description: 'Warns you when there is no space after using the class_list filter', | ||
recommended: true, | ||
url: 'https://shopify.dev/docs/themes/tools/theme-check/checks/space-after-class_list', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there another PR I can check out for this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. knew I was forgetting something! I'll see how to add that one sec There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
}, | ||
type: SourceCodeType.LiquidHtml, | ||
severity: Severity.ERROR, | ||
geddski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
schema: {}, | ||
targets: [], | ||
}, | ||
|
||
create(context) { | ||
return { | ||
async LiquidFilter(node, ancestors) { | ||
if (node.name !== 'class_list') { | ||
return; | ||
} | ||
|
||
const classAttribute = ancestors.find( | ||
(ancestor) => | ||
ancestor.type === NodeTypes.AttrDoubleQuoted || | ||
ancestor.type === NodeTypes.AttrSingleQuoted, | ||
); | ||
|
||
if (!classAttribute) { | ||
return; | ||
} | ||
|
||
const classAttributeContent = | ||
classAttribute.source.slice(classAttribute.position.start, classAttribute.position.end) || | ||
''; | ||
|
||
const regex = /([a-zA-Z0-9._-]+)\s*\|\s*class_list\s*}}([a-zA-Z0-9._-]+)/gm; | ||
|
||
const matches = [...classAttributeContent.matchAll(regex)]; | ||
|
||
for (const match of matches) { | ||
if (match.index === undefined) { | ||
continue; | ||
} | ||
|
||
const liquidVariable = ancestors.find( | ||
(ancestor) => ancestor.type === NodeTypes.LiquidVariable, | ||
); | ||
const liquidVariableContent = | ||
liquidVariable?.source.slice( | ||
liquidVariable.position.start, | ||
liquidVariable.position.end, | ||
) || ''; | ||
const styleSetting = liquidVariableContent.split('|')[0]?.trim(); | ||
|
||
if (styleSetting !== match[1]) { | ||
continue; | ||
} | ||
|
||
const bracketIndex = match[0].indexOf('}}'); | ||
const errorPosition = classAttribute.position.start + match.index + bracketIndex + 2; | ||
|
||
context.report({ | ||
message: `Missing a space after using the class_list filter: '${match[0]}'`, | ||
startIndex: errorPosition, | ||
endIndex: errorPosition + 1, | ||
suggest: [ | ||
{ | ||
message: 'Add a space after the class_list filter', | ||
fix(corrector) { | ||
corrector.insert(errorPosition, ' '); | ||
}, | ||
}, | ||
], | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is general contribution docs, we should probably be a bit more specific about what this instruction is alluding to.