From da0847ca00d88cd8126bf39659e6152e9d38a06b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 15 Aug 2018 09:52:52 +0100 Subject: [PATCH 01/17] Styles: auto-wrap theme provided styles to avoid specifity in the theme stylesheets --- lib/client-assets.php | 1 + package-lock.json | 117 +++++++++++++++--- packages/editor/package.json | 2 + .../editor/src/components/provider/index.js | 15 +++ 4 files changed, 118 insertions(+), 17 deletions(-) diff --git a/lib/client-assets.php b/lib/client-assets.php index 8a2b226eea04b..b0ee951efe5f8 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -1412,6 +1412,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) { 'autosaveInterval' => 10, 'maxUploadFileSize' => $max_upload_size, 'allowedMimeTypes' => get_allowed_mime_types(), + 'styles' => 'h2 { color: red; }' ); $post_autosave = get_autosave_newer_than_post_save( $post ); diff --git a/package-lock.json b/package-lock.json index 20c08dc159964..7fa15f3d5329a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2463,6 +2463,8 @@ "element-closest": "^2.0.2", "lodash": "^4.17.10", "memize": "^1.0.5", + "postcss": "^7.0.2", + "postcss-wrap": "^0.0.4", "react-autosize-textarea": "^3.0.2", "redux-multi": "^0.1.12", "redux-optimist": "^1.0.0", @@ -2471,6 +2473,21 @@ "tinycolor2": "^1.4.1", "tinymce": "^4.7.2", "uuid": "^3.1.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.2", + "bundled": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "bundled": true + } } }, "@wordpress/element": { @@ -2814,7 +2831,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -4796,7 +4812,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -5302,7 +5317,6 @@ "version": "1.9.2", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", - "dev": true, "requires": { "color-name": "1.1.1" } @@ -5310,8 +5324,7 @@ "color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", - "dev": true + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" }, "color-string": { "version": "0.3.0", @@ -7063,8 +7076,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.10.0", @@ -9502,7 +9514,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" }, @@ -9510,8 +9521,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" } } }, @@ -9524,8 +9534,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbol-support-x": { "version": "1.4.2", @@ -12252,8 +12261,7 @@ "js-base64": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.6.tgz", - "integrity": "sha512-O9SR2NVICx6rCqh1qsU91QZ5IoNa+2T1ROJ0OQlfvATKGmnjsAvg3r0E5ufPZ4a95jdKTPXhFWiE/sOZ7a5Rtg==", - "dev": true + "integrity": "sha512-O9SR2NVICx6rCqh1qsU91QZ5IoNa+2T1ROJ0OQlfvATKGmnjsAvg3r0E5ufPZ4a95jdKTPXhFWiE/sOZ7a5Rtg==" }, "js-levenshtein": { "version": "1.1.3", @@ -16453,6 +16461,83 @@ "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", "dev": true }, + "postcss-wrap": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/postcss-wrap/-/postcss-wrap-0.0.4.tgz", + "integrity": "sha1-Eye9caqki+mdoVrG5Bs1X3mJie4=", + "requires": { + "colors": "^1.1.2", + "postcss": "^5.0.13" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "colors": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.1.tgz", + "integrity": "sha512-jg/vxRmv430jixZrC+La5kMbUWqIg32/JsYNZb94+JEmzceYbWKTsv1OuTp+7EaqiaWRR2tPcykibwCRgclIsw==" + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, "postcss-zindex": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", @@ -18426,8 +18511,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-loader": { "version": "0.2.3", @@ -19244,7 +19328,6 @@ "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, "requires": { "has-flag": "^3.0.0" } diff --git a/packages/editor/package.json b/packages/editor/package.json index 88f301c6c5866..3eaeee208a0e5 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -48,6 +48,8 @@ "element-closest": "^2.0.2", "lodash": "^4.17.10", "memize": "^1.0.5", + "postcss": "^7.0.2", + "postcss-wrap": "^0.0.4", "react-autosize-textarea": "^3.0.2", "redux-multi": "^0.1.12", "redux-optimist": "^1.0.0", diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index a6216bad3a464..a2b6a1a36337a 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -2,6 +2,8 @@ * External dependencies */ import { flow } from 'lodash'; +import postcss from 'postcss'; +import wrap from 'postcss-wrap'; /** * WordPress Dependencies @@ -26,6 +28,19 @@ class EditorProvider extends Component { } } + componentDidMount() { + if ( ! this.props.settings.styles ) { + return; + } + postcss( [ wrap( { selector: '.editor-block-list__block' } ) ] ) + .process( this.props.settings.styles ) + .then( ( css ) => { + const node = document.createElement( 'style' ); + node.innerHTML = css; + document.body.appendChild( node ); + } ); + } + componentDidUpdate( prevProps ) { if ( this.props.settings !== prevProps.settings ) { this.props.updateEditorSettings( this.props.settings ); From a3b983bb47ec4403c236ead85050038f3ac12143 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 15 Aug 2018 11:09:18 +0100 Subject: [PATCH 02/17] Change the post-css plugin used (use a smarter one) --- package-lock.json | 127 +++++++----------- packages/editor/package.json | 2 +- .../editor/src/components/provider/index.js | 4 +- 3 files changed, 49 insertions(+), 84 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7fa15f3d5329a..795c775220e24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2464,7 +2464,7 @@ "lodash": "^4.17.10", "memize": "^1.0.5", "postcss": "^7.0.2", - "postcss-wrap": "^0.0.4", + "postcss-prefixwrap": "^1.3.0", "react-autosize-textarea": "^3.0.2", "redux-multi": "^0.1.12", "redux-optimist": "^1.0.0", @@ -9514,6 +9514,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" }, @@ -9521,7 +9522,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true } } }, @@ -12261,7 +12263,8 @@ "js-base64": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.6.tgz", - "integrity": "sha512-O9SR2NVICx6rCqh1qsU91QZ5IoNa+2T1ROJ0OQlfvATKGmnjsAvg3r0E5ufPZ4a95jdKTPXhFWiE/sOZ7a5Rtg==" + "integrity": "sha512-O9SR2NVICx6rCqh1qsU91QZ5IoNa+2T1ROJ0OQlfvATKGmnjsAvg3r0E5ufPZ4a95jdKTPXhFWiE/sOZ7a5Rtg==", + "dev": true }, "js-levenshtein": { "version": "1.1.3", @@ -16188,6 +16191,44 @@ "postcss-value-parser": "^3.0.0" } }, + "postcss-prefixwrap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-prefixwrap/-/postcss-prefixwrap-1.3.0.tgz", + "integrity": "sha512-ru2dL0bn7RgWYzHX7smrGsUbq6vh2JHudAo409TrvjpB46kE5UdSyjdKU0ZWesUfqwYEMlSy622ACu52C9XIww==", + "requires": { + "postcss": "6.0.14" + }, + "dependencies": { + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "postcss": { + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", + "integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", + "requires": { + "chalk": "^2.3.0", + "source-map": "^0.6.1", + "supports-color": "^4.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "requires": { + "has-flag": "^2.0.0" + } + } + } + }, "postcss-reduce-idents": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", @@ -16461,83 +16502,6 @@ "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", "dev": true }, - "postcss-wrap": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/postcss-wrap/-/postcss-wrap-0.0.4.tgz", - "integrity": "sha1-Eye9caqki+mdoVrG5Bs1X3mJie4=", - "requires": { - "colors": "^1.1.2", - "postcss": "^5.0.13" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "colors": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.1.tgz", - "integrity": "sha512-jg/vxRmv430jixZrC+La5kMbUWqIg32/JsYNZb94+JEmzceYbWKTsv1OuTp+7EaqiaWRR2tPcykibwCRgclIsw==" - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, "postcss-zindex": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", @@ -18511,7 +18475,8 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true }, "source-map-loader": { "version": "0.2.3", diff --git a/packages/editor/package.json b/packages/editor/package.json index 3eaeee208a0e5..cd045ceeee719 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -49,7 +49,7 @@ "lodash": "^4.17.10", "memize": "^1.0.5", "postcss": "^7.0.2", - "postcss-wrap": "^0.0.4", + "postcss-prefixwrap": "^1.3.0", "react-autosize-textarea": "^3.0.2", "redux-multi": "^0.1.12", "redux-optimist": "^1.0.0", diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index a2b6a1a36337a..6576b1aaddbc1 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -3,7 +3,7 @@ */ import { flow } from 'lodash'; import postcss from 'postcss'; -import wrap from 'postcss-wrap'; +import wrap from 'postcss-prefixwrap'; /** * WordPress Dependencies @@ -32,7 +32,7 @@ class EditorProvider extends Component { if ( ! this.props.settings.styles ) { return; } - postcss( [ wrap( { selector: '.editor-block-list__block' } ) ] ) + postcss( [ wrap( '.editor-block-list__block' ) ] ) .process( this.props.settings.styles ) .then( ( css ) => { const node = document.createElement( 'style' ); From deccd21e712aee488760b594e1adae7ae44a554f Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 20 Aug 2018 11:22:53 +0100 Subject: [PATCH 03/17] Add support for the editor styles --- lib/client-assets.php | 21 +- package-lock.json | 22 ++ package.json | 1 + .../editor/src/components/provider/index.js | 24 ++- packages/postcss-themes/src/index.js | 2 +- packages/postcss-url/.npmrc | 1 + packages/postcss-url/README.md | 0 packages/postcss-url/package.json | 31 +++ packages/postcss-url/src/index.js | 196 ++++++++++++++++++ .../test/__snapshots__/index.js.snap | 9 + packages/postcss-url/test/index.js | 54 +++++ 11 files changed, 351 insertions(+), 10 deletions(-) create mode 100644 packages/postcss-url/.npmrc create mode 100644 packages/postcss-url/README.md create mode 100644 packages/postcss-url/package.json create mode 100644 packages/postcss-url/src/index.js create mode 100644 packages/postcss-url/test/__snapshots__/index.js.snap create mode 100644 packages/postcss-url/test/index.js diff --git a/lib/client-assets.php b/lib/client-assets.php index b0ee951efe5f8..5d018db209046 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -1400,6 +1400,25 @@ function gutenberg_editor_scripts_and_styles( $hook ) { $max_upload_size = 0; } + // Editor Styles + global $editor_styles; + $styles = array(); + if ( $editor_styles ) { + foreach ( $editor_styles as $style ) { + if (filter_var($style, FILTER_VALIDATE_URL)) { + $styles[] = array( + 'css' => file_get_contents( $style ) + ); + } else { + $file = get_theme_file_path( $style ); + $styles[] = array( + 'css' => file_get_contents( get_theme_file_path( $style ) ), + 'baseURL' => get_theme_file_uri( $style ), + ); + } + } + } + $editor_settings = array( 'alignWide' => $align_wide || ! empty( $gutenberg_theme_support[0]['wide-images'] ), // Backcompat. Use `align-wide` outside of `gutenberg` array. 'availableTemplates' => $available_templates, @@ -1412,7 +1431,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) { 'autosaveInterval' => 10, 'maxUploadFileSize' => $max_upload_size, 'allowedMimeTypes' => get_allowed_mime_types(), - 'styles' => 'h2 { color: red; }' + 'styles' => $styles ); $post_autosave = get_autosave_newer_than_post_save( $post ); diff --git a/package-lock.json b/package-lock.json index 795c775220e24..e6eb3be884ff0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2608,6 +2608,28 @@ "postcss": "^6.0.16" } }, + "@wordpress/postcss-url": { + "version": "file:packages/postcss-url", + "requires": { + "@babel/runtime-corejs2": "7.0.0-beta.56", + "postcss": "^7.0.2" + }, + "dependencies": { + "postcss": { + "version": "7.0.2", + "bundled": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "bundled": true + } + } + }, "@wordpress/redux-routine": { "version": "file:packages/redux-routine", "requires": { diff --git a/package.json b/package.json index 4d1540c2d44af..f91d7520d4215 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/nux": "file:packages/nux", "@wordpress/plugins": "file:packages/plugins", + "@wordpress/postcss-url": "file:packages/postcss-url", "@wordpress/redux-routine": "file:packages/redux-routine", "@wordpress/shortcode": "file:packages/shortcode", "@wordpress/token-list": "file:packages/token-list", diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index 6576b1aaddbc1..b321a2a2102d0 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -1,13 +1,14 @@ /** * External dependencies */ -import { flow } from 'lodash'; +import { flow, map } from 'lodash'; import postcss from 'postcss'; import wrap from 'postcss-prefixwrap'; /** * WordPress Dependencies */ +import urlReplace from '@wordpress/postcss-url'; import { createElement, Component } from '@wordpress/element'; import { DropZoneProvider, SlotFillProvider } from '@wordpress/components'; import { withDispatch } from '@wordpress/data'; @@ -32,13 +33,20 @@ class EditorProvider extends Component { if ( ! this.props.settings.styles ) { return; } - postcss( [ wrap( '.editor-block-list__block' ) ] ) - .process( this.props.settings.styles ) - .then( ( css ) => { - const node = document.createElement( 'style' ); - node.innerHTML = css; - document.body.appendChild( node ); - } ); + + map( this.props.settings.styles, ( { css, baseURL } ) => { + const transforms = [ wrap( '.editor-block-list__block' ) ]; + if ( baseURL ) { + transforms.push( urlReplace( { baseURL } ) ); + } + postcss( transforms ) + .process( css ) + .then( ( output ) => { + const node = document.createElement( 'style' ); + node.innerHTML = output; + document.body.appendChild( node ); + } ); + } ); } componentDidUpdate( prevProps ) { diff --git a/packages/postcss-themes/src/index.js b/packages/postcss-themes/src/index.js index 6ede8e47c5553..5018a0dd39a4e 100644 --- a/packages/postcss-themes/src/index.js +++ b/packages/postcss-themes/src/index.js @@ -1,6 +1,6 @@ const postcss = require( 'postcss' ); -module.exports = postcss.plugin( 'postcss-test-plugin', function( options ) { +module.exports = postcss.plugin( 'postcss-themes', function( options ) { return function( root ) { root.walkRules( ( rule ) => { const themeDecls = {}; diff --git a/packages/postcss-url/.npmrc b/packages/postcss-url/.npmrc new file mode 100644 index 0000000000000..43c97e719a5a8 --- /dev/null +++ b/packages/postcss-url/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/postcss-url/README.md b/packages/postcss-url/README.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/postcss-url/package.json b/packages/postcss-url/package.json new file mode 100644 index 0000000000000..5eb9c57c6736c --- /dev/null +++ b/packages/postcss-url/package.json @@ -0,0 +1,31 @@ +{ + "name": "@wordpress/postcss-url", + "version": "1.0.0-alpha.0", + "description": "PostCSS plugin to replace relative URLs.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "postcss", + "css", + "postcss-plugin", + "url" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/postcss-url/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "main": "build/index.js", + "module": "build-module/index.js", + "dependencies": { + "@babel/runtime-corejs2": "7.0.0-beta.56", + "postcss": "^7.0.2" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/postcss-url/src/index.js b/packages/postcss-url/src/index.js new file mode 100644 index 0000000000000..6f974628087c6 --- /dev/null +++ b/packages/postcss-url/src/index.js @@ -0,0 +1,196 @@ +/** + * External dependencies + */ +import postcss from 'postcss'; +import { parse, resolve } from 'url'; + +/** + * Return `true` if the given path is http/https. + * + * @param {string} filePath path + * + * @return {boolean} is remote path. + */ +function isRemotePath( filePath ) { + return /^(?:https?:)?\/\//.test( filePath ); +} + +/** + * Return `true` if the given filePath is an absolute url. + * + * @param {string} filePath path + * + * @return {boolean} is absolute path. + */ +function isAbsolutePath( filePath ) { + return /^\/(?!\/)/.test( filePath ); +} + +/** + * Whether or not the url should be inluded. + * + * @param {Object} meta url meta info + * + * @return {boolean} is valid. + */ +function isValidUrl( meta ) { + // ignore hashes or data uris + if ( meta.value.indexOf( 'data:' ) === 0 || meta.value.indexOf( '#' ) === 0 ) { + return false; + } + + if ( isAbsolutePath( meta.value ) ) { + return false; + } + + // do not handle the http/https urls if `includeRemote` is false + if ( isRemotePath( meta.value ) ) { + return false; + } + + return true; +} + +/** + * Get all `url()`s, and return the meta info + * + * @param {string} value decl.value + * + * @return {Array} the urls + */ +function getUrls( value ) { + const reg = /url\((\s*)(['"]?)(.+?)\2(\s*)\)/g; + let match; + const urls = []; + + while ( ( match = reg.exec( value ) ) !== null ) { + const meta = { + source: match[ 0 ], + before: match[ 1 ], + quote: match[ 2 ], + value: match[ 3 ], + after: match[ 4 ], + }; + if ( isValidUrl( meta ) ) { + urls.push( meta ); + } + } + return urls; +} + +/** + * Get the absolute path of the url, relative to the basePath + * + * @param {string} str the url + * @param {string} baseURL base URL + * @param {string} absolutePath the absolute path + * + * @return {string} the full path to the file + */ +function getResourcePath( str, baseURL ) { + const pathname = parse( str ).pathname; + const filePath = resolve( baseURL, pathname ); + + return Promise.resolve( filePath ); +} + +/** + * Generate a url() creator based on the url meta info and the options. + * The creator receive a hash as its parameter + * + * @param {Object} meta - the url meta info + * @param {Object} opts - the options + * @return {Function} - the url creator + */ +function createUrl( meta ) { + return function( url ) { + return 'url(' + + meta.before + + meta.quote + + url + + meta.quote + + meta.after + + ')'; + }; +} + +/** + * Process the single `url()` pattern + * + * @param {string} baseURL the base URL for relative URLs + * @return {Promise} the Promise + */ +function processUrl( baseURL ) { + return function( meta ) { + return getResourcePath( meta.value, baseURL ) + .then( createUrl( meta ) ) + .then( ( newUrl ) => { + meta.newUrl = newUrl; + return meta; + } ); + }; +} + +/** + * Replace the raw value's `url()` segment to the new value + * + * @param {string} raw the raw value + * + * @return {string} the new value + */ +function repalceUrls( raw ) { + return function( urls ) { + urls.forEach( ( item ) => { + raw = raw.replace( item.source, item.newUrl ); + } ); + + return raw; + }; +} + +/** + * The error handler + * + * @param {Object} result the postcss result object + * @param {Object} decl the postcss declaration + * + * @return {Function} the error handler + */ +function handleError( result, decl ) { + return function( err ) { + result.warn( err.message || err, { node: decl } ); + }; +} + +/** + * Process one declaration + * + * @param {Object} result - the postcss result object + * @param {Object} decl - the postcss declaration + * @param {Object} opts - the plugin options + * + * @return {Promise} - the Promise + */ +function processDecl( result, decl, opts ) { + const actions = getUrls( decl.value ).map( processUrl( opts.baseURL ) ); + + return Promise.all( actions ) + .then( repalceUrls( decl.value ) ) + .then( ( newValue ) => { + decl.value = newValue; + } ) + .catch( handleError( result, decl ) ); +} + +export default postcss.plugin( 'postcss-url', ( opts ) => { + return ( css, result ) => { + const actions = []; + css.walkDecls( ( decl ) => { + if ( decl.value && decl.value.indexOf( 'url(' ) > -1 ) { + actions.push( processDecl( result, decl, opts ) ); + } + } ); + + return Promise.all( actions ); + }; +} ); diff --git a/packages/postcss-url/test/__snapshots__/index.js.snap b/packages/postcss-url/test/__snapshots__/index.js.snap new file mode 100644 index 0000000000000..e69bc78be6b94 --- /dev/null +++ b/packages/postcss-url/test/__snapshots__/index.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`postcss-url Should not replace absolute paths 1`] = `"body { background: url(/images/test.png); }"`; + +exports[`postcss-url Should not replace remote paths 1`] = `"body { background: url(http://wp-site.local/images/test.png); }"`; + +exports[`postcss-url Should replace complex relative paths 1`] = `"body { background: url(http://wp-site.local/themes/gut/images/test.png); }"`; + +exports[`postcss-url Should replace relative paths 1`] = `"body { background: url(http://wp-site.local/themes/gut/css/images/test.png); }"`; diff --git a/packages/postcss-url/test/index.js b/packages/postcss-url/test/index.js new file mode 100644 index 0000000000000..2d410fea0d729 --- /dev/null +++ b/packages/postcss-url/test/index.js @@ -0,0 +1,54 @@ +/** + * External dependencies + */ +import postcss from 'postcss'; + +/** + * Internal dependencies + */ +import plugin from '../src'; + +/** + * Module constants + */ +const defaultOptions = { + baseURL: 'http://wp-site.local/themes/gut/css/', +}; + +const run = ( input, opts = defaultOptions ) => { + return postcss( [ plugin( opts ) ] ).process( input, { from: undefined } ); +}; + +describe( 'postcss-url', () => { + it( 'Should replace relative paths', () => { + const input = `body { background: url(images/test.png); }`; + return run( input ).then( ( result ) => { + expect( result.css ).toMatchSnapshot(); + expect( result.warnings() ).toHaveLength( 0 ); + } ); + } ); + + it( 'Should replace complex relative paths', () => { + const input = `body { background: url(../images/test.png); }`; + return run( input ).then( ( result ) => { + expect( result.css ).toMatchSnapshot(); + expect( result.warnings() ).toHaveLength( 0 ); + } ); + } ); + + it( 'Should not replace absolute paths', () => { + const input = `body { background: url(/images/test.png); }`; + return run( input ).then( ( result ) => { + expect( result.css ).toMatchSnapshot(); + expect( result.warnings() ).toHaveLength( 0 ); + } ); + } ); + + it( 'Should not replace remote paths', () => { + const input = `body { background: url(http://wp-site.local/images/test.png); }`; + return run( input ).then( ( result ) => { + expect( result.css ).toMatchSnapshot(); + expect( result.warnings() ).toHaveLength( 0 ); + } ); + } ); +} ); From 50e64982f614159d5daa477037d13cac9d2f696a Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 20 Aug 2018 11:44:21 +0100 Subject: [PATCH 04/17] Refactor default Gutenberg editor styles to apply on the editor-block-list__block className This make those styles easily extendable in editor styles --- docs/manifest.json | 6 +++++ edit-post/components/visual-editor/style.scss | 23 ------------------- .../src/components/block-list/style.scss | 8 +++++++ 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/docs/manifest.json b/docs/manifest.json index 343178dc7a8f6..fccd97003d15e 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -443,6 +443,12 @@ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/postcss-themes/README.md", "parent": "packages" }, + { + "title": "@wordpress/postcss-url", + "slug": "packages-postcss-url", + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/postcss-url/README.md", + "parent": "packages" + }, { "title": "@wordpress/redux-routine", "slug": "packages-redux-routine", diff --git a/edit-post/components/visual-editor/style.scss b/edit-post/components/visual-editor/style.scss index c0e6eddae9a4c..f7d84a25f7c15 100644 --- a/edit-post/components/visual-editor/style.scss +++ b/edit-post/components/visual-editor/style.scss @@ -2,31 +2,8 @@ position: relative; padding: 50px 0; - &, - & p { - font-family: $editor-font; - font-size: $editor-font-size; - line-height: $editor-line-height; - } - - &, - & p { - color: $dark-gray-700; - } - & ul, - & ol { - margin: 0; - padding: 0; - } - & ul:not(.wp-block-gallery) { - list-style-type: disc; - } - - & ol { - list-style-type: decimal; - } & .components-button { font-family: $default-font; diff --git a/packages/editor/src/components/block-list/style.scss b/packages/editor/src/components/block-list/style.scss index f082c5d255c69..ed277e7500cdb 100644 --- a/packages/editor/src/components/block-list/style.scss +++ b/packages/editor/src/components/block-list/style.scss @@ -93,6 +93,14 @@ } .editor-block-list__block { + font-family: $editor-font; + line-height: $editor-line-height; + color: $dark-gray-700; + + p { + font-size: $editor-font-size; + } + &.is-hidden *, &.is-hidden > * { visibility: hidden; From ee2d812f814684e7195489d40c329b4901c83c95 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 20 Aug 2018 12:06:25 +0100 Subject: [PATCH 05/17] Fix linting errors --- edit-post/components/visual-editor/style.scss | 3 --- lib/client-assets.php | 12 ++++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/edit-post/components/visual-editor/style.scss b/edit-post/components/visual-editor/style.scss index f7d84a25f7c15..fe7b90ad18519 100644 --- a/edit-post/components/visual-editor/style.scss +++ b/edit-post/components/visual-editor/style.scss @@ -2,9 +2,6 @@ position: relative; padding: 50px 0; - - - & .components-button { font-family: $default-font; } diff --git a/lib/client-assets.php b/lib/client-assets.php index 5d018db209046..77e80159c7dd4 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -1400,19 +1400,19 @@ function gutenberg_editor_scripts_and_styles( $hook ) { $max_upload_size = 0; } - // Editor Styles + // Editor Styles. global $editor_styles; $styles = array(); if ( $editor_styles ) { foreach ( $editor_styles as $style ) { - if (filter_var($style, FILTER_VALIDATE_URL)) { + if ( filter_var( $style, FILTER_VALIDATE_URL ) ) { $styles[] = array( - 'css' => file_get_contents( $style ) + 'css' => file_get_contents( $style ), ); } else { - $file = get_theme_file_path( $style ); + $file = get_theme_file_path( $style ); $styles[] = array( - 'css' => file_get_contents( get_theme_file_path( $style ) ), + 'css' => file_get_contents( get_theme_file_path( $style ) ), 'baseURL' => get_theme_file_uri( $style ), ); } @@ -1431,7 +1431,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) { 'autosaveInterval' => 10, 'maxUploadFileSize' => $max_upload_size, 'allowedMimeTypes' => get_allowed_mime_types(), - 'styles' => $styles + 'styles' => $styles, ); $post_autosave = get_autosave_newer_than_post_save( $post ); From 2aa09f3c7f96cf93749bd302b1d312b96dd546d3 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 20 Aug 2018 12:18:48 +0100 Subject: [PATCH 06/17] Fix e2e tests --- packages/editor/src/components/provider/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index b321a2a2102d0..357a4419f508d 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -40,7 +40,7 @@ class EditorProvider extends Component { transforms.push( urlReplace( { baseURL } ) ); } postcss( transforms ) - .process( css ) + .process( css, { from: 'src/app.css', to: 'dest/app.css' } ) .then( ( output ) => { const node = document.createElement( 'style' ); node.innerHTML = output; From 66523d7cf9df5f8884ec9ab00d269bbfcf960f80 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 23 Aug 2018 11:17:21 +0100 Subject: [PATCH 07/17] Use CSS AST instead of PostCSS --- package-lock.json | 109 +++------- package.json | 1 - packages/editor/CHANGELOG.md | 4 + packages/editor/package.json | 4 +- .../editor/src/components/provider/index.js | 20 +- packages/editor/src/editor-styles/index.js | 3 + .../test/__snapshots__/traverse.js.snap | 7 + .../editor/src/editor-styles/test/traverse.js | 22 ++ .../test/__snapshots__/url-rewrite.js.snap | 25 +++ .../test/__snapshots__/wrap.js.snap | 36 ++++ .../transforms/test/url-rewrite.js | 39 ++++ .../src/editor-styles/transforms/test/wrap.js | 56 +++++ .../editor-styles/transforms/url-rewrite.js | 147 +++++++++++++ .../src/editor-styles/transforms/wrap.js | 27 +++ packages/editor/src/editor-styles/traverse.js | 28 +++ packages/postcss-url/.npmrc | 1 - packages/postcss-url/README.md | 0 packages/postcss-url/package.json | 31 --- packages/postcss-url/src/index.js | 196 ------------------ .../test/__snapshots__/index.js.snap | 9 - packages/postcss-url/test/index.js | 54 ----- webpack.config.js | 1 + 22 files changed, 440 insertions(+), 380 deletions(-) create mode 100644 packages/editor/src/editor-styles/index.js create mode 100644 packages/editor/src/editor-styles/test/__snapshots__/traverse.js.snap create mode 100644 packages/editor/src/editor-styles/test/traverse.js create mode 100644 packages/editor/src/editor-styles/transforms/test/__snapshots__/url-rewrite.js.snap create mode 100644 packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap create mode 100644 packages/editor/src/editor-styles/transforms/test/url-rewrite.js create mode 100644 packages/editor/src/editor-styles/transforms/test/wrap.js create mode 100644 packages/editor/src/editor-styles/transforms/url-rewrite.js create mode 100644 packages/editor/src/editor-styles/transforms/wrap.js create mode 100644 packages/editor/src/editor-styles/traverse.js delete mode 100644 packages/postcss-url/.npmrc delete mode 100644 packages/postcss-url/README.md delete mode 100644 packages/postcss-url/package.json delete mode 100644 packages/postcss-url/src/index.js delete mode 100644 packages/postcss-url/test/__snapshots__/index.js.snap delete mode 100644 packages/postcss-url/test/index.js diff --git a/package-lock.json b/package-lock.json index e6eb3be884ff0..0f6b70d56a8e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2459,12 +2459,11 @@ "@wordpress/viewport": "file:packages/viewport", "@wordpress/wordcount": "file:packages/wordcount", "classnames": "^2.2.5", + "css": "^2.2.3", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", "lodash": "^4.17.10", "memize": "^1.0.5", - "postcss": "^7.0.2", - "postcss-prefixwrap": "^1.3.0", "react-autosize-textarea": "^3.0.2", "redux-multi": "^0.1.12", "redux-optimist": "^1.0.0", @@ -2472,6 +2471,7 @@ "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "tinymce": "^4.7.2", + "traverse": "^0.6.6", "uuid": "^3.1.0" }, "dependencies": { @@ -2608,28 +2608,6 @@ "postcss": "^6.0.16" } }, - "@wordpress/postcss-url": { - "version": "file:packages/postcss-url", - "requires": { - "@babel/runtime-corejs2": "7.0.0-beta.56", - "postcss": "^7.0.2" - }, - "dependencies": { - "postcss": { - "version": "7.0.2", - "bundled": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "bundled": true - } - } - }, "@wordpress/redux-routine": { "version": "file:packages/redux-routine", "requires": { @@ -2835,8 +2813,7 @@ "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, "ansi-escapes": { "version": "3.1.0", @@ -3136,8 +3113,7 @@ "atob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", - "dev": true + "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=" }, "autoprefixer": { "version": "8.2.0", @@ -6156,6 +6132,27 @@ "randomfill": "^1.0.3" } }, + "css": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.3.tgz", + "integrity": "sha512-0W171WccAjQGGTKLhw4m2nnl0zPHUlTO/I8td4XzJgIB8Hg3ZZx71qT4G4eX8OVsSiaAKiUMy73E3nsbPlg2DQ==", + "requires": { + "inherits": "^2.0.1", + "source-map": "^0.1.38", + "source-map-resolve": "^0.5.1", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "css-color-function": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/css-color-function/-/css-color-function-1.3.3.tgz", @@ -6486,8 +6483,7 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "decompress-response": { "version": "3.3.0", @@ -16213,44 +16209,6 @@ "postcss-value-parser": "^3.0.0" } }, - "postcss-prefixwrap": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-prefixwrap/-/postcss-prefixwrap-1.3.0.tgz", - "integrity": "sha512-ru2dL0bn7RgWYzHX7smrGsUbq6vh2JHudAo409TrvjpB46kE5UdSyjdKU0ZWesUfqwYEMlSy622ACu52C9XIww==", - "requires": { - "postcss": "6.0.14" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", - "requires": { - "chalk": "^2.3.0", - "source-map": "^0.6.1", - "supports-color": "^4.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "^2.0.0" - } - } - } - }, "postcss-reduce-idents": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", @@ -17764,8 +17722,7 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "responselike": { "version": "1.0.2", @@ -18535,7 +18492,6 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, "requires": { "atob": "^2.1.1", "decode-uri-component": "^0.2.0", @@ -18556,8 +18512,7 @@ "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" }, "spawn-command": { "version": "0.0.2-1", @@ -19711,6 +19666,11 @@ "punycode": "^2.1.0" } }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, "tree-kill": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", @@ -20199,8 +20159,7 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" }, "url": { "version": "0.11.0", diff --git a/package.json b/package.json index f91d7520d4215..4d1540c2d44af 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/nux": "file:packages/nux", "@wordpress/plugins": "file:packages/plugins", - "@wordpress/postcss-url": "file:packages/postcss-url", "@wordpress/redux-routine": "file:packages/redux-routine", "@wordpress/shortcode": "file:packages/shortcode", "@wordpress/token-list": "file:packages/token-list", diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index 8fa4727e06472..7554a5c62f140 100644 --- a/packages/editor/CHANGELOG.md +++ b/packages/editor/CHANGELOG.md @@ -1,5 +1,9 @@ ## 3.0.0 (Unreleased) +### New Features + +- Add editor styles support. + ### Breaking Changes - The `wideAlign` block supports hook has been removed. Use `alignWide` instead. diff --git a/packages/editor/package.json b/packages/editor/package.json index cd045ceeee719..88861aedf8e76 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -44,12 +44,11 @@ "@wordpress/viewport": "file:../viewport", "@wordpress/wordcount": "file:../wordcount", "classnames": "^2.2.5", + "css":"^2.2.3", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", "lodash": "^4.17.10", "memize": "^1.0.5", - "postcss": "^7.0.2", - "postcss-prefixwrap": "^1.3.0", "react-autosize-textarea": "^3.0.2", "redux-multi": "^0.1.12", "redux-optimist": "^1.0.0", @@ -57,6 +56,7 @@ "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "tinymce": "^4.7.2", + "traverse": "^0.6.6", "uuid": "^3.1.0" }, "devDependencies": { diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index 357a4419f508d..f44f01ecd9417 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -2,13 +2,11 @@ * External dependencies */ import { flow, map } from 'lodash'; -import postcss from 'postcss'; -import wrap from 'postcss-prefixwrap'; /** * WordPress Dependencies */ -import urlReplace from '@wordpress/postcss-url'; +import { compose } from '@wordpress/compose'; import { createElement, Component } from '@wordpress/element'; import { DropZoneProvider, SlotFillProvider } from '@wordpress/components'; import { withDispatch } from '@wordpress/data'; @@ -16,6 +14,7 @@ import { withDispatch } from '@wordpress/data'; /** * Internal dependencies */ +import { traverse, wrap, urlRewrite } from '../../editor-styles'; import RichTextProvider from '../rich-text/provider'; class EditorProvider extends Component { @@ -37,15 +36,14 @@ class EditorProvider extends Component { map( this.props.settings.styles, ( { css, baseURL } ) => { const transforms = [ wrap( '.editor-block-list__block' ) ]; if ( baseURL ) { - transforms.push( urlReplace( { baseURL } ) ); + transforms.push( urlRewrite( baseURL ) ); + } + const updatedCSS = traverse( css, compose( transforms ) ); + if ( updatedCSS ) { + const node = document.createElement( 'style' ); + node.innerHTML = updatedCSS; + document.body.appendChild( node ); } - postcss( transforms ) - .process( css, { from: 'src/app.css', to: 'dest/app.css' } ) - .then( ( output ) => { - const node = document.createElement( 'style' ); - node.innerHTML = output; - document.body.appendChild( node ); - } ); } ); } diff --git a/packages/editor/src/editor-styles/index.js b/packages/editor/src/editor-styles/index.js new file mode 100644 index 0000000000000..35baab8d590b6 --- /dev/null +++ b/packages/editor/src/editor-styles/index.js @@ -0,0 +1,3 @@ +export { default as traverse } from './traverse'; +export { default as urlRewrite } from './transforms/url-rewrite'; +export { default as wrap } from './transforms/wrap'; diff --git a/packages/editor/src/editor-styles/test/__snapshots__/traverse.js.snap b/packages/editor/src/editor-styles/test/__snapshots__/traverse.js.snap new file mode 100644 index 0000000000000..ed3986f17f829 --- /dev/null +++ b/packages/editor/src/editor-styles/test/__snapshots__/traverse.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CSS traverse Should traverse the CSS 1`] = ` +"namespace h1 { + color: red; +}" +`; diff --git a/packages/editor/src/editor-styles/test/traverse.js b/packages/editor/src/editor-styles/test/traverse.js new file mode 100644 index 0000000000000..50d6d8da95bc3 --- /dev/null +++ b/packages/editor/src/editor-styles/test/traverse.js @@ -0,0 +1,22 @@ +/** + * Internal dependencies + */ +import traverse from '../traverse'; + +describe( 'CSS traverse', () => { + it( 'Should traverse the CSS', () => { + const input = `h1 { color: red; }`; + const output = traverse( input, ( node ) => { + if ( node.type === 'rule' ) { + return { + ...node, + selectors: node.selectors.map( ( selector ) => 'namespace ' + selector ), + }; + } + + return node; + } ); + + expect( output ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/editor/src/editor-styles/transforms/test/__snapshots__/url-rewrite.js.snap b/packages/editor/src/editor-styles/transforms/test/__snapshots__/url-rewrite.js.snap new file mode 100644 index 0000000000000..2ea644f584228 --- /dev/null +++ b/packages/editor/src/editor-styles/transforms/test/__snapshots__/url-rewrite.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`URL rewrite should not replace absolute paths 1`] = ` +"h1 { + background: url(/images/test.png); +}" +`; + +exports[`URL rewrite should not replace remote paths 1`] = ` +"h1 { + background: url(http://wp.org/images/test.png); +}" +`; + +exports[`URL rewrite should replace complex relative paths 1`] = ` +"h1 { + background: url(http://wp-site.local/themes/gut/images/test.png); +}" +`; + +exports[`URL rewrite should replace relative paths 1`] = ` +"h1 { + background: url(http://wp-site.local/themes/gut/css/images/test.png); +}" +`; diff --git a/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap b/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap new file mode 100644 index 0000000000000..f5310b7c4c46e --- /dev/null +++ b/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap @@ -0,0 +1,36 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CSS selector wrap should ignore font-face selectors 1`] = ` +"@font-face { + font-family: myFirstFont; + src: url(sansation_light.woff); +}" +`; + +exports[`CSS selector wrap should ignore keyframes 1`] = ` +"@keyframes move_background { + from { + background-position: 0 0; + } +}" +`; + +exports[`CSS selector wrap should replace root tags 1`] = ` +".my-namespace, +.my-namespace h1 { + color: red; +}" +`; + +exports[`CSS selector wrap should wrap multiple selectors 1`] = ` +".my-namespace h1, +.my-namespace h2 { + color: red; +}" +`; + +exports[`CSS selector wrap should wrap regular selectors 1`] = ` +".my-namespace h1 { + color: red; +}" +`; diff --git a/packages/editor/src/editor-styles/transforms/test/url-rewrite.js b/packages/editor/src/editor-styles/transforms/test/url-rewrite.js new file mode 100644 index 0000000000000..abbbf0754187e --- /dev/null +++ b/packages/editor/src/editor-styles/transforms/test/url-rewrite.js @@ -0,0 +1,39 @@ +/** + * Internal dependencies + */ +import traverse from '../../traverse'; +import rewrite from '../url-rewrite'; + +describe( 'URL rewrite', () => { + it( 'should replace relative paths', () => { + const callback = rewrite( 'http://wp-site.local/themes/gut/css/' ); + const input = `h1 { background: url(images/test.png); }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); + + it( 'should replace complex relative paths', () => { + const callback = rewrite( 'http://wp-site.local/themes/gut/css/' ); + const input = `h1 { background: url(../images/test.png); }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); + + it( 'should not replace absolute paths', () => { + const callback = rewrite( 'http://wp-site.local/themes/gut/css/' ); + const input = `h1 { background: url(/images/test.png); }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); + + it( 'should not replace remote paths', () => { + const callback = rewrite( 'http://wp-site.local/themes/gut/css/' ); + const input = `h1 { background: url(http://wp.org/images/test.png); }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/editor/src/editor-styles/transforms/test/wrap.js b/packages/editor/src/editor-styles/transforms/test/wrap.js new file mode 100644 index 0000000000000..97d13430dfff4 --- /dev/null +++ b/packages/editor/src/editor-styles/transforms/test/wrap.js @@ -0,0 +1,56 @@ +/** + * Internal dependencies + */ +import traverse from '../../traverse'; +import wrap from '../wrap'; + +describe( 'CSS selector wrap', () => { + it( 'should wrap regular selectors', () => { + const callback = wrap( '.my-namespace' ); + const input = `h1 { color: red; }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); + + it( 'should wrap multiple selectors', () => { + const callback = wrap( '.my-namespace' ); + const input = `h1, h2 { color: red; }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); + + it( 'should replace root tags', () => { + const callback = wrap( '.my-namespace' ); + const input = `body, h1 { color: red; }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); + + it( 'should ignore keyframes', () => { + const callback = wrap( '.my-namespace' ); + const input = ` + @keyframes move_background { + from { + background-position: 0 0; + } + }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); + + it( 'should ignore font-face selectors', () => { + const callback = wrap( '.my-namespace' ); + const input = ` + @font-face { + font-family: myFirstFont; + src: url(sansation_light.woff); + }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/editor/src/editor-styles/transforms/url-rewrite.js b/packages/editor/src/editor-styles/transforms/url-rewrite.js new file mode 100644 index 0000000000000..113348736f786 --- /dev/null +++ b/packages/editor/src/editor-styles/transforms/url-rewrite.js @@ -0,0 +1,147 @@ +/** + * External dependencies + */ +import { parse, resolve } from 'url'; + +/** + * Return `true` if the given path is http/https. + * + * @param {string} filePath path + * + * @return {boolean} is remote path. + */ +function isRemotePath( filePath ) { + return /^(?:https?:)?\/\//.test( filePath ); +} + +/** + * Return `true` if the given filePath is an absolute url. + * + * @param {string} filePath path + * + * @return {boolean} is absolute path. + */ +function isAbsolutePath( filePath ) { + return /^\/(?!\/)/.test( filePath ); +} + +/** + * Whether or not the url should be inluded. + * + * @param {Object} meta url meta info + * + * @return {boolean} is valid. + */ +function isValidURL( meta ) { + // ignore hashes or data uris + if ( meta.value.indexOf( 'data:' ) === 0 || meta.value.indexOf( '#' ) === 0 ) { + return false; + } + + if ( isAbsolutePath( meta.value ) ) { + return false; + } + + // do not handle the http/https urls if `includeRemote` is false + if ( isRemotePath( meta.value ) ) { + return false; + } + + return true; +} + +/** + * Get the absolute path of the url, relative to the basePath + * + * @param {string} str the url + * @param {string} baseURL base URL + * @param {string} absolutePath the absolute path + * + * @return {string} the full path to the file + */ +function getResourcePath( str, baseURL ) { + const pathname = parse( str ).pathname; + const filePath = resolve( baseURL, pathname ); + + return filePath; +} + +/** + * Process the single `url()` pattern + * + * @param {string} baseURL the base URL for relative URLs + * @return {Promise} the Promise + */ +function processURL( baseURL ) { + return function( meta ) { + const URL = getResourcePath( meta.value, baseURL ); + return { + ...meta, + newUrl: + 'url(' + + meta.before + + meta.quote + + URL + + meta.quote + + meta.after + + ')', + }; + }; +} + +/** + * Get all `url()`s, and return the meta info + * + * @param {string} value decl.value + * + * @return {Array} the urls + */ +function getURLs( value ) { + const reg = /url\((\s*)(['"]?)(.+?)\2(\s*)\)/g; + let match; + const URLs = []; + + while ( ( match = reg.exec( value ) ) !== null ) { + const meta = { + source: match[ 0 ], + before: match[ 1 ], + quote: match[ 2 ], + value: match[ 3 ], + after: match[ 4 ], + }; + if ( isValidURL( meta ) ) { + URLs.push( meta ); + } + } + return URLs; +} + +/** + * Replace the raw value's `url()` segment to the new value + * + * @param {string} raw the raw value + * @param {Array} URLs the URLs to replace + * + * @return {string} the new value + */ +function replaceURLs( raw, URLs ) { + URLs.forEach( ( item ) => { + raw = raw.replace( item.source, item.newUrl ); + } ); + + return raw; +} + +export const rewrite = ( rootURL ) => ( node ) => { + if ( node.type === 'declaration' ) { + const updatedURLs = getURLs( node.value ).map( processURL( rootURL ) ); + return { + ...node, + value: replaceURLs( node.value, updatedURLs ), + }; + } + + return node; +}; + +export default rewrite; diff --git a/packages/editor/src/editor-styles/transforms/wrap.js b/packages/editor/src/editor-styles/transforms/wrap.js new file mode 100644 index 0000000000000..566c7a48daf49 --- /dev/null +++ b/packages/editor/src/editor-styles/transforms/wrap.js @@ -0,0 +1,27 @@ +/** + * @const string IS_ROOT_TAG Regex to check if the selector is a root tag selector. + */ +const IS_ROOT_TAG = /^(body|html).*$/; + +export const wrap = ( namespace ) => ( node ) => { + const updateSelector = ( selector ) => { + // Anything other than a root tag is always prefixed. + if ( ! selector.match( IS_ROOT_TAG ) ) { + return namespace + ' ' + selector; + } + + // HTML and Body elements cannot be contained within our container so lets extract their styles. + return selector.replace( /^(body|html)/, namespace ); + }; + + if ( node.type === 'rule' ) { + return { + ...node, + selectors: node.selectors.map( updateSelector ), + }; + } + + return node; +}; + +export default wrap; diff --git a/packages/editor/src/editor-styles/traverse.js b/packages/editor/src/editor-styles/traverse.js new file mode 100644 index 0000000000000..6c089fc50983f --- /dev/null +++ b/packages/editor/src/editor-styles/traverse.js @@ -0,0 +1,28 @@ +/** + * External dependencies + */ +import { parse, stringify } from 'css'; +import traverse from 'traverse'; + +function traverseCSS( css, callback ) { + try { + const parsed = parse( css ); + + const updated = traverse.map( parsed, function( node ) { + if ( ! node ) { + return node; + } + const updatedNode = callback( node ); + return this.update( updatedNode ); + } ); + + return stringify( updated ); + } catch ( err ) { + // eslint-disable-next-line no-console + console.warn( 'Error while traversing the CSS: ' + err ); + + return null; + } +} + +export default traverseCSS; diff --git a/packages/postcss-url/.npmrc b/packages/postcss-url/.npmrc deleted file mode 100644 index 43c97e719a5a8..0000000000000 --- a/packages/postcss-url/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/packages/postcss-url/README.md b/packages/postcss-url/README.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/packages/postcss-url/package.json b/packages/postcss-url/package.json deleted file mode 100644 index 5eb9c57c6736c..0000000000000 --- a/packages/postcss-url/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@wordpress/postcss-url", - "version": "1.0.0-alpha.0", - "description": "PostCSS plugin to replace relative URLs.", - "author": "The WordPress Contributors", - "license": "GPL-2.0-or-later", - "keywords": [ - "wordpress", - "postcss", - "css", - "postcss-plugin", - "url" - ], - "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/postcss-url/README.md", - "repository": { - "type": "git", - "url": "https://github.com/WordPress/gutenberg.git" - }, - "bugs": { - "url": "https://github.com/WordPress/gutenberg/issues" - }, - "main": "build/index.js", - "module": "build-module/index.js", - "dependencies": { - "@babel/runtime-corejs2": "7.0.0-beta.56", - "postcss": "^7.0.2" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/postcss-url/src/index.js b/packages/postcss-url/src/index.js deleted file mode 100644 index 6f974628087c6..0000000000000 --- a/packages/postcss-url/src/index.js +++ /dev/null @@ -1,196 +0,0 @@ -/** - * External dependencies - */ -import postcss from 'postcss'; -import { parse, resolve } from 'url'; - -/** - * Return `true` if the given path is http/https. - * - * @param {string} filePath path - * - * @return {boolean} is remote path. - */ -function isRemotePath( filePath ) { - return /^(?:https?:)?\/\//.test( filePath ); -} - -/** - * Return `true` if the given filePath is an absolute url. - * - * @param {string} filePath path - * - * @return {boolean} is absolute path. - */ -function isAbsolutePath( filePath ) { - return /^\/(?!\/)/.test( filePath ); -} - -/** - * Whether or not the url should be inluded. - * - * @param {Object} meta url meta info - * - * @return {boolean} is valid. - */ -function isValidUrl( meta ) { - // ignore hashes or data uris - if ( meta.value.indexOf( 'data:' ) === 0 || meta.value.indexOf( '#' ) === 0 ) { - return false; - } - - if ( isAbsolutePath( meta.value ) ) { - return false; - } - - // do not handle the http/https urls if `includeRemote` is false - if ( isRemotePath( meta.value ) ) { - return false; - } - - return true; -} - -/** - * Get all `url()`s, and return the meta info - * - * @param {string} value decl.value - * - * @return {Array} the urls - */ -function getUrls( value ) { - const reg = /url\((\s*)(['"]?)(.+?)\2(\s*)\)/g; - let match; - const urls = []; - - while ( ( match = reg.exec( value ) ) !== null ) { - const meta = { - source: match[ 0 ], - before: match[ 1 ], - quote: match[ 2 ], - value: match[ 3 ], - after: match[ 4 ], - }; - if ( isValidUrl( meta ) ) { - urls.push( meta ); - } - } - return urls; -} - -/** - * Get the absolute path of the url, relative to the basePath - * - * @param {string} str the url - * @param {string} baseURL base URL - * @param {string} absolutePath the absolute path - * - * @return {string} the full path to the file - */ -function getResourcePath( str, baseURL ) { - const pathname = parse( str ).pathname; - const filePath = resolve( baseURL, pathname ); - - return Promise.resolve( filePath ); -} - -/** - * Generate a url() creator based on the url meta info and the options. - * The creator receive a hash as its parameter - * - * @param {Object} meta - the url meta info - * @param {Object} opts - the options - * @return {Function} - the url creator - */ -function createUrl( meta ) { - return function( url ) { - return 'url(' + - meta.before + - meta.quote + - url + - meta.quote + - meta.after + - ')'; - }; -} - -/** - * Process the single `url()` pattern - * - * @param {string} baseURL the base URL for relative URLs - * @return {Promise} the Promise - */ -function processUrl( baseURL ) { - return function( meta ) { - return getResourcePath( meta.value, baseURL ) - .then( createUrl( meta ) ) - .then( ( newUrl ) => { - meta.newUrl = newUrl; - return meta; - } ); - }; -} - -/** - * Replace the raw value's `url()` segment to the new value - * - * @param {string} raw the raw value - * - * @return {string} the new value - */ -function repalceUrls( raw ) { - return function( urls ) { - urls.forEach( ( item ) => { - raw = raw.replace( item.source, item.newUrl ); - } ); - - return raw; - }; -} - -/** - * The error handler - * - * @param {Object} result the postcss result object - * @param {Object} decl the postcss declaration - * - * @return {Function} the error handler - */ -function handleError( result, decl ) { - return function( err ) { - result.warn( err.message || err, { node: decl } ); - }; -} - -/** - * Process one declaration - * - * @param {Object} result - the postcss result object - * @param {Object} decl - the postcss declaration - * @param {Object} opts - the plugin options - * - * @return {Promise} - the Promise - */ -function processDecl( result, decl, opts ) { - const actions = getUrls( decl.value ).map( processUrl( opts.baseURL ) ); - - return Promise.all( actions ) - .then( repalceUrls( decl.value ) ) - .then( ( newValue ) => { - decl.value = newValue; - } ) - .catch( handleError( result, decl ) ); -} - -export default postcss.plugin( 'postcss-url', ( opts ) => { - return ( css, result ) => { - const actions = []; - css.walkDecls( ( decl ) => { - if ( decl.value && decl.value.indexOf( 'url(' ) > -1 ) { - actions.push( processDecl( result, decl, opts ) ); - } - } ); - - return Promise.all( actions ); - }; -} ); diff --git a/packages/postcss-url/test/__snapshots__/index.js.snap b/packages/postcss-url/test/__snapshots__/index.js.snap deleted file mode 100644 index e69bc78be6b94..0000000000000 --- a/packages/postcss-url/test/__snapshots__/index.js.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`postcss-url Should not replace absolute paths 1`] = `"body { background: url(/images/test.png); }"`; - -exports[`postcss-url Should not replace remote paths 1`] = `"body { background: url(http://wp-site.local/images/test.png); }"`; - -exports[`postcss-url Should replace complex relative paths 1`] = `"body { background: url(http://wp-site.local/themes/gut/images/test.png); }"`; - -exports[`postcss-url Should replace relative paths 1`] = `"body { background: url(http://wp-site.local/themes/gut/css/images/test.png); }"`; diff --git a/packages/postcss-url/test/index.js b/packages/postcss-url/test/index.js deleted file mode 100644 index 2d410fea0d729..0000000000000 --- a/packages/postcss-url/test/index.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * External dependencies - */ -import postcss from 'postcss'; - -/** - * Internal dependencies - */ -import plugin from '../src'; - -/** - * Module constants - */ -const defaultOptions = { - baseURL: 'http://wp-site.local/themes/gut/css/', -}; - -const run = ( input, opts = defaultOptions ) => { - return postcss( [ plugin( opts ) ] ).process( input, { from: undefined } ); -}; - -describe( 'postcss-url', () => { - it( 'Should replace relative paths', () => { - const input = `body { background: url(images/test.png); }`; - return run( input ).then( ( result ) => { - expect( result.css ).toMatchSnapshot(); - expect( result.warnings() ).toHaveLength( 0 ); - } ); - } ); - - it( 'Should replace complex relative paths', () => { - const input = `body { background: url(../images/test.png); }`; - return run( input ).then( ( result ) => { - expect( result.css ).toMatchSnapshot(); - expect( result.warnings() ).toHaveLength( 0 ); - } ); - } ); - - it( 'Should not replace absolute paths', () => { - const input = `body { background: url(/images/test.png); }`; - return run( input ).then( ( result ) => { - expect( result.css ).toMatchSnapshot(); - expect( result.warnings() ).toHaveLength( 0 ); - } ); - } ); - - it( 'Should not replace remote paths', () => { - const input = `body { background: url(http://wp-site.local/images/test.png); }`; - return run( input ).then( ( result ) => { - expect( result.css ).toMatchSnapshot(); - expect( result.warnings() ).toHaveLength( 0 ); - } ); - } ); -} ); diff --git a/webpack.config.js b/webpack.config.js index 4d729943384f8..6fe2fe4ac6399 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -114,6 +114,7 @@ const gutenbergPackages = [ ]; const externals = { + fs: 'commonjs fs', react: 'React', 'react-dom': 'ReactDOM', tinymce: 'tinymce', From 6be229d19f5480790a3b18a42dae5de4de29da36 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 23 Aug 2018 12:28:14 +0100 Subject: [PATCH 08/17] Fork css dependency to make it browser friendly --- package-lock.json | 42 +- packages/editor/package.json | 2 +- .../editor/src/editor-styles/ast/index.js | 5 + .../editor/src/editor-styles/ast/parse.js | 685 ++++++++++++++++++ .../editor-styles/ast/stringify/compiler.js | 54 ++ .../editor-styles/ast/stringify/compress.js | 205 ++++++ .../editor-styles/ast/stringify/identity.js | 286 ++++++++ .../src/editor-styles/ast/stringify/index.js | 33 + .../test/__snapshots__/traverse.js.snap | 2 +- .../test/__snapshots__/url-rewrite.js.snap | 8 +- .../test/__snapshots__/wrap.js.snap | 16 +- packages/editor/src/editor-styles/traverse.js | 2 +- webpack.config.js | 1 - 13 files changed, 1297 insertions(+), 44 deletions(-) create mode 100644 packages/editor/src/editor-styles/ast/index.js create mode 100644 packages/editor/src/editor-styles/ast/parse.js create mode 100644 packages/editor/src/editor-styles/ast/stringify/compiler.js create mode 100644 packages/editor/src/editor-styles/ast/stringify/compress.js create mode 100644 packages/editor/src/editor-styles/ast/stringify/identity.js create mode 100644 packages/editor/src/editor-styles/ast/stringify/index.js diff --git a/package-lock.json b/package-lock.json index 0f6b70d56a8e9..bf0cd38cb2b06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2459,9 +2459,9 @@ "@wordpress/viewport": "file:packages/viewport", "@wordpress/wordcount": "file:packages/wordcount", "classnames": "^2.2.5", - "css": "^2.2.3", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", + "inherits": "^2.0.3", "lodash": "^4.17.10", "memize": "^1.0.5", "react-autosize-textarea": "^3.0.2", @@ -2813,7 +2813,8 @@ "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true }, "ansi-escapes": { "version": "3.1.0", @@ -3113,7 +3114,8 @@ "atob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=" + "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", + "dev": true }, "autoprefixer": { "version": "8.2.0", @@ -6132,27 +6134,6 @@ "randomfill": "^1.0.3" } }, - "css": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.3.tgz", - "integrity": "sha512-0W171WccAjQGGTKLhw4m2nnl0zPHUlTO/I8td4XzJgIB8Hg3ZZx71qT4G4eX8OVsSiaAKiUMy73E3nsbPlg2DQ==", - "requires": { - "inherits": "^2.0.1", - "source-map": "^0.1.38", - "source-map-resolve": "^0.5.1", - "urix": "^0.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, "css-color-function": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/css-color-function/-/css-color-function-1.3.3.tgz", @@ -6483,7 +6464,8 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true }, "decompress-response": { "version": "3.3.0", @@ -17722,7 +17704,8 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true }, "responselike": { "version": "1.0.2", @@ -18492,6 +18475,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, "requires": { "atob": "^2.1.1", "decode-uri-component": "^0.2.0", @@ -18512,7 +18496,8 @@ "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true }, "spawn-command": { "version": "0.0.2-1", @@ -20159,7 +20144,8 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true }, "url": { "version": "0.11.0", diff --git a/packages/editor/package.json b/packages/editor/package.json index 88861aedf8e76..1dd0dd02a7fb8 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -44,9 +44,9 @@ "@wordpress/viewport": "file:../viewport", "@wordpress/wordcount": "file:../wordcount", "classnames": "^2.2.5", - "css":"^2.2.3", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", + "inherits": "^2.0.3", "lodash": "^4.17.10", "memize": "^1.0.5", "react-autosize-textarea": "^3.0.2", diff --git a/packages/editor/src/editor-styles/ast/index.js b/packages/editor/src/editor-styles/ast/index.js new file mode 100644 index 0000000000000..b4dc1de499f47 --- /dev/null +++ b/packages/editor/src/editor-styles/ast/index.js @@ -0,0 +1,5 @@ +// Adapted from https://github.com/reworkcss/css +// because we needed to remove source map support. + +export { default as parse } from './parse'; +export { default as stringify } from './stringify'; diff --git a/packages/editor/src/editor-styles/ast/parse.js b/packages/editor/src/editor-styles/ast/parse.js new file mode 100644 index 0000000000000..707340ac93854 --- /dev/null +++ b/packages/editor/src/editor-styles/ast/parse.js @@ -0,0 +1,685 @@ +// Adapted from https://github.com/reworkcss/css +// because we needed to remove source map support. + +// http://www.w3.org/TR/CSS21/grammar.htm +// https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027 +const commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g; + +export default function( css, options ) { + options = options || {}; + + /** + * Positional. + */ + + let lineno = 1; + let column = 1; + + /** + * Update lineno and column based on `str`. + */ + + function updatePosition( str ) { + const lines = str.match( /\n/g ); + if ( lines ) { + lineno += lines.length; + } + const i = str.lastIndexOf( '\n' ); + // eslint-disable-next-line no-bitwise + column = ~i ? str.length - i : column + str.length; + } + + /** + * Mark position and patch `node.position`. + */ + + function position() { + const start = { line: lineno, column: column }; + return function( node ) { + node.position = new Position( start ); + whitespace(); + return node; + }; + } + + /** + * Store position information for a node + */ + + function Position( start ) { + this.start = start; + this.end = { line: lineno, column: column }; + this.source = options.source; + } + + /** + * Non-enumerable source string + */ + + Position.prototype.content = css; + + /** + * Error `msg`. + */ + + const errorsList = []; + + function error( msg ) { + const err = new Error( options.source + ':' + lineno + ':' + column + ': ' + msg ); + err.reason = msg; + err.filename = options.source; + err.line = lineno; + err.column = column; + err.source = css; + + if ( options.silent ) { + errorsList.push( err ); + } else { + throw err; + } + } + + /** + * Parse stylesheet. + */ + + function stylesheet() { + const rulesList = rules(); + + return { + type: 'stylesheet', + stylesheet: { + source: options.source, + rules: rulesList, + parsingErrors: errorsList, + }, + }; + } + + /** + * Opening brace. + */ + + function open() { + return match( /^{\s*/ ); + } + + /** + * Closing brace. + */ + + function close() { + return match( /^}/ ); + } + + /** + * Parse ruleset. + */ + + function rules() { + let node; + const accumulator = []; + whitespace(); + comments( accumulator ); + while ( css.length && css.charAt( 0 ) !== '}' && ( node = atrule() || rule() ) ) { + if ( node !== false ) { + accumulator.push( node ); + comments( accumulator ); + } + } + return accumulator; + } + + /** + * Match `re` and return captures. + */ + + function match( re ) { + const m = re.exec( css ); + if ( ! m ) { + return; + } + const str = m[ 0 ]; + updatePosition( str ); + css = css.slice( str.length ); + return m; + } + + /** + * Parse whitespace. + */ + + function whitespace() { + match( /^\s*/ ); + } + + /** + * Parse comments; + */ + + function comments( accumulator ) { + let c; + accumulator = accumulator || []; + // eslint-disable-next-line no-cond-assign + while ( c = comment() ) { + if ( c !== false ) { + accumulator.push( c ); + } + } + return accumulator; + } + + /** + * Parse comment. + */ + + function comment() { + const pos = position(); + if ( '/' !== css.charAt( 0 ) || '*' !== css.charAt( 1 ) ) { + return; + } + + let i = 2; + while ( '' !== css.charAt( i ) && ( '*' !== css.charAt( i ) || '/' !== css.charAt( i + 1 ) ) ) { + ++i; + } + i += 2; + + if ( '' === css.charAt( i - 1 ) ) { + return error( 'End of comment missing' ); + } + + const str = css.slice( 2, i - 2 ); + column += 2; + updatePosition( str ); + css = css.slice( i ); + column += 2; + + return pos( { + type: 'comment', + comment: str, + } ); + } + + /** + * Parse selector. + */ + + function selector() { + const m = match( /^([^{]+)/ ); + if ( ! m ) { + return; + } + /* @fix Remove all comments from selectors + * http://ostermiller.org/findcomment.html */ + return trim( m[ 0 ] ) + .replace( /\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '' ) + .replace( /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, function( matched ) { + return matched.replace( /,/g, '\u200C' ); + } ) + .split( /\s*(?![^(]*\)),\s*/ ) + .map( function( s ) { + return s.replace( /\u200C/g, ',' ); + } ); + } + + /** + * Parse declaration. + */ + + function declaration() { + const pos = position(); + + // prop + let prop = match( /^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/ ); + if ( ! prop ) { + return; + } + prop = trim( prop[ 0 ] ); + + // : + if ( ! match( /^:\s*/ ) ) { + return error( "property missing ':'" ); + } + + // val + const val = match( /^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/ ); + + const ret = pos( { + type: 'declaration', + property: prop.replace( commentre, '' ), + value: val ? trim( val[ 0 ] ).replace( commentre, '' ) : '', + } ); + + // ; + match( /^[;\s]*/ ); + + return ret; + } + + /** + * Parse declarations. + */ + + function declarations() { + const decls = []; + + if ( ! open() ) { + return error( "missing '{'" ); + } + comments( decls ); + + // declarations + let decl; + // eslint-disable-next-line no-cond-assign + while ( decl = declaration() ) { + if ( decl !== false ) { + decls.push( decl ); + comments( decls ); + } + } + + if ( ! close() ) { + return error( "missing '}'" ); + } + return decls; + } + + /** + * Parse keyframe. + */ + + function keyframe() { + let m; + const vals = []; + const pos = position(); + + // eslint-disable-next-line no-cond-assign + while ( m = match( /^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/ ) ) { + vals.push( m[ 1 ] ); + match( /^,\s*/ ); + } + + if ( ! vals.length ) { + return; + } + + return pos( { + type: 'keyframe', + values: vals, + declarations: declarations(), + } ); + } + + /** + * Parse keyframes. + */ + + function atkeyframes() { + const pos = position(); + let m = match( /^@([-\w]+)?keyframes\s*/ ); + + if ( ! m ) { + return; + } + const vendor = m[ 1 ]; + + // identifier + m = match( /^([-\w]+)\s*/ ); + if ( ! m ) { + return error( '@keyframes missing name' ); + } + const name = m[ 1 ]; + + if ( ! open() ) { + return error( "@keyframes missing '{'" ); + } + + let frame; + let frames = comments(); + // eslint-disable-next-line no-cond-assign + while ( frame = keyframe() ) { + frames.push( frame ); + frames = frames.concat( comments() ); + } + + if ( ! close() ) { + return error( "@keyframes missing '}'" ); + } + + return pos( { + type: 'keyframes', + name: name, + vendor: vendor, + keyframes: frames, + } ); + } + + /** + * Parse supports. + */ + + function atsupports() { + const pos = position(); + const m = match( /^@supports *([^{]+)/ ); + + if ( ! m ) { + return; + } + const supports = trim( m[ 1 ] ); + + if ( ! open() ) { + return error( "@supports missing '{'" ); + } + + const style = comments().concat( rules() ); + + if ( ! close() ) { + return error( "@supports missing '}'" ); + } + + return pos( { + type: 'supports', + supports: supports, + rules: style, + } ); + } + + /** + * Parse host. + */ + + function athost() { + const pos = position(); + const m = match( /^@host\s*/ ); + + if ( ! m ) { + return; + } + + if ( ! open() ) { + return error( "@host missing '{'" ); + } + + const style = comments().concat( rules() ); + + if ( ! close() ) { + return error( "@host missing '}'" ); + } + + return pos( { + type: 'host', + rules: style, + } ); + } + + /** + * Parse media. + */ + + function atmedia() { + const pos = position(); + const m = match( /^@media *([^{]+)/ ); + + if ( ! m ) { + return; + } + const media = trim( m[ 1 ] ); + + if ( ! open() ) { + return error( "@media missing '{'" ); + } + + const style = comments().concat( rules() ); + + if ( ! close() ) { + return error( "@media missing '}'" ); + } + + return pos( { + type: 'media', + media: media, + rules: style, + } ); + } + + /** + * Parse custom-media. + */ + + function atcustommedia() { + const pos = position(); + const m = match( /^@custom-media\s+(--[^\s]+)\s*([^{;]+);/ ); + if ( ! m ) { + return; + } + + return pos( { + type: 'custom-media', + name: trim( m[ 1 ] ), + media: trim( m[ 2 ] ), + } ); + } + + /** + * Parse paged media. + */ + + function atpage() { + const pos = position(); + const m = match( /^@page */ ); + if ( ! m ) { + return; + } + + const sel = selector() || []; + + if ( ! open() ) { + return error( "@page missing '{'" ); + } + let decls = comments(); + + // declarations + let decl; + // eslint-disable-next-line no-cond-assign + while ( decl = declaration() ) { + decls.push( decl ); + decls = decls.concat( comments() ); + } + + if ( ! close() ) { + return error( "@page missing '}'" ); + } + + return pos( { + type: 'page', + selectors: sel, + declarations: decls, + } ); + } + + /** + * Parse document. + */ + + function atdocument() { + const pos = position(); + const m = match( /^@([-\w]+)?document *([^{]+)/ ); + if ( ! m ) { + return; + } + + const vendor = trim( m[ 1 ] ); + const doc = trim( m[ 2 ] ); + + if ( ! open() ) { + return error( "@document missing '{'" ); + } + + const style = comments().concat( rules() ); + + if ( ! close() ) { + return error( "@document missing '}'" ); + } + + return pos( { + type: 'document', + document: doc, + vendor: vendor, + rules: style, + } ); + } + + /** + * Parse font-face. + */ + + function atfontface() { + const pos = position(); + const m = match( /^@font-face\s*/ ); + if ( ! m ) { + return; + } + + if ( ! open() ) { + return error( "@font-face missing '{'" ); + } + let decls = comments(); + + // declarations + let decl; + // eslint-disable-next-line no-cond-assign + while ( decl = declaration() ) { + decls.push( decl ); + decls = decls.concat( comments() ); + } + + if ( ! close() ) { + return error( "@font-face missing '}'" ); + } + + return pos( { + type: 'font-face', + declarations: decls, + } ); + } + + /** + * Parse import + */ + + const atimport = _compileAtrule( 'import' ); + + /** + * Parse charset + */ + + const atcharset = _compileAtrule( 'charset' ); + + /** + * Parse namespace + */ + + const atnamespace = _compileAtrule( 'namespace' ); + + /** + * Parse non-block at-rules + */ + + function _compileAtrule( name ) { + const re = new RegExp( '^@' + name + '\\s*([^;]+);' ); + return function() { + const pos = position(); + const m = match( re ); + if ( ! m ) { + return; + } + const ret = { type: name }; + ret[ name ] = m[ 1 ].trim(); + return pos( ret ); + }; + } + + /** + * Parse at rule. + */ + + function atrule() { + if ( css[ 0 ] !== '@' ) { + return; + } + + return atkeyframes() || + atmedia() || + atcustommedia() || + atsupports() || + atimport() || + atcharset() || + atnamespace() || + atdocument() || + atpage() || + athost() || + atfontface(); + } + + /** + * Parse rule. + */ + + function rule() { + const pos = position(); + const sel = selector(); + + if ( ! sel ) { + return error( 'selector missing' ); + } + comments(); + + return pos( { + type: 'rule', + selectors: sel, + declarations: declarations(), + } ); + } + + return addParent( stylesheet() ); +} + +/** + * Trim `str`. + */ + +function trim( str ) { + return str ? str.replace( /^\s+|\s+$/g, '' ) : ''; +} + +/** + * Adds non-enumerable parent node reference to each node. + */ + +function addParent( obj, parent ) { + const isNode = obj && typeof obj.type === 'string'; + const childParent = isNode ? obj : parent; + + for ( const k in obj ) { + const value = obj[ k ]; + if ( Array.isArray( value ) ) { + value.forEach( function( v ) { + addParent( v, childParent ); + } ); + } else if ( value && typeof value === 'object' ) { + addParent( value, childParent ); + } + } + + if ( isNode ) { + Object.defineProperty( obj, 'parent', { + configurable: true, + writable: true, + enumerable: false, + value: parent || null, + } ); + } + + return obj; +} diff --git a/packages/editor/src/editor-styles/ast/stringify/compiler.js b/packages/editor/src/editor-styles/ast/stringify/compiler.js new file mode 100644 index 0000000000000..fb17a2f15e9dd --- /dev/null +++ b/packages/editor/src/editor-styles/ast/stringify/compiler.js @@ -0,0 +1,54 @@ +// Adapted from https://github.com/reworkcss/css +// because we needed to remove source map support. + +/** + * Expose `Compiler`. + */ + +export default Compiler; + +/** + * Initialize a compiler. + * + * @param {Type} name + * @return {Type} + * @api public + */ + +function Compiler( opts ) { + this.options = opts || {}; +} + +/** + * Emit `str` + */ + +Compiler.prototype.emit = function( str ) { + return str; +}; + +/** + * Visit `node`. + */ + +Compiler.prototype.visit = function( node ) { + return this[ node.type ]( node ); +}; + +/** + * Map visit over array of `nodes`, optionally using a `delim` + */ + +Compiler.prototype.mapVisit = function( nodes, delim ) { + let buf = ''; + delim = delim || ''; + + for ( let i = 0, length = nodes.length; i < length; i++ ) { + buf += this.visit( nodes[ i ] ); + if ( delim && i < length - 1 ) { + buf += this.emit( delim ); + } + } + + return buf; +}; diff --git a/packages/editor/src/editor-styles/ast/stringify/compress.js b/packages/editor/src/editor-styles/ast/stringify/compress.js new file mode 100644 index 0000000000000..6b3f0468c5ad6 --- /dev/null +++ b/packages/editor/src/editor-styles/ast/stringify/compress.js @@ -0,0 +1,205 @@ +// Adapted from https://github.com/reworkcss/css +// because we needed to remove source map support. + +/** + * External dependencies + */ +import inherits from 'inherits'; + +/** + * Internal dependencies + */ +import Base from './compiler'; + +/** + * Expose compiler. + */ + +export default Compiler; + +/** + * Initialize a new `Compiler`. + */ + +function Compiler( options ) { + Base.call( this, options ); +} + +/** + * Inherit from `Base.prototype`. + */ + +inherits( Compiler, Base ); + +/** + * Compile `node`. + */ + +Compiler.prototype.compile = function( node ) { + return node.stylesheet + .rules.map( this.visit, this ) + .join( '' ); +}; + +/** + * Visit comment node. + */ + +Compiler.prototype.comment = function( node ) { + return this.emit( '', node.position ); +}; + +/** + * Visit import node. + */ + +Compiler.prototype.import = function( node ) { + return this.emit( '@import ' + node.import + ';', node.position ); +}; + +/** + * Visit media node. + */ + +Compiler.prototype.media = function( node ) { + return this.emit( '@media ' + node.media, node.position ) + + this.emit( '{' ) + + this.mapVisit( node.rules ) + + this.emit( '}' ); +}; + +/** + * Visit document node. + */ + +Compiler.prototype.document = function( node ) { + const doc = '@' + ( node.vendor || '' ) + 'document ' + node.document; + + return this.emit( doc, node.position ) + + this.emit( '{' ) + + this.mapVisit( node.rules ) + + this.emit( '}' ); +}; + +/** + * Visit charset node. + */ + +Compiler.prototype.charset = function( node ) { + return this.emit( '@charset ' + node.charset + ';', node.position ); +}; + +/** + * Visit namespace node. + */ + +Compiler.prototype.namespace = function( node ) { + return this.emit( '@namespace ' + node.namespace + ';', node.position ); +}; + +/** + * Visit supports node. + */ + +Compiler.prototype.supports = function( node ) { + return this.emit( '@supports ' + node.supports, node.position ) + + this.emit( '{' ) + + this.mapVisit( node.rules ) + + this.emit( '}' ); +}; + +/** + * Visit keyframes node. + */ + +Compiler.prototype.keyframes = function( node ) { + return this.emit( '@' + + ( node.vendor || '' ) + + 'keyframes ' + + node.name, node.position ) + + this.emit( '{' ) + + this.mapVisit( node.keyframes ) + + this.emit( '}' ); +}; + +/** + * Visit keyframe node. + */ + +Compiler.prototype.keyframe = function( node ) { + const decls = node.declarations; + + return this.emit( node.values.join( ',' ), node.position ) + + this.emit( '{' ) + + this.mapVisit( decls ) + + this.emit( '}' ); +}; + +/** + * Visit page node. + */ + +Compiler.prototype.page = function( node ) { + const sel = node.selectors.length ? + node.selectors.join( ', ' ) : + ''; + + return this.emit( '@page ' + sel, node.position ) + + this.emit( '{' ) + + this.mapVisit( node.declarations ) + + this.emit( '}' ); +}; + +/** + * Visit font-face node. + */ + +Compiler.prototype[ 'font-face' ] = function( node ) { + return this.emit( '@font-face', node.position ) + + this.emit( '{' ) + + this.mapVisit( node.declarations ) + + this.emit( '}' ); +}; + +/** + * Visit host node. + */ + +Compiler.prototype.host = function( node ) { + return this.emit( '@host', node.position ) + + this.emit( '{' ) + + this.mapVisit( node.rules ) + + this.emit( '}' ); +}; + +/** + * Visit custom-media node. + */ + +Compiler.prototype[ 'custom-media' ] = function( node ) { + return this.emit( '@custom-media ' + node.name + ' ' + node.media + ';', node.position ); +}; + +/** + * Visit rule node. + */ + +Compiler.prototype.rule = function( node ) { + const decls = node.declarations; + if ( ! decls.length ) { + return ''; + } + + return this.emit( node.selectors.join( ',' ), node.position ) + + this.emit( '{' ) + + this.mapVisit( decls ) + + this.emit( '}' ); +}; + +/** + * Visit declaration node. + */ + +Compiler.prototype.declaration = function( node ) { + return this.emit( node.property + ':' + node.value, node.position ) + this.emit( ';' ); +}; diff --git a/packages/editor/src/editor-styles/ast/stringify/identity.js b/packages/editor/src/editor-styles/ast/stringify/identity.js new file mode 100644 index 0000000000000..a26a800ca9e76 --- /dev/null +++ b/packages/editor/src/editor-styles/ast/stringify/identity.js @@ -0,0 +1,286 @@ +// Adapted from https://github.com/reworkcss/css +// because we needed to remove source map support. + +/** + * External dependencies + */ +import inherits from 'inherits'; + +/** + * Internal dependencies + */ +import Base from './compiler'; + +/** + * Expose compiler. + */ + +export default Compiler; + +/** + * Initialize a new `Compiler`. + */ + +function Compiler( options ) { + options = options || {}; + Base.call( this, options ); + this.indentation = options.indent; +} + +/** + * Inherit from `Base.prototype`. + */ + +inherits( Compiler, Base ); + +/** + * Compile `node`. + */ + +Compiler.prototype.compile = function( node ) { + return this.stylesheet( node ); +}; + +/** + * Visit stylesheet node. + */ + +Compiler.prototype.stylesheet = function( node ) { + return this.mapVisit( node.stylesheet.rules, '\n\n' ); +}; + +/** + * Visit comment node. + */ + +Compiler.prototype.comment = function( node ) { + return this.emit( this.indent() + '/*' + node.comment + '*/', node.position ); +}; + +/** + * Visit import node. + */ + +Compiler.prototype.import = function( node ) { + return this.emit( '@import ' + node.import + ';', node.position ); +}; + +/** + * Visit media node. + */ + +Compiler.prototype.media = function( node ) { + return ( + this.emit( '@media ' + node.media, node.position ) + + this.emit( + ' {\n' + this.indent( 1 ) + ) + + this.mapVisit( node.rules, '\n\n' ) + + this.emit( + this.indent( -1 ) + + '\n}' + ) + ); +}; + +/** + * Visit document node. + */ + +Compiler.prototype.document = function( node ) { + const doc = '@' + ( node.vendor || '' ) + 'document ' + node.document; + + return ( + this.emit( doc, node.position ) + + this.emit( + ' ' + + ' {\n' + + this.indent( 1 ) + ) + + this.mapVisit( node.rules, '\n\n' ) + + this.emit( + this.indent( -1 ) + + '\n}' + ) + ); +}; + +/** + * Visit charset node. + */ + +Compiler.prototype.charset = function( node ) { + return this.emit( '@charset ' + node.charset + ';', node.position ); +}; + +/** + * Visit namespace node. + */ + +Compiler.prototype.namespace = function( node ) { + return this.emit( '@namespace ' + node.namespace + ';', node.position ); +}; + +/** + * Visit supports node. + */ + +Compiler.prototype.supports = function( node ) { + return ( + this.emit( '@supports ' + node.supports, node.position ) + + this.emit( + ' {\n' + + this.indent( 1 ) + ) + + this.mapVisit( node.rules, '\n\n' ) + + this.emit( + this.indent( -1 ) + + '\n}' + ) + ); +}; + +/** + * Visit keyframes node. + */ + +Compiler.prototype.keyframes = function( node ) { + return ( + this.emit( '@' + ( node.vendor || '' ) + 'keyframes ' + node.name, node.position ) + + this.emit( + ' {\n' + + this.indent( 1 ) + ) + + this.mapVisit( node.keyframes, '\n' ) + + this.emit( + this.indent( -1 ) + + '}' + ) + ); +}; + +/** + * Visit keyframe node. + */ + +Compiler.prototype.keyframe = function( node ) { + const decls = node.declarations; + + return ( + this.emit( this.indent() ) + + this.emit( node.values.join( ', ' ), node.position ) + + this.emit( + ' {\n' + + this.indent( 1 ) + ) + + this.mapVisit( decls, '\n' ) + + this.emit( + this.indent( -1 ) + + '\n' + + this.indent() + '}\n' + ) + ); +}; + +/** + * Visit page node. + */ + +Compiler.prototype.page = function( node ) { + const sel = node.selectors.length ? + node.selectors.join( ', ' ) + ' ' : + ''; + + return this.emit( '@page ' + sel, node.position ) + + this.emit( '{\n' ) + + this.emit( this.indent( 1 ) ) + + this.mapVisit( node.declarations, '\n' ) + + this.emit( this.indent( -1 ) ) + + this.emit( '\n}' ); +}; + +/** + * Visit font-face node. + */ + +Compiler.prototype[ 'font-face' ] = function( node ) { + return this.emit( '@font-face ', node.position ) + + this.emit( '{\n' ) + + this.emit( this.indent( 1 ) ) + + this.mapVisit( node.declarations, '\n' ) + + this.emit( this.indent( -1 ) ) + + this.emit( '\n}' ); +}; + +/** + * Visit host node. + */ + +Compiler.prototype.host = function( node ) { + return ( + this.emit( '@host', node.position ) + + this.emit( + ' {\n' + + this.indent( 1 ) + ) + + this.mapVisit( node.rules, '\n\n' ) + + this.emit( + this.indent( -1 ) + + '\n}' + ) + ); +}; + +/** + * Visit custom-media node. + */ + +Compiler.prototype[ 'custom-media' ] = function( node ) { + return this.emit( '@custom-media ' + node.name + ' ' + node.media + ';', node.position ); +}; + +/** + * Visit rule node. + */ + +Compiler.prototype.rule = function( node ) { + const indent = this.indent(); + const decls = node.declarations; + if ( ! decls.length ) { + return ''; + } + + return this.emit( node.selectors.map( function( s ) { + return indent + s; + } ).join( ',\n' ), node.position ) + + this.emit( ' {\n' ) + + this.emit( this.indent( 1 ) ) + + this.mapVisit( decls, '\n' ) + + this.emit( this.indent( -1 ) ) + + this.emit( '\n' + this.indent() + '}' ); +}; + +/** + * Visit declaration node. + */ + +Compiler.prototype.declaration = function( node ) { + return this.emit( this.indent() ) + + this.emit( node.property + ': ' + node.value, node.position ) + + this.emit( ';' ); +}; + +/** + * Increase, decrease or return current indentation. + */ + +Compiler.prototype.indent = function( level ) { + this.level = this.level || 1; + + if ( null !== level ) { + this.level += level; + return ''; + } + + return Array( this.level ).join( this.indentation || ' ' ); +}; diff --git a/packages/editor/src/editor-styles/ast/stringify/index.js b/packages/editor/src/editor-styles/ast/stringify/index.js new file mode 100644 index 0000000000000..0253ad968cc67 --- /dev/null +++ b/packages/editor/src/editor-styles/ast/stringify/index.js @@ -0,0 +1,33 @@ +// Adapted from https://github.com/reworkcss/css +// because we needed to remove source map support. + +/** + * Internal dependencies. + */ +import Compressed from './compress'; +import Identity from './identity'; + +/** + * Stringfy the given AST `node`. + * + * Options: + * + * - `compress` space-optimized output + * - `sourcemap` return an object with `.code` and `.map` + * + * @param {Object} node + * @param {Object} [options] + * @return {String} + * @api public + */ + +export default function( node, options ) { + options = options || {}; + + const compiler = options.compress ? + new Compressed( options ) : + new Identity( options ); + + const code = compiler.compile( node ); + return code; +} diff --git a/packages/editor/src/editor-styles/test/__snapshots__/traverse.js.snap b/packages/editor/src/editor-styles/test/__snapshots__/traverse.js.snap index ed3986f17f829..1ff3cab7d6336 100644 --- a/packages/editor/src/editor-styles/test/__snapshots__/traverse.js.snap +++ b/packages/editor/src/editor-styles/test/__snapshots__/traverse.js.snap @@ -2,6 +2,6 @@ exports[`CSS traverse Should traverse the CSS 1`] = ` "namespace h1 { - color: red; +color: red; }" `; diff --git a/packages/editor/src/editor-styles/transforms/test/__snapshots__/url-rewrite.js.snap b/packages/editor/src/editor-styles/transforms/test/__snapshots__/url-rewrite.js.snap index 2ea644f584228..48aaf43221e7d 100644 --- a/packages/editor/src/editor-styles/transforms/test/__snapshots__/url-rewrite.js.snap +++ b/packages/editor/src/editor-styles/transforms/test/__snapshots__/url-rewrite.js.snap @@ -2,24 +2,24 @@ exports[`URL rewrite should not replace absolute paths 1`] = ` "h1 { - background: url(/images/test.png); +background: url(/images/test.png); }" `; exports[`URL rewrite should not replace remote paths 1`] = ` "h1 { - background: url(http://wp.org/images/test.png); +background: url(http://wp.org/images/test.png); }" `; exports[`URL rewrite should replace complex relative paths 1`] = ` "h1 { - background: url(http://wp-site.local/themes/gut/images/test.png); +background: url(http://wp-site.local/themes/gut/images/test.png); }" `; exports[`URL rewrite should replace relative paths 1`] = ` "h1 { - background: url(http://wp-site.local/themes/gut/css/images/test.png); +background: url(http://wp-site.local/themes/gut/css/images/test.png); }" `; diff --git a/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap b/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap index f5310b7c4c46e..7c64e352456fb 100644 --- a/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap +++ b/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap @@ -2,35 +2,35 @@ exports[`CSS selector wrap should ignore font-face selectors 1`] = ` "@font-face { - font-family: myFirstFont; - src: url(sansation_light.woff); +font-family: myFirstFont; +src: url(sansation_light.woff); }" `; exports[`CSS selector wrap should ignore keyframes 1`] = ` "@keyframes move_background { - from { - background-position: 0 0; - } +from { +background-position: 0 0; +} }" `; exports[`CSS selector wrap should replace root tags 1`] = ` ".my-namespace, .my-namespace h1 { - color: red; +color: red; }" `; exports[`CSS selector wrap should wrap multiple selectors 1`] = ` ".my-namespace h1, .my-namespace h2 { - color: red; +color: red; }" `; exports[`CSS selector wrap should wrap regular selectors 1`] = ` ".my-namespace h1 { - color: red; +color: red; }" `; diff --git a/packages/editor/src/editor-styles/traverse.js b/packages/editor/src/editor-styles/traverse.js index 6c089fc50983f..43b18251bd7df 100644 --- a/packages/editor/src/editor-styles/traverse.js +++ b/packages/editor/src/editor-styles/traverse.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { parse, stringify } from 'css'; +import { parse, stringify } from './ast'; import traverse from 'traverse'; function traverseCSS( css, callback ) { diff --git a/webpack.config.js b/webpack.config.js index 6fe2fe4ac6399..4d729943384f8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -114,7 +114,6 @@ const gutenbergPackages = [ ]; const externals = { - fs: 'commonjs fs', react: 'React', 'react-dom': 'ReactDOM', tinymce: 'tinymce', From a5eb8423150b804c5bb88cbe6ae069748632163f Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 23 Aug 2018 12:41:42 +0100 Subject: [PATCH 09/17] Adding documentation about how the editor styles work --- docs/extensibility/theme-support.md | 9 ++++++++- docs/reference/faq.md | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/extensibility/theme-support.md b/docs/extensibility/theme-support.md index f3520e4b29410..618c94c7ccf7f 100644 --- a/docs/extensibility/theme-support.md +++ b/docs/extensibility/theme-support.md @@ -177,7 +177,14 @@ This flag will make sure users are only able to choose colors from the `editor-c ## Editor styles -A theme can provide a stylesheet that will change the editor's appearance. You can use this to change colors, fonts, and any other visual aspect of the editor. +Gutenberg supports the theme's [editor styles](https://codex.wordpress.org/Editor_Style). These styles are applied differently from the classic editor. + + - In the classic editor, the stylesheet is applied as is in the iframe of the post content editor. + - Since Gutenberg doesn't make use of iFrames, this is not possible. Instead Gutenberg wrap all the provided styles with `.editor-block-list__block` to avoid leaking styles outside the editor's content area. + + This technique should allow the editor styles to work properly in both editors in most cases. + +Alternatively, a theme can provide a stylesheet that will change the editor's appearance entirely. You can use this to change colors, fonts, and any other visual aspect of the editor. ### Add the stylesheet diff --git a/docs/reference/faq.md b/docs/reference/faq.md index d5162a405be16..55e33a993bdaa 100644 --- a/docs/reference/faq.md +++ b/docs/reference/faq.md @@ -151,7 +151,7 @@ Other features, like the new _wide_ and _full-wide_ alignment options, will simp ## How will editor styles work? -Themes can provide editor styles for blocks by using the following hook: +Refular editor styles will work as is in most casees and themes can also load extra stylesheets by using the following hook: ```php function gutenbergtheme_editor_styles() { @@ -160,6 +160,8 @@ function gutenbergtheme_editor_styles() { add_action( 'enqueue_block_editor_assets', 'gutenbergtheme_editor_styles' ); ``` +*Details:* [Editor Styles](../../docs/extensibility/theme-support.md#editor-styles) + ## Should I be concerned that Gutenberg will make my plugin obsolete? The goal of Gutenberg is not to put anyone out of business. It's to evolve WordPress so there's more business to be had in the future, for everyone. From aae8dc033bc7b0922e27a1ef16268ff6fce00e54 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 24 Aug 2018 10:07:42 +0100 Subject: [PATCH 10/17] Make the editor styles support opt-in --- docs/extensibility/theme-support.md | 10 ++++++++-- docs/reference/faq.md | 2 +- lib/client-assets.php | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/extensibility/theme-support.md b/docs/extensibility/theme-support.md index 618c94c7ccf7f..82bda5af758aa 100644 --- a/docs/extensibility/theme-support.md +++ b/docs/extensibility/theme-support.md @@ -177,12 +177,18 @@ This flag will make sure users are only able to choose colors from the `editor-c ## Editor styles -Gutenberg supports the theme's [editor styles](https://codex.wordpress.org/Editor_Style). These styles are applied differently from the classic editor. +Gutenberg supports the theme's [editor styles](https://codex.wordpress.org/Editor_Style). This support is opt-in because these styles are applied differently from the classic editor. - In the classic editor, the stylesheet is applied as is in the iframe of the post content editor. - Since Gutenberg doesn't make use of iFrames, this is not possible. Instead Gutenberg wrap all the provided styles with `.editor-block-list__block` to avoid leaking styles outside the editor's content area. - This technique should allow the editor styles to work properly in both editors in most cases. +This technique should allow the editor styles to work properly in both editors in most cases. + +Enabling editor styles support is done using: + +```php +add_theme_support( 'editor-styles' ); +``` Alternatively, a theme can provide a stylesheet that will change the editor's appearance entirely. You can use this to change colors, fonts, and any other visual aspect of the editor. diff --git a/docs/reference/faq.md b/docs/reference/faq.md index 55e33a993bdaa..c97ee773afcbb 100644 --- a/docs/reference/faq.md +++ b/docs/reference/faq.md @@ -151,7 +151,7 @@ Other features, like the new _wide_ and _full-wide_ alignment options, will simp ## How will editor styles work? -Refular editor styles will work as is in most casees and themes can also load extra stylesheets by using the following hook: +Refular editor styles are opt-in and will work as is in most cases. Themes can also load extra stylesheets by using the following hook: ```php function gutenbergtheme_editor_styles() { diff --git a/lib/client-assets.php b/lib/client-assets.php index 77e80159c7dd4..52082929f02bf 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -1403,7 +1403,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) { // Editor Styles. global $editor_styles; $styles = array(); - if ( $editor_styles ) { + if ( $editor_styles && current_theme_supports( 'editor-styles' ) ) { foreach ( $editor_styles as $style ) { if ( filter_var( $style, FILTER_VALIDATE_URL ) ) { $styles[] = array( From d9b76c122139414fd186629f0078a2255514ec86 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 24 Aug 2018 10:20:48 +0100 Subject: [PATCH 11/17] Use the editor styles to apply the default Gutenberg styles --- lib/client-assets.php | 8 +++++- .../src/components/block-list/style.scss | 8 ------ packages/editor/src/editor-styles.scss | 25 +++++++++++++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 packages/editor/src/editor-styles.scss diff --git a/lib/client-assets.php b/lib/client-assets.php index 52082929f02bf..180799dd853db 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -1402,7 +1402,13 @@ function gutenberg_editor_scripts_and_styles( $hook ) { // Editor Styles. global $editor_styles; - $styles = array(); + $styles = array( + array( + 'css' => file_get_contents( + gutenberg_dir_path() . 'build/editor/editor-styles.css' + ), + ), + ); if ( $editor_styles && current_theme_supports( 'editor-styles' ) ) { foreach ( $editor_styles as $style ) { if ( filter_var( $style, FILTER_VALIDATE_URL ) ) { diff --git a/packages/editor/src/components/block-list/style.scss b/packages/editor/src/components/block-list/style.scss index ed277e7500cdb..f082c5d255c69 100644 --- a/packages/editor/src/components/block-list/style.scss +++ b/packages/editor/src/components/block-list/style.scss @@ -93,14 +93,6 @@ } .editor-block-list__block { - font-family: $editor-font; - line-height: $editor-line-height; - color: $dark-gray-700; - - p { - font-size: $editor-font-size; - } - &.is-hidden *, &.is-hidden > * { visibility: hidden; diff --git a/packages/editor/src/editor-styles.scss b/packages/editor/src/editor-styles.scss new file mode 100644 index 0000000000000..1774d79f59384 --- /dev/null +++ b/packages/editor/src/editor-styles.scss @@ -0,0 +1,25 @@ +body { + font-family: $editor-font; + line-height: $editor-line-height; + color: $dark-gray-700; + font-size: $editor-font-size; +} + +p { + font-size: $editor-font-size; +} + + +ul, +ol { + margin: 0; + padding: 0; +} + +ul:not(.wp-block-gallery) { + list-style-type: disc; +} + +ol { + list-style-type: decimal; +} From 60e1489567996b55c319bdc6f2ef89456a8043de Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 24 Aug 2018 10:23:29 +0100 Subject: [PATCH 12/17] Fix typo --- docs/reference/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/faq.md b/docs/reference/faq.md index c97ee773afcbb..8842bad8ab378 100644 --- a/docs/reference/faq.md +++ b/docs/reference/faq.md @@ -151,7 +151,7 @@ Other features, like the new _wide_ and _full-wide_ alignment options, will simp ## How will editor styles work? -Refular editor styles are opt-in and will work as is in most cases. Themes can also load extra stylesheets by using the following hook: +Regular editor styles are opt-in and will work as is in most cases. Themes can also load extra stylesheets by using the following hook: ```php function gutenbergtheme_editor_styles() { From 7eeaec949732c125b71d916934d4477df0bda333 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 27 Aug 2018 11:51:19 +0100 Subject: [PATCH 13/17] Exclude built files from unit tests --- test/unit/jest.config.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/unit/jest.config.json b/test/unit/jest.config.json index 5746d6c9630ed..fec5aad1e2bc8 100644 --- a/test/unit/jest.config.json +++ b/test/unit/jest.config.json @@ -22,6 +22,8 @@ ], "testPathIgnorePatterns": [ "/node_modules/", - "/test/e2e" + "/test/e2e", + "/.*/build/", + "/.*/build-module/" ] } From 9ba8f93281aa1da0ede8b34573b3bfd5abfcd7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Tue, 4 Sep 2018 14:23:03 +0200 Subject: [PATCH 14/17] Inherit color on headings placed in wp-block-heading. Allows using `.wp-block-heading { color: blue; }`. --- packages/block-library/src/heading/editor.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/heading/editor.scss b/packages/block-library/src/heading/editor.scss index ddc9aff37ff2b..b3b695a511e8a 100644 --- a/packages/block-library/src/heading/editor.scss +++ b/packages/block-library/src/heading/editor.scss @@ -5,6 +5,7 @@ h4, h5, h6 { + color: inherit; margin: 0; } From b7b2ecb863632346aeca679600deff6e095ffa86 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 4 Sep 2018 13:50:56 +0100 Subject: [PATCH 15/17] Add the editor width smart transform --- .../editor/src/components/provider/index.js | 7 +++- packages/editor/src/editor-styles.scss | 4 ++ packages/editor/src/editor-styles/index.js | 1 + .../editor-styles/transforms/editor-width.js | 42 +++++++++++++++++++ .../test/__snapshots__/editor-width.js.snap | 15 +++++++ .../test/__snapshots__/wrap.js.snap | 7 ++++ .../transforms/test/editor-width.js | 21 ++++++++++ .../src/editor-styles/transforms/test/wrap.js | 8 ++++ .../editor-styles/transforms/url-rewrite.js | 2 +- .../src/editor-styles/transforms/wrap.js | 15 +++++-- 10 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 packages/editor/src/editor-styles/transforms/editor-width.js create mode 100644 packages/editor/src/editor-styles/transforms/test/__snapshots__/editor-width.js.snap create mode 100644 packages/editor/src/editor-styles/transforms/test/editor-width.js diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index f44f01ecd9417..7f8f1c10586c2 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -14,7 +14,7 @@ import { withDispatch } from '@wordpress/data'; /** * Internal dependencies */ -import { traverse, wrap, urlRewrite } from '../../editor-styles'; +import { traverse, wrap, urlRewrite, editorWidth } from '../../editor-styles'; import RichTextProvider from '../rich-text/provider'; class EditorProvider extends Component { @@ -34,7 +34,10 @@ class EditorProvider extends Component { } map( this.props.settings.styles, ( { css, baseURL } ) => { - const transforms = [ wrap( '.editor-block-list__block' ) ]; + const transforms = [ + editorWidth, + wrap( '.editor-block-list__block', [ 'html' ] ), + ]; if ( baseURL ) { transforms.push( urlRewrite( baseURL ) ); } diff --git a/packages/editor/src/editor-styles.scss b/packages/editor/src/editor-styles.scss index 1774d79f59384..052bf022be5d0 100644 --- a/packages/editor/src/editor-styles.scss +++ b/packages/editor/src/editor-styles.scss @@ -1,3 +1,7 @@ +html { + width: 610px; +} + body { font-family: $editor-font; line-height: $editor-line-height; diff --git a/packages/editor/src/editor-styles/index.js b/packages/editor/src/editor-styles/index.js index 35baab8d590b6..1d53fff364337 100644 --- a/packages/editor/src/editor-styles/index.js +++ b/packages/editor/src/editor-styles/index.js @@ -1,3 +1,4 @@ export { default as traverse } from './traverse'; export { default as urlRewrite } from './transforms/url-rewrite'; export { default as wrap } from './transforms/wrap'; +export { default as editorWidth } from './transforms/editor-width'; diff --git a/packages/editor/src/editor-styles/transforms/editor-width.js b/packages/editor/src/editor-styles/transforms/editor-width.js new file mode 100644 index 0000000000000..4c43ae329d1e1 --- /dev/null +++ b/packages/editor/src/editor-styles/transforms/editor-width.js @@ -0,0 +1,42 @@ +/** + * External dependencies + */ +import { find } from 'lodash'; + +export const getEditorWidthRules = ( width ) => { + return { + type: 'rule', + selectors: [ + 'body.gutenberg-editor-page .editor-post-title__block', + 'body.gutenberg-editor-page .editor-default-block-appender', + 'body.gutenberg-editor-page .editor-block-list__block', + ], + declarations: [ + { + type: 'declaration', + property: 'max-width', + value: width, + }, + ], + }; +}; + +const editorWidth = ( node ) => { + if ( + node.type === 'rule' && + find( node.selectors, ( selector ) => selector.trim() === 'html' ) + ) { + const widthDeclaration = find( + node.declarations, + ( declaration ) => declaration.property === 'width' + ); + + if ( widthDeclaration ) { + return getEditorWidthRules( widthDeclaration.value ); + } + } + + return node; +}; + +export default editorWidth; diff --git a/packages/editor/src/editor-styles/transforms/test/__snapshots__/editor-width.js.snap b/packages/editor/src/editor-styles/transforms/test/__snapshots__/editor-width.js.snap new file mode 100644 index 0000000000000..d1260cff31677 --- /dev/null +++ b/packages/editor/src/editor-styles/transforms/test/__snapshots__/editor-width.js.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Editor Width should generate the editor width styles 1`] = ` +"body.gutenberg-editor-page .editor-post-title__block, +body.gutenberg-editor-page .editor-default-block-appender, +body.gutenberg-editor-page .editor-block-list__block { +max-width: 300px; +}" +`; + +exports[`Editor Width should only replace the html declaration 1`] = ` +"h1 { +width: 300px; +}" +`; diff --git a/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap b/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap index 7c64e352456fb..868585dc2952a 100644 --- a/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap +++ b/packages/editor/src/editor-styles/transforms/test/__snapshots__/wrap.js.snap @@ -15,6 +15,13 @@ background-position: 0 0; }" `; +exports[`CSS selector wrap should ignore selectors 1`] = ` +".my-namespace h1, +body { +color: red; +}" +`; + exports[`CSS selector wrap should replace root tags 1`] = ` ".my-namespace, .my-namespace h1 { diff --git a/packages/editor/src/editor-styles/transforms/test/editor-width.js b/packages/editor/src/editor-styles/transforms/test/editor-width.js new file mode 100644 index 0000000000000..fe8b1c480dad2 --- /dev/null +++ b/packages/editor/src/editor-styles/transforms/test/editor-width.js @@ -0,0 +1,21 @@ +/** + * Internal dependencies + */ +import traverse from '../../traverse'; +import editorWidth from '../editor-width'; + +describe( 'Editor Width', () => { + it( 'should only replace the html declaration', () => { + const input = `h1 { width: 300px; }`; + const output = traverse( input, editorWidth ); + + expect( output ).toMatchSnapshot(); + } ); + + it( 'should generate the editor width styles', () => { + const input = `html { width: 300px; }`; + const output = traverse( input, editorWidth ); + + expect( output ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/editor/src/editor-styles/transforms/test/wrap.js b/packages/editor/src/editor-styles/transforms/test/wrap.js index 97d13430dfff4..ba2e02cd6b00a 100644 --- a/packages/editor/src/editor-styles/transforms/test/wrap.js +++ b/packages/editor/src/editor-styles/transforms/test/wrap.js @@ -21,6 +21,14 @@ describe( 'CSS selector wrap', () => { expect( output ).toMatchSnapshot(); } ); + it( 'should ignore selectors', () => { + const callback = wrap( '.my-namespace', 'body' ); + const input = `h1, body { color: red; }`; + const output = traverse( input, callback ); + + expect( output ).toMatchSnapshot(); + } ); + it( 'should replace root tags', () => { const callback = wrap( '.my-namespace' ); const input = `body, h1 { color: red; }`; diff --git a/packages/editor/src/editor-styles/transforms/url-rewrite.js b/packages/editor/src/editor-styles/transforms/url-rewrite.js index 113348736f786..5f454bd29ef3d 100644 --- a/packages/editor/src/editor-styles/transforms/url-rewrite.js +++ b/packages/editor/src/editor-styles/transforms/url-rewrite.js @@ -132,7 +132,7 @@ function replaceURLs( raw, URLs ) { return raw; } -export const rewrite = ( rootURL ) => ( node ) => { +const rewrite = ( rootURL ) => ( node ) => { if ( node.type === 'declaration' ) { const updatedURLs = getURLs( node.value ).map( processURL( rootURL ) ); return { diff --git a/packages/editor/src/editor-styles/transforms/wrap.js b/packages/editor/src/editor-styles/transforms/wrap.js index 566c7a48daf49..dad6df2b0c8e6 100644 --- a/packages/editor/src/editor-styles/transforms/wrap.js +++ b/packages/editor/src/editor-styles/transforms/wrap.js @@ -1,14 +1,23 @@ +/** + * External dependencies + */ +import { includes } from 'lodash'; + /** * @const string IS_ROOT_TAG Regex to check if the selector is a root tag selector. */ const IS_ROOT_TAG = /^(body|html).*$/; -export const wrap = ( namespace ) => ( node ) => { +const wrap = ( namespace, ignore = [] ) => ( node ) => { const updateSelector = ( selector ) => { + if ( includes( ignore, selector.trim() ) ) { + return selector; + } + // Anything other than a root tag is always prefixed. - if ( ! selector.match( IS_ROOT_TAG ) ) { + {if ( ! selector.match( IS_ROOT_TAG ) ) { return namespace + ' ' + selector; - } + }} // HTML and Body elements cannot be contained within our container so lets extract their styles. return selector.replace( /^(body|html)/, namespace ); From c58f0eb98bd9ffab76607f1bf3cef5f76d5dc635 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 4 Sep 2018 17:31:03 +0100 Subject: [PATCH 16/17] Use `.wp-block` instead of `html` to define the width --- packages/editor/src/components/provider/index.js | 2 +- packages/editor/src/editor-styles.scss | 2 +- packages/editor/src/editor-styles/transforms/editor-width.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index 7f8f1c10586c2..ab27bf981ff09 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -36,7 +36,7 @@ class EditorProvider extends Component { map( this.props.settings.styles, ( { css, baseURL } ) => { const transforms = [ editorWidth, - wrap( '.editor-block-list__block', [ 'html' ] ), + wrap( '.editor-block-list__block', [ '.wp-block' ] ), ]; if ( baseURL ) { transforms.push( urlRewrite( baseURL ) ); diff --git a/packages/editor/src/editor-styles.scss b/packages/editor/src/editor-styles.scss index 052bf022be5d0..5f9966c075184 100644 --- a/packages/editor/src/editor-styles.scss +++ b/packages/editor/src/editor-styles.scss @@ -1,4 +1,4 @@ -html { +.wp-block { width: 610px; } diff --git a/packages/editor/src/editor-styles/transforms/editor-width.js b/packages/editor/src/editor-styles/transforms/editor-width.js index 4c43ae329d1e1..a777aaab863bb 100644 --- a/packages/editor/src/editor-styles/transforms/editor-width.js +++ b/packages/editor/src/editor-styles/transforms/editor-width.js @@ -24,7 +24,7 @@ export const getEditorWidthRules = ( width ) => { const editorWidth = ( node ) => { if ( node.type === 'rule' && - find( node.selectors, ( selector ) => selector.trim() === 'html' ) + find( node.selectors, ( selector ) => selector.trim() === '.wp-block' ) ) { const widthDeclaration = find( node.declarations, From 53f789b1f0323562e8a139ae4b1f4451b36fa302 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 5 Sep 2018 09:07:46 +0100 Subject: [PATCH 17/17] Fix unit tests --- .../editor/src/editor-styles/transforms/test/editor-width.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/editor-styles/transforms/test/editor-width.js b/packages/editor/src/editor-styles/transforms/test/editor-width.js index fe8b1c480dad2..5b5b20b98edac 100644 --- a/packages/editor/src/editor-styles/transforms/test/editor-width.js +++ b/packages/editor/src/editor-styles/transforms/test/editor-width.js @@ -13,7 +13,7 @@ describe( 'Editor Width', () => { } ); it( 'should generate the editor width styles', () => { - const input = `html { width: 300px; }`; + const input = `.wp-block { width: 300px; }`; const output = traverse( input, editorWidth ); expect( output ).toMatchSnapshot();