Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from EECOLOR/jsx-spread-object-expression
support retrieving props from a spread with object expression
- Loading branch information
Showing
7 changed files
with
268 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
{ | ||
extends: "airbnb-base" | ||
extends: "airbnb-base", | ||
rules: { | ||
no-use-before-define: ["error", { functions: false }] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/* eslint-env mocha */ | ||
import assert from 'assert'; | ||
import entries from 'object.entries'; | ||
import fromEntries from 'object.fromentries'; | ||
import { getOpeningElement, setParserName, fallbackToBabylon } from '../helper'; | ||
import getProp from '../../src/getProp'; | ||
|
||
const literal = { | ||
source: '<div {...{ id: "foo" }} />', | ||
target: '<div id="foo" />', | ||
offset: { keyOffset: -6, valueOffset: -7 }, | ||
}; | ||
|
||
const expression1 = { | ||
source: '<div {...{ id }} />', | ||
target: '<div id={id} />', | ||
offset: { keyOffset: -6, valueOffset: -2 }, | ||
}; | ||
|
||
const expression2 = { | ||
source: '<div {...{ id: `foo${bar}baz` }} />', // eslint-disable-line no-template-curly-in-string | ||
target: '<div id={`foo${bar}baz`} />', // eslint-disable-line no-template-curly-in-string | ||
offset: { keyOffset: -6, valueOffset: -6 }, | ||
}; | ||
|
||
describe('getProp', () => { | ||
it('should create the correct AST for literal with flow parser', () => { | ||
actualTest('flow', literal); | ||
}); | ||
it('should create the correct AST for literal with babel parser', () => { | ||
actualTest('babel', literal); | ||
}); | ||
it('should create the correct AST for expression with flow parser (1)', () => { | ||
actualTest('flow', expression1); | ||
}); | ||
it('should create the correct AST for expression with babel parser (1)', () => { | ||
actualTest('babel', expression1); | ||
}); | ||
it('should create the correct AST for expression with flow parser (2)', () => { | ||
actualTest('flow', expression2); | ||
}); | ||
it('should create the correct AST for expression with babel parser (2)', () => { | ||
actualTest('babel', expression2); | ||
}); | ||
}); | ||
|
||
function actualTest(parserName, test) { | ||
setParserName(parserName); | ||
const { source, target, offset } = test; | ||
const sourceProps = stripConstructors(getOpeningElement(source).attributes); | ||
const targetProps = stripConstructors(getOpeningElement(target).attributes); | ||
const prop = 'id'; | ||
const sourceResult = getProp(sourceProps, prop); | ||
const targetResult = getProp(targetProps, prop); | ||
|
||
if (fallbackToBabylon && parserName === 'babel' && test === literal) { | ||
// Babylon (node < 6) adds an `extra: null` prop to a literal if it is parsed from a | ||
// JSXAttribute, other literals don't get this. | ||
sourceResult.value.extra = null; | ||
} | ||
|
||
assert.deepStrictEqual( | ||
adjustLocations(sourceResult, offset), | ||
targetResult, | ||
); | ||
} | ||
|
||
function stripConstructors(value) { | ||
return JSON.parse(JSON.stringify(value)); | ||
} | ||
|
||
function adjustLocations(node, { keyOffset, valueOffset }) { | ||
const hasExpression = !!node.value.expression; | ||
return { | ||
...adjustNodeLocations(node, { | ||
startOffset: keyOffset, | ||
endOffset: valueOffset + (hasExpression ? 1 : 0), | ||
}), | ||
name: adjustNodeLocations(node.name, { startOffset: keyOffset, endOffset: keyOffset }), | ||
value: { | ||
...adjustNodeLocations(node.value, { | ||
startOffset: valueOffset - (hasExpression ? 1 : 0), | ||
endOffset: valueOffset + (hasExpression ? 1 : 0), | ||
}), | ||
...(hasExpression | ||
? { | ||
expression: adjustLocationsRecursively( | ||
node.value.expression, | ||
{ startOffset: valueOffset, endOffset: valueOffset }, | ||
), | ||
} | ||
: {} | ||
), | ||
}, | ||
}; | ||
} | ||
|
||
function adjustNodeLocations(node, { startOffset, endOffset }) { | ||
if (!node.loc) return node; | ||
const [start, end] = node.range || []; | ||
return { | ||
...node, | ||
...(node.start !== undefined ? { start: node.start + startOffset } : {}), | ||
...(node.end !== undefined ? { end: node.end + endOffset } : {}), | ||
loc: { | ||
...node.loc, | ||
start: { | ||
...node.loc.start, | ||
column: node.loc.start.column + startOffset, | ||
}, | ||
end: { | ||
...node.loc.end, | ||
column: node.loc.end.column + endOffset, | ||
}, | ||
}, | ||
...(node.range !== undefined ? { range: [start + startOffset, end + endOffset] } : {}), | ||
}; | ||
} | ||
|
||
function adjustLocationsRecursively(node, { startOffset, endOffset }) { | ||
if (Array.isArray(node)) { | ||
return node.map(x => adjustLocationsRecursively(x, { startOffset, endOffset })); | ||
} | ||
if (node && typeof node === 'object') { | ||
return adjustNodeLocations( | ||
mapValues(node, x => adjustLocationsRecursively(x, { startOffset, endOffset })), | ||
{ startOffset, endOffset }, | ||
); | ||
} | ||
|
||
return node; | ||
} | ||
|
||
function mapValues(o, f) { | ||
return fromEntries(entries(o).map(([k, v]) => [k, f(v)])); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters