Skip to content

Commit

Permalink
Enables jsx plugin in case jsx syntax is used in js files (#2530)
Browse files Browse the repository at this point in the history
* Enables jsx plugin in case jsx syntax is used in js files

* Consider pragma when enabling JSX in .js files

* Add tests for JSX support in JS even if no dependencies are specified

* Make comment more clear

* Cache regex creation
  • Loading branch information
lustoykov authored and DeMoorJasper committed Feb 8, 2019
1 parent bdef68b commit f23c844
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 0 deletions.
@@ -0,0 +1,3 @@
import * as Hyperapp from 'hyperapp'

module.exports = <div />;
@@ -0,0 +1,3 @@
{
"private": true
}
@@ -0,0 +1,3 @@
import * as Nerv from 'nervjs';

module.exports = <div />;
@@ -0,0 +1,3 @@
{
"private": true
}
@@ -0,0 +1,3 @@
const Preact = require('preact');

module.exports = <div />;
@@ -0,0 +1,3 @@
{
"private": true
}
@@ -0,0 +1,3 @@
import * as React from 'react';

module.exports = <div />;
@@ -0,0 +1,3 @@
{
"private": true
}
89 changes: 89 additions & 0 deletions packages/core/integration-tests/test/javascript.js
Expand Up @@ -1390,6 +1390,28 @@ describe('javascript', function() {
assert(file.includes('React.createElement("div"'));
});

it('should support compiling JSX in JS files with React dependency even if React is not specified as dependency', async function() {
let originalPkg = await fs.readFile(
__dirname + '/integration/jsx-react-no-dep/package.json'
);

await bundle(
path.join(__dirname, '/integration/jsx-react-no-dep/index.js')
);

let file = await fs.readFile(
path.join(__dirname, '/dist/index.js'),
'utf8'
);

assert(file.includes('React.createElement("div"'));

await fs.writeFile(
__dirname + '/integration/jsx-react-no-dep/package.json',
originalPkg
);
});

it('should support compiling JSX in JS files with Preact dependency', async function() {
await bundle(path.join(__dirname, '/integration/jsx-preact/index.js'));

Expand All @@ -1400,6 +1422,28 @@ describe('javascript', function() {
assert(file.includes('h("div"'));
});

it('should support compiling JSX in JS files with Preact dependency even if Preact is not specified as dependency', async function() {
let originalPkg = await fs.readFile(
__dirname + '/integration/jsx-preact-no-dep/package.json'
);

await bundle(
path.join(__dirname, '/integration/jsx-preact-no-dep/index.js')
);

let file = await fs.readFile(
path.join(__dirname, '/dist/index.js'),
'utf8'
);

assert(file.includes('h("div"'));

await fs.writeFile(
__dirname + '/integration/jsx-preact-no-dep/package.json',
originalPkg
);
});

it('should support compiling JSX in JS files with Nerv dependency', async function() {
await bundle(path.join(__dirname, '/integration/jsx-nervjs/index.js'));

Expand All @@ -1410,14 +1454,59 @@ describe('javascript', function() {
assert(file.includes('Nerv.createElement("div"'));
});

it('should support compiling JSX in JS files with Nerv dependency even if Nerv is not specified as dependency', async function() {
let originalPkg = await fs.readFile(
__dirname + '/integration/jsx-nervjs-no-dep/package.json'
);

await bundle(
path.join(__dirname, '/integration/jsx-nervjs-no-dep/index.js')
);

let file = await fs.readFile(
path.join(__dirname, '/dist/index.js'),
'utf8'
);

assert(file.includes('Nerv.createElement("div"'));

await fs.writeFile(
__dirname + '/integration/jsx-nervjs-no-dep/package.json',
originalPkg
);
});

it('should support compiling JSX in JS files with Hyperapp dependency', async function() {
await bundle(path.join(__dirname, '/integration/jsx-hyperapp/index.js'));

let file = await fs.readFile(
path.join(__dirname, '/dist/index.js'),
'utf8'
);

assert(file.includes('h("div"'));
});

it('should support compiling JSX in JS files with Hyperapp dependency even if Hyperapp is not specified as dependency', async function() {
let originalPkg = await fs.readFile(
__dirname + '/integration/jsx-hyperapp-no-dep/package.json'
);

await bundle(
path.join(__dirname, '/integration/jsx-hyperapp-no-dep/index.js')
);

let file = await fs.readFile(
path.join(__dirname, '/dist/index.js'),
'utf8'
);

assert(file.includes('h("div"'));

await fs.writeFile(
__dirname + '/integration/jsx-hyperapp-no-dep/package.json',
originalPkg
);
});

it('should support optional dependencies in try...catch blocks', async function() {
Expand Down
32 changes: 32 additions & 0 deletions packages/core/parcel-bundler/src/transforms/babel/jsx.js
Expand Up @@ -12,6 +12,34 @@ const JSX_PRAGMA = {
hyperapp: 'h'
};

function createJSXRegexFor(dependency) {
// result looks like /from\s+[`"']react[`"']|require\([`"']react[`"']\)/
return new RegExp(
`from\\s+[\`"']${dependency}[\`"']|require\\([\`"']${dependency}[\`"']\\)`
);
}

/**
* Solves a use case when JSX is used in .js files, but
* package.json is empty or missing yet and therefore pragma cannot
* be determined based on pkg.dependencies / pkg.devDependencies
*/
const cacheJsxRegexFor = {};
function maybeCreateFallbackPragma(asset) {
for (const dep in JSX_PRAGMA) {
let regex = cacheJsxRegexFor[dep];

if (!regex) {
regex = createJSXRegexFor(dep);
cacheJsxRegexFor[dep] = regex;
}

if (asset.contents.match(regex)) {
return JSX_PRAGMA[dep];
}
}
}

/**
* Generates a babel config for JSX. Attempts to detect react or react-like libraries
* and changes the pragma accordingly.
Expand All @@ -37,6 +65,10 @@ async function getJSXConfig(asset, isSourceModule) {
}
}

if (!pragma) {
pragma = maybeCreateFallbackPragma(asset);
}

if (pragma || JSX_EXTENSIONS[path.extname(asset.name)]) {
return {
internal: true,
Expand Down

0 comments on commit f23c844

Please sign in to comment.