### Install @greenwood/plugin-markdown with yarn Source: https://greenwoodjs.dev/docs/plugins/markdown Install the plugin using yarn. ```bash yarn add @greenwood/plugin-markdown --dev ``` -------------------------------- ### Install @greenwood/plugin-markdown with pnpm Source: https://greenwoodjs.dev/docs/plugins/markdown Install the plugin using pnpm. ```bash pnpm add -D @greenwood/plugin-markdown ``` -------------------------------- ### Install with pnpm Source: https://greenwoodjs.dev/docs/plugins/postcss Install the plugin as a dev dependency using pnpm. ```bash pnpm add -D @greenwood/plugin-postcss ``` -------------------------------- ### Install @greenwood/plugin-import-raw with npm, yarn, or pnpm Source: https://greenwoodjs.dev/docs/plugins/raw Install the plugin with your preferred package manager. The package is installed as a dev dependency. ```bash npm i -D @greenwood/plugin-import-raw ``` ```bash yarn add @greenwood/plugin-import-raw --dev ``` ```bash pnpm add -D @greenwood/plugin-import-raw ``` -------------------------------- ### Install with yarn Source: https://greenwoodjs.dev/docs/plugins/postcss Install the plugin as a dev dependency using yarn. ```bash yarn add @greenwood/plugin-postcss --dev ``` -------------------------------- ### Install with npm Source: https://greenwoodjs.dev/docs/plugins/postcss Install the plugin as a dev dependency using npm. ```bash npm i -D @greenwood/plugin-postcss ``` -------------------------------- ### Install @greenwood/plugin-import-jsx with npm Source: https://greenwoodjs.dev/docs/plugins/jsx Install the plugin as a dev dependency using npm. ```bash npm i -D @greenwood/plugin-import-jsx ``` -------------------------------- ### Sample Project Source Layout Source: https://greenwoodjs.dev/docs/reference/appendix An example directory structure for a Greenwood project source workspace. ```text src/ api/ greeting.js nested/ endpoint.js pages/ blog/ first-post.js index.js index.js ``` -------------------------------- ### Install @greenwood/plugin-markdown with npm Source: https://greenwoodjs.dev/docs/plugins/markdown Install the plugin using npm. Use your favorite JavaScript package manager. ```bash npm i -D @greenwood/plugin-markdown ``` -------------------------------- ### Input CSS example Source: https://greenwoodjs.dev/docs/plugins/postcss Example CSS input that demonstrates placeholder styling and responsive images. ```css /* input */ ::placeholder { color: gray; } .image { background-image: url(image@1x.png); } @media (min-resolution: 2dppx) { .image { background-image: url(image@2x.png); } } ``` -------------------------------- ### Install @greenwood/plugin-renderer-lit with npm Source: https://greenwoodjs.dev/docs/plugins/lit-ssr Installs the plugin as a dev dependency using npm. ```bash npm i -D @greenwood/plugin-renderer-lit ``` -------------------------------- ### Install @greenwood/plugin-renderer-lit with pnpm Source: https://greenwoodjs.dev/docs/plugins/lit-ssr Installs the plugin as a dev dependency using pnpm. ```bash pnpm add -D @greenwood/plugin-renderer-lit ``` -------------------------------- ### Generated Import Map Example Source: https://greenwoodjs.dev/docs/introduction/web-standards Sample of an import map that Greenwood generates after installing the lit package. Includes imports for 'lit', 'lit-html', and 'lit-element', mapping to /node_modules/ paths. ```html ``` -------------------------------- ### Server plugin example: LiveReload server Source: https://greenwoodjs.dev/docs/reference/plugins-api Excerpt of Greenwood's internal LiveReload server plugin. It defines a LiveReloadServer class with start and stop methods (stop not shown) and exports a server plugin that provides the server instance. ```javascript import livereload from "livereload"; class LiveReloadServer { constructor(compilation, options = {}) { this.compilation = compilation; this.options = options; this.liveReloadServer = livereload.createServer({ /* options */ }); } async start() { const { userWorkspace } = this.compilation.context; return this.liveReloadServer.watch(userWorkspace, () => { console.info(`Now watching directory "${userWorkspace}" for changes.`); return Promise.resolve(true); }); } } /** @type {import("@greenwood/cli").ServerPlugin} */ export function myServerPlugin(options = {}) { return { type: "server", name: "plugin-livereload", provider: (compilation) => new LiveReloadServer(compilation, options), }; } ``` -------------------------------- ### Install @greenwood/plugin-import-jsx with yarn Source: https://greenwoodjs.dev/docs/plugins/jsx Install the plugin as a dev dependency using yarn. ```bash yarn add @greenwood/plugin-import-jsx --dev ``` -------------------------------- ### Install @greenwood/plugin-import-jsx with pnpm Source: https://greenwoodjs.dev/docs/plugins/jsx Install the plugin as a dev dependency using pnpm. ```bash pnpm add -D @greenwood/plugin-import-jsx ``` -------------------------------- ### Resource Plugin Interface Example Source: https://greenwoodjs.dev/docs/reference/plugins-api Defines the base structure of a Greenwood resource plugin, including the constructor with compilation and options, the extensions and servePage properties, and the plugin export function that returns a resource provider. Use this as a starting point for creating custom resource plugins. ```javascript class ExampleResource { constructor(compilation, options = {}) { this.compilation = compilation; // Greenwood's compilation object this.options = options; // any optional configuration provided by the user of your plugin this.extensions = ["foo", "bar"]; // add custom extensions for file watching + live reload here, ex. ts for TypeScript this.servePage = `static|dynamic`; // optionally opt-in to Greenwood using the plugin's serve lifecycle for processing static pages ('static') or SSR pages and API routes ('dynamic') } // lifecycles go here } /** @type {import("@greenwood/cli").ResourcePlugin} */ export function myExampleResourcePlugin(options = {}) { return { type: "resource", name: "my-example-resource-plugin", provider: (compilation) => new ExampleResource(compilation, options), }; } ``` -------------------------------- ### Install @greenwood/plugin-renderer-lit with yarn Source: https://greenwoodjs.dev/docs/plugins/lit-ssr Installs the plugin as a dev dependency using yarn. ```bash yarn add @greenwood/plugin-renderer-lit --dev ``` -------------------------------- ### Initialize a new Greenwood project with npx Source: https://greenwoodjs.dev/docs/introduction/setup Run this command to scaffold a new project. It will prompt for TypeScript setup and package manager selection. ```bash $ npx @greenwood/init@latest ``` -------------------------------- ### Example meta files in workspace root Source: https://greenwoodjs.dev/docs/resources/assets Shows the top-level workspace files that Greenwood automatically copies to the build output root. ```text src/ favicon.ico robots.txt sitemap.xml ``` -------------------------------- ### Example workspace structure with layouts directory Source: https://greenwoodjs.dev/docs/pages/layouts Shows the recommended directory structure for a Greenwood project with a layouts/ folder containing app.html and blog.html. ```text src/ pages/ index.html blog/ first-post.md second-post.md layouts/ app.html blog.html ``` -------------------------------- ### Create postcss.config.js Source: https://greenwoodjs.dev/docs/plugins/postcss Example PostCSS configuration file that uses the autoprefixer plugin. ```javascript export default { plugins: [(await import("autoprefixer")).default], }; ``` -------------------------------- ### Install Lit as a peer dependency with pnpm Source: https://greenwoodjs.dev/docs/plugins/lit-ssr Alternative to npm/yarn for installing the required Lit peer dependency. ```bash pnpm add lit ``` -------------------------------- ### Install Greenwood CLI with pnpm Source: https://greenwoodjs.dev/docs/introduction/setup Installs the Greenwood CLI as a dev dependency using pnpm. ```bash pnpm add -D @greenwood/cli@latest ``` -------------------------------- ### Install Greenwood CLI with npm Source: https://greenwoodjs.dev/docs/introduction/setup Installs the Greenwood CLI as a dev dependency using npm. ```bash npm i -D @greenwood/cli@latest ``` -------------------------------- ### Create a CSS Module file (header.module.css) Source: https://greenwoodjs.dev/docs/plugins/css-modules Create a CSS file ending in .module.css. This example defines classes like .container, .navBarMenu, and .navBarMenuItem with nested selectors and a media query. ```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; } } ``` -------------------------------- ### Install Lit as a peer dependency with yarn Source: https://greenwoodjs.dev/docs/plugins/lit-ssr Alternative to npm for installing the required Lit peer dependency. ```bash yarn add lit ``` -------------------------------- ### Reference asset from HTML Source: https://greenwoodjs.dev/docs/resources/assets HTML example linking to a PDF asset from the assets directory. ```html

