From 3beb1a3c0376b2a43b8a7658c6c94ceb90aa67ac Mon Sep 17 00:00:00 2001 From: Salama Ashoush Date: Tue, 25 Jul 2023 12:52:58 +0200 Subject: [PATCH] :sparkles: inline/load css to style element --- packages/esbuild-plugin-next/src/index.ts | 35 ++++++++++++++++++++++- packages/esbuild-plugin/src/index.ts | 35 +++++++++++++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/packages/esbuild-plugin-next/src/index.ts b/packages/esbuild-plugin-next/src/index.ts index 1d0f9bdb8..327cb5807 100644 --- a/packages/esbuild-plugin-next/src/index.ts +++ b/packages/esbuild-plugin-next/src/index.ts @@ -11,12 +11,20 @@ import type { Plugin } from 'esbuild'; const vanillaCssNamespace = 'vanilla-extract-css-ns'; +async function hashString(str: string) { + const encoder = new TextEncoder(); + const data = encoder.encode(str); + const hash = await crypto.subtle.digest('SHA-256', data); + return hash; +} + interface VanillaExtractPluginOptions { outputCss?: boolean; runtime?: boolean; processCss?: (css: string) => Promise; identifiers?: IdentifierOption; compilerVitePlugins?: CreateCompilerOptions['vitePlugins']; + useStyleLoader?: boolean; } export function vanillaExtractPlugin({ outputCss = true, @@ -24,6 +32,7 @@ export function vanillaExtractPlugin({ processCss, identifiers: identOption, compilerVitePlugins: vitePlugins, + useStyleLoader, }: VanillaExtractPluginOptions = {}): Plugin { if (runtime) { // If using runtime CSS then just apply fileScopes and debug IDs to code @@ -60,11 +69,35 @@ export function vanillaExtractPlugin({ if (typeof processCss === 'function') { css = await processCss(css); } + const resolveDir = dirname(filePath); + + if (useStyleLoader) { + // replace slashes with dashes to make it a valid CSS identifier + const elementId = `vanilla-extract-${ + build.initialOptions.minify + ? await hashString(filePath) + : filePath.replace(/\//g, '__') + }`; + const contents = ` + if (document.getElementById('${elementId}') === null) { + const element = document.createElement('style'); + element.id = '${elementId}'; + element.textContent = \`${css}\`; + document.head.append(element); + } + export default {}; + `; + return { + contents, + loader: 'js', + resolveDir, + }; + } return { contents: css, loader: 'css', - resolveDir: dirname(filePath), + resolveDir, }; }, ); diff --git a/packages/esbuild-plugin/src/index.ts b/packages/esbuild-plugin/src/index.ts index c86aa777b..f5d3a7484 100644 --- a/packages/esbuild-plugin/src/index.ts +++ b/packages/esbuild-plugin/src/index.ts @@ -14,6 +14,13 @@ import type { Plugin } from 'esbuild'; const vanillaCssNamespace = 'vanilla-extract-css-ns'; +async function hashString(str: string) { + const encoder = new TextEncoder(); + const data = encoder.encode(str); + const hash = await crypto.subtle.digest('SHA-256', data); + return hash; +} + interface VanillaExtractPluginOptions { outputCss?: boolean; /** @@ -24,6 +31,7 @@ interface VanillaExtractPluginOptions { processCss?: (css: string) => Promise; identifiers?: IdentifierOption; esbuildOptions?: CompileOptions['esbuildOptions']; + useStyleLoader?: boolean; } export function vanillaExtractPlugin({ outputCss, @@ -32,6 +40,7 @@ export function vanillaExtractPlugin({ processCss, identifiers, esbuildOptions, + useStyleLoader, }: VanillaExtractPluginOptions = {}): Plugin { if (runtime) { // If using runtime CSS then just apply fileScopes and debug IDs to code @@ -58,9 +67,31 @@ export function vanillaExtractPlugin({ } const rootDir = build.initialOptions.absWorkingDir ?? process.cwd(); + const filePath = join(rootDir, fileName); + const resolveDir = dirname(filePath); - const resolveDir = dirname(join(rootDir, fileName)); - + // If using style-loader then we need to return a JS file that inserts the CSS into the DOM + if (useStyleLoader) { + const elementId = `vanilla-extract-${ + build.initialOptions.minify + ? await hashString(fileName) + : fileName.replace(/[^a-zA-Z0-9-_]/g, '-') + }`; + const contents = ` + if (document.getElementById('${elementId}') === null) { + const element = document.createElement('style'); + element.id = '${elementId}'; + element.textContent = \`${source}\`; + document.head.append(element); + } + export default {}; + `; + return { + contents, + loader: 'js', + resolveDir, + }; + } return { contents: source, loader: 'css',