Skip to content

Commit

Permalink
basic TS support and caveats for other formats
Browse files Browse the repository at this point in the history
  • Loading branch information
thescientist13 committed Oct 18, 2024
1 parent 46e06fb commit 55a6dd2
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/plugin-css-modules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ From there, Greenwood will scope your CSS by prefixing with the filename and a h
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 ✅ */
Expand Down
3 changes: 2 additions & 1 deletion packages/plugin-css-modules/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"acorn-import-attributes": "^1.9.5",
"acorn-walk": "^8.0.0",
"css-tree": "^3.0.0",
"node-html-parser": "^1.2.21"
"node-html-parser": "^1.2.21",
"sucrase": "^3.35.0"
},
"devDependencies": {
"@greenwood/cli": "^0.30.0-alpha.6"
Expand Down
17 changes: 12 additions & 5 deletions packages/plugin-css-modules/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as acornWalk from 'acorn-walk';
import * as acorn from 'acorn';
import { hashString } from '@greenwood/cli/src/lib/hashing-utils.js';
import { importAttributes } from 'acorn-import-attributes';
import { transform } from 'sucrase';

const MODULES_MAP_FILENAME = '__css-modules-map.json';
/*
Expand All @@ -33,10 +34,14 @@ function getCssModulesMap(compilation) {

function walkAllImportsForCssModules(scriptUrl, sheets, compilation) {
const scriptContents = fs.readFileSync(scriptUrl, 'utf-8');
const result = transform(scriptContents, {
transforms: ['typescript', 'jsx'],
jsxRuntime: 'preserve'
});

acornWalk.simple(
acorn.Parser.extend(importAttributes).parse(scriptContents, {
ecmaVersion: '2020',
acorn.Parser.extend(importAttributes).parse(result.code, {
ecmaVersion: 'latest',
sourceType: 'module'
}),
{
Expand Down Expand Up @@ -67,6 +72,7 @@ function walkAllImportsForCssModules(scriptUrl, sheets, compilation) {
// drill down from a SelectorList to its first Selector
// and check its first child to see if it is a ClassSelector
// and if so, hash that initial class selector

if (node.type === 'SelectorList') {
if (node.children?.head?.data?.type === 'Selector') {
if (node.children?.head?.data?.children?.head?.data?.type === 'ClassSelector') {
Expand Down Expand Up @@ -117,8 +123,9 @@ function walkAllImportsForCssModules(scriptUrl, sheets, compilation) {
}
})
);
} else if (node.source.value.endsWith('.js')) {
// TODO this will be an issue with say TypeScript...
} else if (value.endsWith('.js') || value.endsWith('.jsx') || value.endsWith('.ts')) {
// no good way to get at async plugin processing so right now
// we can only support what we can provide to acorn
const recursiveScriptUrl = new URL(value, scriptUrl);

if (fs.existsSync(recursiveScriptUrl)) {
Expand Down Expand Up @@ -240,7 +247,7 @@ class StripCssModulesResource extends ResourceInterface {

acornWalk.simple(
acorn.Parser.extend(importAttributes).parse(contents, {
ecmaVersion: '2020',
ecmaVersion: 'latest',
sourceType: 'module'
}),
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
import fs from 'fs/promises';
import { greenwoodPluginCssModules } from '../../../src/index.js';
import { transform } from 'sucrase';

class NaiveTsResource {
constructor(compilation, options) {
this.compilation = compilation;
this.options = options;

this.extensions = ['ts'];
this.contentType = 'text/javascript';
}

async shouldServe(url) {
return url.pathname.split('.').pop() === this.extensions[0];
}

async serve(url) {
const scriptContents = await fs.readFile(url, 'utf-8');
const result = transform(scriptContents, {
transforms: ['typescript', 'jsx'],
jsxRuntime: 'preserve'
});

return new Response(result.code, {
headers: new Headers({
'Content-Type': this.contentType
})
});
}
}

export default {
prerender: true,
plugins: [
{
type: 'resource',
name: 'plugin-naive-ts',
provider: (compilation, options) => new NaiveTsResource(compilation, options)
},
greenwoodPluginCssModules()
]
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Use Case
* Run Greenwood build with CSS Modules plugin and pre-rendering.
* Run Greenwood build with CSS Modules plugin and pre-rendering, including an example using TypeScript.
*
* User Result
* Should generate a Greenwood project with CSS Modules properly transformed.
Expand Down Expand Up @@ -28,7 +28,7 @@
* header.js
* header.module.css
* logo/
* logo.js
* logo.ts
* logo.module.css
* index.html
*/
Expand Down Expand Up @@ -253,7 +253,7 @@ describe('Build Greenwood With: ', function() {
expect(styleText).to.contain(expectedFooterCss.replace(/\[placeholder\]/g, scopedHash));
});

it('should have the source <app-header> CSS class names as scoped class names inlined in a <style> tag', () => {
it('should have the source <app-footer> CSS class names as scoped class names inlined in a <style> tag', () => {
const styles = dom.window.document.querySelectorAll('head style');
const styleText = styles[0].textContent;
let classes = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import header from './header.module.css';
import '../logo/logo.js';
import '../logo/logo.ts';

export default class Header extends HTMLElement {
connectedCallback() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import styles from './logo.module.css';

interface Props { title: string }

export default class Logo extends HTMLElement {
connectedCallback() {
this.innerHTML = `
Expand Down

0 comments on commit 55a6dd2

Please sign in to comment.