Welcome to My Site!

Download our product catalog
``` -------------------------------- ### Install Greenwood CLI with yarn Source: https://greenwoodjs.dev/docs/introduction/setup Installs the Greenwood CLI as a dev dependency using yarn. ```bash yarn add @greenwood/cli@latest --save-dev ``` -------------------------------- ### Web Components with CSS Modules: HeadingBox example and output Source: https://greenwoodjs.dev/docs/plugins/markdown Shows a complete Web Components example: a custom element class using a CSS Modules plugin, its usage in markdown with custom content, and the resulting HTML output. The component reads a heading attribute and projects slotted content into styled containers. ```javascript import styles from "./heading-box.module.css"; export default class HeadingBox extends HTMLElement { connectedCallback() { const heading = this.getAttribute("heading"); this.innerHTML = `

${heading}

${this.innerHTML}
`; } } customElements.define("app-heading-box", HeadingBox); ``` ```html

Some content about plugins.

``` ```html

Plugins

Some content about plugins.

``` -------------------------------- ### Install Lit as a peer dependency with npm Source: https://greenwoodjs.dev/docs/plugins/lit-ssr Required because Lit is a peerDependency of the plugin. Run this in your project to install Lit. ```bash npm i lit ``` -------------------------------- ### Install @greenwood/plugin-css-modules with npm, yarn, or pnpm Source: https://greenwoodjs.dev/docs/plugins/css-modules Install the plugin with your preferred package manager. The package name is @greenwood/plugin-css-modules. ```bash npm i -D @greenwood/plugin-css-modules ``` ```bash yarn add @greenwood/plugin-css-modules --dev ``` ```bash pnpm add -D @greenwood/plugin-css-modules ``` -------------------------------- ### Fetch API Route Handler Example Source: https://greenwoodjs.dev/docs/introduction/web-standards An API route handler example showing the use of Fetch standard's Request and Response objects. It logs the request URL and returns a Response with headers. ```javascript // a standard request object is passed in // and a standard response object should be returned export async function handler(request) { console.log("endpoint visited", request.url); return new Response("...", { headers: new Headers({ /* ... */ }), }); } ``` -------------------------------- ### Output CSS example Source: https://greenwoodjs.dev/docs/plugins/postcss Generated CSS output after PostCSS processing, showing vendor prefixes and media query adjustments. ```css /* output */ ::-moz-placeholder { color: gray; } ::placeholder { color: gray; } .image { background-image: url(image@1x.png); } @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) { .image { background-image: url(image@2x.png); } } ``` -------------------------------- ### Example app.html layout with page placeholder Source: https://greenwoodjs.dev/docs/pages/layouts An app layout that wraps all pages. Uses to position the processed page layout content. ```html

Welcome to My Site!

``` -------------------------------- ### Example assets directory structure Source: https://greenwoodjs.dev/docs/resources/assets Shows the layout of an assets/ directory in a Greenwood project. Files placed here are copied as-is to the build output. ```text src/ assets/ my-image.webp download.pdf ``` -------------------------------- ### Set basePath for sub-path deployment Source: https://greenwoodjs.dev/docs/reference/configuration Configure basePath when hosting from a sub-path like GitHub Pages. Example uses '/app-a'. ```javascript export default { basePath: "/app-a", }; ``` -------------------------------- ### Add Prism syntax highlighting plugin Source: https://greenwoodjs.dev/docs/plugins/markdown Install @mapbox/rehype-prism and pass it as a markdown plugin in the configuration file to enable syntax highlighting. ```javascript export default { markdown: { plugins: ["@mapbox/rehype-prism"], }, }; ``` -------------------------------- ### Reference asset from markdown Source: https://greenwoodjs.dev/docs/resources/assets Markdown example referencing an image from the assets directory using a relative path. ```markdown # This is my page ![my-image](/assets/images/my-image.webp) ``` -------------------------------- ### Frontmatter imports example Source: https://greenwoodjs.dev/docs/content-as-data/frontmatter Includes scripts and styles on a per-page basis using the 'imports' key. Filepaths and attributes are space-delimited. ```markdown --- imports: - /components/my-component/component.js type="module" foo="bar" - /components/my-component/component.css --- # My Demo Page ``` -------------------------------- ### Example page.html layout with content placeholder Source: https://greenwoodjs.dev/docs/pages/layouts A basic page layout that wraps page content. Uses to position the incoming page's content. ```html

