From f38e872456c7a87aaf1489bc2b1c0a6b2e03e5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Gomes?= Date: Thu, 1 Aug 2019 15:33:27 +0100 Subject: [PATCH 1/5] Make interpolate-components smaller. The library depended on `react-addons-create-fragment`, a deprecated utility that is easily replaceable with native Fragment support in React. Dropping this library saves around 1.6KB (compressed). In addition, this change updates all dependencies and adds minification to the build process, to reduce the library size further. --- .babelrc | 4 +- .gitignore | 1 + .npmignore | 1 + CHANGELOG.md | 6 ++ package.json | 35 +++++--- src/index.es6 | 127 ---------------------------- src/index.js | 136 ++++++++++++++++++++++++++++++ src/{tokenize.es6 => tokenize.js} | 0 8 files changed, 167 insertions(+), 143 deletions(-) delete mode 100644 src/index.es6 create mode 100644 src/index.js rename src/{tokenize.es6 => tokenize.js} (100%) diff --git a/.babelrc b/.babelrc index 69803ca..69ade47 100644 --- a/.babelrc +++ b/.babelrc @@ -1,6 +1,6 @@ { "presets": [ - "react", - "es2015" + "@babel/react", + "@babel/env" ] } diff --git a/.gitignore b/.gitignore index d9618b1..d30cf32 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules .DS_Store lib/ .idea/ +.build/ diff --git a/.npmignore b/.npmignore index 7b81124..45d50b3 100644 --- a/.npmignore +++ b/.npmignore @@ -2,3 +2,4 @@ src/ /npm-debug.log* .DS_Store .idea/ +.build/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 0245459..eeee994 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.0.0 +* Breaking change: require React ^16.2.0 (for native Fragment support) +* 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 diff --git a/package.json b/package.json index 00253f3..a8fac37 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,37 @@ { "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": "^0.5.1", + "mocha": "^2.5.3", + "rimraf": "^2.6.3", + "terser": "^4.1.2" }, "bugs": { "url": "https://github.com/Automattic/interpolate-components/issues" @@ -33,9 +41,8 @@ "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" + "react": "^16.2.0", + "react-dom": "^16.2.0" }, "author": "Bob Ralian (http://github.com/rralian)", "license": "GPL-2.0" diff --git a/src/index.es6 b/src/index.es6 deleted file mode 100644 index f0d86f1..0000000 --- a/src/index.es6 +++ /dev/null @@ -1,127 +0,0 @@ -/** - * External Dependencies - */ -import React from 'react'; -import createFragment from 'react-addons-create-fragment'; - -/** - * Internal Dependencies - */ -import tokenize from './tokenize'; - -let currentMixedString; - -function getCloseIndex( openIndex, tokens ) { - var openToken = tokens[ openIndex ], - nestLevel = 0, - token, i; - for ( i = openIndex + 1; i < tokens.length; i++ ) { - 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 ) { - var children = [], - childrenObject = {}, - openComponent, clonedOpenComponent, openIndex, closeIndex, token, i, grandChildTokens, grandChildren, siblingTokens, siblings; - - for ( i = 0; i < tokens.length; i++ ) { - 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 ) { - closeIndex = getCloseIndex( openIndex, tokens ); - grandChildTokens = tokens.slice( ( openIndex + 1 ), closeIndex ); - grandChildren = buildChildren( grandChildTokens, components ); - clonedOpenComponent = React.cloneElement( openComponent, {}, grandChildren ); - children.push( clonedOpenComponent ); - - if ( closeIndex < tokens.length - 1 ) { - siblingTokens = tokens.slice( closeIndex + 1 ); - siblings = buildChildren( siblingTokens, components ); - children = children.concat( siblings ); - } - } - - if ( children.length === 1 ) { - return children[ 0 ]; - } - - children.forEach( ( child, index ) => { - if ( child ) { - childrenObject[ `interpolation-child-${index}` ] = child; - } - } ); - - return createFragment( childrenObject ); -} - -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; - } - - let 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; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..f4cddd2 --- /dev/null +++ b/src/index.js @@ -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 ( + + { children.map( ( child, index ) => ( + { child } + ) ) } + + ); +} + +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; diff --git a/src/tokenize.es6 b/src/tokenize.js similarity index 100% rename from src/tokenize.es6 rename to src/tokenize.js From e8816494a975964eac135c7b773c1d027917825b Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 2 Nov 2021 14:04:35 +0100 Subject: [PATCH 2/5] Move react and react-dom out of dependencies These are peerDependencies for consumers and devDependencies for testing. We should not be depending on our own version of react. Fixes #15 --- CHANGELOG.md | 3 ++- package.json | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eeee994..22bad84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ ## 2.0.0 * Breaking change: require React ^16.2.0 (for native Fragment support) +* Breaking change: `react` and `react-com` are now `peerDependencies`. * Drop deprecated react-addons-create-fragment from library * Update to Babel 7 -* Add Terser to build pipeline, for minification +* Add Terser to build pipeline for minification ## 1.1.1 * Drop deprecated React.createClass, React.DOM from test diff --git a/package.json b/package.json index a8fac37..1cd2c44 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,15 @@ "@babel/register": "^7.5.5", "mkdirp": "^0.5.1", "mocha": "^2.5.3", + "react": "^16.2.0", + "react-dom": "^16.2.0", "rimraf": "^2.6.3", "terser": "^4.1.2" }, + "peerDependencies": { + "react": ">=16.2.0", + "react-dom": ">=16.2.0" + }, "bugs": { "url": "https://github.com/Automattic/interpolate-components/issues" }, @@ -40,10 +46,6 @@ "directories": { "test": "test" }, - "dependencies": { - "react": "^16.2.0", - "react-dom": "^16.2.0" - }, "author": "Bob Ralian (http://github.com/rralian)", "license": "GPL-2.0" } From 2b0c5daf92a003d3f0e9765b664e49baff253689 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 2 Nov 2021 14:10:43 +0100 Subject: [PATCH 3/5] Update devDeps --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1cd2c44..77e79f5 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,12 @@ "@babel/preset-env": "^7.5.5", "@babel/preset-react": "^7.0.0", "@babel/register": "^7.5.5", - "mkdirp": "^0.5.1", - "mocha": "^2.5.3", + "mkdirp": "^1.0.4", + "mocha": "^9.1.3", "react": "^16.2.0", "react-dom": "^16.2.0", - "rimraf": "^2.6.3", - "terser": "^4.1.2" + "rimraf": "^3.0.2", + "terser": "^5.9.0" }, "peerDependencies": { "react": ">=16.2.0", From 45597727bec8399f4dfb3974704ff30d393f9793 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 2 Nov 2021 14:19:03 +0100 Subject: [PATCH 4/5] Update circleci config Fixes #13 --- circle.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 6161605..7e4d307 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,10 @@ -machine: - node: - version: 5.11.1 +version: 2.1 + +jobs: + test: + docker: + - image: node:lts-slim + steps: + - checkout + - npm install + - npm run test From 63c09fb9eebdf10d2b86911bb868b10cc0d96f24 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 2 Nov 2021 14:21:48 +0100 Subject: [PATCH 5/5] fixup! Update circleci config --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 7e4d307..0faacf9 100644 --- a/circle.yml +++ b/circle.yml @@ -1,7 +1,7 @@ version: 2.1 jobs: - test: + build: docker: - image: node:lts-slim steps: