Skip to content
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

Upgrade to acorn 6 #112

Merged
merged 2 commits into from
Nov 29, 2018
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
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@
"tasks"
],
"dependencies": {
"acorn-jsx": "^4.1.1",
"acorn-jsx-walk": "^1.0.1",
"acorn": "^6.0.4",
"acorn-jsx": "^5.0.1",
"acorn-stage3": "^1.0.0",
"acorn-walk": "^6.1.1",
"chalk": "^2.4.1",
"clone-deep": "^4.0.0",
"commander": "^2.15.1",
Expand Down
62 changes: 62 additions & 0 deletions src/acorn-jsx-walk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Originally from: https://github.com/sderosiaux/acorn-jsx-walk

import { Parser } from 'acorn';
import { simple as walk, base } from 'acorn-walk';
import jsx from 'acorn-jsx';
import stage3 from 'acorn-stage3';

//
// Extends acorn walk with JSX elements
//

// See: https://github.com/RReverser/acorn-jsx/issues/23#issuecomment-403753801
Object.assign(base, {
FieldDefinition(node, state, callback) {
if (node.value !== null) {
callback(node.value, state);
}
},

JSXAttribute(node, state, callback) {
if (node.value !== null) {
callback(node.value, state);
}
},

JSXElement(node, state, callback) {
node.openingElement.attributes.forEach(attribute => {
callback(attribute, state);
});
node.children.forEach(node => {
callback(node, state);
});
},

JSXEmptyExpression(node, state, callback) {
// Comments. Just ignore.
},

JSXExpressionContainer(node, state, callback) {
callback(node.expression, state);
},

JSXFragment(node, state, callback) {
node.children.forEach(node => {
callback(node, state);
});
},

JSXSpreadAttribute(node, state, callback) {
callback(node.argument, state);
},

JSXText() {}
});

export default (source, options) => {
const ast = Parser.extend(stage3, jsx()).parse(source, {
sourceType: 'module',
ecmaVersion: 10,
});
walk(ast, options || {});
};
2 changes: 1 addition & 1 deletion src/parser.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint no-console: 0 */
/* eslint no-eval: 0 */
import fs from 'fs';
import jsxwalk from 'acorn-jsx-walk';
import chalk from 'chalk';
import cloneDeep from 'clone-deep';
import deepMerge from 'deepmerge';
Expand All @@ -10,6 +9,7 @@ import { parse } from 'esprima';
import _ from 'lodash';
import parse5 from 'parse5';
import sortObject from 'sortobject';
import jsxwalk from './acorn-jsx-walk';
import flattenObjectKeys from './flatten-object-keys';
import omitEmptyObject from './omit-empty-object';
import nodesToString from './nodes-to-string';
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ var msg = [
_t('YouTube has more than {{count}} billion users.', {count: 1}),
_t('You have {{count}} messages.', {
count: 10
});
}),
].join('\n');

console.log(msg);
11 changes: 11 additions & 0 deletions test/fixtures/trans-acorn-broken.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Fragment } from 'react';

// Provoke an acorn parsing error by inserting a `async` keyword in a wrong place. This should lead
// to no translateions being extracted from this file.
const async Component = () => (
<Fragment>
<Trans>Broken</Trans>
</Fragment>
);

export default Component;
37 changes: 37 additions & 0 deletions test/fixtures/trans-acorn.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Fragment } from 'react';

class Component extends React.Component {
// noop just to see if acorn can parse this
state = {
};

static async onClick(
a,
b,
...args
) {
console.log(a, b, ...args);
// noop just to see if acorn can parse this
}

render() {
// This does not work yet.
const spreadProps = {
i18nKey: 'spread',
};

return (
<Fragment>
{
// Empty expression should not fail
}
<>
<Trans i18nKey="simple">Simple i18nKey</Trans>
<Trans {...spreadProps}>Spread i18nKey</Trans>
</>
</Fragment>
);
}
}

export default Component;
3 changes: 3 additions & 0 deletions test/fixtures/trans.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Fragment } from 'react';

const Component = () => (
<Fragment>
{
// Empty expression should not fail
}
<Fragment>
<Trans i18nKey="jsx-quotes-double">Use double quotes for the i18nKey attribute</Trans>
<Trans i18nKey='jsx-quotes-single'>Use single quote for the i18nKey attribute</Trans>
Expand Down
7 changes: 3 additions & 4 deletions test/jsx-parser.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { test } from 'tap';
import { parse } from 'acorn-jsx';
import { Parser } from 'acorn';
import jsx from 'acorn-jsx';
import ensureArray from 'ensure-array';
import _get from 'lodash/get';
import nodesToString from '../src/nodes-to-string';

const jsxToString = (code) => {
try {
const ast = parse(`<Trans>${code}</Trans>`, {
plugins: { jsx: true }
});
const ast = Parser.extend(jsx()).parse(`<Trans>${code}</Trans>`);

const nodes = ensureArray(_get(ast, 'body[0].expression.children'));
if (nodes.length === 0) {
Expand Down
47 changes: 47 additions & 0 deletions test/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,53 @@ test('Parse wrapped Trans components', (t) => {
t.end();
});

test('Parse Trans components with modern acorn features', (t) => {
const parser = new Parser({
lngs: ['en'],
trans: {
fallbackKey: true
},
nsSeparator: false,
keySeparator: '.', // Specify the keySeparator for this test to make sure the fallbackKey won't be separated
fallbackLng: 'en'
});

const content = fs.readFileSync(path.resolve(__dirname, 'fixtures/trans-acorn.jsx'), 'utf-8');
parser.parseTransFromString(content);
t.same(parser.get(), {
en: {
translation: {
// Passing keys to <Trans> via object spread is not yet supported:
'Spread i18nKey': 'Spread i18nKey',
// 'spread': 'Spread i18nKey', // this would be expected.
'simple': 'Simple i18nKey'
}
}
});
t.end();
});

test('Parse Trans components should fail with broken syntax', (t) => {
const parser = new Parser({
lngs: ['en'],
trans: {
fallbackKey: true
},
nsSeparator: false,
keySeparator: '.', // Specify the keySeparator for this test to make sure the fallbackKey won't be separated
fallbackLng: 'en'
});

const content = fs.readFileSync(path.resolve(__dirname, 'fixtures/trans-acorn-broken.jsx'), 'utf-8');
parser.parseTransFromString(content);
t.same(parser.get(), {
en: {
translation: {}
}
});
t.end();
});

test('Parse HTML attribute', (t) => {
test('parseAttrFromString(content)', (t) => {
const parser = new Parser({
Expand Down