Welcome to my site!

``` -------------------------------- ### Configure devServer with extensions, port, and proxy Source: https://greenwoodjs.dev/docs/reference/configuration Example configuration for the devServer object in greenwood.config.js. Sets custom extensions to watch, a custom port, and proxy rules with highest specificity at the end. ```javascript export default { devServer: { extensions: ["txt"], port: 3000, proxy: { "/api": "https://stage.myapp.com", "/api/foo": "https://foo.otherdomain.net", }, }, }; ``` -------------------------------- ### API Route File Structure Source: https://greenwoodjs.dev/docs/pages/api-routes Example of the file-based routing convention where the file path determines the API endpoint. ```text src/ pages/ api/ greeting.js ``` -------------------------------- ### Importing CSS from node_modules Source: https://greenwoodjs.dev/docs/resources/styles Examples of importing CSS files from node_modules using relative paths, the /node_modules/ alias, or bare module specifiers. ```css /* after having installed Open Props */ /* npm i open-props */ @import "../../node_modules/open-props/borders.min.css"; @import "../../node_modules/open-props/fonts.min.css"; /* this would also work */ @import "/node_modules/open-props/borders.min.css"; @import "/node_modules/open-props/fonts.min.css"; /* or for packages that support an exports map */ @import "open-props/borders"; @import "open-props/fonts"; ``` -------------------------------- ### Bare Specifier Import Example Source: https://greenwoodjs.dev/docs/resources/scripts Shows using a bare specifier to import a package from npm in a JavaScript file. Greenwood automatically builds an import map for bare specifiers. ```javascript // after having installed Lit import { html, LitElement } from "lit"; class SimpleGreeting extends LitElement { static properties = { name: { type: String }, }; render() { return html`

Hello, ${this.name ?? "World"}!

