├── .gitignore ├── LICENSE ├── README.md ├── lib ├── extension.d.ts ├── extension.js ├── index.d.ts ├── index.js ├── loaders │ ├── wp-editor-query-loader.d.ts │ └── wp-editor-query-loader.js ├── postcss.d.ts ├── postcss.js ├── store.d.ts ├── store.js ├── types.d.ts └── types.js ├── package.json ├── prettier.config.js ├── src ├── extension.ts ├── index.ts ├── loaders │ └── wp-editor-query-loader.ts ├── postcss.ts └── types.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tom Lawton, Kelly Mears 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bud-wp-editor-query 2 | 3 | **Use with Bud `^6.16.1`** 4 | 5 | Extracts wp-editor styles into a separate stylesheet. For WordPress FSE themes. A rewrite of the now defunct https://github.com/talss89/wp-editor-query-plugin 6 | 7 | Thanks to [@kellymears](https://github.com/kellymears) and [@strarsis](https://github.com/strarsis) for their help on this. 8 | 9 | This is based off the refactor and suggestions that [@kellymears made in this PR](https://github.com/talss89/wp-editor-query-plugin/pull/2). 10 | 11 | *This is an early release, and may contain bugs. Please raise issues here - I will be maintaining this actively.* 12 | 13 | ## Install 14 | 15 | `npm install bud-wp-editor-query --save-dev` or `yarn add bud-wp-editor-query -D` 16 | 17 | ## How to use 18 | 19 | 1. Install it. 20 | 2. Mark your stylesheet using the `@media (wp-editor)` syntax below. 21 | 3. Your editor styles will be extracted to `./dist/editor/.css` (or `./public/editor/.css` if you're using Sage). 22 | 23 | ## Syntax 24 | 25 | ```css 26 | @media all, (wp-editor) { 27 | /* Style is in both main and editor CSS */ 28 | 29 | .your-styles-here { 30 | color: blue; 31 | } 32 | } 33 | 34 | @media (wp-editor) { 35 | /* Style is ONLY in editor CSS */ 36 | 37 | .your-styles-here { 38 | color: blue; 39 | } 40 | } 41 | 42 | /* Nesting is OK... */ 43 | 44 | @media (wp-editor) { 45 | @media screen and (min-width: 1024px) { 46 | .your-style-here { 47 | width: 50%; 48 | } 49 | } 50 | } 51 | 52 | /* Non standard bubble-up... */ 53 | 54 | .parent { 55 | color: red; 56 | 57 | @media (wp-editor) { 58 | color: blue; 59 | } 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /lib/extension.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { Bud } from '@roots/bud-framework'; 3 | import { Extension } from '@roots/bud-framework/extension'; 4 | import type { WebpackPluginInstance } from '@roots/bud-framework/config'; 5 | interface Options { 6 | } 7 | export default class BudWpEditorQuery extends Extension { 8 | register(bud: Bud): Promise; 9 | configAfter(bud: Bud): Promise; 10 | } 11 | export {}; 12 | -------------------------------------------------------------------------------- /lib/extension.js: -------------------------------------------------------------------------------- 1 | import { __decorate } from "tslib"; 2 | import { Bud } from '@roots/bud-framework'; 3 | import { Extension } from '@roots/bud-framework/extension'; 4 | import { bind, label } from '@roots/bud-framework/extension/decorators'; 5 | let BudWpEditorQuery = class BudWpEditorQuery extends Extension { 6 | async register(bud) { 7 | bud.build.setLoader(`wp-editor-query-loader`, await bud.module.resolve('bud-wp-editor-query/wp-editor-query-loader')); 8 | bud.build.setItem(`wp-editor`, { 9 | loader: 'wp-editor-query-loader', 10 | options: {}, 11 | }); 12 | } 13 | async configAfter(bud) { 14 | bud.build.rules.css?.setUse((items = []) => { 15 | items.splice(items.indexOf('postcss'), 0, 'wp-editor'); 16 | return items; 17 | }); 18 | bud.build.rules.css?.setUse((items = []) => { 19 | items.splice(items.indexOf('postcss'), 0, 'wp-editor'); 20 | return items; 21 | }); 22 | bud.build.rules.sass?.setUse((items = []) => { 23 | items.splice(items.indexOf('postcss'), 0, 'wp-editor'); 24 | return items; 25 | }); 26 | bud.build.rules['css-module']?.setUse((items = []) => { 27 | items.splice(items.indexOf('postcss'), 0, 'wp-editor'); 28 | return items; 29 | }); 30 | bud.build.rules['sass-module']?.setUse((items = []) => { 31 | items.splice(items.indexOf('postcss'), 0, 'wp-editor'); 32 | return items; 33 | }); 34 | } 35 | }; 36 | __decorate([ 37 | bind 38 | ], BudWpEditorQuery.prototype, "register", null); 39 | __decorate([ 40 | bind 41 | ], BudWpEditorQuery.prototype, "configAfter", null); 42 | BudWpEditorQuery = __decorate([ 43 | label(`bud-wp-editor-query`) 44 | ], BudWpEditorQuery); 45 | export default BudWpEditorQuery; 46 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * bud-wp-editor-query 3 | * 4 | * @see https://bud.js.org 5 | * @see https://github.com/roots/bud 6 | */ 7 | import BudWpEditorQuery from './extension.js' 8 | import './types.js' 9 | export default BudWpEditorQuery 10 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bud-wp-editor-query 3 | * 4 | * @see https://bud.js.org 5 | * @see https://github.com/roots/bud 6 | */ 7 | import BudWpEditorQuery from './extension.js' 8 | import './types.js' 9 | export default BudWpEditorQuery 10 | -------------------------------------------------------------------------------- /lib/loaders/wp-editor-query-loader.d.ts: -------------------------------------------------------------------------------- 1 | import type {LoaderContext} from 'webpack' 2 | interface Options {} 3 | export default function (this: LoaderContext, source: any): void 4 | export {} 5 | -------------------------------------------------------------------------------- /lib/loaders/wp-editor-query-loader.js: -------------------------------------------------------------------------------- 1 | import { parse, join } from 'node:path'; 2 | import postcss from 'postcss'; 3 | import { ExtractEditorRules, RemoveEditorRules } from '../postcss.js'; 4 | const matchWpEditorUntilEndOfExpression = /,?(and|not)?\s*?\(\s?wp-editor\s?\).*(?=[{])/g; 5 | export default function (source) { 6 | const callback = this.async(); 7 | const parsed = parse(this.resourcePath); 8 | if (!containsQuery(source)) 9 | return callback(null, source); 10 | postcss([ExtractEditorRules]) 11 | .process(source, { from: parsed.base }) 12 | /** 13 | * Extract the editor specific css from the source file 14 | * and emit it as a separate file 15 | */ 16 | .then(extracted => { 17 | const emitPath = join(`editor`, parsed.base); 18 | if (extracted.toString().length > 0) 19 | this.emitFile(emitPath.replace(/\.(scss|sass)$/i, '.css'), extracted.toString().replace(matchWpEditorUntilEndOfExpression, '')); 20 | /** 21 | * Remove the editor specific css from the source file 22 | * and return the result 23 | */ 24 | postcss([RemoveEditorRules]) 25 | .process(source, { from: parsed.base }) 26 | .then(result => { 27 | callback(null, result.toString().replace(matchWpEditorUntilEndOfExpression, '')); 28 | }); 29 | }); 30 | } 31 | const containsQuery = source => source.match(/@media.*\(?wp-editor\)?/); 32 | -------------------------------------------------------------------------------- /lib/postcss.d.ts: -------------------------------------------------------------------------------- 1 | export declare const ExtractEditorRules: { 2 | postcssPlugin: string 3 | prepare(): { 4 | Once(): void 5 | OnceExit(root: any): void 6 | AtRule: { 7 | media: (atRule: any) => any 8 | } 9 | } 10 | } 11 | export declare const RemoveEditorRules: { 12 | postcssPlugin: string 13 | AtRule: { 14 | media: (atRule: any) => any 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/postcss.js: -------------------------------------------------------------------------------- 1 | import mediaquery from 'css-mediaquery'; 2 | import postcss from 'postcss'; 3 | const unwrapAndBubble = atRule => { 4 | if (atRule.nodes && atRule.nodes.length > 0 && atRule.nodes[0].type == 'decl') { 5 | atRule.replaceWith(atRule.nodes); 6 | } 7 | }; 8 | const shouldReplaceItself = ast => { 9 | return (ast.length < 2 && 10 | (isMediaQueryForEditor(ast) || isMediaQueryForFrontend(ast))); 11 | }; 12 | const isMediaQueryForEditor = ast => { 13 | return (ast.filter(a => a.expressions.filter(e => e.feature === `wp-editor`).length > 0).length > 0); 14 | }; 15 | const isMediaQueryForFrontend = ast => { 16 | return (ast.filter(a => a.expressions.filter(e => e.feature === `wp-editor`).length === 0).length > 0); 17 | }; 18 | export const ExtractEditorRules = { 19 | postcssPlugin: 'WpEditorExtractRules', 20 | prepare() { 21 | const variables = {}; 22 | return { 23 | Once() { 24 | variables.extracted = new postcss.Root(); 25 | }, 26 | OnceExit(root) { 27 | root.nodes = variables.extracted.nodes; 28 | root.cleanRaws(); 29 | }, 30 | AtRule: { 31 | media: atRule => { 32 | if (atRule.params.indexOf('wp-editor') === -1) 33 | return; 34 | const ast = mediaquery.parse(atRule.params); 35 | if (isMediaQueryForEditor(ast)) { 36 | // The block is exclusively for the editor. 37 | unwrapAndBubble(atRule); 38 | return variables.extracted.append(atRule.nodes); 39 | } 40 | atRule.remove(); 41 | }, 42 | }, 43 | }; 44 | }, 45 | }; 46 | export const RemoveEditorRules = { 47 | postcssPlugin: 'WpEditorRemoveRules', 48 | AtRule: { 49 | media: atRule => { 50 | if (atRule.params.indexOf('wp-editor') === -1) 51 | return; 52 | const ast = mediaquery.parse(atRule.params); 53 | if (isMediaQueryForFrontend(ast)) { 54 | unwrapAndBubble(atRule); 55 | if (shouldReplaceItself(ast)) { 56 | return atRule.replaceWith(atRule.nodes); 57 | } 58 | return; 59 | } 60 | atRule.remove(); 61 | }, 62 | }, 63 | }; 64 | -------------------------------------------------------------------------------- /lib/store.d.ts: -------------------------------------------------------------------------------- 1 | declare class EditorStore { 2 | styles: string 3 | constructor() 4 | add(content: any): void 5 | } 6 | declare const _default: EditorStore 7 | export default _default 8 | -------------------------------------------------------------------------------- /lib/store.js: -------------------------------------------------------------------------------- 1 | class EditorStore { 2 | styles 3 | constructor() { 4 | this.styles = '' 5 | } 6 | add(content) { 7 | this.styles += `${content}\n\n` 8 | } 9 | } 10 | export default new EditorStore() 11 | -------------------------------------------------------------------------------- /lib/types.d.ts: -------------------------------------------------------------------------------- 1 | import type BudWpEditorQuery from './extension.js'; 2 | import type { Item, Loader, Rule } from '@roots/bud-build'; 3 | declare module '@roots/bud-framework' { 4 | interface Modules { 5 | 'bud-wp-editor-query': BudWpEditorQuery; 6 | } 7 | interface Loaders { 8 | 'wp-editor-query-loader': Loader; 9 | } 10 | interface Items { 11 | 'wp-editor': Item; 12 | } 13 | interface Rules { 14 | 'sass': Rule; 15 | } 16 | interface Bud { 17 | } 18 | interface Context { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/types.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bud-wp-editor-query", 3 | "version": "0.0.4", 4 | "description": "Extract WordPress editor styles and split into another stylesheet using specialised media queries", 5 | "main": "index.js", 6 | "repository": "https://github.com/talss89/bud-wp-editor-query", 7 | "contributors": [ 8 | { 9 | "email": "tom.lawton@cndu.it", 10 | "name": "Tom Lawton", 11 | "url": "https://github.com/talss89" 12 | }, 13 | { 14 | "email": "developers@tinypixel.dev", 15 | "name": "Kelly Mears", 16 | "url": "https://github.com/kellymears" 17 | }, 18 | { 19 | "email": "strarsis@gmail.com", 20 | "name": "strarsis", 21 | "url": "https://github.com/strarsis" 22 | } 23 | ], 24 | "license": "MIT", 25 | "type": "module", 26 | "exports": { 27 | ".": "./lib/index.js", 28 | "./types": "./lib/types.js", 29 | "./wp-editor-query-loader": "./lib/loaders/wp-editor-query-loader.js" 30 | }, 31 | "typesVersions": { 32 | "*": { 33 | ".": [ 34 | "./lib/index.js" 35 | ], 36 | "types": [ 37 | "./lib/types.js" 38 | ] 39 | } 40 | }, 41 | "types": "./lib/index.d.ts", 42 | "module": "./lib/index.js", 43 | "files": [ 44 | "lib", 45 | "src" 46 | ], 47 | "devDependencies": { 48 | "@roots/bud": "^6.16.1", 49 | "@roots/bud-framework": "^6.16.1", 50 | "@roots/bud-postcss": "^6.16.1", 51 | "@types/node": "18.17.9", 52 | "tslib": "2.6.2" 53 | }, 54 | "dependencies": { 55 | "css-mediaquery": "^0.1.2" 56 | }, 57 | "bud": {"version": ">=6.16.0"} 58 | } 59 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | arrowParens: `avoid`, 3 | bracketSpacing: false, 4 | overrides: [ 5 | { 6 | files: `*.mdx`, 7 | options: { 8 | parser: `mdx`, 9 | }, 10 | }, 11 | { 12 | files: `*.d.ts`, 13 | options: { 14 | parser: `typescript`, 15 | }, 16 | }, 17 | ], 18 | printWidth: 75, 19 | semi: false, 20 | singleQuote: true, 21 | tabWidth: 2, 22 | trailingComma: `all`, 23 | useTabs: false, 24 | } 25 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import {Bud} from '@roots/bud-framework' 2 | 3 | import {Extension} from '@roots/bud-framework/extension' 4 | import type {WebpackPluginInstance} from '@roots/bud-framework/config' 5 | 6 | import {bind, label} from '@roots/bud-framework/extension/decorators' 7 | 8 | interface Options {} 9 | 10 | @label(`bud-wp-editor-query`) 11 | export default class BudWpEditorQuery extends Extension< 12 | Options, 13 | WebpackPluginInstance 14 | > { 15 | @bind 16 | public override async register(bud: Bud) { 17 | bud.build.setLoader( 18 | `wp-editor-query-loader`, 19 | await bud.module.resolve( 20 | 'bud-wp-editor-query/wp-editor-query-loader', 21 | ), 22 | ) 23 | bud.build.setItem(`wp-editor`, { 24 | loader: 'wp-editor-query-loader', 25 | options: {}, 26 | }) 27 | } 28 | 29 | @bind 30 | public override async configAfter(bud: Bud) { 31 | 32 | bud.build.rules.css?.setUse((items = []) => { 33 | items.splice(items.indexOf('postcss'), 0, 'wp-editor') 34 | return items 35 | }) 36 | 37 | bud.build.rules.css?.setUse((items = []) => { 38 | items.splice(items.indexOf('postcss'), 0, 'wp-editor') 39 | return items 40 | }) 41 | 42 | bud.build.rules.sass?.setUse((items = []) => { 43 | items.splice(items.indexOf('postcss'), 0, 'wp-editor') 44 | return items 45 | }) 46 | 47 | bud.build.rules['css-module']?.setUse((items = []) => { 48 | items.splice(items.indexOf('postcss'), 0, 'wp-editor') 49 | return items 50 | }) 51 | 52 | bud.build.rules['sass-module']?.setUse((items = []) => { 53 | items.splice(items.indexOf('postcss'), 0, 'wp-editor') 54 | return items 55 | }) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * bud-wp-editor-query 3 | * 4 | * @see https://bud.js.org 5 | * @see https://github.com/roots/bud 6 | */ 7 | 8 | import BudWpEditorQuery from './extension.js' 9 | import './types.js' 10 | export default BudWpEditorQuery 11 | -------------------------------------------------------------------------------- /src/loaders/wp-editor-query-loader.ts: -------------------------------------------------------------------------------- 1 | import type {LoaderContext} from 'webpack' 2 | import {parse, join} from 'node:path' 3 | 4 | import postcss from 'postcss' 5 | import {ExtractEditorRules, RemoveEditorRules} from '../postcss.js' 6 | 7 | interface Options {} 8 | 9 | const matchWpEditorUntilEndOfExpression = /,?(and|not)?\s*?\(\s?wp-editor\s?\).*(?=[{])/g; 10 | 11 | export default function (this: LoaderContext, source) { 12 | const callback = this.async() 13 | const parsed = parse(this.resourcePath) 14 | 15 | if (!containsQuery(source)) return callback(null, source) 16 | 17 | postcss([ExtractEditorRules]) 18 | .process(source, {from: parsed.base}) 19 | /** 20 | * Extract the editor specific css from the source file 21 | * and emit it as a separate file 22 | */ 23 | .then(extracted => { 24 | const emitPath = join(`editor`, parsed.base) 25 | 26 | if (extracted.toString().length > 0) 27 | this.emitFile(emitPath.replace(/\.(scss|sass)$/i, '.css'), extracted.toString().replace(matchWpEditorUntilEndOfExpression, '')) 28 | 29 | /** 30 | * Remove the editor specific css from the source file 31 | * and return the result 32 | */ 33 | postcss([RemoveEditorRules]) 34 | .process(source, {from: parsed.base}) 35 | .then(result => { 36 | callback(null, result.toString().replace(matchWpEditorUntilEndOfExpression, '')) 37 | }) 38 | }) 39 | } 40 | 41 | const containsQuery = source => source.match(/@media.*\(?wp-editor\)?/) 42 | -------------------------------------------------------------------------------- /src/postcss.ts: -------------------------------------------------------------------------------- 1 | import mediaquery from 'css-mediaquery' 2 | import postcss from 'postcss' 3 | 4 | const unwrapAndBubble = atRule => { 5 | if (atRule.nodes && atRule.nodes.length > 0 && atRule.nodes[0].type == 'decl') { 6 | atRule.replaceWith(atRule.nodes) 7 | } 8 | } 9 | 10 | const shouldReplaceItself = ast => { 11 | return ( 12 | ast.length < 2 && 13 | (isMediaQueryForEditor(ast) || isMediaQueryForFrontend(ast)) 14 | ) 15 | } 16 | 17 | const isMediaQueryForEditor = ast => { 18 | return ( 19 | ast.filter( 20 | a => a.expressions.filter(e => e.feature === `wp-editor`).length > 0, 21 | ).length > 0 22 | ) 23 | } 24 | 25 | const isMediaQueryForFrontend = ast => { 26 | return ( 27 | ast.filter( 28 | a => 29 | a.expressions.filter(e => e.feature === `wp-editor`).length === 0, 30 | ).length > 0 31 | ) 32 | } 33 | 34 | export const ExtractEditorRules = { 35 | postcssPlugin: 'WpEditorExtractRules', 36 | prepare() { 37 | const variables: any = {} 38 | return { 39 | Once() { 40 | variables.extracted = new postcss.Root() 41 | }, 42 | OnceExit(root) { 43 | root.nodes = variables.extracted.nodes 44 | root.cleanRaws() 45 | }, 46 | AtRule: { 47 | media: atRule => { 48 | if (atRule.params.indexOf('wp-editor') === -1) return 49 | 50 | const ast = mediaquery.parse(atRule.params) 51 | 52 | if (isMediaQueryForEditor(ast)) { 53 | // The block is exclusively for the editor. 54 | unwrapAndBubble(atRule) 55 | return variables.extracted.append(atRule.nodes) 56 | } 57 | atRule.remove() 58 | }, 59 | }, 60 | } 61 | }, 62 | } 63 | 64 | export const RemoveEditorRules = { 65 | postcssPlugin: 'WpEditorRemoveRules', 66 | AtRule: { 67 | media: atRule => { 68 | 69 | if (atRule.params.indexOf('wp-editor') === -1) return 70 | 71 | const ast = mediaquery.parse(atRule.params) 72 | 73 | if (isMediaQueryForFrontend(ast)) { 74 | unwrapAndBubble(atRule) 75 | 76 | if (shouldReplaceItself(ast)) { 77 | return atRule.replaceWith(atRule.nodes) 78 | } 79 | 80 | return 81 | } 82 | 83 | atRule.remove() 84 | }, 85 | }, 86 | } 87 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type BudWpEditorQuery from './extension.js' 2 | import type {Item, Loader, Rule} from '@roots/bud-build' 3 | 4 | declare module '@roots/bud-framework' { 5 | interface Modules { 6 | 'bud-wp-editor-query': BudWpEditorQuery 7 | } 8 | 9 | interface Loaders { 10 | 'wp-editor-query-loader': Loader 11 | } 12 | 13 | interface Items { 14 | 'wp-editor': Item 15 | } 16 | 17 | interface Rules { 18 | 'sass': Rule 19 | } 20 | 21 | interface Bud {} 22 | 23 | interface Context {} 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": false, 4 | "allowUnreachableCode": false, 5 | "composite": true, 6 | "declaration": true, 7 | "declarationMap": false, 8 | "emitDecoratorMetadata": false, 9 | "esModuleInterop": true, 10 | "experimentalDecorators": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "importHelpers": true, 13 | "incremental": true, 14 | "jsx": "react-jsx", 15 | "jsxImportSource": "@roots/bud-support", 16 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 17 | "module": "NodeNext", 18 | "moduleDetection": "force", 19 | "moduleResolution": "NodeNext", 20 | "noImplicitOverride": true, 21 | "noUnusedLocals": true, 22 | "removeComments": false, 23 | "resolveJsonModule": false, 24 | "sourceMap": false, 25 | "skipLibCheck": true, 26 | "strictBindCallApply": true, 27 | "target": "es2022", 28 | // "types": ["node", "vitest/importMeta", "webpack", "webpack-env"], 29 | "verbatimModuleSyntax": true, 30 | "rootDir": "src", 31 | "outDir": "lib", 32 | "types": [ 33 | "node", 34 | "webpack", 35 | "@roots/bud-framework", 36 | "@roots/bud-build", 37 | "@roots/bud-extensions", 38 | "@roots/bud-support", 39 | "@roots/bud-api", 40 | "@roots/bud-postcss" 41 | ] 42 | }, 43 | "include": ["./src"], 44 | "exclude": ["test", "node_modules", "dist"] 45 | } 46 | --------------------------------------------------------------------------------