Skip to content

Commit

Permalink
feature/issue 1233 CSS Modules ™️ plugin (#1285)
Browse files Browse the repository at this point in the history
* initial port from website repo

* build and prerender test case

* build and develop test cases

* README and website documentation

* only patch JSDOM within test setup blocks

* handle module types for scripts

* support any custom identifier

* update test case label

* testing for nested CSS module

* better path detection handling

* additional nested and recursive test cases

* clean up and final testing

* misc refactoring

* basic TS support and caveats for other formats
  • Loading branch information
thescientist13 authored Oct 18, 2024
1 parent 0a96e8e commit 03f5999
Show file tree
Hide file tree
Showing 34 changed files with 1,778 additions and 11 deletions.
12 changes: 7 additions & 5 deletions packages/cli/src/config/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,19 @@ function greenwoodResourceLoader (compilation, browser = false) {
};

// filter first for any bare specifiers
if (await checkResourceExists(idUrl) && extension !== 'js') {
for (const plugin of resourcePlugins) {
if (plugin.shouldResolve && await plugin.shouldResolve(idUrl)) {
idUrl = new URL((await plugin.resolve(idUrl)).url);
if (await checkResourceExists(idUrl) && !id.startsWith('\x00')) {
if (extension !== 'js') {
for (const plugin of resourcePlugins) {
if (plugin.shouldResolve && await plugin.shouldResolve(idUrl)) {
idUrl = new URL((await plugin.resolve(idUrl)).url);
}
}
}

const request = new Request(idUrl, {
headers
});
let response = new Response('');
let response = new Response('', { headers: { 'Content-Type': 'text/javascript' } });

for (const plugin of resourcePlugins) {
if (plugin.shouldServe && await plugin.shouldServe(idUrl, request)) {
Expand Down
10 changes: 5 additions & 5 deletions packages/cli/src/lifecycles/prerender.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fs from 'fs/promises';
import { checkResourceExists, trackResourcesForRoute } from '../lib/resource-utils.js';
import { checkResourceExists, trackResourcesForRoute, mergeResponse } from '../lib/resource-utils.js';
import os from 'os';
import { WorkerPool } from '../lib/threadpool.js';

Expand Down Expand Up @@ -30,12 +30,12 @@ async function interceptPage(url, request, plugins, body) {
});

for (const plugin of plugins) {
if (plugin.shouldPreIntercept && await plugin.shouldPreIntercept(url, request, response)) {
response = await plugin.preIntercept(url, request, response);
if (plugin.shouldPreIntercept && await plugin.shouldPreIntercept(url, request, response.clone())) {
response = mergeResponse(response, await plugin.preIntercept(url, request, response.clone()));
}

if (plugin.shouldIntercept && await plugin.shouldIntercept(url, request, response)) {
response = await plugin.intercept(url, request, response);
if (plugin.shouldIntercept && await plugin.shouldIntercept(url, request, response.clone())) {
response = mergeResponse(response, await plugin.intercept(url, request, response.clone()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ describe('Build Greenwood With: ', function() {
});
});
});

});

after(function() {
Expand Down
126 changes: 126 additions & 0 deletions packages/plugin-css-modules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# @greenwood/plugin-css-modules

## Overview

A Greenwood plugin for authoring [**CSS Modules ™️**](https://github.com/css-modules/css-modules). It is a modest implementation of [the specification](https://github.com/css-modules/icss). 🙂

This is NOT to be confused with [CSS Module _Scripts_](https://web.dev/articles/css-module-scripts), which Greenwood already supports.

> This package assumes you already have `@greenwood/cli` installed.
## Installation

You can use your favorite JavaScript package manager to install this package.

_examples:_
```bash
# npm
npm i -D @greenwood/plugin-css-modules

# yarn
yarn add @greenwood/plugin-css-modules --dev
```

## Usage

Add this plugin to your _greenwood.config.js_.

```javascript
import { greenwoodPluginCssModules } from '@greenwood/plugin-css-modules';

export default {
...

plugins: [
greenwoodPluginCssModules()
]
}
```

Now you can create a CSS file that ends in _.module.css_

```css
/* header.module.css */
.container {
display: flex;
justify-content: space-between;
}

.navBarMenu {
border: 1px solid #020202;
}

.navBarMenuItem {
& a {
text-decoration: none;
color: #020202;
}
}

@media screen and (min-width: 768px) {
.container {
padding: 10px 20px;
}
}
```


And reference that in your (Light DOM) HTML based Web Component

```js
// header.js
import styles from './header.module.css';

export default class Header extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<header class="${styles.container}">
<ul class="${styles.navBarMenu}">
<li class="${styles.navBarMenuItem}">
<a href="/about/" title="Documentation">About</a>
</li>
<li class="${styles.navBarMenuItem}">
<a href="/contact/" title="Guides">Contact</a>
</li>
</ul>
</header>
`;
}
}

customElements.define('app-header', Header);
```

From there, Greenwood will scope your CSS by prefixing with the filename and a hash, and inline that into a `<style>` tag in the HTML and strip the reference to the _module.css_ file from your JavaScript file.


## Caveats

> This plugin aims to cover a representative majority of the specification, though if you find missing capabilities please consider submitting an issue and / or PR!
There are some caveats to consider when using this plugin:

1. This plugin only supports usage of CSS Modules within vanilla JavaScript, or [TypeScript (_.ts_)](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-typescript) and [JSX (_.jsx_)](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-import-jsx) when combined with our plugins
1. This plugin only checks for [lower camelCase](https://github.com/css-modules/css-modules/blob/master/docs/naming.md) based class names
```css
/* works ✅ */
.navBar { }

/* does not work 🚫 */
.nav-bar { }
```
1. Destructuring is not supported, so this will not work
```js
import styles from './header.module.css';

export default class Header extends HTMLElement {
connectedCallback() {
const { container, navBar, ... } = styles;

// ...
}
}

customElements.define('app-header', Header);
```
1. From the spec, [exporting `@value` variables](https://github.com/css-modules/css-modules/blob/master/docs/values-variables.md) is not supported
38 changes: 38 additions & 0 deletions packages/plugin-css-modules/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@greenwood/plugin-css-modules",
"version": "0.30.0-alpha.6",
"description": "A Greenwood plugin for authoring CSS Modules",
"repository": "https://github.com/ProjectEvergreen/greenwood",
"homepage": "https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-css-modules",
"author": "Owen Buckley <[email protected]>",
"license": "MIT",
"keywords": [
"Greenwood",
"Static Site Generator",
"Full Stack Web Development",
"Web Components",
"CSS Modules"
],
"main": "src/index.js",
"type": "module",
"files": [
"src/"
],
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"@greenwood/cli": "^0.4.0"
},
"dependencies": {
"acorn": "^8.0.1",
"acorn-import-attributes": "^1.9.5",
"acorn-walk": "^8.0.0",
"css-tree": "^3.0.0",
"node-html-parser": "^1.2.21",
"sucrase": "^3.35.0"
},
"devDependencies": {
"@greenwood/cli": "^0.30.0-alpha.6"
}
}
Loading

0 comments on commit 03f5999

Please sign in to comment.