`; } } customElements.define("simple-greeting", SimpleGreeting); ``` -------------------------------- ### Implement a Resource Plugin for Custom Import Maps Source: https://greenwoodjs.dev/docs/introduction/web-standards This example demonstrates creating a Greenwood Resource plugin to programmatically generate and inject an import map for heroicons SVG files, enabling the use of bare specifiers. ```javascript import { mergeImportMap } from "@greenwood/cli/src/lib/node-modules-utils.js"; import fs from "node:fs"; const NODE_MODULES_LOCATION = new URL("./node_modules/", import.meta.url); const PACKAGE_NAME = "heroicons"; let importMap; function generateImportMap() { if (!importMap) { importMap = new Map(); fs.globSync("**/**/*.svg", { cwd: `${NODE_MODULES_LOCATION.pathname}/${PACKAGE_NAME}/`, }).forEach((file) => { const resolved = new URL(`./${PACKAGE_NAME}/${file}`, NODE_MODULES_LOCATION); // add the query param for import raw plugin support importMap.set(`${PACKAGE_NAME}/${file}?type=raw`, `/~${resolved.pathname}?type=raw`); }); } return Object.fromEntries(importMap); } class HeroiconsImportMap { constructor(compilation) { this.compilation = compilation; } async shouldIntercept(url, request) { const { protocol, pathname } = url; const hasMatchingPageRoute = this.compilation.graph.find((node) => node.route === pathname); return ( process.env.__GWD_COMMAND__ === "develop" && protocol.startsWith("http") && hasMatchingPageRoute && request.headers.get("Accept").indexOf("text/html") >= 0 ); } async intercept(url, request, response) { const body = await response.text(); const newBody = mergeImportMap(body, generateImportMap()); return new Response(newBody); } } // make sure to include this into your Greenwood config file export function heroiconsImportMapPlugin() { return { type: "resource", name: "heroicons-import-map-plugin", provider: (compilation) => new HeroiconsImportMap(compilation), }; } ``` -------------------------------- ### Register Greenwood plugins Source: https://greenwoodjs.dev/docs/reference/configuration Register an array of plugins, either from the Greenwood team or custom ones. The example imports and uses greenwoodCssModulesPlugin. ```javascript import { greenwoodCssModulesPlugin } from "@greenwood/plugin-css-modules"; export default { plugins: [greenwoodCssModulesPlugin()], }; ``` -------------------------------- ### Frontmatter layout example Source: https://greenwoodjs.dev/docs/content-as-data/frontmatter Uses the 'layout' frontmatter key to wrap the page with a specific layout. By default, Greenwood uses _src/layouts/page.html_. ```markdown --- layout: blog --- # My First Blog Post This is my first blog post, I hope you like it! ``` -------------------------------- ### TypeScript configuration with Config type Source: https://greenwoodjs.dev/docs/reference/appendix Author a Greenwood configuration file in TypeScript using the Config type imported from @greenwood/cli. The example sets the prerender option to true and exports the config as default. ```typescript import type { Config } from "@greenwood/cli"; const config: Config = { prerender: true, }; export default config; ``` -------------------------------- ### Show @greenwood/init CLI options Source: https://greenwoodjs.dev/docs/introduction/setup Displays the full list of options for the init CLI, including flags for accepting defaults, specifying a name, enabling TypeScript, and choosing a package manager. ```text Usage: @greenwood/init [options] Options: -V, --version output the version number -y, --yes Accept all default options --name Name and directory location to scaffold your application with --ts [choice] Optionally configure your project with TypeScript (choices: "yes", "no") -i, --install Install dependencies with the package manager of your choice (choices: "npm", "pnpm", "yarn", "no") -h, --help display help for command ``` -------------------------------- ### Script Tags Example Source: https://greenwoodjs.dev/docs/resources/scripts Shows standard HTML script tags in a Greenwood page. Greenwood uses these as entry points for bundling and production processing. ```html ``` -------------------------------- ### Dynamic prerendering with getStaticPaths and getStaticParams Source: https://greenwoodjs.dev/docs/pages/server-rendering Use this example to prerender dynamic routes as static HTML. `getStaticPaths` must return an array of objects with at least one property matching the bracket value, and `getStaticParams` can augment the params object. This generates static HTML files for each product, e.g. `/product/1/index.html`. ```javascript import { getProducts } from '../../services/products.js' export async function getStaticPaths() { const products = await getProducts(); return products.map((product) => { return { // return as many other params as you want params: { id: product.id, name: product.name, }, }; }); } export async function getStaticParams({ params }) { const products = await getProducts(); const product = products.find((product) => product.id === params.id); return { product }; } // you also use getBody if you prefer export default class ProductDetailsPage extends HTMLElement { #product; constructor({ params }) { super(); this.#product = params.product; } connectedCallback() { this.innerHTML = `

