Skip to content

Commit

Permalink
feature/issue 30 home page (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
thescientist13 authored Aug 26, 2024
1 parent 828a615 commit cfbdd3c
Show file tree
Hide file tree
Showing 44 changed files with 2,129 additions and 118 deletions.
12 changes: 9 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ This would emit the following generated HTML
#### Interactive Components (Declarative Shadow DOM)
For interactive components that would require client side interactivity, like for event handlers, these component should be authored rendering into a Shadow Root with [Declarative Shadow DOM](https://developer.chrome.com/docs/css-ui/declarative-shadow-dom) and using [Constructable Stylesheets](https://web.dev/articles/constructable-stylesheets).
For interactive components that would require client side interactivity, like event handlers, these components should be authored rendering into a Shadow Root using [Declarative Shadow DOM](https://developer.chrome.com/docs/css-ui/declarative-shadow-dom) and with Greenwood's [raw plugin](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-import-raw).
<details>
Ideally we would be using <a href="https://web.dev/articles/constructable-stylesheets">Constructable Stylesheets and Import Attributes</a> but CSS Import Attributes are <a href="https://github.com/ProjectEvergreen/www.greenwoodjs.dev/pull/57#issuecomment-2295349811">not baseline yet</a>. 😞
</details>
```css
/* card.css */
Expand All @@ -115,7 +119,7 @@ For interactive components that would require client side interactivity, like fo
```
```js
import sheet from "./card.css" with { type: "css" };
import styles from "./card.css?type=raw";
export default class Card extends HTMLElement {
selectItem() {
Expand All @@ -130,6 +134,9 @@ export default class Card extends HTMLElement {
template.innerHTML = `
<div class="card">
<style>
${styles}
</style>
<h3>${title}</h3>
<img src="${thumbnail}" alt="${title}" loading="lazy" width="100%">
<button>View Item Details</button>
Expand All @@ -139,7 +146,6 @@ export default class Card extends HTMLElement {
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
this.shadowRoot.adoptedStylesheets = [sheet];
this.shadowRoot?.querySelector("button").addEventListener("click", this.selectItem.bind(this));
}
}
Expand Down
2 changes: 1 addition & 1 deletion greenwood.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { greenwoodPluginCssModules } from "./plugin-css-modules.js";

export default {
prerender: true,
plugins: [greenwoodPluginImportRaw(), greenwoodPluginCssModules()],
plugins: [greenwoodPluginCssModules(), greenwoodPluginImportRaw()],
markdown: {
plugins: ["@mapbox/rehype-prism"],
},
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions patches/wc-compiler+0.14.0.patch
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
diff --git a/node_modules/wc-compiler/src/dom-shim.js b/node_modules/wc-compiler/src/dom-shim.js
index be289a3..dd4692a 100644
index be289a3..db07eb9 100644
--- a/node_modules/wc-compiler/src/dom-shim.js
+++ b/node_modules/wc-compiler/src/dom-shim.js
@@ -102,6 +102,9 @@ class ShadowRoot extends DocumentFragment {
@@ -83,6 +83,9 @@ class Document extends Node {
createDocumentFragment(html) {
return new DocumentFragment(html);
}
+
+ querySelector() { }
+ querySelectorAll() { }
}

// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
@@ -102,6 +105,10 @@ class ShadowRoot extends DocumentFragment {
super();
this.mode = options.mode || 'closed';
this.adoptedStyleSheets = [];
+ // TODO not sure if this is the right base class for these?
+ this.querySelector = noop;
+ this.querySelectorAll = noop;
+ this.getElementById = noop;
}
}

179 changes: 113 additions & 66 deletions plugin-css-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ function walkAllImportsForCssModules(scriptUrl, sheets, compilation) {
let scopedCssContents = cssContents;

const ast = parse(cssContents, {
// positions: true,
onParseError(error) {
console.log(error.formattedMessage);
},
Expand All @@ -66,14 +67,34 @@ function walkAllImportsForCssModules(scriptUrl, sheets, compilation) {
if (node.children?.head?.data?.type === "Selector") {
if (node.children?.head?.data?.children?.head?.data?.type === "ClassSelector") {
const { name } = node.children.head.data.children.head.data;

const scopedClassName = `${scope}-${hash}-${name}`;
classNameMap[name] = scopedClassName;

scopedCssContents = scopedCssContents.replace(
`\.${name}`,
`\.${scopedClassName}`,
);
/*
* bit of a hacky solution since as we are walking class names one at a time, if we have multiple uses of .heading (for example)
* then by the end we could have .my-component-111-header.my-component-111-header.etc, since we want to replace all instances (e.g. the g flag in Regex)
*
* csstree supports loc so we _could_ target the class replacement down to start / end points, but that unfortunately slows things down a lot
*/
// TODO this is a pretty ugly find / replace technique...
// will definitely want to refactor and test this well
if (
scopedCssContents.indexOf(`.${scopedClassName} `) < 0 &&
scopedCssContents.indexOf(`.${scopedClassName} {`) < 0
) {
scopedCssContents = scopedCssContents.replace(
new RegExp(String.raw`.${name} `, "g"),
`.${scope}-${hash}-${name} `,
);
scopedCssContents = scopedCssContents.replace(
new RegExp(String.raw`.${name},`, "g"),
`.${scope}-${hash}-${name},`,
);
scopedCssContents = scopedCssContents.replace(
new RegExp(String.raw`.${name}:`, "g"),
`.${scope}-${hash}-${name}:`,
);
}
}
}
}
Expand Down Expand Up @@ -137,7 +158,7 @@ class CssModulesResource extends ResourceInterface {
}

async shouldResolve(url) {
return url.pathname.endsWith("module.css");
return url.protocol === "file:" && url.pathname.endsWith("module.css");
}

async resolve(url) {
Expand All @@ -159,78 +180,104 @@ class CssModulesResource extends ResourceInterface {
return new Request(matchedUrl);
}

async shouldServe(url) {
// async shouldIntercept(url) {
// console.log('css modules intercept', { url });
// const { pathname, protocol } = url;
// const mapKey = `${protocol}//${pathname}`;
// // // console.log(this.compilation.context.scratchDir)
// // // console.log(new URL('./__css-modules-map.json', this.compilation.context.scratchDir));
// const cssModulesMap = getCssModulesMap(this.compilation);
// // console.log("shouldServer", { cssModulesMap, url });
// return protocol === "file:" && pathname.endsWith(this.extensions[0]) && cssModulesMap[mapKey];
// }

// async intercept(url) {
// console.log('css modules intercept', { url });
// const { pathname, protocol } = url;
// const mapKey = `${protocol}//${pathname}`;
// const cssModulesMap = getCssModulesMap(this.compilation);
// // console.log("@@@@@@", { url, cssModulesMap });
// const cssModule = `export default ${JSON.stringify(cssModulesMap[mapKey].module)}`;

// // console.log("@@@@@@", { cssModule });
// return new Response(cssModule, {
// headers: {
// "Content-Type": this.contentType,
// },
// });
// }

// this happens "first" as the HTML is returned, to find viable references to CSS Modules
// better way than just checking for /?
async shouldIntercept(url) {
const { pathname, protocol } = url;
const mapKey = `${protocol}//${pathname}`;
// // console.log(this.compilation.context.scratchDir)
// // console.log(new URL('./__css-modules-map.json', this.compilation.context.scratchDir));
const cssModulesMap = getCssModulesMap(this.compilation);
// console.log("shouldServer", { cssModulesMap, url });
return protocol === "file:" && pathname.endsWith(this.extensions[0]) && cssModulesMap[mapKey];

return (
url.pathname.endsWith("/") ||
(protocol === "file:" && pathname.endsWith(this.extensions[0]) && cssModulesMap[mapKey])
);
}

async serve(url) {
async intercept(url, request, response) {
const { pathname, protocol } = url;
const mapKey = `${protocol}//${pathname}`;
const cssModulesMap = getCssModulesMap(this.compilation);
// console.log("@@@@@@", { url, cssModulesMap });
const cssModule = `export default ${JSON.stringify(cssModulesMap[mapKey].module)}`;

// console.log("@@@@@@", { cssModule });
return new Response(cssModule, {
headers: {
"Content-Type": this.contentType,
},
});
}

// this happens "first" as the HTML is returned, to find viable references to CSS Modules
// better way than just checking for /?
async shouldIntercept(url) {
return url.pathname.endsWith("/");
}

async intercept(url, request, response) {
const body = await response.text();
const dom = htmlparser.parse(body, { script: true });
const scripts = dom.querySelectorAll("head script");
const sheets = []; // TODO use a map here?

for (const script of scripts) {
const type = script.getAttribute("type");
const src = script.getAttribute("src");
if (src && ["module", "module-shim"].includes(type)) {
// console.log("check this file for CSS Modules", src);
// await resolveForRelativeUrl(new URL(src, import.meta.url this.compilation.context.userWorkspace)
const scriptUrl = new URL(
`./${src.replace(/\.\.\//g, "").replace(/\.\//g, "")}`,
this.compilation.context.userWorkspace,
);
walkAllImportsForCssModules(scriptUrl, sheets, this.compilation);
if (url.pathname.endsWith("/")) {
const body = await response.text();
const dom = htmlparser.parse(body, { script: true });
const scripts = dom.querySelectorAll("head script");
const sheets = []; // TODO use a map here?

for (const script of scripts) {
const type = script.getAttribute("type");
const src = script.getAttribute("src");
if (src && ["module", "module-shim"].includes(type)) {
// console.log("check this file for CSS Modules", src);
// await resolveForRelativeUrl(new URL(src, import.meta.url this.compilation.context.userWorkspace)
const scriptUrl = new URL(
`./${src.replace(/\.\.\//g, "").replace(/\.\//g, "")}`,
this.compilation.context.userWorkspace,
);
walkAllImportsForCssModules(scriptUrl, sheets, this.compilation);
}
}
}

const cssModulesMap = getCssModulesMap(this.compilation);
// console.log({ cssModulesMap });

// for(const cssModule of cssModulesMap) {
// // console.log({ cssModule });
// }
Object.keys(cssModulesMap).forEach((key) => {
sheets.push(cssModulesMap[key].contents);
});

const newBody = body.replace(
"</head>",
`
<style>
${sheets.join("\n")}
</style>
</head>
`,
);
const cssModulesMap = getCssModulesMap(this.compilation);
// console.log({ cssModulesMap });

// for(const cssModule of cssModulesMap) {
// // console.log({ cssModule });
// }
Object.keys(cssModulesMap).forEach((key) => {
sheets.push(cssModulesMap[key].contents);
});

const newBody = body.replace(
"</head>",
`
<style>
${sheets.join("\n")}
</style>
</head>
`,
);

return new Response(newBody);
return new Response(newBody);
} else if (
url.pathname.endsWith("/") ||
(protocol === "file:" && pathname.endsWith(this.extensions[0]) && cssModulesMap[mapKey])
) {
const cssModule = `export default ${JSON.stringify(cssModulesMap[mapKey].module)}`;

return new Response(cssModule, {
headers: {
"Content-Type": this.contentType,
},
});
}
}

async shouldOptimize(url, response) {
Expand Down
10 changes: 5 additions & 5 deletions src/assets/api-routes.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions src/assets/build-ssg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/assets/github.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions src/assets/html.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit cfbdd3c

Please sign in to comment.