From 94bb2a4e27e43e2594865527cbed6e6d9bf97f78 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Tue, 24 Dec 2024 19:26:03 -0500 Subject: [PATCH 1/4] handle import attributes with postcss plugin when prerendering --- packages/cli/src/config/rollup.config.js | 6 +- packages/plugin-postcss/package.json | 3 +- packages/plugin-postcss/src/index.js | 3 +- .../greenwood.config.js | 8 ++ .../loaders-default.import-attributes.spec.js | 90 +++++++++++++++++++ .../src/components/header/header.css | 3 + .../src/components/header/header.js | 24 +++++ .../src/pages/index.html | 14 +++ .../src/styles/theme.css | 7 ++ yarn.lock | 5 ++ 10 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 packages/plugin-postcss/test/cases/loaders-default.import-attributes/greenwood.config.js create mode 100644 packages/plugin-postcss/test/cases/loaders-default.import-attributes/loaders-default.import-attributes.spec.js create mode 100644 packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/components/header/header.css create mode 100644 packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/components/header/header.js create mode 100644 packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/pages/index.html create mode 100644 packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/styles/theme.css diff --git a/packages/cli/src/config/rollup.config.js b/packages/cli/src/config/rollup.config.js index a4d07608b..8c976d70f 100644 --- a/packages/cli/src/config/rollup.config.js +++ b/packages/cli/src/config/rollup.config.js @@ -242,7 +242,11 @@ function greenwoodImportMetaUrl(compilation) { headers }); let canTransform = false; - let response = new Response(code); + let response = new Response(code, { + headers: { + 'Content-Type': 'text/javascript' + } + }); // handle any custom imports or pre-processing first to ensure valid JavaScript for parsing if (await checkResourceExists(idUrl)) { diff --git a/packages/plugin-postcss/package.json b/packages/plugin-postcss/package.json index fa8a94c8b..252601b1a 100644 --- a/packages/plugin-postcss/package.json +++ b/packages/plugin-postcss/package.json @@ -32,6 +32,7 @@ "postcss-preset-env": "^7.0.1" }, "devDependencies": { - "@greenwood/cli": "^0.31.0-alpha.1" + "@greenwood/cli": "^0.31.0-alpha.1", + "@spectrum-css/typography": "^6.2.0" } } diff --git a/packages/plugin-postcss/src/index.js b/packages/plugin-postcss/src/index.js index 25e9bc241..0609d0739 100644 --- a/packages/plugin-postcss/src/index.js +++ b/packages/plugin-postcss/src/index.js @@ -35,8 +35,7 @@ class PostCssResource extends ResourceInterface { async shouldPreIntercept(url, request, response) { return url.protocol === 'file:' - && url.pathname.split('.').pop() === this.extensions[0] - && (request?.headers?.get('Content-Type')?.includes('text/css') || response?.headers?.get('Content-Type')?.includes('text/css')); + && (request?.headers?.get('Accept')?.includes('text/css') && response?.headers?.get('Content-Type')?.includes('text/css')); } async preIntercept(url, request, response) { diff --git a/packages/plugin-postcss/test/cases/loaders-default.import-attributes/greenwood.config.js b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/greenwood.config.js new file mode 100644 index 000000000..8180684f6 --- /dev/null +++ b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/greenwood.config.js @@ -0,0 +1,8 @@ +import { greenwoodPluginPostCss } from '../../../src/index.js'; + +export default { + prerender: true, + plugins: [ + greenwoodPluginPostCss() + ] +}; \ No newline at end of file diff --git a/packages/plugin-postcss/test/cases/loaders-default.import-attributes/loaders-default.import-attributes.spec.js b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/loaders-default.import-attributes.spec.js new file mode 100644 index 000000000..0c1244d8e --- /dev/null +++ b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/loaders-default.import-attributes.spec.js @@ -0,0 +1,90 @@ +/* + * Use Case + * Run Greenwood with default PostCSS config when using Import Attributes. + * + * User Result + * Should generate a bare bones Greenwood build with the user's CSS file correctly minified. + * + * User Command + * greenwood build + * + * User Config + * const pluginPostCss = require('@greenwood/plugin-postcss'); + * + * { + * plugins: [ + * pluginPostCss() + * ] + * } + * + * User Workspace + * src/ + * components/ + * header/ + * header.js + * header.css + * pages/ + * index.html + * styles/ + * theme.css + */ +import chai from 'chai'; +import fs from 'fs'; +import glob from 'glob-promise'; +import path from 'path'; +import { runSmokeTest } from '../../../../../test/smoke-test.js'; +import { getOutputTeardownFiles } from '../../../../../test/utils.js'; +import { Runner } from 'gallinago'; +import { fileURLToPath, URL } from 'url'; + +const expect = chai.expect; + +describe('Build Greenwood With: ', function() { + const LABEL = 'Default PostCSS configuration and CSS Import Attributes'; + const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js'); + const outputPath = fileURLToPath(new URL('.', import.meta.url)); + let runner; + + before(function() { + this.context = { + publicDir: path.join(outputPath, 'public') + }; + runner = new Runner(false, true); + }); + + describe(LABEL, function() { + + before(function() { + runner.setup(outputPath); + runner.runCommand(cliPath, 'build'); + }); + + runSmokeTest(['public', 'index'], LABEL); + + describe('Page referencing external nested CSS file', function() { + it('should output correctly processed nested CSS as non nested', function() { + const expectedCss = 'body{color:red}h1{color:blue}'; + const cssFiles = glob.sync(path.join(this.context.publicDir, 'styles', '*.css')); + const css = fs.readFileSync(cssFiles[0], 'utf-8'); + + expect(cssFiles.length).to.equal(1); + expect(css).to.equal(expectedCss); + }); + + it('should output correctly processed import attributes in header.js bundle output', function() { + const headerFiles = glob.sync(path.join(this.context.publicDir, 'header.*.js')); + console.log({ headerFiles }); + const js = fs.readFileSync(headerFiles[0], 'utf-8'); + + expect(headerFiles.length).to.equal(1); + expect(js.indexOf('import e from"/styles/theme.1126086472.css"with{type:"css"};') >= 0).to.equal(true); + expect(js.indexOf('import t from"/header.CaS9Xrom.css"with{type:"css"};') >= 0).to.equal(true); + expect(js.indexOf('const s=new CSSStyleSheet;s.replaceSync(\'.spectrum{--spectrum-font-family-ar:myriad-arabic') >= 0).to.equal(true); + }); + }); + }); + + after(function() { + runner.teardown(getOutputTeardownFiles(outputPath)); + }); +}); \ No newline at end of file diff --git a/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/components/header/header.css b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/components/header/header.css new file mode 100644 index 000000000..eadfef754 --- /dev/null +++ b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/components/header/header.css @@ -0,0 +1,3 @@ +header { + background-color: aqua; +} \ No newline at end of file diff --git a/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/components/header/header.js b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/components/header/header.js new file mode 100644 index 000000000..567206531 --- /dev/null +++ b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/components/header/header.js @@ -0,0 +1,24 @@ +import themeSheet from '../../styles/theme.css' with { type: 'css' }; +import headerSheet from './header.css' with { type: 'css' }; +import SpectrumTypography from '@spectrum-css/typography' with { type: 'css' }; + +export default class Header extends HTMLElement { + connectedCallback() { + if (!this.shadowRoot) { + const template = document.createElement('template'); + + template.innerHTML = ` +
+ Welcome to my site +
+ `; + + this.attachShadow({ mode: 'open' }); + this.shadowRoot.appendChild(template.content.cloneNode(true)); + } + + this.shadowRoot.adoptedStyleSheets = [themeSheet, headerSheet, SpectrumTypography]; + } +} + +customElements.define('app-header', Header); \ No newline at end of file diff --git a/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/pages/index.html b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/pages/index.html new file mode 100644 index 000000000..c627b7dc7 --- /dev/null +++ b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/pages/index.html @@ -0,0 +1,14 @@ + + + + + + + + + + +

Hello World!

+ + + \ No newline at end of file diff --git a/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/styles/theme.css b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/styles/theme.css new file mode 100644 index 000000000..9ff1eeec0 --- /dev/null +++ b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/src/styles/theme.css @@ -0,0 +1,7 @@ +body { + color: red; +} + +h1 { + color: blue; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b3e814571..356ab3850 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4179,6 +4179,11 @@ resolved "https://registry.yarnpkg.com/@spectrum-css/card/-/card-9.3.0.tgz#736d6035f75b34e49e9fcb0198ba4667d024ebf9" integrity sha512-uMWwh/kkkcOSf8W5Kz1TyQjaWxKeTKm1Gm5XaAyHu+IHk+mYJPrDeZNoYfyIqZ+x/RgyVcCzx2H3VXA/uUaaeA== +"@spectrum-css/typography@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@spectrum-css/typography/-/typography-6.2.0.tgz#2bf928d9fb7fb51bc67feeb3d8a82f8ee093ee87" + integrity sha512-yaqc4wFY3asUhh3zzdqRI5dVJow7xJ2nL2bo4AADDLT305Dtje5PYO89m+f3nTVf2utKE3H8nlJbWUG57QpFqw== + "@spectrum-web-components/action-button@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@spectrum-web-components/action-button/-/action-button-1.0.1.tgz#98fd0af1d825ec5c40a54f2d1cc1d311815a169e" From e1f7f4c0988f88e718407d9fdc2661622c9ee4aa Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Tue, 24 Dec 2024 20:39:36 -0500 Subject: [PATCH 2/4] refine implementation --- packages/cli/src/config/rollup.config.js | 6 +----- packages/plugin-postcss/src/index.js | 8 ++++++-- .../loaders-default.import-attributes.spec.js | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/config/rollup.config.js b/packages/cli/src/config/rollup.config.js index 8c976d70f..a4d07608b 100644 --- a/packages/cli/src/config/rollup.config.js +++ b/packages/cli/src/config/rollup.config.js @@ -242,11 +242,7 @@ function greenwoodImportMetaUrl(compilation) { headers }); let canTransform = false; - let response = new Response(code, { - headers: { - 'Content-Type': 'text/javascript' - } - }); + let response = new Response(code); // handle any custom imports or pre-processing first to ensure valid JavaScript for parsing if (await checkResourceExists(idUrl)) { diff --git a/packages/plugin-postcss/src/index.js b/packages/plugin-postcss/src/index.js index 0609d0739..05bde0f0d 100644 --- a/packages/plugin-postcss/src/index.js +++ b/packages/plugin-postcss/src/index.js @@ -35,7 +35,7 @@ class PostCssResource extends ResourceInterface { async shouldPreIntercept(url, request, response) { return url.protocol === 'file:' - && (request?.headers?.get('Accept')?.includes('text/css') && response?.headers?.get('Content-Type')?.includes('text/css')); + && (request?.headers?.get('Content-Type')?.includes('text/css') || response?.headers?.get('Content-Type')?.includes('text/css')); } async preIntercept(url, request, response) { @@ -46,7 +46,11 @@ class PostCssResource extends ResourceInterface { ? (await postcss(plugins).process(body, { from: normalizePathnameForWindows(url) })).css : body; - return new Response(css); + // preserve original headers (content type / accept) + // since this could be used in JS or CSS contexts + return new Response(css, { + headers: response.headers + }); } } diff --git a/packages/plugin-postcss/test/cases/loaders-default.import-attributes/loaders-default.import-attributes.spec.js b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/loaders-default.import-attributes.spec.js index 0c1244d8e..2f1562e86 100644 --- a/packages/plugin-postcss/test/cases/loaders-default.import-attributes/loaders-default.import-attributes.spec.js +++ b/packages/plugin-postcss/test/cases/loaders-default.import-attributes/loaders-default.import-attributes.spec.js @@ -73,7 +73,6 @@ describe('Build Greenwood With: ', function() { it('should output correctly processed import attributes in header.js bundle output', function() { const headerFiles = glob.sync(path.join(this.context.publicDir, 'header.*.js')); - console.log({ headerFiles }); const js = fs.readFileSync(headerFiles[0], 'utf-8'); expect(headerFiles.length).to.equal(1); From 3011839b72b88ca42a0da859de69c70a3727d5b0 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Tue, 24 Dec 2024 20:58:43 -0500 Subject: [PATCH 3/4] refactor babel plugin intercepting to not rely on extension check --- packages/plugin-babel/src/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/plugin-babel/src/index.js b/packages/plugin-babel/src/index.js index a3a6b3178..826ccc178 100644 --- a/packages/plugin-babel/src/index.js +++ b/packages/plugin-babel/src/index.js @@ -37,8 +37,12 @@ class BabelResource extends ResourceInterface { this.contentType = ['text/javascript']; } - async shouldPreIntercept(url) { - return url.pathname.split('.').pop() === this.extensions[0] && !url.pathname.startsWith('/node_modules/'); + async shouldPreIntercept(url, request, response) { + const { protocol, pathname } = url; + + return protocol === 'file:' + && !pathname.startsWith('/node_modules/') + && response.headers.get('Content-Type').indexOf(this.contentType) >= 0; } async preIntercept(url, request, response) { From 80ab65573ad40490e1b9352d0651612919f120de Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Fri, 27 Dec 2024 20:04:33 -0500 Subject: [PATCH 4/4] revert babel plugin change --- packages/plugin-babel/src/index.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/plugin-babel/src/index.js b/packages/plugin-babel/src/index.js index 826ccc178..a3a6b3178 100644 --- a/packages/plugin-babel/src/index.js +++ b/packages/plugin-babel/src/index.js @@ -37,12 +37,8 @@ class BabelResource extends ResourceInterface { this.contentType = ['text/javascript']; } - async shouldPreIntercept(url, request, response) { - const { protocol, pathname } = url; - - return protocol === 'file:' - && !pathname.startsWith('/node_modules/') - && response.headers.get('Content-Type').indexOf(this.contentType) >= 0; + async shouldPreIntercept(url) { + return url.pathname.split('.').pop() === this.extensions[0] && !url.pathname.startsWith('/node_modules/'); } async preIntercept(url, request, response) {