${this.#product.name}

${this.#product.description}

`; } } ``` -------------------------------- ### Sample Project Build Output Source: https://greenwoodjs.dev/docs/reference/appendix The resulting structure in the public directory after a Greenwood build, including route and chunk files. ```text public/ api/ greeting.js nested-endpoint.js blog-first-post.route.js blog-first-post.chunk.[hash].js blog-index.route.js blog-index.route.chunk.[hash].js index.route.js index.route.chunk.[hash].js ``` -------------------------------- ### Show Greenwood CLI help with --help flag Source: https://greenwoodjs.dev/docs/reference/cli Run the Greenwood CLI with the --help flag to display all available commands, options, and version information. The output shows the current version (v0.33.0) and lists the build, develop, and serve commands. ```shell $ ./node_modules/.bin/greenwood --help ------------------------------------------------------- Welcome to Greenwood (v0.33.0) ♻️ ------------------------------------------------------- Usage: greenwood Options: -h, --help Show help information -V, --version Show version number Commands: build Generate a production build. develop Start a local development server. serve Start a production server. ``` -------------------------------- ### Create a dynamic server route with a JavaScript file in _pages/_ Source: https://greenwoodjs.dev/docs/pages/server-rendering Create a JavaScript file in the _pages/_ directory to define a dynamic server route. The file structure shown results in the route being served at the path _/users/_. ```plaintext src/ pages/ users.js ``` -------------------------------- ### Linking CSS from node_modules in HTML Source: https://greenwoodjs.dev/docs/resources/styles Using a tag to reference a CSS file installed via npm. ```html ``` -------------------------------- ### Invoke Custom Element in HTML Source: https://greenwoodjs.dev/docs/introduction/web-standards Usage example of the defined x-card custom element within an HTML document. ```html ``` -------------------------------- ### Frontmatter in a markdown file Source: https://greenwoodjs.dev/docs/content-as-data/active-frontmatter Example frontmatter block with fields like layout, title, published, description, author, and image. ```markdown --- layout: post title: Git Explorer published: 04.07.2020 description: Local git repository viewer author: Owen Buckley image: /assets/blog-post-images/git.png --- ``` -------------------------------- ### Custom frontmatter data example Source: https://greenwoodjs.dev/docs/content-as-data/frontmatter Defines custom frontmatter properties that become available on the 'data' property of the page object. ```markdown --- author: Jon Doe date: 04/07/2020 --- # First Post My first post ``` -------------------------------- ### Reference a collection in HTML with activeFrontmatter Source: https://greenwoodjs.dev/docs/content-as-data/collections Use activeFrontmatter to access a collection in HTML. The example passes the 'navigation' collection to a custom element. ```html Home Page ``` -------------------------------- ### Frontmatter title example Source: https://greenwoodjs.dev/docs/content-as-data/frontmatter Sets the page title using the 'title' frontmatter key. If not set, the title is inferred from the file name. ```markdown --- title: My Blog Post --- # This is a post The is a markdown file with the title defined in frontmatter. ``` -------------------------------- ### Frontmatter label example Source: https://greenwoodjs.dev/docs/content-as-data/frontmatter Sets a custom label for a page using the 'label' frontmatter key. The label can be used for display in links. ```markdown --- label: "My Blog Post from 3/5/2020" --- # My Blog Post ``` -------------------------------- ### Configure markdown plugin with rehype-slug Source: https://greenwoodjs.dev/docs/plugins/markdown Pass remark or rehype compatible plugins as an array to the markdown plugin options. This example adds rehype-slug. ```javascript import { greenwoodPluginMarkdown } from '@greenwood/plugin-markdown'; export default { plugins: [ greenwoodPluginMarkdown({ // npm i -D rehype-slug plugins: [ "rehype-slug" ], }) ] } ``` -------------------------------- ### SPA project structure Source: https://greenwoodjs.dev/docs/pages/routing Example project structure for a Single Page Application (SPA) in Greenwood. Place an index.html at the root of the workspace with no _pages/ directory to opt out of file-based routing. Note: SPA projects do not support layouts or (active) frontmatter. ```text src/ components/ footer.js header.js routes/ products.js home.js styles.css index.js index.html ``` -------------------------------- ### Default greenwood.config.js configuration Source: https://greenwoodjs.dev/docs/reference/configuration Shows the default values for all Greenwood configuration options. Use as a starting point for customizing your project's configuration. ```javascript /** @type {import('@greenwood/cli').Config} */ export default { activeContent: false, basePath: "", devServer: { extensions: [], hud: true, port: 1984, host: "localhost", }, isolation: false, layoutsDirectory: "layouts", // e.g. ./src/layouts optimization: "default", pagesDirectory: "pages", // e.g. ./src/pages plugins: [], polyfills: { importAttributes: null, // e.g. ['css', 'json'] importMaps: false, }, port: 8080, prerender: false, staticRouter: false, useTsc: false, workspace: new URL("./src/", import.meta.url), }; ``` -------------------------------- ### Import Prism theme CSS Source: https://greenwoodjs.dev/docs/plugins/markdown Include a Prism theme from a CSS file in your project to style highlighted code. ```css @import url("../node_modules/prismjs/themes/prism-tomorrow.css"); ``` -------------------------------- ### Generated script tag with basePath Source: https://greenwoodjs.dev/docs/reference/configuration Greenwood automatically prefixes script and link tags with the basePath. This example shows a script tag referencing '/app-a/some-script.a243dccss.js'. ```html ``` -------------------------------- ### Resolve Lifecycle Example Source: https://greenwoodjs.dev/docs/reference/plugins-api Implements the resolve lifecycle for a resource plugin that serves files from the user workspace. The shouldResolve method checks for a file extension, excludes node_modules, and verifies the file exists; the resolve method returns a new Request pointing to the workspace file. Use this when you need to resolve resources that are not in the default locations. ```javascript import fs from "node:fs"; class UserWorkspaceResource { async shouldResolve(url) { const { pathname } = url; const { userWorkspace } = this.compilation.context; const hasExtension = !["", "/"].includes(pathname.split(".").pop()); return ( hasExtension && !pathname.startsWith("/node_modules") && fs.existsSync(new URL(`.${pathname}`, userWorkspace).pathname) ); } async resolve(url) { const { pathname } = url; const { userWorkspace } = this.compilation.context; const workspaceUrl = new URL(`.${pathname}`, userWorkspace); return new Request(workspaceUrl); } } export function myWorkspaceResourcePlugin(options = {}) { return { type: "resource", name: "my-workspace-resource-plugin", provider: (compilation) => new UserWorkspaceResource(compilation, options), }; } ``` -------------------------------- ### Configure markdown plugin with rehype-autolink-headings and options Source: https://greenwoodjs.dev/docs/plugins/markdown Use object syntax to pass options to a markdown plugin. This example configures rehype-autolink-headings with behavior 'append'. ```javascript import { greenwoodPluginMarkdown } from '@greenwood/plugin-markdown'; export default { plugins: [ greenwoodPluginMarkdown({ plugins: [ "rehype-slug", { name: "rehype-autolink-headings", options: { behavior: "append" }, }, ], }) ] } ``` -------------------------------- ### Dynamic routing with [slug].js Source: https://greenwoodjs.dev/docs/pages/server-rendering Use this page component to serve all routes matching `/blog//`. The `params` object from the constructor provides the `slug` value, which is used to fetch and render the post content. ```javascript import { getPostBySlug } from '../../services/posts.js'; export default class BlogPostPage extends HTMLElement { #slug; constructor({ params }) { super(); this.#slug = params.slug; } async connectedCallback() { const post = await getPostBySlug(this.#slug); this.innerHTML = `

${post.title}

${post.content}

`; } } ``` -------------------------------- ### Standard CSS Usage in HTML Source: https://greenwoodjs.dev/docs/resources/styles Demonstrates using ``` -------------------------------- ### JSDoc type reference for ImportRawPlugin Source: https://greenwoodjs.dev/docs/plugins/raw JSDoc type reference for the plugin's exported type. Use in JavaScript files to get type checking. ```javascript /** @type {import('@greenwood/plugin-import-raw').ImportRawPlugin} */ ```