From ddf413e033516ebc6c399b8f5dddc27d9a1318d8 Mon Sep 17 00:00:00 2001 From: Ben Word Date: Sun, 8 Dec 2024 12:55:42 -0500 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Combine=20plugins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/scripts/build/wordpress.js | 123 -------------- .../build/{theme-json.ts => wordpress.ts} | 150 +++++++++++++++--- vite.config.js | 2 +- 3 files changed, 125 insertions(+), 150 deletions(-) delete mode 100644 resources/scripts/build/wordpress.js rename resources/scripts/build/{theme-json.ts => wordpress.ts} (61%) diff --git a/resources/scripts/build/wordpress.js b/resources/scripts/build/wordpress.js deleted file mode 100644 index 3486557c84..0000000000 --- a/resources/scripts/build/wordpress.js +++ /dev/null @@ -1,123 +0,0 @@ -import { - defaultRequestToExternal, - defaultRequestToHandle, - } from '@wordpress/dependency-extraction-webpack-plugin/lib/util' - - function extractNamedImports(imports) { - return imports - .match(/{([^}]+)}/)[1] - .split(',') - .map((s) => s.trim()) - } - - function handleNamedReplacement(namedImports, external) { - return namedImports - .map((imports) => { - const [name, alias = name] = imports - .split(' as ') - .map((script) => script.trim()) - - return `const ${alias} = ${external.join('.')}.${name};` - }) - .join('\n') - } - - function handleReplacements(imports, external) { - if (imports.includes('{')) { - const namedImports = extractNamedImports(imports) - - return handleNamedReplacement(namedImports, external) - } - - if (imports.includes('* as')) { - const alias = imports.match(/\*\s+as\s+(\w+)/)[1] - - return `const ${alias} = ${external.join('.')};` - } - - const name = imports.trim() - - return `const ${name} = ${external.join('.')};` - } - - function wordpressPlugin() { - const dependencies = new Set() - - return { - name: 'wordpress-plugin', - enforce: 'post', - transform(code, id) { - if (!id.endsWith('.js')) { - return - } - - const imports = [ - ...(code.match(/^import .+ from ['"]@wordpress\/[^'"]+['"]/gm) || []), - ...(code.match(/^import ['"]@wordpress\/[^'"]+['"]/gm) || []), - ] - - imports.forEach((statement) => { - const match = - statement.match(/^import (.+) from ['"]@wordpress\/([^'"]+)['"]/) || - statement.match(/^import ['"]@wordpress\/([^'"]+)['"]/) - - if (!match) { - return - } - - const [, imports, pkg] = match - - if (!pkg) { - return - } - - const external = defaultRequestToExternal(`@wordpress/${pkg}`) - const handle = defaultRequestToHandle(`@wordpress/${pkg}`) - - if (external && handle) { - dependencies.add(handle) - - const replacement = imports - ? handleReplacements(imports, external) - : `const ${pkg.replace(/-([a-z])/g, (_, letter) => - letter.toUpperCase() - )} = ${external.join('.')};` - - code = code.replace(statement, replacement) - } - }) - - return { code, map: null } - }, - generateBundle() { - this.emitFile({ - type: 'asset', - fileName: 'editor.deps.json', - source: JSON.stringify([...dependencies]), - }) - }, - } - } - - function wordpressRollupPlugin() { - return { - name: 'wordpress-rollup-plugin', - options(opts) { - opts.external = (id) => id.startsWith('@wordpress/') - opts.output = opts.output || {} - opts.output.globals = (id) => { - if (id.startsWith('@wordpress/')) { - const packageName = id.replace('@wordpress/', '') - - return `wp.${packageName.replace(/-([a-z])/g, (_, letter) => - letter.toUpperCase() - )}` - } - } - - return opts - }, - } - } - - export { wordpressPlugin, wordpressRollupPlugin } \ No newline at end of file diff --git a/resources/scripts/build/theme-json.ts b/resources/scripts/build/wordpress.ts similarity index 61% rename from resources/scripts/build/theme-json.ts rename to resources/scripts/build/wordpress.ts index c5197df261..1a209fe85d 100644 --- a/resources/scripts/build/theme-json.ts +++ b/resources/scripts/build/wordpress.ts @@ -1,6 +1,11 @@ import type { Plugin } from 'vite' import resolveConfig from 'tailwindcss/resolveConfig' +import { + defaultRequestToExternal, + defaultRequestToHandle, +} from '@wordpress/dependency-extraction-webpack-plugin/lib/util' +// Theme JSON Types interface ThemeJsonColor { name: string slug: string @@ -78,33 +83,51 @@ interface ThemeJsonOptions { title?: string } -/** - * Converts a slug or kebab-case string into Title Case. - * - * Examples: - * - Input: "primary-color" -> Output: "Primary Color" - * - Input: "text-lg" -> Output: "Text Lg" - */ +// WordPress Plugin Helper Functions +function extractNamedImports(imports: string): string[] { + return imports + .match(/{([^}]+)}/)[1] + .split(',') + .map((s) => s.trim()) +} + +function handleNamedReplacement(namedImports: string[], external: string[]): string { + return namedImports + .map((imports) => { + const [name, alias = name] = imports + .split(' as ') + .map((script) => script.trim()) + + return `const ${alias} = ${external.join('.')}.${name};` + }) + .join('\n') +} + +function handleReplacements(imports: string, external: string[]): string { + if (imports.includes('{')) { + const namedImports = extractNamedImports(imports) + return handleNamedReplacement(namedImports, external) + } + + if (imports.includes('* as')) { + const alias = imports.match(/\*\s+as\s+(\w+)/)[1] + return `const ${alias} = ${external.join('.')};` + } + + const name = imports.trim() + return `const ${name} = ${external.join('.')};` +} + +// Theme JSON Helper Functions const toTitleCase = (slug: string): string => slug .split('-') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(' ') -/** - * Checks if a value is a valid CSS color. - * - * Examples: - * - Input: "#ff0000" -> true - * - Input: "rgb(255, 255, 0)" -> true - * - Input: "invalid-color" -> false - */ const isValidColor = (value: any): boolean => typeof value === 'string' && (value.startsWith('#') || value.startsWith('rgb')) -/** - * Recursively processes a Tailwind color object into an array of ThemeJsonColor. - */ const processColors = ( obj: Record, prefix = '' @@ -129,7 +152,7 @@ const processColors = ( return palette } -// Convert Tailwind values to Theme JSON structures +// Conversion Functions const convertTailwindColorsToThemeJson = (config: any): ThemeJsonColor[] => processColors(resolveConfig(config).theme.colors) @@ -139,7 +162,7 @@ const convertTailwindFontFamiliesToThemeJson = ( Object.entries(resolveConfig(config).theme.fontFamily).map(([name, value]) => ({ name: toTitleCase(name), slug: name.toLowerCase(), - fontFamily: Array.isArray(value) ? value.join(', ') : value, + fontFamily: Array.isArray(value) ? value.join(', ') : String(value), })) const convertTailwindFontSizesToThemeJson = ( @@ -151,14 +174,91 @@ const convertTailwindFontSizesToThemeJson = ( size: Array.isArray(value) ? value[0] : value, })) -// Merge default settings with user options const mergeSettings = ( defaults: ThemeJsonSettings, overrides: ThemeJsonSettings | undefined ): ThemeJsonSettings => ({ ...defaults, ...overrides }) -// Plugin definition -export default function wordPressThemeJson(options: ThemeJsonOptions = {}): Plugin { +// Plugin Exports +export function wordpressPlugin(): Plugin { + const dependencies = new Set() + + return { + name: 'wordpress-plugin', + enforce: 'post', + transform(code: string, id: string) { + if (!id.endsWith('.js')) { + return + } + + const imports = [ + ...(code.match(/^import .+ from ['"]@wordpress\/[^'"]+['"]/gm) || []), + ...(code.match(/^import ['"]@wordpress\/[^'"]+['"]/gm) || []), + ] + + imports.forEach((statement) => { + const match = + statement.match(/^import (.+) from ['"]@wordpress\/([^'"]+)['"]/) || + statement.match(/^import ['"]@wordpress\/([^'"]+)['"]/) + + if (!match) { + return + } + + const [, imports, pkg] = match + + if (!pkg) { + return + } + + const external = defaultRequestToExternal(`@wordpress/${pkg}`) + const handle = defaultRequestToHandle(`@wordpress/${pkg}`) + + if (external && handle) { + dependencies.add(handle) + + const replacement = imports + ? handleReplacements(imports, external) + : `const ${pkg.replace(/-([a-z])/g, (_, letter) => + letter.toUpperCase() + )} = ${external.join('.')};` + + code = code.replace(statement, replacement) + } + }) + + return { code, map: null } + }, + generateBundle() { + this.emitFile({ + type: 'asset', + fileName: 'editor.deps.json', + source: JSON.stringify([...dependencies]), + }) + }, + } +} + +export function wordpressRollupPlugin(): Plugin { + return { + name: 'wordpress-rollup-plugin', + options(opts: any) { + opts.external = (id: string) => id.startsWith('@wordpress/') + opts.output = opts.output || {} + opts.output.globals = (id: string) => { + if (id.startsWith('@wordpress/')) { + const packageName = id.replace('@wordpress/', '') + return `wp.${packageName.replace(/-([a-z])/g, (_, letter) => + letter.toUpperCase() + )}` + } + } + return opts + }, + } +} + +export function wordPressThemeJson(options: ThemeJsonOptions = {}): Plugin { const defaultSettings: ThemeJsonSettings = { background: { backgroundImage: true }, color: { @@ -182,7 +282,6 @@ export default function wordPressThemeJson(options: ThemeJsonOptions = {}): Plug return { name: 'wordpress-theme-json', - generateBundle() { const themeJson: Record = { __generated__: '⚠️ This file is generated. Do not edit.', @@ -216,7 +315,6 @@ export default function wordPressThemeJson(options: ThemeJsonOptions = {}): Plug } } - // Append optional fields if (options.customTemplates) themeJson.customTemplates = options.customTemplates if (options.patterns) themeJson.patterns = options.patterns if (options.styles) themeJson.styles = options.styles @@ -230,4 +328,4 @@ export default function wordPressThemeJson(options: ThemeJsonOptions = {}): Plug }) }, } -} \ No newline at end of file +} \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 086d3c11f0..badc2a81a3 100644 --- a/vite.config.js +++ b/vite.config.js @@ -3,8 +3,8 @@ import laravel from 'laravel-vite-plugin' import { wordpressPlugin, wordpressRollupPlugin, + wordPressThemeJson, } from './resources/scripts/build/wordpress' -import wordPressThemeJson from './resources/scripts/build/theme-json' import tailwindConfig from './tailwind.config.js' export default defineConfig({