From af3760bb68e791d2231fb6d0f5907cea99822b37 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 11 May 2019 00:06:16 +0200 Subject: [PATCH 01/16] support nesting --- karma.conf.js | 2 +- .../jss-plugin-template/.size-snapshot.json | 24 ++-- packages/jss-plugin-template/package.json | 3 + .../jss-plugin-template/src/index.test.js | 128 +++++++++++------- packages/jss-plugin-template/src/parse.js | 38 +++++- .../jss-preset-default/.size-snapshot.json | 12 +- packages/jss-starter-kit/.size-snapshot.json | 12 +- packages/react-jss/.size-snapshot.json | 12 +- 8 files changed, 146 insertions(+), 85 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index df9adc070..fcfb87c96 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -44,7 +44,7 @@ module.exports = config => { frameworks: ['benchmark'], // Using a fixed position for a file name, m.b. should use an args parser later. files: [process.argv[4] || 'packages/jss/benchmark/**/*.js'], - preprocessors: {'packages/jss/benchmark/**/*.js': ['webpack']}, + preprocessors: {'packages/**/benchmark/**/*.js': ['webpack']}, reporters: ['benchmark'], // Some tests are slow. browserNoActivityTimeout: 20000 diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index 285a135d6..9e62748d7 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,23 +1,23 @@ { "dist/jss-plugin-template.js": { - "bundled": 1777, - "minified": 730, - "gzipped": 453 + "bundled": 2573, + "minified": 969, + "gzipped": 544 }, "dist/jss-plugin-template.min.js": { - "bundled": 1418, - "minified": 564, - "gzipped": 355 + "bundled": 2131, + "minified": 751, + "gzipped": 433 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 1341, - "minified": 686, - "gzipped": 423 + "bundled": 2138, + "minified": 968, + "gzipped": 522 }, "dist/jss-plugin-template.esm.js": { - "bundled": 1123, - "minified": 518, - "gzipped": 341, + "bundled": 1920, + "minified": 800, + "gzipped": 438, "treeshaked": { "rollup": { "code": 21, diff --git a/packages/jss-plugin-template/package.json b/packages/jss-plugin-template/package.json index 38ebc6937..db952f25a 100644 --- a/packages/jss-plugin-template/package.json +++ b/packages/jss-plugin-template/package.json @@ -40,5 +40,8 @@ "@babel/runtime": "^7.3.1", "jss": "10.0.0-alpha.16", "tiny-warning": "^1.0.2" + }, + "devDepenndencies": { + "jss-plugin-nested": "^10.0.0-alpha.7" } } diff --git a/packages/jss-plugin-template/src/index.test.js b/packages/jss-plugin-template/src/index.test.js index 6b3115bd2..64e7e7d1e 100644 --- a/packages/jss-plugin-template/src/index.test.js +++ b/packages/jss-plugin-template/src/index.test.js @@ -4,13 +4,14 @@ import expect from 'expect.js' import {stripIndent} from 'common-tags' import {create} from 'jss' import sinon from 'sinon' +import nested from 'jss-plugin-nested' import template from '.' const settings = { createGenerateId: () => rule => `${rule.key}-id` } -describe('jss-plugin-template', () => { +describe.only('jss-plugin-template', () => { let spy let jss @@ -23,98 +24,97 @@ describe('jss-plugin-template', () => { console.warn.restore() }) - describe('template literals', () => { - it('should convert a single single property/value', () => { - const sheet = jss.createStyleSheet({ - a: ` + it('should convert a single single property/value', () => { + const sheet = jss.createStyleSheet({ + a: ` color: red; ` - }) - expect(sheet.toString()).to.be(stripIndent` + }) + expect(sheet.toString()).to.be(stripIndent` .a-id { color: red; } `) - }) + }) - it('should parse multiple props/values', () => { - const sheet = jss.createStyleSheet({ - a: ` + it('should parse multiple props/values', () => { + const sheet = jss.createStyleSheet({ + a: ` color: red; float: left; ` - }) - expect(sheet.toString()).to.be(stripIndent` + }) + expect(sheet.toString()).to.be(stripIndent` .a-id { color: red; float: left; } `) - expect(spy.callCount).to.be(0) - }) - - it('should warn when there is no colon found', () => { - jss.createStyleSheet({ - a: 'color red;' - }) + expect(spy.callCount).to.be(0) + }) - expect(spy.callCount).to.be(1) - expect(spy.calledWithExactly('Warning: [JSS] Malformed CSS string "color red;"')).to.be(true) + it('should warn when there is no colon found', () => { + jss.createStyleSheet({ + a: 'color red;' }) - it('should strip spaces', () => { - const sheet = jss.createStyleSheet({ - a: ` + expect(spy.callCount).to.be(1) + expect(spy.args[0][0]).to.be('Warning: [JSS] Missing colon in "color red".') + }) + + it('should strip spaces', () => { + const sheet = jss.createStyleSheet({ + a: ` color: red ; float: left ; ` - }) - expect(sheet.toString()).to.be(stripIndent` + }) + expect(sheet.toString()).to.be(stripIndent` .a-id { color: red; float: left; } `) - }) + }) - it('should allow skiping last semicolon', () => { - const sheet = jss.createStyleSheet({ - a: ` + it('should allow skiping last semicolon', () => { + const sheet = jss.createStyleSheet({ + a: ` color: red; float: left ` - }) - expect(sheet.toString()).to.be(stripIndent` + }) + expect(sheet.toString()).to.be(stripIndent` .a-id { color: red; float: left; } `) - }) + }) - it('should support @media', () => { - const sheet = jss.createStyleSheet({ - '@media print': { - button: 'color: black' - } - }) - expect(sheet.toString()).to.be(stripIndent` + it('should support @media', () => { + const sheet = jss.createStyleSheet({ + '@media print': { + button: 'color: black' + } + }) + expect(sheet.toString()).to.be(stripIndent` @media print { .button-id { color: black; } } `) - }) + }) - it('should support @keyframes', () => { - const sheet = jss.createStyleSheet({ - '@keyframes a': { - from: 'opacity: 0', - to: 'opacity: 1' - } - }) - expect(sheet.toString()).to.be(stripIndent` + it('should support @keyframes', () => { + const sheet = jss.createStyleSheet({ + '@keyframes a': { + from: 'opacity: 0', + to: 'opacity: 1' + } + }) + expect(sheet.toString()).to.be(stripIndent` @keyframes keyframes-a-id { from { opacity: 0; @@ -124,6 +124,34 @@ describe('jss-plugin-template', () => { } } `) + }) + + it('should support nesting', () => { + jss = create(settings).use(template(), nested()) + + const sheet = jss.createStyleSheet({ + a: ` + color: green; + & .b { + color: red; + } + ` }) + expect(sheet.toString()).to.be(stripIndent` + .a-id { + color: green; + } + .a-id .b { + color: red; + } + `) }) + + it('should warn when opening curly brace is missing', () => {}) + it('should warn when closing curly brace is missing', () => {}) + it('should warn when closing curly brace is not on an own line', () => {}) + it('should warn when closing curly brace is not provided', () => {}) + it('should support multiple first level nested rules ', () => {}) + it('should support multiple deeply nested rules', () => {}) + it('should regular props after a nested rule', () => {}) }) diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index f2368b31a..511a9db56 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -1,7 +1,7 @@ // @flow import warning from 'tiny-warning' -const semiWithNl = /;\n/ +const semiWithNl = /[;\n]|[}\n]/ /** * Naive CSS parser. @@ -9,21 +9,51 @@ const semiWithNl = /;\n/ * - Requires semicolon and new line after the value (except of last line) * - No nested rules support */ -export default (cssText: string) => { +export default (cssText: string): Object => { const style = {} const split = cssText.split(semiWithNl) + let nestedRuleProp + for (let i = 0; i < split.length; i++) { const decl = (split[i] || '').trim() if (!decl) continue + + if (nestedRuleProp === undefined) { + const ampIndex = decl.indexOf('&') + // We have a nested rule. + if (ampIndex !== -1) { + const openCurlyIndex = decl.indexOf('{') + if (openCurlyIndex === -1) { + warning(false, `[JSS] Missing opening curly brace in "${decl}".`) + continue + } + nestedRuleProp = decl.substr(0, openCurlyIndex).trim() + style[nestedRuleProp] = {} + continue + } + } else { + const closeCurlyIndex = decl.indexOf('}') + if (closeCurlyIndex !== -1) { + nestedRuleProp = undefined + continue + } + } + const colonIndex = decl.indexOf(':') if (colonIndex === -1) { - warning(false, `[JSS] Malformed CSS string "${decl}"`) + warning(false, `[JSS] Missing colon in "${decl}".`) continue } + const prop = decl.substr(0, colonIndex).trim() const value = decl.substr(colonIndex + 1).trim() - style[prop] = value + + if (nestedRuleProp) { + style[nestedRuleProp][prop] = value + } else { + style[prop] = value + } } return style } diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index 315e6f5df..409726e3c 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-preset-default.js": { - "bundled": 54595, - "minified": 19525, - "gzipped": 6444 + "bundled": 55391, + "minified": 19764, + "gzipped": 6524 }, "dist/jss-preset-default.min.js": { - "bundled": 53841, - "minified": 19066, - "gzipped": 6229 + "bundled": 54554, + "minified": 19253, + "gzipped": 6297 }, "dist/jss-preset-default.cjs.js": { "bundled": 1329, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index 09e23b690..8ed240f74 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-starter-kit.js": { - "bundled": 70137, - "minified": 29563, - "gzipped": 9075 + "bundled": 70933, + "minified": 29802, + "gzipped": 9159 }, "dist/jss-starter-kit.min.js": { - "bundled": 69383, - "minified": 29105, - "gzipped": 8867 + "bundled": 70096, + "minified": 29292, + "gzipped": 8937 }, "dist/jss-starter-kit.cjs.js": { "bundled": 2592, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index a3e1662e2..0bbe295c5 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/react-jss.js": { - "bundled": 109904, - "minified": 37407, - "gzipped": 12068 + "bundled": 110700, + "minified": 37646, + "gzipped": 12153 }, "dist/react-jss.min.js": { - "bundled": 85345, - "minified": 30142, - "gzipped": 9883 + "bundled": 86058, + "minified": 30329, + "gzipped": 9946 }, "dist/react-jss.cjs.js": { "bundled": 15499, From 8f74c0b49b3e02569dfd35c6ad8ff6e161da227b Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 11 May 2019 00:27:50 +0200 Subject: [PATCH 02/16] gain 10% perf by trimming a semicolon later --- .../jss-plugin-template/.size-snapshot.json | 14 +++++++------- .../jss-plugin-template/src/index.test.js | 19 +++++++------------ packages/jss-plugin-template/src/parse.js | 6 +++--- .../jss-preset-default/.size-snapshot.json | 8 ++++---- packages/jss-starter-kit/.size-snapshot.json | 8 ++++---- packages/react-jss/.size-snapshot.json | 8 ++++---- 6 files changed, 29 insertions(+), 34 deletions(-) diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index 9e62748d7..b230f6256 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,21 +1,21 @@ { "dist/jss-plugin-template.js": { - "bundled": 2573, + "bundled": 2581, "minified": 969, - "gzipped": 544 + "gzipped": 545 }, "dist/jss-plugin-template.min.js": { - "bundled": 2131, + "bundled": 2139, "minified": 751, - "gzipped": 433 + "gzipped": 436 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 2138, + "bundled": 2146, "minified": 968, - "gzipped": 522 + "gzipped": 520 }, "dist/jss-plugin-template.esm.js": { - "bundled": 1920, + "bundled": 1928, "minified": 800, "gzipped": 438, "treeshaked": { diff --git a/packages/jss-plugin-template/src/index.test.js b/packages/jss-plugin-template/src/index.test.js index 64e7e7d1e..525f5514b 100644 --- a/packages/jss-plugin-template/src/index.test.js +++ b/packages/jss-plugin-template/src/index.test.js @@ -59,7 +59,7 @@ describe.only('jss-plugin-template', () => { }) expect(spy.callCount).to.be(1) - expect(spy.args[0][0]).to.be('Warning: [JSS] Missing colon in "color red".') + expect(spy.args[0][0]).to.be('Warning: [JSS] Missing colon in "color red;".') }) it('should strip spaces', () => { @@ -77,25 +77,20 @@ describe.only('jss-plugin-template', () => { `) }) - it('should allow skiping last semicolon', () => { - const sheet = jss.createStyleSheet({ + it.skip('should warn when semicolon not found', () => { + jss.createStyleSheet({ a: ` color: red; float: left ` }) - expect(sheet.toString()).to.be(stripIndent` - .a-id { - color: red; - float: left; - } - `) + expect(spy.args[0][0]).to.be('Warning: [JSS] Missing semicolon in "float: left".') }) it('should support @media', () => { const sheet = jss.createStyleSheet({ '@media print': { - button: 'color: black' + button: 'color: black;' } }) expect(sheet.toString()).to.be(stripIndent` @@ -110,8 +105,8 @@ describe.only('jss-plugin-template', () => { it('should support @keyframes', () => { const sheet = jss.createStyleSheet({ '@keyframes a': { - from: 'opacity: 0', - to: 'opacity: 1' + from: 'opacity: 0;', + to: 'opacity: 1;' } }) expect(sheet.toString()).to.be(stripIndent` diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index 511a9db56..66cc670f4 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -1,7 +1,7 @@ // @flow import warning from 'tiny-warning' -const semiWithNl = /[;\n]|[}\n]/ +const semiWithNl = /\n/ /** * Naive CSS parser. @@ -28,7 +28,7 @@ export default (cssText: string): Object => { warning(false, `[JSS] Missing opening curly brace in "${decl}".`) continue } - nestedRuleProp = decl.substr(0, openCurlyIndex).trim() + nestedRuleProp = decl.substr(0, openCurlyIndex - 1) style[nestedRuleProp] = {} continue } @@ -47,7 +47,7 @@ export default (cssText: string): Object => { } const prop = decl.substr(0, colonIndex).trim() - const value = decl.substr(colonIndex + 1).trim() + const value = decl.substring(colonIndex + 1, decl.length - 1).trim() if (nestedRuleProp) { style[nestedRuleProp][prop] = value diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index 409726e3c..8210ac625 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-preset-default.js": { - "bundled": 55391, + "bundled": 55399, "minified": 19764, - "gzipped": 6524 + "gzipped": 6522 }, "dist/jss-preset-default.min.js": { - "bundled": 54554, + "bundled": 54562, "minified": 19253, - "gzipped": 6297 + "gzipped": 6296 }, "dist/jss-preset-default.cjs.js": { "bundled": 1329, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index 8ed240f74..f2c98e18f 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-starter-kit.js": { - "bundled": 70933, + "bundled": 70941, "minified": 29802, - "gzipped": 9159 + "gzipped": 9157 }, "dist/jss-starter-kit.min.js": { - "bundled": 70096, + "bundled": 70104, "minified": 29292, - "gzipped": 8937 + "gzipped": 8935 }, "dist/jss-starter-kit.cjs.js": { "bundled": 2592, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index 0bbe295c5..b9e237c04 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/react-jss.js": { - "bundled": 110700, + "bundled": 110708, "minified": 37646, - "gzipped": 12153 + "gzipped": 12152 }, "dist/react-jss.min.js": { - "bundled": 86058, + "bundled": 86066, "minified": 30329, - "gzipped": 9946 + "gzipped": 9945 }, "dist/react-jss.cjs.js": { "bundled": 15499, From 45428698f1bb62631be4f81b03ac34c3f2d681e8 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 11 May 2019 00:32:29 +0200 Subject: [PATCH 03/16] use substring --- .../jss-plugin-template/.size-snapshot.json | 22 +++++++++---------- packages/jss-plugin-template/src/parse.js | 4 ++-- .../jss-preset-default/.size-snapshot.json | 12 +++++----- packages/jss-starter-kit/.size-snapshot.json | 12 +++++----- packages/react-jss/.size-snapshot.json | 12 +++++----- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index b230f6256..5a782fdd3 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,23 +1,23 @@ { "dist/jss-plugin-template.js": { - "bundled": 2581, - "minified": 969, - "gzipped": 545 + "bundled": 2587, + "minified": 975, + "gzipped": 543 }, "dist/jss-plugin-template.min.js": { - "bundled": 2139, - "minified": 751, - "gzipped": 436 + "bundled": 2145, + "minified": 757, + "gzipped": 433 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 2146, - "minified": 968, + "bundled": 2152, + "minified": 974, "gzipped": 520 }, "dist/jss-plugin-template.esm.js": { - "bundled": 1928, - "minified": 800, - "gzipped": 438, + "bundled": 1934, + "minified": 806, + "gzipped": 439, "treeshaked": { "rollup": { "code": 21, diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index 66cc670f4..1d414bc6c 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -28,7 +28,7 @@ export default (cssText: string): Object => { warning(false, `[JSS] Missing opening curly brace in "${decl}".`) continue } - nestedRuleProp = decl.substr(0, openCurlyIndex - 1) + nestedRuleProp = decl.substring(0, openCurlyIndex - 1) style[nestedRuleProp] = {} continue } @@ -46,7 +46,7 @@ export default (cssText: string): Object => { continue } - const prop = decl.substr(0, colonIndex).trim() + const prop = decl.substring(0, colonIndex).trim() const value = decl.substring(colonIndex + 1, decl.length - 1).trim() if (nestedRuleProp) { diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index 8210ac625..eddbb5973 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-preset-default.js": { - "bundled": 55399, - "minified": 19764, - "gzipped": 6522 + "bundled": 55405, + "minified": 19770, + "gzipped": 6521 }, "dist/jss-preset-default.min.js": { - "bundled": 54562, - "minified": 19253, - "gzipped": 6296 + "bundled": 54568, + "minified": 19259, + "gzipped": 6294 }, "dist/jss-preset-default.cjs.js": { "bundled": 1329, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index f2c98e18f..f6ce14fca 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-starter-kit.js": { - "bundled": 70941, - "minified": 29802, - "gzipped": 9157 + "bundled": 70947, + "minified": 29808, + "gzipped": 9156 }, "dist/jss-starter-kit.min.js": { - "bundled": 70104, - "minified": 29292, - "gzipped": 8935 + "bundled": 70110, + "minified": 29298, + "gzipped": 8934 }, "dist/jss-starter-kit.cjs.js": { "bundled": 2592, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index b9e237c04..dd3076f6e 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/react-jss.js": { - "bundled": 110708, - "minified": 37646, - "gzipped": 12152 + "bundled": 110714, + "minified": 37652, + "gzipped": 12150 }, "dist/react-jss.min.js": { - "bundled": 86066, - "minified": 30329, - "gzipped": 9945 + "bundled": 86072, + "minified": 30335, + "gzipped": 9943 }, "dist/react-jss.cjs.js": { "bundled": 15499, From 44cd7e6d68a6c6265eb3bdd0d74990b1141e2252 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sun, 12 May 2019 21:50:43 +0200 Subject: [PATCH 04/16] more tests --- .../jss-plugin-template/.size-snapshot.json | 24 ++-- .../benchmark/tests/parse.js | 14 +++ packages/jss-plugin-template/package.json | 5 +- .../jss-plugin-template/src/index.test.js | 105 ++++++++++++++++-- packages/jss-plugin-template/src/parse.js | 13 ++- .../jss-preset-default/.size-snapshot.json | 12 +- packages/jss-starter-kit/.size-snapshot.json | 12 +- packages/react-jss/.size-snapshot.json | 12 +- yarn.lock | 5 + 9 files changed, 159 insertions(+), 43 deletions(-) diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index 5a782fdd3..2a8f352c1 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,23 +1,23 @@ { "dist/jss-plugin-template.js": { - "bundled": 2587, - "minified": 975, - "gzipped": 543 + "bundled": 2865, + "minified": 1051, + "gzipped": 555 }, "dist/jss-plugin-template.min.js": { - "bundled": 2145, - "minified": 757, - "gzipped": 433 + "bundled": 2325, + "minified": 774, + "gzipped": 439 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 2152, - "minified": 974, - "gzipped": 520 + "bundled": 2469, + "minified": 1093, + "gzipped": 531 }, "dist/jss-plugin-template.esm.js": { - "bundled": 1934, - "minified": 806, - "gzipped": 439, + "bundled": 2251, + "minified": 925, + "gzipped": 450, "treeshaked": { "rollup": { "code": 21, diff --git a/packages/jss-plugin-template/benchmark/tests/parse.js b/packages/jss-plugin-template/benchmark/tests/parse.js index 9e125255a..d5f08885d 100644 --- a/packages/jss-plugin-template/benchmark/tests/parse.js +++ b/packages/jss-plugin-template/benchmark/tests/parse.js @@ -1,3 +1,4 @@ +import stylis from 'stylis' import parse from '../../src/parse' const css = ` @@ -37,8 +38,21 @@ const css = ` font-variant: normal normal; border-spacing: 0px; ` + +stylis.set({ + global: false, + keyframe: false, + prefix: false, + compress: false, + semicolon: true +}) + suite('Parse', () => { benchmark('parse()', () => { parse(css) }) + + benchmark('stylis()', () => { + stylis('#id', css) + }) }) diff --git a/packages/jss-plugin-template/package.json b/packages/jss-plugin-template/package.json index db952f25a..636b08852 100644 --- a/packages/jss-plugin-template/package.json +++ b/packages/jss-plugin-template/package.json @@ -41,7 +41,8 @@ "jss": "10.0.0-alpha.16", "tiny-warning": "^1.0.2" }, - "devDepenndencies": { - "jss-plugin-nested": "^10.0.0-alpha.7" + "devDependencies": { + "jss-plugin-nested": "^10.0.0-alpha.16", + "stylis": "^3.5.4" } } diff --git a/packages/jss-plugin-template/src/index.test.js b/packages/jss-plugin-template/src/index.test.js index 525f5514b..25828cff2 100644 --- a/packages/jss-plugin-template/src/index.test.js +++ b/packages/jss-plugin-template/src/index.test.js @@ -17,7 +17,7 @@ describe.only('jss-plugin-template', () => { beforeEach(() => { spy = sinon.spy(console, 'warn') - jss = create(settings).use(template()) + jss = create(settings).use(template(), nested()) }) afterEach(() => { @@ -122,14 +122,60 @@ describe.only('jss-plugin-template', () => { }) it('should support nesting', () => { - jss = create(settings).use(template(), nested()) + const sheet = jss.createStyleSheet({ + a: ` + color: green; + & .b { + color: red; + } + ` + }) + expect(sheet.toString()).to.be(stripIndent` + .a-id { + color: green; + } + .a-id .b { + color: red; + } + `) + }) + it('should warn when opening curly brace is missing', () => { + jss.createStyleSheet({ + a: ` + color: green; + & .b + color: red; + } + ` + }) + expect(spy.args[0][0]).to.be('Warning: [JSS] Missing opening curly brace in "& .b".') + }) + + it('should warn when closing curly brace is not on an own line', () => { + jss.createStyleSheet({ + a: ` + color: green; + & .b { + color: red; + } .a { color: blue; } + ` + }) + expect(spy.args[0][0]).to.be( + 'Warning: [JSS] Missing opening curly brace in "} .a { color: blue; }".' + ) + }) + + it('should support multiple first level nested rules', () => { const sheet = jss.createStyleSheet({ a: ` color: green; & .b { color: red; } + & .c { + color: blue; + } ` }) expect(sheet.toString()).to.be(stripIndent` @@ -139,14 +185,55 @@ describe.only('jss-plugin-template', () => { .a-id .b { color: red; } + .a-id .c { + color: blue; + } `) }) - it('should warn when opening curly brace is missing', () => {}) - it('should warn when closing curly brace is missing', () => {}) - it('should warn when closing curly brace is not on an own line', () => {}) - it('should warn when closing curly brace is not provided', () => {}) - it('should support multiple first level nested rules ', () => {}) - it('should support multiple deeply nested rules', () => {}) - it('should regular props after a nested rule', () => {}) + it.skip('should support multiple deeply nested rules', () => { + const sheet = jss.createStyleSheet({ + a: ` + color: green; + & .b { + color: red; + & .c { + color: blue; + } + } + ` + }) + expect(sheet.toString()).to.be(stripIndent` + .a-id { + color: green; + } + .a-id .b { + color: red; + } + .a-id .b .c { + color: blue; + } + `) + }) + + it('should regular props after a nested rule', () => { + const sheet = jss.createStyleSheet({ + a: ` + color: green; + & .b { + color: red; + } + float: left; + ` + }) + expect(sheet.toString()).to.be(stripIndent` + .a-id { + color: green; + float: left; + } + .a-id .b { + color: red; + } + `) + }) }) diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index 1d414bc6c..957aa047e 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -9,7 +9,7 @@ const semiWithNl = /\n/ * - Requires semicolon and new line after the value (except of last line) * - No nested rules support */ -export default (cssText: string): Object => { +const parse = (cssText: string): Object => { const style = {} const split = cssText.split(semiWithNl) let nestedRuleProp @@ -19,8 +19,9 @@ export default (cssText: string): Object => { if (!decl) continue + const ampIndex = decl.indexOf('&') + if (nestedRuleProp === undefined) { - const ampIndex = decl.indexOf('&') // We have a nested rule. if (ampIndex !== -1) { const openCurlyIndex = decl.indexOf('{') @@ -34,8 +35,14 @@ export default (cssText: string): Object => { } } else { const closeCurlyIndex = decl.indexOf('}') + if (closeCurlyIndex !== -1) { nestedRuleProp = undefined + // Closing brace should be on it's own line, otherwise the rest on that line + // will be ignored, so we should warn the user. + if (decl.length !== 1) { + warning(false, `[JSS] Missing opening curly brace in "${decl}".`) + } continue } } @@ -57,3 +64,5 @@ export default (cssText: string): Object => { } return style } + +export default parse diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index eddbb5973..a7a1992f8 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-preset-default.js": { - "bundled": 55405, - "minified": 19770, - "gzipped": 6521 + "bundled": 55680, + "minified": 19846, + "gzipped": 6533 }, "dist/jss-preset-default.min.js": { - "bundled": 54568, - "minified": 19259, - "gzipped": 6294 + "bundled": 54745, + "minified": 19276, + "gzipped": 6301 }, "dist/jss-preset-default.cjs.js": { "bundled": 1329, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index f6ce14fca..ed1492b77 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-starter-kit.js": { - "bundled": 70947, - "minified": 29808, - "gzipped": 9156 + "bundled": 71222, + "minified": 29884, + "gzipped": 9168 }, "dist/jss-starter-kit.min.js": { - "bundled": 70110, - "minified": 29298, - "gzipped": 8934 + "bundled": 70287, + "minified": 29315, + "gzipped": 8939 }, "dist/jss-starter-kit.cjs.js": { "bundled": 2592, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index dd3076f6e..5ba716d80 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/react-jss.js": { - "bundled": 110714, - "minified": 37652, - "gzipped": 12150 + "bundled": 110989, + "minified": 37728, + "gzipped": 12163 }, "dist/react-jss.min.js": { - "bundled": 86072, - "minified": 30335, - "gzipped": 9943 + "bundled": 86249, + "minified": 30352, + "gzipped": 9949 }, "dist/react-jss.cjs.js": { "bundled": 15499, diff --git a/yarn.lock b/yarn.lock index da2b868c0..27975828b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8979,6 +8979,11 @@ strong-log-transformer@^2.0.0: minimist "^1.2.0" through "^2.3.4" +stylis@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" + integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q== + supports-color@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" From 159e2c868de1c78f873d95cf6b61534deafff82e Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 01:01:37 +0200 Subject: [PATCH 05/16] make semicolons optional, update docs --- docs/jss-plugin-template.md | 10 +-- .../jss-plugin-template/.size-snapshot.json | 20 +++--- .../jss-plugin-template/src/index.test.js | 15 ++-- packages/jss-plugin-template/src/parse.js | 68 +++++++++---------- .../jss-preset-default/.size-snapshot.json | 12 ++-- packages/jss-starter-kit/.size-snapshot.json | 12 ++-- packages/react-jss/.size-snapshot.json | 12 ++-- 7 files changed, 75 insertions(+), 74 deletions(-) diff --git a/docs/jss-plugin-template.md b/docs/jss-plugin-template.md index 99f8467c9..11b809a73 100644 --- a/docs/jss-plugin-template.md +++ b/docs/jss-plugin-template.md @@ -1,10 +1,9 @@ ## Enables string templates -Allows you to use string templates to declare CSS rules. It implements a **very naive** but **very fast (~42000 ops/sec)** runtime CSS parser, with certain limitations: +Allows you to use string templates to declare CSS rules. It implements a **simplified** but **very fast** runtime CSS parser, with certain limitations: -- Supports only rule body (no selectors) -- Requires semicolon and a new line after the value (except the last line) -- No nested rules support +- Requires new lines at the end of each declaration. +- Requires a closing curly brace of a nested rule to be on a separate line. ```js const styles = { @@ -14,6 +13,9 @@ const styles = { color: red; margin: 20px 40px; padding: 10px; + &:hover span { + color: green; + } `, '@media print': { button: `color: black` diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index 2a8f352c1..fbe7ecc7f 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,23 +1,23 @@ { "dist/jss-plugin-template.js": { - "bundled": 2865, - "minified": 1051, - "gzipped": 555 + "bundled": 2772, + "minified": 1069, + "gzipped": 564 }, "dist/jss-plugin-template.min.js": { - "bundled": 2325, - "minified": 774, - "gzipped": 439 + "bundled": 2238, + "minified": 792, + "gzipped": 446 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 2469, + "bundled": 2392, "minified": 1093, - "gzipped": 531 + "gzipped": 533 }, "dist/jss-plugin-template.esm.js": { - "bundled": 2251, + "bundled": 2174, "minified": 925, - "gzipped": 450, + "gzipped": 453, "treeshaked": { "rollup": { "code": 21, diff --git a/packages/jss-plugin-template/src/index.test.js b/packages/jss-plugin-template/src/index.test.js index 25828cff2..b9a46442b 100644 --- a/packages/jss-plugin-template/src/index.test.js +++ b/packages/jss-plugin-template/src/index.test.js @@ -77,14 +77,19 @@ describe.only('jss-plugin-template', () => { `) }) - it.skip('should warn when semicolon not found', () => { - jss.createStyleSheet({ + it('should not require semicolon', () => { + const sheet = jss.createStyleSheet({ a: ` - color: red; + color: red float: left ` }) - expect(spy.args[0][0]).to.be('Warning: [JSS] Missing semicolon in "float: left".') + expect(sheet.toString()).to.be(stripIndent` + .a-id { + color: red; + float: left; + } + `) }) it('should support @media', () => { @@ -191,7 +196,7 @@ describe.only('jss-plugin-template', () => { `) }) - it.skip('should support multiple deeply nested rules', () => { + it('should support multiple deeply nested rules', () => { const sheet = jss.createStyleSheet({ a: ` color: green; diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index 957aa047e..fb4041d27 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -1,67 +1,61 @@ // @flow import warning from 'tiny-warning' -const semiWithNl = /\n/ - /** - * Naive CSS parser. - * - Supports only rule body (no selectors) - * - Requires semicolon and new line after the value (except of last line) - * - No nested rules support + * Simplified CSS parser. + * - Requires new lines at the end of each declaration. + * - Requires a closing curly brace of a nested rule to be on a separate line. */ const parse = (cssText: string): Object => { const style = {} - const split = cssText.split(semiWithNl) - let nestedRuleProp + const lines = cssText.split('\n') + const rules = [style] - for (let i = 0; i < split.length; i++) { - const decl = (split[i] || '').trim() + for (let i = 0; i < lines.length; i++) { + const decl = (lines[i] || '').trim() if (!decl) continue const ampIndex = decl.indexOf('&') - if (nestedRuleProp === undefined) { - // We have a nested rule. - if (ampIndex !== -1) { - const openCurlyIndex = decl.indexOf('{') - if (openCurlyIndex === -1) { - warning(false, `[JSS] Missing opening curly brace in "${decl}".`) - continue - } - nestedRuleProp = decl.substring(0, openCurlyIndex - 1) - style[nestedRuleProp] = {} + if (ampIndex !== -1) { + const openCurlyIndex = decl.indexOf('{') + if (openCurlyIndex === -1) { + warning(false, `[JSS] Missing opening curly brace in "${decl}".`) continue } - } else { - const closeCurlyIndex = decl.indexOf('}') + const key = decl.substring(0, openCurlyIndex - 1) + const nestedStyle = {} + rules[rules.length - 1][key] = nestedStyle + rules.push(nestedStyle) + continue + } - if (closeCurlyIndex !== -1) { - nestedRuleProp = undefined - // Closing brace should be on it's own line, otherwise the rest on that line - // will be ignored, so we should warn the user. - if (decl.length !== 1) { - warning(false, `[JSS] Missing opening curly brace in "${decl}".`) - } - continue + // We are closing a nested rule. + if (decl.indexOf('}') !== -1) { + rules.pop() + // Closing brace should be on it's own line, otherwise the rest on that line + // will be ignored, so we should warn the user. + if (decl.length !== 1) { + warning(false, `[JSS] Missing opening curly brace in "${decl}".`) } + continue } const colonIndex = decl.indexOf(':') + if (colonIndex === -1) { warning(false, `[JSS] Missing colon in "${decl}".`) continue } const prop = decl.substring(0, colonIndex).trim() - const value = decl.substring(colonIndex + 1, decl.length - 1).trim() - - if (nestedRuleProp) { - style[nestedRuleProp][prop] = value - } else { - style[prop] = value - } + // We need to remove semicolon from value if there is one. + const semi = decl[decl.length - 1] === ';' ? 1 : 0 + const value = decl.substring(colonIndex + 1, decl.length - semi).trim() + rules[rules.length - 1][prop] = value } + return style } diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index a7a1992f8..834ab2b8f 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-preset-default.js": { - "bundled": 55680, - "minified": 19846, - "gzipped": 6533 + "bundled": 55587, + "minified": 19860, + "gzipped": 6544 }, "dist/jss-preset-default.min.js": { - "bundled": 54745, - "minified": 19276, - "gzipped": 6301 + "bundled": 54658, + "minified": 19291, + "gzipped": 6313 }, "dist/jss-preset-default.cjs.js": { "bundled": 1329, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index ed1492b77..74a600069 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-starter-kit.js": { - "bundled": 71222, - "minified": 29884, - "gzipped": 9168 + "bundled": 71129, + "minified": 29899, + "gzipped": 9175 }, "dist/jss-starter-kit.min.js": { - "bundled": 70287, - "minified": 29315, - "gzipped": 8939 + "bundled": 70200, + "minified": 29330, + "gzipped": 8947 }, "dist/jss-starter-kit.cjs.js": { "bundled": 2592, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index 5ba716d80..a63fda5d0 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/react-jss.js": { - "bundled": 110989, - "minified": 37728, - "gzipped": 12163 + "bundled": 110896, + "minified": 37744, + "gzipped": 12180 }, "dist/react-jss.min.js": { - "bundled": 86249, - "minified": 30352, - "gzipped": 9949 + "bundled": 86162, + "minified": 30368, + "gzipped": 9971 }, "dist/react-jss.cjs.js": { "bundled": 15499, From bdbfe142f88c39dc5325a474e317d6ae7015ba8a Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 01:11:14 +0200 Subject: [PATCH 06/16] include all tests --- packages/jss-plugin-template/src/index.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jss-plugin-template/src/index.test.js b/packages/jss-plugin-template/src/index.test.js index b9a46442b..b0c2e12e6 100644 --- a/packages/jss-plugin-template/src/index.test.js +++ b/packages/jss-plugin-template/src/index.test.js @@ -11,7 +11,7 @@ const settings = { createGenerateId: () => rule => `${rule.key}-id` } -describe.only('jss-plugin-template', () => { +describe('jss-plugin-template', () => { let spy let jss From 134dab3fb42164924e898c2135a5834a78aa4e63 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 02:07:48 +0200 Subject: [PATCH 07/16] add caching --- .../jss-plugin-template/.size-snapshot.json | 24 ++++++++-------- .../benchmark/tests/createSheet.js | 2 +- packages/jss-plugin-template/src/index.js | 28 ++++++++++++++----- .../jss-plugin-template/src/index.test.js | 8 +++++- .../jss-preset-default/.size-snapshot.json | 12 ++++---- packages/jss-starter-kit/.size-snapshot.json | 12 ++++---- packages/react-jss/.size-snapshot.json | 12 ++++---- 7 files changed, 59 insertions(+), 39 deletions(-) diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index fbe7ecc7f..35ecf91c9 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,23 +1,23 @@ { "dist/jss-plugin-template.js": { - "bundled": 2772, - "minified": 1069, - "gzipped": 564 + "bundled": 3019, + "minified": 1136, + "gzipped": 604 }, "dist/jss-plugin-template.min.js": { - "bundled": 2238, - "minified": 792, - "gzipped": 446 + "bundled": 2485, + "minified": 859, + "gzipped": 487 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 2392, - "minified": 1093, - "gzipped": 533 + "bundled": 2611, + "minified": 1165, + "gzipped": 575 }, "dist/jss-plugin-template.esm.js": { - "bundled": 2174, - "minified": 925, - "gzipped": 453, + "bundled": 2388, + "minified": 991, + "gzipped": 496, "treeshaked": { "rollup": { "code": 21, diff --git a/packages/jss-plugin-template/benchmark/tests/createSheet.js b/packages/jss-plugin-template/benchmark/tests/createSheet.js index 5adf1e0f3..f51984001 100644 --- a/packages/jss-plugin-template/benchmark/tests/createSheet.js +++ b/packages/jss-plugin-template/benchmark/tests/createSheet.js @@ -4,7 +4,7 @@ import template from '../../src/index' import parse from '../../src/parse' const options = {Renderer: null} -const jss = create(options).use(template()) +const jss = create(options).use(template({cache: false})) const css = ` color: rgb(77, 77, 77); diff --git a/packages/jss-plugin-template/src/index.js b/packages/jss-plugin-template/src/index.js index bcff9739b..8bde25f9f 100644 --- a/packages/jss-plugin-template/src/index.js +++ b/packages/jss-plugin-template/src/index.js @@ -2,13 +2,27 @@ import {type Plugin} from 'jss' import parse from './parse' -const onProcessRule = rule => { - if (typeof rule.style === 'string') { - // $FlowFixMe: We can safely assume that rule has the style property - rule.style = parse(rule.style) +export const cache = {} + +type Options = {|cache: boolean|} + +export default function templatePlugin(options: Options = {cache: true}): Plugin { + const onProcessStyle = style => { + if (typeof style !== 'string') { + return style + } + + if (style in cache) { + return cache[style] + } + + if (options.cache) { + cache[style] = parse(style) + return cache[style] + } + + return parse(style) } -} -export default function templatePlugin(): Plugin { - return {onProcessRule} + return {onProcessStyle} } diff --git a/packages/jss-plugin-template/src/index.test.js b/packages/jss-plugin-template/src/index.test.js index b0c2e12e6..e98848875 100644 --- a/packages/jss-plugin-template/src/index.test.js +++ b/packages/jss-plugin-template/src/index.test.js @@ -5,7 +5,7 @@ import {stripIndent} from 'common-tags' import {create} from 'jss' import sinon from 'sinon' import nested from 'jss-plugin-nested' -import template from '.' +import template, {cache} from '.' const settings = { createGenerateId: () => rule => `${rule.key}-id` @@ -37,6 +37,12 @@ describe('jss-plugin-template', () => { `) }) + it('should cache parsed template', () => { + const a = `color: red` + jss.createStyleSheet({a}) + expect(cache[a]).to.eql({color: 'red'}) + }) + it('should parse multiple props/values', () => { const sheet = jss.createStyleSheet({ a: ` diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index 834ab2b8f..4c3fc9333 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-preset-default.js": { - "bundled": 55587, - "minified": 19860, - "gzipped": 6544 + "bundled": 55818, + "minified": 19949, + "gzipped": 6586 }, "dist/jss-preset-default.min.js": { - "bundled": 54658, - "minified": 19291, - "gzipped": 6313 + "bundled": 54889, + "minified": 19377, + "gzipped": 6353 }, "dist/jss-preset-default.cjs.js": { "bundled": 1329, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index 74a600069..f40258d86 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-starter-kit.js": { - "bundled": 71129, - "minified": 29899, - "gzipped": 9175 + "bundled": 71360, + "minified": 29958, + "gzipped": 9211 }, "dist/jss-starter-kit.min.js": { - "bundled": 70200, - "minified": 29330, - "gzipped": 8947 + "bundled": 70431, + "minified": 29389, + "gzipped": 8987 }, "dist/jss-starter-kit.cjs.js": { "bundled": 2592, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index a63fda5d0..9db717d5e 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/react-jss.js": { - "bundled": 110896, - "minified": 37744, - "gzipped": 12180 + "bundled": 111127, + "minified": 37831, + "gzipped": 12221 }, "dist/react-jss.min.js": { - "bundled": 86162, - "minified": 30368, - "gzipped": 9971 + "bundled": 86393, + "minified": 30451, + "gzipped": 9999 }, "dist/react-jss.cjs.js": { "bundled": 15499, From 1d77e7e96cacda78cb9228946a1b5e896724d11d Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 09:00:19 +0200 Subject: [PATCH 08/16] fix warning message --- packages/jss-plugin-template/.size-snapshot.json | 6 +++--- packages/jss-plugin-template/src/index.test.js | 2 +- packages/jss-plugin-template/src/parse.js | 2 +- packages/jss-preset-default/.size-snapshot.json | 2 +- packages/jss-starter-kit/.size-snapshot.json | 2 +- packages/react-jss/.size-snapshot.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index 35ecf91c9..836a31d4d 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -2,7 +2,7 @@ "dist/jss-plugin-template.js": { "bundled": 3019, "minified": 1136, - "gzipped": 604 + "gzipped": 607 }, "dist/jss-plugin-template.min.js": { "bundled": 2485, @@ -12,12 +12,12 @@ "dist/jss-plugin-template.cjs.js": { "bundled": 2611, "minified": 1165, - "gzipped": 575 + "gzipped": 578 }, "dist/jss-plugin-template.esm.js": { "bundled": 2388, "minified": 991, - "gzipped": 496, + "gzipped": 498, "treeshaked": { "rollup": { "code": 21, diff --git a/packages/jss-plugin-template/src/index.test.js b/packages/jss-plugin-template/src/index.test.js index e98848875..b75d885cc 100644 --- a/packages/jss-plugin-template/src/index.test.js +++ b/packages/jss-plugin-template/src/index.test.js @@ -173,7 +173,7 @@ describe('jss-plugin-template', () => { ` }) expect(spy.args[0][0]).to.be( - 'Warning: [JSS] Missing opening curly brace in "} .a { color: blue; }".' + 'Warning: [JSS] Missing closing curly brace in "} .a { color: blue; }".' ) }) diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index fb4041d27..8c82d3f15 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -37,7 +37,7 @@ const parse = (cssText: string): Object => { // Closing brace should be on it's own line, otherwise the rest on that line // will be ignored, so we should warn the user. if (decl.length !== 1) { - warning(false, `[JSS] Missing opening curly brace in "${decl}".`) + warning(false, `[JSS] Missing closing curly brace in "${decl}".`) } continue } diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index 4c3fc9333..8bb5739b5 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -2,7 +2,7 @@ "dist/jss-preset-default.js": { "bundled": 55818, "minified": 19949, - "gzipped": 6586 + "gzipped": 6588 }, "dist/jss-preset-default.min.js": { "bundled": 54889, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index f40258d86..784e4960d 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -2,7 +2,7 @@ "dist/jss-starter-kit.js": { "bundled": 71360, "minified": 29958, - "gzipped": 9211 + "gzipped": 9214 }, "dist/jss-starter-kit.min.js": { "bundled": 70431, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index 9db717d5e..758b57820 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -2,7 +2,7 @@ "dist/react-jss.js": { "bundled": 111127, "minified": 37831, - "gzipped": 12221 + "gzipped": 12223 }, "dist/react-jss.min.js": { "bundled": 86393, From 8a4e4cf28af499e36570fb2d2eb65220710e56c2 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 09:31:42 +0200 Subject: [PATCH 09/16] add changelog --- changelog.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 822f67c2d..5e01b8b2c 100755 --- a/changelog.md +++ b/changelog.md @@ -3,7 +3,11 @@ ### Bug fixes - [jss-plugin-expand] Fix attributes spread for `border-bottom`, `border-top`, `border-left` and `border-right` ([#1083](https://github.com/cssinjs/jss/pull/1083)) -- [jss-plugin-props-sort] Fix sorting in Node 11 ([#1084](https://github.com/cssinjs/jss/pull/1083)) +- [jss-plugin-props-sort] Fix sorting in Node 11 ([#1085](https://github.com/cssinjs/jss/pull/1085)) + +### Improvements + +- [jss-plugin-template] Added nesting support ([#1103](https://github.com/cssinjs/jss/pull/1103)) ## 10.0.0-alpha.16 (2019-3-24) From 3ba236dc97646a920c5533eea555555b9b774819 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 10:34:26 +0200 Subject: [PATCH 10/16] better docs --- docs/jss-plugin-template.md | 14 +++++++--- .../jss-plugin-template/.size-snapshot.json | 8 +++--- packages/jss-plugin-template/src/parse.js | 26 ++++++++++++++++--- .../jss-preset-default/.size-snapshot.json | 4 +-- packages/jss-starter-kit/.size-snapshot.json | 4 +-- packages/react-jss/.size-snapshot.json | 4 +-- 6 files changed, 44 insertions(+), 16 deletions(-) diff --git a/docs/jss-plugin-template.md b/docs/jss-plugin-template.md index 11b809a73..1d13afe22 100644 --- a/docs/jss-plugin-template.md +++ b/docs/jss-plugin-template.md @@ -1,9 +1,17 @@ ## Enables string templates -Allows you to use string templates to declare CSS rules. It implements a **simplified** but **very fast** runtime CSS parser, with certain limitations: +This parser is not meant to be a complete one but to enable authoring styles using a template string with nesting syntax support, fastest parse performance and small footprint. -- Requires new lines at the end of each declaration. -- Requires a closing curly brace of a nested rule to be on a separate line. +Design of this parser has two main principles: + +1. It does not parse entire CSS. It uses only specific markers to separate selectors from props and values. +1. It uses warnings to make sure expected syntax is used instead of supporting the full syntax. + +To do that it requires some constraints: + +- Parser expects a new line after each declaration (`color: red;\n`). +- Parser expects an ampersand, selector and opening curly brace for nesting syntax on a single line (`& selector {`). +- Parser expects a closing curly brace on a separate line. ```js const styles = { diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index 836a31d4d..2628a1698 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,21 +1,21 @@ { "dist/jss-plugin-template.js": { - "bundled": 3019, + "bundled": 3764, "minified": 1136, "gzipped": 607 }, "dist/jss-plugin-template.min.js": { - "bundled": 2485, + "bundled": 3230, "minified": 859, "gzipped": 487 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 2611, + "bundled": 3316, "minified": 1165, "gzipped": 578 }, "dist/jss-plugin-template.esm.js": { - "bundled": 2388, + "bundled": 3093, "minified": 991, "gzipped": 498, "treeshaked": { diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index 8c82d3f15..e2b94426b 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -2,9 +2,29 @@ import warning from 'tiny-warning' /** - * Simplified CSS parser. - * - Requires new lines at the end of each declaration. - * - Requires a closing curly brace of a nested rule to be on a separate line. + * A simplified CSS parser. + * + * This parser is not meant to be a complete one but to enable authoring styles + * using a template string with nesting syntax support, fastest parse performance and small footprint. + * + * Design of this parser has two main principles: + * + * 1. It does not parse entire CSS. It uses only specific markers to separate selectors from props and values. + * 2. It uses warnings to make sure expected syntax is used instead of supporting the full syntax. + * + * To do that it requires some constraints: + * - Parser expects a new line after each declaration (`color: red;\n`). + * - Parser expects an ampersand, selector and opening curly brace for nesting syntax on a single line (`& selector {`). + * - Parser expects a closing curly brace on a separate line. + * + * Example + * + * ` + * color: red; + * &: hover { + * color: green; + * } + * ` */ const parse = (cssText: string): Object => { const style = {} diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index 8bb5739b5..fe26694b8 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,11 +1,11 @@ { "dist/jss-preset-default.js": { - "bundled": 55818, + "bundled": 56563, "minified": 19949, "gzipped": 6588 }, "dist/jss-preset-default.min.js": { - "bundled": 54889, + "bundled": 55634, "minified": 19377, "gzipped": 6353 }, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index 784e4960d..8ab2730c9 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,11 +1,11 @@ { "dist/jss-starter-kit.js": { - "bundled": 71360, + "bundled": 72105, "minified": 29958, "gzipped": 9214 }, "dist/jss-starter-kit.min.js": { - "bundled": 70431, + "bundled": 71176, "minified": 29389, "gzipped": 8987 }, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index 758b57820..503a79f0d 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,11 +1,11 @@ { "dist/react-jss.js": { - "bundled": 111127, + "bundled": 111872, "minified": 37831, "gzipped": 12223 }, "dist/react-jss.min.js": { - "bundled": 86393, + "bundled": 87138, "minified": 30451, "gzipped": 9999 }, From df99f729fa4bb3dba44dd7d2e7106c120c623c05 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 10:39:34 +0200 Subject: [PATCH 11/16] add benchmark section to the docs --- docs/jss-plugin-template.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/jss-plugin-template.md b/docs/jss-plugin-template.md index 1d13afe22..1ef28392f 100644 --- a/docs/jss-plugin-template.md +++ b/docs/jss-plugin-template.md @@ -34,3 +34,12 @@ const styles = { } } ``` + +### Benchmark + +``` +Chrome 74.0.3729 (Mac OS X 10.14.3) Parse: parse() at 117245 ops/sec +Chrome 74.0.3729 (Mac OS X 10.14.3) Parse: stylis() at 46939 ops/sec +Chrome 74.0.3729 (Mac OS X 10.14.3) + Parse: parse() at 117245 ops/sec (2.50x faster than stylis()) +``` From 72626dca59b4f7fa6bf14839d73b1d65b16200a7 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 10:48:32 +0200 Subject: [PATCH 12/16] update example --- packages/jss-plugin-template/.size-snapshot.json | 8 ++++---- packages/jss-plugin-template/src/parse.js | 2 +- packages/jss-preset-default/.size-snapshot.json | 4 ++-- packages/jss-starter-kit/.size-snapshot.json | 4 ++-- packages/react-jss/.size-snapshot.json | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index 2628a1698..aa00e8117 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,21 +1,21 @@ { "dist/jss-plugin-template.js": { - "bundled": 3764, + "bundled": 3763, "minified": 1136, "gzipped": 607 }, "dist/jss-plugin-template.min.js": { - "bundled": 3230, + "bundled": 3229, "minified": 859, "gzipped": 487 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 3316, + "bundled": 3315, "minified": 1165, "gzipped": 578 }, "dist/jss-plugin-template.esm.js": { - "bundled": 3093, + "bundled": 3092, "minified": 991, "gzipped": 498, "treeshaked": { diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index e2b94426b..36f7925d0 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -21,7 +21,7 @@ import warning from 'tiny-warning' * * ` * color: red; - * &: hover { + * &:hover { * color: green; * } * ` diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index fe26694b8..1b00c0d99 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,11 +1,11 @@ { "dist/jss-preset-default.js": { - "bundled": 56563, + "bundled": 56562, "minified": 19949, "gzipped": 6588 }, "dist/jss-preset-default.min.js": { - "bundled": 55634, + "bundled": 55633, "minified": 19377, "gzipped": 6353 }, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index 8ab2730c9..f3e8abbf9 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,11 +1,11 @@ { "dist/jss-starter-kit.js": { - "bundled": 72105, + "bundled": 72104, "minified": 29958, "gzipped": 9214 }, "dist/jss-starter-kit.min.js": { - "bundled": 71176, + "bundled": 71175, "minified": 29389, "gzipped": 8987 }, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index 503a79f0d..7ca7f03e1 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,11 +1,11 @@ { "dist/react-jss.js": { - "bundled": 111872, + "bundled": 111871, "minified": 37831, "gzipped": 12223 }, "dist/react-jss.min.js": { - "bundled": 87138, + "bundled": 87137, "minified": 30451, "gzipped": 9999 }, From b5feccc0cd7b4b0ad95487fce380160925f68ced Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 12:14:05 +0200 Subject: [PATCH 13/16] more perf and bundle size improvements --- docs/jss-plugin-template.md | 6 ++--- package.json | 2 +- .../jss-plugin-template/.size-snapshot.json | 24 +++++++++---------- packages/jss-plugin-template/src/parse.js | 17 +++++++------ .../jss-preset-default/.size-snapshot.json | 12 +++++----- packages/jss-starter-kit/.size-snapshot.json | 12 +++++----- packages/react-jss/.size-snapshot.json | 12 +++++----- webpack.config.js | 12 +++++++--- 8 files changed, 51 insertions(+), 46 deletions(-) diff --git a/docs/jss-plugin-template.md b/docs/jss-plugin-template.md index 1ef28392f..37ad921f1 100644 --- a/docs/jss-plugin-template.md +++ b/docs/jss-plugin-template.md @@ -38,8 +38,8 @@ const styles = { ### Benchmark ``` -Chrome 74.0.3729 (Mac OS X 10.14.3) Parse: parse() at 117245 ops/sec -Chrome 74.0.3729 (Mac OS X 10.14.3) Parse: stylis() at 46939 ops/sec +Chrome 74.0.3729 (Mac OS X 10.14.3) Parse: parse() at 122983 ops/sec +Chrome 74.0.3729 (Mac OS X 10.14.3) Parse: stylis() at 47582 ops/sec Chrome 74.0.3729 (Mac OS X 10.14.3) - Parse: parse() at 117245 ops/sec (2.50x faster than stylis()) + Parse: parse() at 122983 ops/sec (2.58x faster than stylis()) ``` diff --git a/package.json b/package.json index 4267af747..d55576451 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "format:ci": "yarn format --list-different", "test": "karma start --single-run", "test:watch": "karma start", - "test:bench": "cross-env BENCHMARK=true karma start --single-run", + "test:bench": "cross-env NODE_ENV=production BENCHMARK=true karma start --single-run", "pre-commit": "lint-staged && yarn check:all", "---Release Scripts---": "", "version": "yarn build && yarn test && node ./scripts/update-changelog && node ./scripts/add-git-files", diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index aa00e8117..7d8be3796 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,23 +1,23 @@ { "dist/jss-plugin-template.js": { - "bundled": 3763, - "minified": 1136, - "gzipped": 607 + "bundled": 3671, + "minified": 1112, + "gzipped": 594 }, "dist/jss-plugin-template.min.js": { - "bundled": 3229, - "minified": 859, - "gzipped": 487 + "bundled": 2995, + "minified": 793, + "gzipped": 462 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 3315, - "minified": 1165, - "gzipped": 578 + "bundled": 3268, + "minified": 1178, + "gzipped": 571 }, "dist/jss-plugin-template.esm.js": { - "bundled": 3092, - "minified": 991, - "gzipped": 498, + "bundled": 3045, + "minified": 1004, + "gzipped": 491, "treeshaked": { "rollup": { "code": 21, diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index 36f7925d0..4eff7118d 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -32,7 +32,7 @@ const parse = (cssText: string): Object => { const rules = [style] for (let i = 0; i < lines.length; i++) { - const decl = (lines[i] || '').trim() + const decl = lines[i].trim() if (!decl) continue @@ -42,7 +42,6 @@ const parse = (cssText: string): Object => { const openCurlyIndex = decl.indexOf('{') if (openCurlyIndex === -1) { warning(false, `[JSS] Missing opening curly brace in "${decl}".`) - continue } const key = decl.substring(0, openCurlyIndex - 1) const nestedStyle = {} @@ -52,13 +51,14 @@ const parse = (cssText: string): Object => { } // We are closing a nested rule. - if (decl.indexOf('}') !== -1) { + if (decl === '}') { rules.pop() - // Closing brace should be on it's own line, otherwise the rest on that line - // will be ignored, so we should warn the user. - if (decl.length !== 1) { - warning(false, `[JSS] Missing closing curly brace in "${decl}".`) - } + continue + } + + // We are closing a nested rule, but the curly brace is not on a separate line. + if (process.env.NODE_ENV !== 'production' && decl.indexOf('}') !== -1) { + warning(false, `[JSS] Missing closing curly brace in "${decl}".`) continue } @@ -66,7 +66,6 @@ const parse = (cssText: string): Object => { if (colonIndex === -1) { warning(false, `[JSS] Missing colon in "${decl}".`) - continue } const prop = decl.substring(0, colonIndex).trim() diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index 1b00c0d99..46e2e1ce2 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-preset-default.js": { - "bundled": 56562, - "minified": 19949, - "gzipped": 6588 + "bundled": 56470, + "minified": 19925, + "gzipped": 6576 }, "dist/jss-preset-default.min.js": { - "bundled": 55633, - "minified": 19377, - "gzipped": 6353 + "bundled": 55399, + "minified": 19311, + "gzipped": 6331 }, "dist/jss-preset-default.cjs.js": { "bundled": 1329, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index f3e8abbf9..5ba75e0d0 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-starter-kit.js": { - "bundled": 72104, - "minified": 29958, - "gzipped": 9214 + "bundled": 72012, + "minified": 29934, + "gzipped": 9196 }, "dist/jss-starter-kit.min.js": { - "bundled": 71175, - "minified": 29389, - "gzipped": 8987 + "bundled": 70941, + "minified": 29323, + "gzipped": 8965 }, "dist/jss-starter-kit.cjs.js": { "bundled": 2592, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index 7ca7f03e1..15e854cd0 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/react-jss.js": { - "bundled": 111871, - "minified": 37831, - "gzipped": 12223 + "bundled": 111779, + "minified": 37807, + "gzipped": 12210 }, "dist/react-jss.min.js": { - "bundled": 87137, - "minified": 30451, - "gzipped": 9999 + "bundled": 86903, + "minified": 30385, + "gzipped": 9975 }, "dist/react-jss.cjs.js": { "bundled": 15499, diff --git a/webpack.config.js b/webpack.config.js index 64c11910f..02753f8a6 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -11,15 +11,21 @@ const plugins = [ }) ] +const babelPlugins = ['@babel/proposal-class-properties', '@babel/proposal-object-rest-spread'] + +if (process.env.BENCHMARK) { + babelPlugins.push('dev-expression') +} + module.exports = { - mode: 'none', + mode: process.env.BENCHMARK ? 'production' : 'none', entry: './packages/jss/src/index', output: { library: 'jss', libraryTarget: 'umd' }, optimization: { - nodeEnv: false + nodeEnv: process.env.BENCHMARK ? 'production' : false }, plugins, module: { @@ -30,7 +36,7 @@ module.exports = { exclude: /node_modules/, options: { presets: ['@babel/react', '@babel/flow', '@babel/env'], - plugins: ['@babel/proposal-class-properties', '@babel/proposal-object-rest-spread'] + plugins: babelPlugins } } ] From a4478878dc1c152dbdca1cb87042bc4a5e874765 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 13 May 2019 21:18:51 +0200 Subject: [PATCH 14/16] use the JssStyles typ --- packages/jss-plugin-template/.size-snapshot.json | 8 ++++---- packages/jss-plugin-template/src/parse.js | 3 ++- packages/jss/src/index.js | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index 7d8be3796..e85e82106 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,21 +1,21 @@ { "dist/jss-plugin-template.js": { - "bundled": 3671, + "bundled": 3670, "minified": 1112, "gzipped": 594 }, "dist/jss-plugin-template.min.js": { - "bundled": 2995, + "bundled": 2994, "minified": 793, "gzipped": 462 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 3268, + "bundled": 3267, "minified": 1178, "gzipped": 571 }, "dist/jss-plugin-template.esm.js": { - "bundled": 3045, + "bundled": 3044, "minified": 1004, "gzipped": 491, "treeshaked": { diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index 4eff7118d..e41209f52 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -1,5 +1,6 @@ // @flow import warning from 'tiny-warning' +import type {JssStyles} from 'jss' /** * A simplified CSS parser. @@ -26,7 +27,7 @@ import warning from 'tiny-warning' * } * ` */ -const parse = (cssText: string): Object => { +const parse = (cssText: string): JssStyles => { const style = {} const lines = cssText.split('\n') const rules = [style] diff --git a/packages/jss/src/index.js b/packages/jss/src/index.js index 51150eb81..5d474c88f 100644 --- a/packages/jss/src/index.js +++ b/packages/jss/src/index.js @@ -26,6 +26,7 @@ export type { JssValue, JssOptions, JssStyle, + JssStyles, Plugin, GenerateId, RuleListOptions, From dcac2a70731f4f9de9a2b6f45d88bf1431a6eec5 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Fri, 17 May 2019 17:23:05 +0200 Subject: [PATCH 15/16] trim the nested selector, refactor tests --- .../jss-plugin-template/.size-snapshot.json | 24 +-- .../jss-plugin-template/src/index.test.js | 191 ----------------- packages/jss-plugin-template/src/parse.js | 3 +- .../jss-plugin-template/src/parse.test.js | 194 ++++++++++++++++++ .../jss-preset-default/.size-snapshot.json | 12 +- packages/jss-starter-kit/.size-snapshot.json | 12 +- packages/react-jss/.size-snapshot.json | 12 +- 7 files changed, 226 insertions(+), 222 deletions(-) create mode 100644 packages/jss-plugin-template/src/parse.test.js diff --git a/packages/jss-plugin-template/.size-snapshot.json b/packages/jss-plugin-template/.size-snapshot.json index e85e82106..192e75f62 100644 --- a/packages/jss-plugin-template/.size-snapshot.json +++ b/packages/jss-plugin-template/.size-snapshot.json @@ -1,23 +1,23 @@ { "dist/jss-plugin-template.js": { - "bundled": 3670, - "minified": 1112, - "gzipped": 594 + "bundled": 3694, + "minified": 1128, + "gzipped": 603 }, "dist/jss-plugin-template.min.js": { - "bundled": 2994, - "minified": 793, - "gzipped": 462 + "bundled": 3066, + "minified": 820, + "gzipped": 474 }, "dist/jss-plugin-template.cjs.js": { - "bundled": 3267, - "minified": 1178, - "gzipped": 571 + "bundled": 3289, + "minified": 1194, + "gzipped": 579 }, "dist/jss-plugin-template.esm.js": { - "bundled": 3044, - "minified": 1004, - "gzipped": 491, + "bundled": 3066, + "minified": 1020, + "gzipped": 501, "treeshaked": { "rollup": { "code": 21, diff --git a/packages/jss-plugin-template/src/index.test.js b/packages/jss-plugin-template/src/index.test.js index b75d885cc..8b72633e7 100644 --- a/packages/jss-plugin-template/src/index.test.js +++ b/packages/jss-plugin-template/src/index.test.js @@ -3,7 +3,6 @@ import expect from 'expect.js' import {stripIndent} from 'common-tags' import {create} from 'jss' -import sinon from 'sinon' import nested from 'jss-plugin-nested' import template, {cache} from '.' @@ -12,92 +11,18 @@ const settings = { } describe('jss-plugin-template', () => { - let spy let jss beforeEach(() => { - spy = sinon.spy(console, 'warn') jss = create(settings).use(template(), nested()) }) - afterEach(() => { - console.warn.restore() - }) - - it('should convert a single single property/value', () => { - const sheet = jss.createStyleSheet({ - a: ` - color: red; - ` - }) - expect(sheet.toString()).to.be(stripIndent` - .a-id { - color: red; - } - `) - }) - it('should cache parsed template', () => { const a = `color: red` jss.createStyleSheet({a}) expect(cache[a]).to.eql({color: 'red'}) }) - it('should parse multiple props/values', () => { - const sheet = jss.createStyleSheet({ - a: ` - color: red; - float: left; - ` - }) - expect(sheet.toString()).to.be(stripIndent` - .a-id { - color: red; - float: left; - } - `) - expect(spy.callCount).to.be(0) - }) - - it('should warn when there is no colon found', () => { - jss.createStyleSheet({ - a: 'color red;' - }) - - expect(spy.callCount).to.be(1) - expect(spy.args[0][0]).to.be('Warning: [JSS] Missing colon in "color red;".') - }) - - it('should strip spaces', () => { - const sheet = jss.createStyleSheet({ - a: ` - color: red ; - float: left ; - ` - }) - expect(sheet.toString()).to.be(stripIndent` - .a-id { - color: red; - float: left; - } - `) - }) - - it('should not require semicolon', () => { - const sheet = jss.createStyleSheet({ - a: ` - color: red - float: left - ` - }) - expect(sheet.toString()).to.be(stripIndent` - .a-id { - color: red; - float: left; - } - `) - }) - it('should support @media', () => { const sheet = jss.createStyleSheet({ '@media print': { @@ -131,120 +56,4 @@ describe('jss-plugin-template', () => { } `) }) - - it('should support nesting', () => { - const sheet = jss.createStyleSheet({ - a: ` - color: green; - & .b { - color: red; - } - ` - }) - expect(sheet.toString()).to.be(stripIndent` - .a-id { - color: green; - } - .a-id .b { - color: red; - } - `) - }) - - it('should warn when opening curly brace is missing', () => { - jss.createStyleSheet({ - a: ` - color: green; - & .b - color: red; - } - ` - }) - expect(spy.args[0][0]).to.be('Warning: [JSS] Missing opening curly brace in "& .b".') - }) - - it('should warn when closing curly brace is not on an own line', () => { - jss.createStyleSheet({ - a: ` - color: green; - & .b { - color: red; - } .a { color: blue; } - ` - }) - expect(spy.args[0][0]).to.be( - 'Warning: [JSS] Missing closing curly brace in "} .a { color: blue; }".' - ) - }) - - it('should support multiple first level nested rules', () => { - const sheet = jss.createStyleSheet({ - a: ` - color: green; - & .b { - color: red; - } - & .c { - color: blue; - } - ` - }) - expect(sheet.toString()).to.be(stripIndent` - .a-id { - color: green; - } - .a-id .b { - color: red; - } - .a-id .c { - color: blue; - } - `) - }) - - it('should support multiple deeply nested rules', () => { - const sheet = jss.createStyleSheet({ - a: ` - color: green; - & .b { - color: red; - & .c { - color: blue; - } - } - ` - }) - expect(sheet.toString()).to.be(stripIndent` - .a-id { - color: green; - } - .a-id .b { - color: red; - } - .a-id .b .c { - color: blue; - } - `) - }) - - it('should regular props after a nested rule', () => { - const sheet = jss.createStyleSheet({ - a: ` - color: green; - & .b { - color: red; - } - float: left; - ` - }) - expect(sheet.toString()).to.be(stripIndent` - .a-id { - color: green; - float: left; - } - .a-id .b { - color: red; - } - `) - }) }) diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index e41209f52..10d5742f6 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -43,8 +43,9 @@ const parse = (cssText: string): JssStyles => { const openCurlyIndex = decl.indexOf('{') if (openCurlyIndex === -1) { warning(false, `[JSS] Missing opening curly brace in "${decl}".`) + break } - const key = decl.substring(0, openCurlyIndex - 1) + const key = decl.substring(0, openCurlyIndex - 1).trim() const nestedStyle = {} rules[rules.length - 1][key] = nestedStyle rules.push(nestedStyle) diff --git a/packages/jss-plugin-template/src/parse.test.js b/packages/jss-plugin-template/src/parse.test.js new file mode 100644 index 000000000..9375fc464 --- /dev/null +++ b/packages/jss-plugin-template/src/parse.test.js @@ -0,0 +1,194 @@ +/* eslint-disable no-underscore-dangle */ + +import expect from 'expect.js' +import sinon from 'sinon' +import parse from './parse' + +describe('jss-plugin-template parse()', () => { + let warnSpy + + beforeEach(() => { + warnSpy = sinon.spy(console, 'warn') + }) + + afterEach(() => { + console.warn.restore() + }) + + it('should convert a single single property/value', () => { + const styles = parse(` + color: red; + `) + expect(styles).to.eql({ + color: 'red' + }) + expect(warnSpy.callCount).to.be(0) + }) + + it('should parse multiple props/values', () => { + const styles = parse(` + color: red; + float: left; + `) + expect(styles).to.eql({ + color: 'red', + float: 'left' + }) + expect(warnSpy.callCount).to.be(0) + }) + + it('should warn when there is no colon found', () => { + parse('color red;') + expect(warnSpy.callCount).to.be(1) + expect(warnSpy.args[0][0]).to.be('Warning: [JSS] Missing colon in "color red;".') + }) + + it('should strip spaces', () => { + const styles = parse(` + color: red ; + float: left ; + `) + expect(styles).to.eql({ + color: 'red', + float: 'left' + }) + expect(warnSpy.callCount).to.be(0) + }) + + it('should not require semicolon', () => { + const styles = parse(` + color: red + float: left + `) + expect(styles).to.eql({ + color: 'red', + float: 'left' + }) + expect(warnSpy.callCount).to.be(0) + }) + + it('should support nesting', () => { + const styles = parse(` + color: green; + & .b { + color: red; + } + `) + expect(styles).to.eql({ + color: 'green', + '& .b': { + color: 'red' + } + }) + expect(warnSpy.callCount).to.be(0) + }) + + it('should warn when opening curly brace is missing', () => { + const styles = parse(` + color: green; + & .b + color: red; + } + `) + expect(styles).to.eql({ + color: 'green' + }) + expect(warnSpy.args[0][0]).to.be('Warning: [JSS] Missing opening curly brace in "& .b".') + }) + + it('should warn when closing curly brace is not on an own line', () => { + const styles = parse(` + color: green; + & .b { + color: red; + } .a { color: blue; } + `) + expect(styles).to.eql({ + color: 'green', + '& .b': { + color: 'red' + } + }) + expect(warnSpy.args[0][0]).to.be( + 'Warning: [JSS] Missing closing curly brace in "} .a { color: blue; }".' + ) + }) + + it('should support multiple first level nested rules', () => { + const styles = parse(` + color: green; + & .b { + color: red; + } + & .c { + color: blue; + } + `) + expect(styles).to.eql({ + color: 'green', + '& .b': { + color: 'red' + }, + '& .c': { + color: 'blue' + } + }) + expect(warnSpy.callCount).to.be(0) + }) + + it('should support multiple deeply nested rules', () => { + const styles = parse(` + color: green; + & .b { + color: red; + & .c { + color: blue; + } + } + `) + expect(styles).to.eql({ + color: 'green', + '& .b': { + color: 'red', + '& .c': { + color: 'blue' + } + } + }) + expect(warnSpy.callCount).to.be(0) + }) + + it('should regular props after a nested rule', () => { + const styles = parse(` + color: green; + & .b { + color: red; + } + float: left; + `) + expect(styles).to.eql({ + color: 'green', + '& .b': { + color: 'red' + }, + float: 'left' + }) + expect(warnSpy.callCount).to.be(0) + }) + + it('should expect ampersand in the middle of the line', () => { + const styles = parse(` + color: green; + body & .b { + color: red; + } + `) + expect(styles).to.eql({ + color: 'green', + 'body & .b': { + color: 'red' + } + }) + expect(warnSpy.callCount).to.be(0) + }) +}) diff --git a/packages/jss-preset-default/.size-snapshot.json b/packages/jss-preset-default/.size-snapshot.json index 46e2e1ce2..1c650a98e 100644 --- a/packages/jss-preset-default/.size-snapshot.json +++ b/packages/jss-preset-default/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-preset-default.js": { - "bundled": 56470, - "minified": 19925, - "gzipped": 6576 + "bundled": 56494, + "minified": 19941, + "gzipped": 6581 }, "dist/jss-preset-default.min.js": { - "bundled": 55399, - "minified": 19311, - "gzipped": 6331 + "bundled": 55471, + "minified": 19338, + "gzipped": 6341 }, "dist/jss-preset-default.cjs.js": { "bundled": 1329, diff --git a/packages/jss-starter-kit/.size-snapshot.json b/packages/jss-starter-kit/.size-snapshot.json index 5ba75e0d0..d201904d9 100644 --- a/packages/jss-starter-kit/.size-snapshot.json +++ b/packages/jss-starter-kit/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/jss-starter-kit.js": { - "bundled": 72012, - "minified": 29934, - "gzipped": 9196 + "bundled": 72036, + "minified": 29950, + "gzipped": 9203 }, "dist/jss-starter-kit.min.js": { - "bundled": 70941, - "minified": 29323, - "gzipped": 8965 + "bundled": 71013, + "minified": 29350, + "gzipped": 8975 }, "dist/jss-starter-kit.cjs.js": { "bundled": 2592, diff --git a/packages/react-jss/.size-snapshot.json b/packages/react-jss/.size-snapshot.json index 15e854cd0..a69318324 100644 --- a/packages/react-jss/.size-snapshot.json +++ b/packages/react-jss/.size-snapshot.json @@ -1,13 +1,13 @@ { "dist/react-jss.js": { - "bundled": 111779, - "minified": 37807, - "gzipped": 12210 + "bundled": 111803, + "minified": 37823, + "gzipped": 12217 }, "dist/react-jss.min.js": { - "bundled": 86903, - "minified": 30385, - "gzipped": 9975 + "bundled": 86975, + "minified": 30412, + "gzipped": 9985 }, "dist/react-jss.cjs.js": { "bundled": 15499, From 8b831fd5c9a7b2d8568b92d98549a9216daf1c3e Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sun, 2 Jun 2019 12:27:07 +0200 Subject: [PATCH 16/16] Test implementation without using .split() --- package.json | 2 +- .../benchmark/tests/parse.js | 6 +- packages/jss-plugin-template/src/parse.js | 70 ++++++++++++++++++- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index d55576451..4267af747 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "format:ci": "yarn format --list-different", "test": "karma start --single-run", "test:watch": "karma start", - "test:bench": "cross-env NODE_ENV=production BENCHMARK=true karma start --single-run", + "test:bench": "cross-env BENCHMARK=true karma start --single-run", "pre-commit": "lint-staged && yarn check:all", "---Release Scripts---": "", "version": "yarn build && yarn test && node ./scripts/update-changelog && node ./scripts/add-git-files", diff --git a/packages/jss-plugin-template/benchmark/tests/parse.js b/packages/jss-plugin-template/benchmark/tests/parse.js index d5f08885d..da6149548 100644 --- a/packages/jss-plugin-template/benchmark/tests/parse.js +++ b/packages/jss-plugin-template/benchmark/tests/parse.js @@ -1,5 +1,5 @@ import stylis from 'stylis' -import parse from '../../src/parse' +import parse, {parse2} from '../../src/parse' const css = ` color: rgb(77, 77, 77); @@ -52,6 +52,10 @@ suite('Parse', () => { parse(css) }) + benchmark('parse2()', () => { + parse2(css) + }) + benchmark('stylis()', () => { stylis('#id', css) }) diff --git a/packages/jss-plugin-template/src/parse.js b/packages/jss-plugin-template/src/parse.js index 10d5742f6..ee47feee7 100644 --- a/packages/jss-plugin-template/src/parse.js +++ b/packages/jss-plugin-template/src/parse.js @@ -27,7 +27,75 @@ import type {JssStyles} from 'jss' * } * ` */ + +// Test implementation without using .split() const parse = (cssText: string): JssStyles => { + const style = {} + const rules = [style] + let line + let done + let prevNlIndex = 0 + + while (!done) { + const nextNlIndex = cssText.indexOf('\n', prevNlIndex) + + if (nextNlIndex === -1) { + done = true + line = cssText.substring(prevNlIndex).trim() + } else { + line = cssText.substring(prevNlIndex, nextNlIndex).trim() + prevNlIndex = nextNlIndex + 1 + } + + if (!line) continue + + const ampIndex = line.indexOf('&') + + if (ampIndex !== -1) { + const openCurlyIndex = line.indexOf('{') + if (openCurlyIndex === -1) { + warning(false, `[JSS] Missing opening curly brace in "${line}".`) + break + } + const key = line.substring(0, openCurlyIndex - 1).trim() + const nestedStyle = {} + rules[rules.length - 1][key] = nestedStyle + rules.push(nestedStyle) + continue + } + + // We are closing a nested rule. + if (line === '}') { + rules.pop() + continue + } + + // We are closing a nested rule, but the curly brace is not on a separate line. + if (process.env.NODE_ENV !== 'production' && line.indexOf('}') !== -1) { + warning(false, `[JSS] Missing closing curly brace in "${line}".`) + continue + } + + const colonIndex = line.indexOf(':') + + if (colonIndex === -1) { + warning(false, `[JSS] Missing colon in "${line}".`) + } + + const prop = line.substring(0, colonIndex).trim() + // We need to remove semicolon from value if there is one. + const semi = line[line.length - 1] === ';' ? 1 : 0 + const value = line.substring(colonIndex + 1, line.length - semi).trim() + rules[rules.length - 1][prop] = value + } + + return style +} + +export default parse + +// Temporarily here for comparison. +export const parse2 = (cssText: string): JssStyles => { const style = {} const lines = cssText.split('\n') const rules = [style] @@ -79,5 +147,3 @@ const parse = (cssText: string): JssStyles => { return style } - -export default parse