Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Make interpolate-components smaller. #14

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"presets": [
"react",
"es2015"
"@babel/react",
"@babel/env"
]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules
.DS_Store
lib/
.idea/
.build/
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ src/
/npm-debug.log*
.DS_Store
.idea/
.build/
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 2.0.0
* Breaking change: require React ^16.2.0 (for native Fragment support)
* Breaking change: `react` and `react-com` are now `peerDependencies`.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Breaking change: `react` and `react-com` are now `peerDependencies`.
* Breaking change: `react` and `react-dom` are now `peerDependencies`.

* Drop deprecated react-addons-create-fragment from library
* Update to Babel 7
* Add Terser to build pipeline for minification

## 1.1.1
* Drop deprecated React.createClass, React.DOM from test
* Bump to allow for React ^16.0.0
Expand Down
13 changes: 10 additions & 3 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
machine:
node:
version: 5.11.1
version: 2.1

jobs:
build:
docker:
- image: node:lts-slim
steps:
- checkout
- npm install
- npm run test
41 changes: 25 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
{
"name": "interpolate-components",
"version": "1.1.1",
"version": "2.0.0",
"description": "Convert strings into structured React components.",
"repository": {
"type": "git",
"url": "git+https://github.com/Automattic/interpolate-components.git"
},
"main": "lib/index.js",
"main": "lib/index.min.js",
"keywords": [
"react",
"react-component",
"interpolation"
],
"scripts": {
"compile": "babel -sd lib/ src/",
"prepublish": "npm run compile",
"test": "mocha --require babel-register --reporter spec test/test.jsx"
"clean": "rimraf .build lib",
"compile": "babel --source-maps=inline -d .build/ src/",
"minify:index": "terser .build/index.js --compress --mangle --source-map 'content=inline' -o lib/index.min.js",
"minify:tokenize": "terser .build/tokenize.js --compress --mangle --source-map 'content=inline' -o lib/tokenize.min.js",
"minify": "mkdirp lib && npm run minify:index && npm run minify:tokenize",
"build": "npm run clean && npm run compile && npm run minify",
"prepare": "npm run build",
"test": "mocha --require @babel/register --reporter spec test/test.jsx"
},
"devDependencies": {
"babel-cli": "^6.9.0",
"babel-core": "^6.9.1",
"babel-preset-es2015": "^6.9.0",
"babel-preset-react": "^6.5.0",
"babel-register": "^6.9.0",
"mocha": "^2.3.4"
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.5.5",
"mkdirp": "^1.0.4",
"mocha": "^9.1.3",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"rimraf": "^3.0.2",
"terser": "^5.9.0"
},
"peerDependencies": {
"react": ">=16.2.0",
"react-dom": ">=16.2.0"
},
"bugs": {
"url": "https://github.com/Automattic/interpolate-components/issues"
Expand All @@ -32,11 +46,6 @@
"directories": {
"test": "test"
},
"dependencies": {
"react": "^0.14.3 || ^15.1.0 || ^16.0.0",
"react-addons-create-fragment": "^0.14.3 || ^15.1.0",
"react-dom": "^0.14.3 || ^15.1.0 || ^16.0.0"
},
"author": "Bob Ralian <[email protected]> (http://github.com/rralian)",
"license": "GPL-2.0"
}
127 changes: 0 additions & 127 deletions src/index.es6

This file was deleted.

136 changes: 136 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/**
* External Dependencies
*/
import React, { Fragment } from 'react';

/**
* Internal Dependencies
*/
import tokenize from './tokenize';

let currentMixedString;

function getCloseIndex( openIndex, tokens ) {
const openToken = tokens[ openIndex ];
let nestLevel = 0;
for ( let i = openIndex + 1; i < tokens.length; i++ ) {
const token = tokens[ i ];
if ( token.value === openToken.value ) {
if ( token.type === 'componentOpen' ) {
nestLevel++;
continue;
}
if ( token.type === 'componentClose' ) {
if ( nestLevel === 0 ) {
return i;
}
nestLevel--;
}
}
}
// if we get this far, there was no matching close token
throw new Error( `Missing closing component token \`${ openToken.value }\`` );
}

function buildChildren( tokens, components ) {
let children = [];
let openComponent, openIndex;

for ( let i = 0; i < tokens.length; i++ ) {
const token = tokens[ i ];
if ( token.type === 'string' ) {
children.push( token.value );
continue;
}
// component node should at least be set
if (
! components.hasOwnProperty( token.value ) ||
typeof components[ token.value ] === 'undefined'
) {
throw new Error( `Invalid interpolation, missing component node: \`${ token.value }\`` );
}
// should be either ReactElement or null (both type "object"), all other types deprecated
if ( typeof components[ token.value ] !== 'object' ) {
throw new Error(
`Invalid interpolation, component node must be a ReactElement or null: \`${ token.value }\``,
'\n> ' + currentMixedString
);
}
// we should never see a componentClose token in this loop
if ( token.type === 'componentClose' ) {
throw new Error( `Missing opening component token: \`${ token.value }\`` );
}
if ( token.type === 'componentOpen' ) {
openComponent = components[ token.value ];
openIndex = i;
break;
}
// componentSelfClosing token
children.push( components[ token.value ] );
continue;
}

if ( openComponent ) {
const closeIndex = getCloseIndex( openIndex, tokens );
const grandChildTokens = tokens.slice( openIndex + 1, closeIndex );
const grandChildren = buildChildren( grandChildTokens, components );
const clonedOpenComponent = React.cloneElement( openComponent, {}, grandChildren );
children.push( clonedOpenComponent );

if ( closeIndex < tokens.length - 1 ) {
const siblingTokens = tokens.slice( closeIndex + 1 );
const siblings = buildChildren( siblingTokens, components );
children = children.concat( siblings );
}
}

if ( children.length === 1 ) {
return children[ 0 ];
}

return (
<Fragment>
{ children.map( ( child, index ) => (
<Fragment key={ `interpolation-child-${ index }` }>{ child }</Fragment>
) ) }
</Fragment>
);
}

function interpolate( options ) {
const { mixedString, components, throwErrors } = options;

currentMixedString = mixedString;

if ( ! components ) {
return mixedString;
}

if ( typeof components !== 'object' ) {
if ( throwErrors ) {
throw new Error(
`Interpolation Error: unable to process \`${ mixedString }\` because components is not an object`
);
}

return mixedString;
}

const tokens = tokenize( mixedString );

try {
return buildChildren( tokens, components );
} catch ( error ) {
if ( throwErrors ) {
throw new Error(
`Interpolation Error: unable to process \`${ mixedString }\` because of error \`${
error.message
}\``
);
}

return mixedString;
}
}

export default interpolate;
File renamed without changes.