From e19520278b6bd93e5b1718d2ba0eec3046fa03ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=A8=E6=9D=89?= Date: Mon, 6 Dec 2021 20:24:04 +0800 Subject: [PATCH 1/3] feat: Simple CSS injection support --- src/cli-main.ts | 1 + src/esbuild/index.ts | 2 +- src/esbuild/postcss.ts | 96 +++++++++++++++++++++++++++++++++--------- src/options.ts | 1 + test/index.test.ts | 15 +++++++ 5 files changed, 93 insertions(+), 22 deletions(-) diff --git a/src/cli-main.ts b/src/cli-main.ts index ea0c8d41..b2722bb5 100644 --- a/src/cli-main.ts +++ b/src/cli-main.ts @@ -84,6 +84,7 @@ export async function main(options: Options = {}) { }) .option('--loader ', 'Specify the loader for a file extension') .option('--no-config', 'Disable config file') + .option('--inject-style', 'Inject style tag to document head') .action(async (files: string[], flags) => { const { build } = await import('.') Object.assign(options, { diff --git a/src/esbuild/index.ts b/src/esbuild/index.ts index c491cd8c..fa934100 100644 --- a/src/esbuild/index.ts +++ b/src/esbuild/index.ts @@ -108,7 +108,7 @@ export async function runEsbuild( }), options.tsconfigDecoratorMetadata && swcPlugin(), nativeNodeModulesPlugin(), - postcssPlugin({ css }), + postcssPlugin({ css, inject: options.injectStyle }), sveltePlugin({ css }), ...(options.esbuildPlugins || []), ] diff --git a/src/esbuild/postcss.ts b/src/esbuild/postcss.ts index 2f07cf14..2fdf4e8d 100644 --- a/src/esbuild/postcss.ts +++ b/src/esbuild/postcss.ts @@ -1,12 +1,14 @@ import fs from 'fs' import path from 'path' -import { Plugin } from 'esbuild' +import { Plugin, transform } from 'esbuild' import { getPostcss } from '../utils' export const postcssPlugin = ({ css, + inject }: { css?: Map + inject?: boolean }): Plugin => { return { name: 'postcss', @@ -34,11 +36,47 @@ export const postcssPlugin = ({ } } + build.onResolve({ filter: /^style-inject$/ }, () => { + return { path: 'style-inject', namespace: 'style-inject' } + }) + + build.onLoad({ filter: /^style-inject$/, namespace: 'style-inject' }, () => { + return { + // Taken from https://github.com/egoist/style-inject/blob/master/src/index.js (MIT) + contents: ` + export default function styleInject(css, { insertAt } = {}) { + if (!css || typeof document === 'undefined') return + + const head = document.head || document.getElementsByTagName('head')[0] + const style = document.createElement('style') + style.type = 'text/css' + + if (insertAt === 'top') { + if (head.firstChild) { + head.insertBefore(style, head.firstChild) + } else { + head.appendChild(style) + } + } else { + head.appendChild(style) + } + + if (style.styleSheet) { + style.styleSheet.cssText = css + } else { + style.appendChild(document.createTextNode(css)) + } + } + `, + loader: 'js' + } + }) + build.onLoad({ filter: /\.css$/ }, async (args) => { let contents: string if (css && args.path.endsWith('.svelte.css')) { - contents = css.get(args.path) as string + contents = css.get(args.path)! } else { contents = await fs.promises.readFile(args.path, 'utf8') } @@ -46,33 +84,49 @@ export const postcssPlugin = ({ // Load postcss config const { plugins, options } = await getPostcssConfig(args.path) - // Return if no postcss plugins are supplied - if (!plugins || plugins.length === 0) { - return { - contents, - loader: 'css', + walk: { + // Return if no postcss plugins are supplied + if (!plugins || plugins.length === 0) { + break walk } + + // Load postcss + const postcss = getPostcss() + if (!postcss) { + return { + errors: [ + { + text: `postcss is not installed`, + }, + ], + } + } + + // Transform CSS + const result = await postcss + ?.default(plugins) + .process(contents, { ...options, from: args.path }) + + contents = result.css } - // Load postcss - const postcss = getPostcss() - if (!postcss) { + if (inject) { + contents = (await transform(contents, { + minify: true, + loader: 'css' + })).code + + contents = `import styleInject from 'style-inject' +styleInject(\`${contents}\`)` + return { - errors: [ - { - text: `postcss is not installed`, - }, - ], + contents, + loader: 'js' } } - // Transform CSS - const result = await postcss - ?.default(plugins) - .process(contents, { ...options, from: args.path }) - return { - contents: result.css, + contents, loader: 'css', } }) diff --git a/src/options.ts b/src/options.ts index 415fceba..459e06f5 100644 --- a/src/options.ts +++ b/src/options.ts @@ -122,4 +122,5 @@ export type Options = { * Use a custom tsconfig */ tsconfig?: string + injectStyle?: boolean } diff --git a/test/index.test.ts b/test/index.test.ts index 3e30cc85..98deeabd 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -697,3 +697,18 @@ test('decorator metadata', async (t) => { const contents = await getFileContent('dist/input.js') t.assert(contents.includes(`Reflect.metadata("design:type"`)) }) + +test('inject style', async (t) => { + const { outFiles, output } = await run( + t.title, + { + 'input.ts': `import './style.css'`, + 'style.css': `.hello { color: red }` + }, + { + flags: ['--inject-style', '--minify'], + } + ) + t.deepEqual(outFiles, ['input.js']) + t.assert(output.includes('.hello{color:red}')) +}) From 1ea515568a593b80d12c72209b149154850089aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=A8=E6=9D=89?= Date: Mon, 6 Dec 2021 20:29:35 +0800 Subject: [PATCH 2/3] change comment --- src/esbuild/postcss.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/esbuild/postcss.ts b/src/esbuild/postcss.ts index 2fdf4e8d..fe6ea3e5 100644 --- a/src/esbuild/postcss.ts +++ b/src/esbuild/postcss.ts @@ -85,7 +85,7 @@ export const postcssPlugin = ({ const { plugins, options } = await getPostcssConfig(args.path) walk: { - // Return if no postcss plugins are supplied + // Break walk block if no postcss plugins are supplied if (!plugins || plugins.length === 0) { break walk } From 51ca5ecb40b6ea5b5077b2229d2b62c4c39a1463 Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Wed, 8 Dec 2021 14:48:43 +0800 Subject: [PATCH 3/3] tweaks --- src/esbuild/postcss.ts | 52 +++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/esbuild/postcss.ts b/src/esbuild/postcss.ts index fe6ea3e5..54a23d75 100644 --- a/src/esbuild/postcss.ts +++ b/src/esbuild/postcss.ts @@ -5,7 +5,7 @@ import { getPostcss } from '../utils' export const postcssPlugin = ({ css, - inject + inject, }: { css?: Map inject?: boolean @@ -27,7 +27,7 @@ export const postcssPlugin = ({ const result = await loadConfig({}, path.dirname(file)) configCache.set(file, result) return result - } catch (error) { + } catch (error: any) { if (error.message.includes('No PostCSS Config found in')) { const result = { plugins: [], options: {} } return result @@ -36,14 +36,16 @@ export const postcssPlugin = ({ } } - build.onResolve({ filter: /^style-inject$/ }, () => { - return { path: 'style-inject', namespace: 'style-inject' } + build.onResolve({ filter: /^#style-inject$/ }, () => { + return { path: '#style-inject', namespace: '#style-inject' } }) - build.onLoad({ filter: /^style-inject$/, namespace: 'style-inject' }, () => { - return { - // Taken from https://github.com/egoist/style-inject/blob/master/src/index.js (MIT) - contents: ` + build.onLoad( + { filter: /^#style-inject$/, namespace: '#style-inject' }, + () => { + return { + // Taken from https://github.com/egoist/style-inject/blob/master/src/index.js (MIT) + contents: ` export default function styleInject(css, { insertAt } = {}) { if (!css || typeof document === 'undefined') return @@ -68,9 +70,10 @@ export const postcssPlugin = ({ } } `, - loader: 'js' + loader: 'js', + } } - }) + ) build.onLoad({ filter: /\.css$/ }, async (args) => { let contents: string @@ -84,12 +87,7 @@ export const postcssPlugin = ({ // Load postcss config const { plugins, options } = await getPostcssConfig(args.path) - walk: { - // Break walk block if no postcss plugins are supplied - if (!plugins || plugins.length === 0) { - break walk - } - + if (plugins || plugins.length > 0) { // Load postcss const postcss = getPostcss() if (!postcss) { @@ -111,17 +109,23 @@ export const postcssPlugin = ({ } if (inject) { - contents = (await transform(contents, { - minify: true, - loader: 'css' - })).code - - contents = `import styleInject from 'style-inject' -styleInject(\`${contents}\`)` + contents = ( + await transform(contents, { + minify: build.initialOptions.minify, + minifyIdentifiers: build.initialOptions.minifyIdentifiers, + minifySyntax: build.initialOptions.minifySyntax, + minifyWhitespace: build.initialOptions.minifyWhitespace, + loader: 'css', + }) + ).code + + contents = `import styleInject from '#style-inject';styleInject(${JSON.stringify( + contents + )})` return { contents, - loader: 'js' + loader: 'js', } }