[ci] yarn format

This commit is contained in:
natemoo-re 2021-07-07 20:10:09 +00:00 committed by GitHub Actions
parent 53fcae1a9a
commit 1bbe98ae54
52 changed files with 263 additions and 233 deletions

View file

@ -3,7 +3,8 @@ layout: ~/layouts/Main.astro
title: Island Architecture title: Island Architecture
draft: true draft: true
--- ---
<!--
<!--
@aFuzzyBear: I have been spending most the day learning more about Island Architecture, wrote plenty of notes, listened to Fred K Schott's interview on Speakeasy(https://www.youtube.com/watch?v=mgkwZqVkrwo) and the interview with Jason Lengstrof (https://www.youtube.com/watch?v=z15YLsLMtu4) @aFuzzyBear: I have been spending most the day learning more about Island Architecture, wrote plenty of notes, listened to Fred K Schott's interview on Speakeasy(https://www.youtube.com/watch?v=mgkwZqVkrwo) and the interview with Jason Lengstrof (https://www.youtube.com/watch?v=z15YLsLMtu4)
Figured I might give writing this a wee go, Figured I might give writing this a wee go,
@ -29,7 +30,7 @@ In the summer of 2020, he managed to formulated his thoughts of how web architec
His seminal post outlines and discusses the general concept of 'islands' as an architectural design process that could be used in Web Development, allowing for better improvements in overall site performance, SEO, UX, and everywhere else. His given explanation describing this new paradigm, was extraordinarily succinct: His seminal post outlines and discusses the general concept of 'islands' as an architectural design process that could be used in Web Development, allowing for better improvements in overall site performance, SEO, UX, and everywhere else. His given explanation describing this new paradigm, was extraordinarily succinct:
> "The general idea of an *“Islands”* architecture is deceptively simple: Render HTML pages on the server, and inject placeholders or slots around highly dynamic regions. These placeholders/slots contain the server-rendered HTML output from their corresponding widget. They denote regions that can then be "hydrated" on the client into small self-contained widgets, reusing their server-rendered initial HTML."-Jason Miller > "The general idea of an _“Islands”_ architecture is deceptively simple: Render HTML pages on the server, and inject placeholders or slots around highly dynamic regions. These placeholders/slots contain the server-rendered HTML output from their corresponding widget. They denote regions that can then be "hydrated" on the client into small self-contained widgets, reusing their server-rendered initial HTML."-Jason Miller
To develop a better understanding of what Jason meant with his proposal, let's quickly explore the backdrop, before we explain 'Island Architecture' and how it is applied into Astro as our primary ethos. To develop a better understanding of what Jason meant with his proposal, let's quickly explore the backdrop, before we explain 'Island Architecture' and how it is applied into Astro as our primary ethos.
@ -37,7 +38,7 @@ To develop a better understanding of what Jason meant with his proposal, let's q
Think of a simple webpage. On which are many different types of components that are shown on this page, components that are shared across the site, others contain fixed content, some are a bit more elaborate that may perhaps use different state's or need to fetch multiple data streams from external sources. Think of a simple webpage. On which are many different types of components that are shown on this page, components that are shared across the site, others contain fixed content, some are a bit more elaborate that may perhaps use different state's or need to fetch multiple data streams from external sources.
Such an site would would have very few actual 'moving' pieces, or *dynamic* elements. For the most part the content tends to be fixed, and static. Such an site would would have very few actual 'moving' pieces, or _dynamic_ elements. For the most part the content tends to be fixed, and static.
In order to allow for dynamism and interactivity we are often left making overly complex solutions to deliver the slightest form of action on the application. In order to allow for dynamism and interactivity we are often left making overly complex solutions to deliver the slightest form of action on the application.
@ -47,13 +48,13 @@ Given the [catalogue of patterns](https://en.wikipedia.org/wiki/List_of_software
Web developers tend to gravitate towards tried and tested practices, and none fit the requirements better than the [Model-View-Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) (**MVC**) design pattern. Web developers tend to gravitate towards tried and tested practices, and none fit the requirements better than the [Model-View-Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) (**MVC**) design pattern.
Where the **Model** contains the data structures and logic that governs the use of the data in the application. **Views** are the visual representation of the data that the user sees, and the **Controller** connects the views to their relevant data *Models* based on their interactions with the User. Where the **Model** contains the data structures and logic that governs the use of the data in the application. **Views** are the visual representation of the data that the user sees, and the **Controller** connects the views to their relevant data _Models_ based on their interactions with the User.
This design pattern works well for our [client-server](https://en.wikipedia.org/wiki/Client%E2%80%93server_model) based applications. Since the models are placed on the *servers*, the views that are sent back over the wire tend to be static *documents*, controllers are sent along with the static files to facilitate the behaviours that web developers created for their application, in the form of *scripts*. This design pattern works well for our [client-server](https://en.wikipedia.org/wiki/Client%E2%80%93server_model) based applications. Since the models are placed on the _servers_, the views that are sent back over the wire tend to be static _documents_, controllers are sent along with the static files to facilitate the behaviours that web developers created for their application, in the form of _scripts_.
## Rise of the Frameworks ## Rise of the Frameworks
A vast swathe of libraries, frameworks and tooling rose up to meet the challenges of providing a Developer Experience (DX) that would let them create their applications, *'freely'*. A vast swathe of libraries, frameworks and tooling rose up to meet the challenges of providing a Developer Experience (DX) that would let them create their applications, _'freely'_.
Helping to abstract away much of the complexity needed in implementing architectural design decisions into their application. Helping to abstract away much of the complexity needed in implementing architectural design decisions into their application.
@ -137,7 +138,7 @@ Into this new age ESM world, we have had a dearth of innovation from the establi
Basic questions of : Websites or WebApp's were still unresolved. Where to render the site, on the server or on the client, perhaps a bit of both? What determines the need for dynamic content and what specifies content to be static? Basic questions of : Websites or WebApp's were still unresolved. Where to render the site, on the server or on the client, perhaps a bit of both? What determines the need for dynamic content and what specifies content to be static?
Witnessing frameworks slowly go full circle and return to Server-Side-Rendering (*SSR*) their applications was in part only allowed to be considered in an ESM world, however it was bit of an admission of culpability of sorts. Witnessing frameworks slowly go full circle and return to Server-Side-Rendering (_SSR_) their applications was in part only allowed to be considered in an ESM world, however it was bit of an admission of culpability of sorts.
By inadvertently admitting that the current model is flawed, opened up the space for a new form of discourse to enter, and help redefine the ecosystem moving forward. By inadvertently admitting that the current model is flawed, opened up the space for a new form of discourse to enter, and help redefine the ecosystem moving forward.
@ -155,15 +156,15 @@ In the introduction we placed a quote from Jason, describing the general concept
Jason asks us to think of a Island architecture as a static HTML document. One that is rendered entirely on the server. Jason asks us to think of a Island architecture as a static HTML document. One that is rendered entirely on the server.
The document contains multiple separate embedded applications, that are injected into placeholders or '*slots*', which form dynamic regions on the page. The document contains multiple separate embedded applications, that are injected into placeholders or '_slots_', which form dynamic regions on the page.
The Server renders HTML outputs form each of these dynamic components, and places them onto the static document being sent back down to the End-User. The Server renders HTML outputs form each of these dynamic components, and places them onto the static document being sent back down to the End-User.
These slots, of dynamic regions, can then be '*hydrated*'. [Hydration](https://en.wikipedia.org/wiki/Hydration_(web_development)) is a process that allows for Client-Sided JS to convert and make static HTML, dynamic, reusing their initial server-rendered HTML. These slots, of dynamic regions, can then be '_hydrated_'. [Hydration](<https://en.wikipedia.org/wiki/Hydration_(web_development)>) is a process that allows for Client-Sided JS to convert and make static HTML, dynamic, reusing their initial server-rendered HTML.
This 'micro' architecture is similar to both 'micro-frontends' and 'micro-services'. Both share the concept of breaking applications into small indivisible units. But the problem is that a lot of the small modular units are rarely composed in HTML. This 'micro' architecture is similar to both 'micro-frontends' and 'micro-services'. Both share the concept of breaking applications into small indivisible units. But the problem is that a lot of the small modular units are rarely composed in HTML.
With Island-Architecture, he proposes a form of progressive enhancement for the dynamic components by using a technique known as *Partial Hydration*. With Island-Architecture, he proposes a form of progressive enhancement for the dynamic components by using a technique known as _Partial Hydration_.
Lets look at this following analogy: Lets look at this following analogy:
@ -181,7 +182,7 @@ This would then load the functionality for the carousel in-place, transforming i
## Island Hydration ## Island Hydration
By now the idea of Island-architecture must be settling in, and one must be thinking, this is just [Progressive Hydration](https://en.wikipedia.org/wiki/Hydration_(web_development)#Progressive_rehydration), and you wouldn't be overly off mark. By now the idea of Island-architecture must be settling in, and one must be thinking, this is just [Progressive Hydration](<https://en.wikipedia.org/wiki/Hydration_(web_development)#Progressive_rehydration>), and you wouldn't be overly off mark.
Progressive Hydration that is used in frameworks like: Angluar, React, Preact, Vue. Are individual components, which are loaded and then initialised over a period of time. Progressive Hydration that is used in frameworks like: Angluar, React, Preact, Vue. Are individual components, which are loaded and then initialised over a period of time.
@ -197,7 +198,7 @@ Since there is no outer `<div id='root'>` element that needs to be initialised b
Every region of the page is an isolated unit, an island, on its own, connected to others by the HTML page. With such an approach the benefits do begin to stack up. Every region of the page is an isolated unit, an island, on its own, connected to others by the HTML page. With such an approach the benefits do begin to stack up.
A key benefit is seen with the site performance. Since isolation is inherent, if a single issue affects a component, it wouldn't affect the other *islands* on the page. A key benefit is seen with the site performance. Since isolation is inherent, if a single issue affects a component, it wouldn't affect the other _islands_ on the page.
## Exploring the Island ## Exploring the Island
@ -205,7 +206,7 @@ As we explore further into the Island, we can see immediate trade differences be
Quickly wandering back to the Status Quo for a brief interlude. We use SSR with SPA's to help tackle the downside of SPA's and its SEO. Appealing to the search engines in this manner has another negative affect on the UX. Quickly wandering back to the Status Quo for a brief interlude. We use SSR with SPA's to help tackle the downside of SPA's and its SEO. Appealing to the search engines in this manner has another negative affect on the UX.
>"...visitors are left waiting for the actual functionality of a page to arrive while staring at a frustratingly fake version of that page." - Jason Miller > "...visitors are left waiting for the actual functionality of a page to arrive while staring at a frustratingly fake version of that page." - Jason Miller
There are other issues that stem from traditional SSR, and being idly unawares of such performance pitfalls, gives rise to an orchestra of potential problems. There are other issues that stem from traditional SSR, and being idly unawares of such performance pitfalls, gives rise to an orchestra of potential problems.

View file

@ -3,8 +3,6 @@ layout: ~/layouts/Main.astro
title: Astro Components title: Astro Components
--- ---
## ✨ `.astro` Syntax ## ✨ `.astro` Syntax
Astro comes with its own server-side, component-based templating language. Think of it as HTML enhanced with the full power of JavaScript. Astro comes with its own server-side, component-based templating language. Think of it as HTML enhanced with the full power of JavaScript.
@ -204,5 +202,3 @@ If youd prefer to organize assets alongside Astro components, you may import
### TODO: Composition (Slots) ### TODO: Composition (Slots)
[code-ext]: https://marketplace.visualstudio.com/items?itemName=astro-build.astro-vscode [code-ext]: https://marketplace.visualstudio.com/items?itemName=astro-build.astro-vscode

View file

@ -5,7 +5,7 @@ title: Astro Pages
**Pages** are a special type of [Astro Component](./astro-components) that handle routing, data loading, and templating for each page of your website. You can think of them like any other Astro component, just with extra responsibilities. **Pages** are a special type of [Astro Component](./astro-components) that handle routing, data loading, and templating for each page of your website. You can think of them like any other Astro component, just with extra responsibilities.
Astro also supports Markdown for content-heavy pages, like blog posts and documentation. See [Markdown Content](./markdown-content.md) for more information on writing pages with Markdown. Astro also supports Markdown for content-heavy pages, like blog posts and documentation. See [Markdown Content](./markdown-content.md) for more information on writing pages with Markdown.
## File-based Routing ## File-based Routing
@ -37,9 +37,10 @@ console.log(data);
<!-- Output the result to the page --> <!-- Output the result to the page -->
<div>{JSON.stringify(data)}</div> <div>{JSON.stringify(data)}</div>
``` ```
### `fetch()` ### `fetch()`
Astro pages have access to the global `fetch()` function in their setup script. `fetch()` is a native JavaScript API ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)) that lets you make HTTP requests for things like APIs and resources. Astro pages have access to the global `fetch()` function in their setup script. `fetch()` is a native JavaScript API ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)) that lets you make HTTP requests for things like APIs and resources.
Even though Astro component scripts run inside of Node.js (and not in the browser) Astro provides this native API so that you can fetch data at page build time. Even though Astro component scripts run inside of Node.js (and not in the browser) Astro provides this native API so that you can fetch data at page build time.
@ -49,7 +50,7 @@ Even though Astro component scripts run inside of Node.js (and not in the browse
## Page Templating ## Page Templating
All Astro components are responsible for returning HTML. Astro Pages return HTML as well, but have the unique responsibility of returning a full `<html>...</html>` page response, including `<head>` ([MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)) and `<body>` ([MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)). All Astro components are responsible for returning HTML. Astro Pages return HTML as well, but have the unique responsibility of returning a full `<html>...</html>` page response, including `<head>` ([MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)) and `<body>` ([MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)).
`<!doctype html>` is optional, and will be added automatically. `<!doctype html>` is optional, and will be added automatically.
@ -67,6 +68,3 @@ All Astro components are responsible for returning HTML. Astro Pages return HTML
</body> </body>
</html> </html>
``` ```

View file

@ -11,7 +11,7 @@ title: Collections
- Working with remote data - Working with remote data
- Mixing remote and local data - Mixing remote and local data
**Use a Collection when you need to generate multiple pages from a single template.** If you just want to generate a single page (ex: a long list of every post on your site) then you can just fetch that data on a normal Astro page without using the Collection API. **Use a Collection when you need to generate multiple pages from a single template.** If you just want to generate a single page (ex: a long list of every post on your site) then you can just fetch that data on a normal Astro page without using the Collection API.
## Using Collections ## Using Collections

View file

@ -22,6 +22,7 @@ However, there are plenty of cases where you might like to include an interactiv
- A "Buy Now" button - A "Buy Now" button
With Astro, you can hydrate these components individually, without forcing the rest of the page to ship any other unnecesary JavaScript. This technique is called **partial hydration.** With Astro, you can hydrate these components individually, without forcing the rest of the page to ship any other unnecesary JavaScript. This technique is called **partial hydration.**
## Hydrate Frontend Components ## Hydrate Frontend Components
To hydrate your components in the client, you may use any of the following techniques: To hydrate your components in the client, you may use any of the following techniques:
@ -34,4 +35,4 @@ To hydrate your components in the client, you may use any of the following techn
Astro components (`.astro`) are HTML-only templating languages with no client-side runtime. You cannot hydrate an Astro component to run on the client (because the JavaScript front-matter only ever runs at build time). Astro components (`.astro`) are HTML-only templating languages with no client-side runtime. You cannot hydrate an Astro component to run on the client (because the JavaScript front-matter only ever runs at build time).
If you want to make your Astro component interactive on the client, you should convert it to React, Svelte, or Vue. Otherwise, you can consider adding a `<script>` tag to your Astro component that will run JavaScript on the page. If you want to make your Astro component interactive on the client, you should convert it to React, Svelte, or Vue. Otherwise, you can consider adding a `<script>` tag to your Astro component that will run JavaScript on the page.

View file

@ -3,17 +3,17 @@ layout: ~/layouts/Main.astro
title: Layouts title: Layouts
--- ---
**Layouts** are a special type of [Component](./astro-components) that help you share and reuse common page layouts within your project. **Layouts** are a special type of [Component](./astro-components) that help you share and reuse common page layouts within your project.
Layouts are just like any other reusable Astro component. There's no new syntax or APIs to learn. However, reusable page layouts are such a common pattern in web development that we created this guide to help you use them. Layouts are just like any other reusable Astro component. There's no new syntax or APIs to learn. However, reusable page layouts are such a common pattern in web development that we created this guide to help you use them.
## Usage ## Usage
Astro layouts support props, slots, and all of the other features of Astro components. Layouts are just normal components, after all! Astro layouts support props, slots, and all of the other features of Astro components. Layouts are just normal components, after all!
Unlike other components, layouts will often contain the full page `<html>`, `<head>` and `<body>` (often referred to as the **page shell**). Unlike other components, layouts will often contain the full page `<html>`, `<head>` and `<body>` (often referred to as the **page shell**).
It's a common pattern to put all of your layout components in a single `src/layouts` directory. It's a common pattern to put all of your layout components in a single `src/layouts` directory.
## Example ## Example
@ -54,12 +54,10 @@ import BaseLayout from '../layouts/BaseLayout.astro'
</BaseLayout> </BaseLayout>
``` ```
## Nesting Layouts ## Nesting Layouts
You can nest layouts when you want to create more specific page types without copy-pasting. It is common in Astro to have one generic `BaseLayout` and then many more specific layouts (`PostLayout`, `ProductLayout`, etc.) that reuse and build on top of it. You can nest layouts when you want to create more specific page types without copy-pasting. It is common in Astro to have one generic `BaseLayout` and then many more specific layouts (`PostLayout`, `ProductLayout`, etc.) that reuse and build on top of it.
```astro ```astro
--- ---
// src/layouts/PostLayout.astro // src/layouts/PostLayout.astro
@ -93,7 +91,7 @@ const {title, description} = Astro.props;
<link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
``` ```
Notice how this layout doesn't include your page shell, and only includes some generic elements that should go in your `<head>`. This lets you combine multiple layout components together, to include things Notice how this layout doesn't include your page shell, and only includes some generic elements that should go in your `<head>`. This lets you combine multiple layout components together, to include things
```astro ```astro
--- ---
@ -119,15 +117,16 @@ The one downside to this approach is that you'll need to define the `<html>`, `<
## Markdown Layouts ## Markdown Layouts
Layouts are essential for Markdown files. Markdown files can declare a layout in the file frontmatter. Each Markdown file will be rendered to HTML and then injected into the layout's `<slot />` location. Layouts are essential for Markdown files. Markdown files can declare a layout in the file frontmatter. Each Markdown file will be rendered to HTML and then injected into the layout's `<slot />` location.
````markdown ```markdown
--- ---
title: Blog Post title: Blog Post
layout: ../layouts/PostLayout.astro layout: ../layouts/PostLayout.astro
--- ---
This blog post will be **rendered** inside of the `<PostLayout />` layout. This blog post will be **rendered** inside of the `<PostLayout />` layout.
```` ```
Markdown pages always pass a `content` prop to their layout, which is useful to grab information about the page, title, metadata, table of contents headers, and more. Markdown pages always pass a `content` prop to their layout, which is useful to grab information about the page, title, metadata, table of contents headers, and more.
@ -152,4 +151,4 @@ const { content } = Astro.props;
</html> </html>
``` ```
📚 Learn more about Astro's markdown support in our [Markdown guide](/docs/guides/markdown-content.md). 📚 Learn more about Astro's markdown support in our [Markdown guide](/docs/guides/markdown-content.md).

View file

@ -10,6 +10,7 @@ Astro includes an opinionated folder layout for your project. Every Astro projec
- `package.json` - A project manifest. - `package.json` - A project manifest.
The easiest way to set up your new project is with `npm init astro`. Check out our [Installation Guide](/docs/quick-start.md) for a walkthrough of how to set up your project automatically (with `npm init astro`) or manually. The easiest way to set up your new project is with `npm init astro`. Check out our [Installation Guide](/docs/quick-start.md) for a walkthrough of how to set up your project automatically (with `npm init astro`) or manually.
## Project Structure ## Project Structure
``` ```
@ -48,9 +49,9 @@ Your non-Astro UI components (React, Preact, Svelte, Vue, etc.) can also live in
### `src/pages` ### `src/pages`
[Pages](/docs/core-concepts/astro-pages.md) contain all pages (`.astro` and `.md` supported) for your website. It is **required** that you put your pages in this directory. [Pages](/docs/core-concepts/astro-pages.md) contain all pages (`.astro` and `.md` supported) for your website. It is **required** that you put your pages in this directory.
### `public/` ### `public/`
For most users, the majority of your files will live inside of the `src/` directory so that Astro can properly handle and optimize them in your final build. By contrast, the `public/` directory is the place for any files to live outside of the Astro build process. For most users, the majority of your files will live inside of the `src/` directory so that Astro can properly handle and optimize them in your final build. By contrast, the `public/` directory is the place for any files to live outside of the Astro build process.
If you put a file into the public folder, it will not be processed by Astro. Instead it will be copied into the build folder untouched. This can be useful for assets like images and fonts, or when you need to include a specific file like `robots.txt` or `manifest.webmanifest`. If you put a file into the public folder, it will not be processed by Astro. Instead it will be copied into the build folder untouched. This can be useful for assets like images and fonts, or when you need to include a specific file like `robots.txt` or `manifest.webmanifest`.

View file

@ -5,4 +5,4 @@ title: Examples
If you prefer to learn by example, check out our [Examples Library](https://github.com/snowpackjs/astro/tree/main/examples) on GitHub. If you prefer to learn by example, check out our [Examples Library](https://github.com/snowpackjs/astro/tree/main/examples) on GitHub.
<!-- Once we merge astro-docs back into the main repo, we can actually fetch the list of examples at build-time by scanning the examples/ directory! --> <!-- Once we merge astro-docs back into the main repo, we can actually fetch the list of examples at build-time by scanning the examples/ directory! -->

View file

@ -8,6 +8,7 @@ Astro support `fetch()` and "top-level await" to help you do remote data fetchin
**Important:** These are not yet available inside of non-page Astro components. Instead, do all of your data loading inside of your pages, and then pass them to your components as props. **Important:** These are not yet available inside of non-page Astro components. Instead, do all of your data loading inside of your pages, and then pass them to your components as props.
## Example ## Example
```astro ```astro
// Example: src/pages/foo.astro // Example: src/pages/foo.astro
// top-level `fetch()` and `await` are both supported natively in Astro (pages only). // top-level `fetch()` and `await` are both supported natively in Astro (pages only).
@ -22,4 +23,4 @@ const allPokemon = allPokemonResult.results;
{allPokemon.map((pokemon) => (<h1>{pokemon.name}</h1>))} {allPokemon.map((pokemon) => (<h1>{pokemon.name}</h1>))}
</body> </body>
</html> </html>
``` ```

View file

@ -20,8 +20,6 @@ The following guides are based on some shared assumptions:
} }
``` ```
## Building The App ## Building The App
You may run `npm run build` command to build the app. You may run `npm run build` command to build the app.
@ -64,7 +62,8 @@ By default, the build output will be placed at `dist/`. You may deploy this `dis
cd - cd -
``` ```
> You can also run the above script in your CI setup to enable automatic deployment on each push.
> You can also run the above script in your CI setup to enable automatic deployment on each push.
### GitHub Actions ### GitHub Actions
@ -232,10 +231,10 @@ You can deploy your Astro project with Microsoft Azure [Static Web Apps](https:/
- An Azure account and a subscription key. You can create a [free Azure account here](https://azure.microsoft.com/free). - An Azure account and a subscription key. You can create a [free Azure account here](https://azure.microsoft.com/free).
- Your app code pushed to [GitHub](https://github.com). - Your app code pushed to [GitHub](https://github.com).
- The [SWA Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps) in [Visual Studio Code](https://code.visualstudio.com). - The [SWA Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps) in [Visual Studio Code](https://code.visualstudio.com).
Install the extension in VS Code and navigate to your app root. Open the Static Web Apps extension, sign in to Azure, and click the '+' sign to create a new Static Web App. You will be prompted to designate which subscription key to use. Install the extension in VS Code and navigate to your app root. Open the Static Web Apps extension, sign in to Azure, and click the '+' sign to create a new Static Web App. You will be prompted to designate which subscription key to use.
Follow the wizard started by the extension to give your app a name, choose a framework preset, and designate the app root (usually `/`) and built file location `/dist`. The wizard will run and will create a GitHub action in your repo in a `.github` folder. Follow the wizard started by the extension to give your app a name, choose a framework preset, and designate the app root (usually `/`) and built file location `/dist`. The wizard will run and will create a GitHub action in your repo in a `.github` folder.
The action will work to deploy your app (watch its progress in your repo's Actions tab) and, when successfully completed, you can view your app in the address provided in the extension's progress window by clicking the 'Browse Website' button that appears when the GitHub action has run. The action will work to deploy your app (watch its progress in your repo's Actions tab) and, when successfully completed, you can view your app in the address provided in the extension's progress window by clicking the 'Browse Website' button that appears when the GitHub action has run.

View file

@ -29,7 +29,7 @@ export function getUser() {
} }
// src/index.js // src/index.js
import {getUser} from './user.js'; import { getUser } from './user.js';
``` ```
All browsers now support ESM, so Astro is able to ship this code directly to the browser during development. All browsers now support ESM, so Astro is able to ship this code directly to the browser during development.
@ -38,7 +38,7 @@ All browsers now support ESM, so Astro is able to ship this code directly to the
Astro includes built-in support to build TypeScript files (`*.ts`) to JavaScript. Astro components also support TypeScript in the frontmatter script section. Astro includes built-in support to build TypeScript files (`*.ts`) to JavaScript. Astro components also support TypeScript in the frontmatter script section.
Note that this built-in support is build only. By default, Astro does not type-check your TypeScript code. Note that this built-in support is build only. By default, Astro does not type-check your TypeScript code.
<!-- To integrate type checking into your development/build workflow, add the [@snowpack/plugin-typescript](https://www.npmjs.com/package/@snowpack/plugin-typescript) plugin. --> <!-- To integrate type checking into your development/build workflow, add the [@snowpack/plugin-typescript](https://www.npmjs.com/package/@snowpack/plugin-typescript) plugin. -->
@ -46,7 +46,7 @@ Note that this built-in support is build only. By default, Astro does not type-c
Astro includes built-in support to build JSX files (`*.jsx` & `*.tsx`) to JavaScript. Astro includes built-in support to build JSX files (`*.jsx` & `*.tsx`) to JavaScript.
If you are using Preact, Astro will detect your Preact import and switch to use the Preact-style JSX `h()` function. This is all done automatically for you. If you are using Preact, Astro will detect your Preact import and switch to use the Preact-style JSX `h()` function. This is all done automatically for you.
**Note: Astro does not support JSX in `.js`/`.ts` files.** **Note: Astro does not support JSX in `.js`/`.ts` files.**
@ -121,7 +121,7 @@ When you start up your dev server or run a new build, you may see a message that
## Node Builtins ## Node Builtins
We encourage Astro users to avoid Node.js builtins (`fs`, `path`, etc) whenever possible. Astro aims to be compatible with multiple JavaScript runtimes in the future. This includes [Deno](https://deno.land/) and [Cloudflare Workers](https://workers.cloudflare.com/) which do not support Node builtin modules such as `fs`. We encourage Astro users to avoid Node.js builtins (`fs`, `path`, etc) whenever possible. Astro aims to be compatible with multiple JavaScript runtimes in the future. This includes [Deno](https://deno.land/) and [Cloudflare Workers](https://workers.cloudflare.com/) which do not support Node builtin modules such as `fs`.
Our aim is to provide Astro alternatives to common Node.js builtins. However, no such alternatives exist today. So, if you _really_ need to use these builtin modules we don't want to stop you. Astro supports Node.js builtins using Node's newer `node:` prefix. If you want to read a file, for example, you can do so like this: Our aim is to provide Astro alternatives to common Node.js builtins. However, no such alternatives exist today. So, if you _really_ need to use these builtin modules we don't want to stop you. Astro supports Node.js builtins using Node's newer `node:` prefix. If you want to read a file, for example, you can do so like this:

View file

@ -4,6 +4,7 @@ title: Markdown Content
--- ---
Astro comes with out-of-the-box Markdown support powered by the expansive [remark](https://remark.js.org/) ecosystem. Astro comes with out-of-the-box Markdown support powered by the expansive [remark](https://remark.js.org/) ecosystem.
## Remark and Rehype Plugins ## Remark and Rehype Plugins
In addition to [custom components inside the `<Markdown>` component](#markdown-component), Astro comes with [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants), and syntax highlighting via [Prism](https://prismjs.com/) pre-enabled. In addition to [custom components inside the `<Markdown>` component](#markdown-component), Astro comes with [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants), and syntax highlighting via [Prism](https://prismjs.com/) pre-enabled.
@ -42,10 +43,7 @@ You can provide names of the plugins as well as import them:
// astro.config.js // astro.config.js
export default { export default {
markdownOptions: { markdownOptions: {
remarkPlugins: [ remarkPlugins: [import('remark-slug'), [import('remark-autolink-headings'), { behavior: 'prepend' }]],
import('remark-slug'),
[import('remark-autolink-headings'), { behavior: 'prepend' }],
],
}, },
}; };
``` ```

View file

@ -3,9 +3,9 @@ layout: ~/layouts/Main.astro
title: Publish Components to NPM title: Publish Components to NPM
--- ---
Built a great Astro component? **Publish it to [npm!](https://npmjs.com/)** Built a great Astro component? **Publish it to [npm!](https://npmjs.com/)**
Once published to npm, Astro components can be installed and used in your project like any other npm package. npm is a great way to share Astro components across projects within your team, your company, or the entire world. Once published to npm, Astro components can be installed and used in your project like any other npm package. npm is a great way to share Astro components across projects within your team, your company, or the entire world.
## Basic NPM Package Setup ## Basic NPM Package Setup
@ -66,7 +66,7 @@ import Capitalize from '@example/my-components/Capitalize.astro';
<Capitalize phrase={`Hello world`} /> <Capitalize phrase={`Hello world`} />
``` ```
This is a less common scenario, and we only recommend it if you have good reason. Because Astro is completely rendered at build-time, there are no client-side performance concerns to our default recommendation to export your components from a single `index.js` file. This is a less common scenario, and we only recommend it if you have good reason. Because Astro is completely rendered at build-time, there are no client-side performance concerns to our default recommendation to export your components from a single `index.js` file.
To support importing by file within your package, add each file to your **package.json** `exports` map: To support importing by file within your package, add each file to your **package.json** `exports` map:
@ -80,4 +80,4 @@ To support importing by file within your package, add each file to your **packag
+ "./Capitalize.astro": "./Capitalize.astro" + "./Capitalize.astro": "./Capitalize.astro"
} }
} }
``` ```

View file

@ -17,7 +17,7 @@ With Astro, you can use your favorite JavaScript framework and automatically shi
## 🔧 Quick Start ## 🔧 Quick Start
> __Important__: Astro is built with [ESM modules](https://nodejs.org/api/esm.html) which are not supported in older version of Node.js. The minimum supported version is __14.16.1__. > **Important**: Astro is built with [ESM modules](https://nodejs.org/api/esm.html) which are not supported in older version of Node.js. The minimum supported version is **14.16.1**.
```bash ```bash
# create your project # create your project

View file

@ -21,7 +21,7 @@ cd <project-name>
npm init astro npm init astro
``` ```
Follow the CLI instructions to install Astro with one of our official project starter templates. Follow the CLI instructions to install Astro with one of our official project starter templates.
Once completed, jump over to our [Quickstart Guide](/docs/quick-start.md#start-your-project) for a 30-second walkthrough on how to start & build your new Astro project! Once completed, jump over to our [Quickstart Guide](/docs/quick-start.md#start-your-project) for a 30-second walkthrough on how to start & build your new Astro project!
@ -46,7 +46,7 @@ npm init --yes
### Install Astro ### Install Astro
If you've followed the instructions above, you should have a directory with a single `package.json` file inside of it. You can now install Astro in your project. If you've followed the instructions above, you should have a directory with a single `package.json` file inside of it. You can now install Astro in your project.
We'll use `npm` in the examples below, but you could also use `yarn` or `pnpm` if you prefer an npm alternative. If you aren't familiar with `yarn` or `pnpm`, then we strongly recommend sticking with `npm`. We'll use `npm` in the examples below, but you could also use `yarn` or `pnpm` if you prefer an npm alternative. If you aren't familiar with `yarn` or `pnpm`, then we strongly recommend sticking with `npm`.
@ -90,4 +90,3 @@ Success! You're now ready to start developing! Jump over to our [Quickstart Guid
📚 Learn more about Astro's project structure in our [Project Structure guide](/docs/core-concepts/project-structure.md). 📚 Learn more about Astro's project structure in our [Project Structure guide](/docs/core-concepts/project-structure.md).
📚 Learn more about Astro's component syntax in our [Astro Components guide](/docs/core-concepts/astro-components.md). 📚 Learn more about Astro's component syntax in our [Astro Components guide](/docs/core-concepts/astro-components.md).
📚 Learn more about Astro's file-based routing in our [Routing guide](core-concepts/astro-pages). 📚 Learn more about Astro's file-based routing in our [Routing guide](core-concepts/astro-pages).

View file

@ -1,4 +1,4 @@
--- ---
layout: ~/layouts/Main.astro layout: ~/layouts/Main.astro
title: Data Sources / CMS title: Data Sources / CMS
--- ---

View file

@ -1,4 +1,4 @@
--- ---
layout: ~/layouts/Main.astro layout: ~/layouts/Main.astro
title: Deploy Astro title: Deploy Astro
--- ---

View file

@ -1,4 +1,4 @@
--- ---
layout: ~/layouts/Main.astro layout: ~/layouts/Main.astro
title: Developer Tools title: Developer Tools
--- ---

View file

@ -1,4 +1,4 @@
--- ---
layout: ~/layouts/Main.astro layout: ~/layouts/Main.astro
title: State Management title: State Management
--- ---

View file

@ -1,4 +1,4 @@
--- ---
layout: ~/layouts/Main.astro layout: ~/layouts/Main.astro
title: Styles & CSS Libraries title: Styles & CSS Libraries
--- ---

View file

@ -23,9 +23,7 @@ npm run start
npm run build npm run build
``` ```
To deploy your Astro site to production, upload the contents of the `/dist` folder (generated by running `npm run build`) to your favorite hosting provider. To deploy your Astro site to production, upload the contents of the `/dist` folder (generated by running `npm run build`) to your favorite hosting provider.
## Start your project ## Start your project
@ -39,7 +37,6 @@ Your application is now running on [http://localhost:3000](http://localhost:3000
Astro will listen for file changes in your `src/` directory, so you do not need to restart the application as you make changes during development. Astro will listen for file changes in your `src/` directory, so you do not need to restart the application as you make changes during development.
## Build your project ## Build your project
Go back to your command-line terminal, and run the following command in your project directory: Go back to your command-line terminal, and run the following command in your project directory:
@ -48,4 +45,4 @@ Go back to your command-line terminal, and run the following command in your pro
npm run build npm run build
``` ```
This will build your site and write it to disk in the `dist/` directory. Astro sites are static, so they can be deployed to your favorite host (Vercel, Netlify, an S3 bucket, etc.). This will build your site and write it to disk in the `dist/` directory. Astro sites are static, so they can be deployed to your favorite host (Vercel, Netlify, an S3 bucket, etc.).

View file

@ -65,6 +65,7 @@ const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of po
`Astro.site` returns a `URL` made from `buildOptions.site` in your Astro config. If undefined, this will return a URL generated from `localhost`. `Astro.site` returns a `URL` made from `buildOptions.site` in your Astro config. If undefined, this will return a URL generated from `localhost`.
## Collections API ## Collections API
### `collection` prop ### `collection` prop
```jsx ```jsx
@ -179,4 +180,3 @@ export default function () {
[config]: ../README.md#%EF%B8%8F-configuration [config]: ../README.md#%EF%B8%8F-configuration
[docs-collections]: ./collections.md [docs-collections]: ./collections.md
[rss]: #-rss-feed [rss]: #-rss-feed

View file

@ -17,8 +17,8 @@ import { Markdown } from 'astro/components';
``` ```
See our [Markdown Guide](/docs/guides/markdown-content.md) for more info. See our [Markdown Guide](/docs/guides/markdown-content.md) for more info.
<!-- TODO: We should move some of the specific component info here. -->
<!-- TODO: We should move some of the specific component info here. -->
## `<Prism />` ## `<Prism />`

View file

@ -21,7 +21,6 @@ Specifies should port to run on. Defaults to `3000`.
Builds your site for production. Builds your site for production.
## Global Flags ## Global Flags
### `--config path` ### `--config path`

View file

@ -27,4 +27,4 @@ export default {
## Snowpack Config ## Snowpack Config
Astro is powered internally by Snowpack. You can configure Snowpack directly by creating a `snowpack.config.js` file. See [snowpack.dev](https://www.snowpack.dev/reference/configuration) for full documentation on this file. Astro is powered internally by Snowpack. You can configure Snowpack directly by creating a `snowpack.config.js` file. See [snowpack.dev](https://www.snowpack.dev/reference/configuration) for full documentation on this file.

View file

@ -108,7 +108,7 @@ export interface Ast {
// instance: Script; // instance: Script;
meta: { meta: {
features: number; features: number;
} };
} }
export interface Warning { export interface Warning {

View file

@ -29,7 +29,7 @@ export class Parser {
js: Script[] = []; js: Script[] = [];
meta_tags = {}; meta_tags = {};
last_auto_closed_tag?: LastAutoClosedTag; last_auto_closed_tag?: LastAutoClosedTag;
feature_flags: 0 feature_flags: 0;
constructor(template: string, options: ParserOptions) { constructor(template: string, options: ParserOptions) {
if (typeof template !== 'string') { if (typeof template !== 'string') {
@ -268,7 +268,7 @@ export default function parse(template: string, options: ParserOptions = {}): As
// instance: instance_scripts[0], // instance: instance_scripts[0],
module: astro_scripts[0], module: astro_scripts[0],
meta: { meta: {
features: parser.feature_flags features: parser.feature_flags,
} },
}; };
} }

View file

@ -56,7 +56,6 @@ function parent_is_head(stack) {
return false; return false;
} }
export default function tag(parser: Parser) { export default function tag(parser: Parser) {
const start = parser.index++; const start = parser.index++;
@ -80,7 +79,7 @@ export default function tag(parser: Parser) {
const name = read_tag_name(parser); const name = read_tag_name(parser);
if(CUSTOM_ELEMENT.test(name)) { if (CUSTOM_ELEMENT.test(name)) {
parser.feature_flags |= FEATURE_CUSTOM_ELEMENT; parser.feature_flags |= FEATURE_CUSTOM_ELEMENT;
} }

View file

@ -1,2 +1 @@
export const FEATURE_CUSTOM_ELEMENT = 1 << 0;
export const FEATURE_CUSTOM_ELEMENT = 1 << 0;

View file

@ -1,5 +1,5 @@
import type { ImportSpecifier, ImportDefaultSpecifier, ImportNamespaceSpecifier } from '@babel/types'; import type { ImportSpecifier, ImportDefaultSpecifier, ImportNamespaceSpecifier } from '@babel/types';
import type { AstroMarkdownOptions } from '@astrojs/markdown-support' import type { AstroMarkdownOptions } from '@astrojs/markdown-support';
export interface AstroConfigRaw { export interface AstroConfigRaw {
dist: string; dist: string;
@ -10,7 +10,7 @@ export interface AstroConfigRaw {
jsx?: string; jsx?: string;
} }
export { AstroMarkdownOptions } export { AstroMarkdownOptions };
export interface AstroConfig { export interface AstroConfig {
dist: string; dist: string;
projectRoot: URL; projectRoot: URL;
@ -185,4 +185,4 @@ export interface Renderer {
renderToStaticMarkup: AsyncRendererComponentFn<{ renderToStaticMarkup: AsyncRendererComponentFn<{
html: string; html: string;
}>; }>;
} }

View file

@ -144,7 +144,7 @@ function generateAttributes(attrs: Record<string, string>): string {
return result + '}'; return result + '}';
} }
function getComponentUrl(astroConfig: AstroConfig, url: string, parentUrl: string | URL){ function getComponentUrl(astroConfig: AstroConfig, url: string, parentUrl: string | URL) {
const componentExt = path.extname(url); const componentExt = path.extname(url);
const ext = PlainExtensions.has(componentExt) ? '.js' : `${componentExt}.js`; const ext = PlainExtensions.has(componentExt) ? '.js' : `${componentExt}.js`;
const outUrl = new URL(url, parentUrl); const outUrl = new URL(url, parentUrl);
@ -166,9 +166,11 @@ function getComponentWrapper(_name: string, { url, importSpecifier }: ComponentI
if (isCustomElementTag(name)) { if (isCustomElementTag(name)) {
return { return {
wrapper: `__astro_component(...__astro_element_registry.astroComponentArgs("${name}", ${JSON.stringify({ hydrate: kind, displayName: _name })}))`, wrapper: `__astro_component(...__astro_element_registry.astroComponentArgs("${name}", ${JSON.stringify({ hydrate: kind, displayName: _name })}))`,
wrapperImports: [`import {AstroElementRegistry} from 'astro/dist/internal/element-registry.js';`,`import {__astro_component} from 'astro/dist/internal/__astro_component.js';`], wrapperImports: [
`import {AstroElementRegistry} from 'astro/dist/internal/element-registry.js';`,
`import {__astro_component} from 'astro/dist/internal/__astro_component.js';`,
],
}; };
} else { } else {
const getComponentExport = () => { const getComponentExport = () => {
switch (importSpecifier.type) { switch (importSpecifier.type) {
@ -187,10 +189,12 @@ function getComponentWrapper(_name: string, { url, importSpecifier }: ComponentI
} }
}; };
const importInfo = kind ? { const importInfo = kind
componentUrl: getComponentUrl(astroConfig, url, pathToFileURL(filename)), ? {
componentExport: getComponentExport() componentUrl: getComponentUrl(astroConfig, url, pathToFileURL(filename)),
} : {}; componentExport: getComponentExport(),
}
: {};
return { return {
wrapper: `__astro_component(${name}, ${JSON.stringify({ hydrate: kind, displayName: _name, ...importInfo })})`, wrapper: `__astro_component(${name}, ${JSON.stringify({ hydrate: kind, displayName: _name, ...importInfo })})`,
@ -392,9 +396,9 @@ function compileModule(ast: Ast, module: Script, state: CodegenState, compileOpt
}); });
} }
const { start, end } = componentImport; const { start, end } = componentImport;
if(ast.meta.features & FEATURE_CUSTOM_ELEMENT && componentImport.specifiers.length === 0) { if (ast.meta.features & FEATURE_CUSTOM_ELEMENT && componentImport.specifiers.length === 0) {
// Add possible custom element, but only if the AST says there are custom elements. // Add possible custom element, but only if the AST says there are custom elements.
const moduleImportName = camelCase(importUrl+ 'Module'); const moduleImportName = camelCase(importUrl + 'Module');
state.importStatements.add(`import * as ${moduleImportName} from '${importUrl}';\n`); state.importStatements.add(`import * as ${moduleImportName} from '${importUrl}';\n`);
state.customElementCandidates.set(moduleImportName, getComponentUrl(astroConfig, importUrl, pathToFileURL(filename))); state.customElementCandidates.set(moduleImportName, getComponentUrl(astroConfig, importUrl, pathToFileURL(filename)));
} else { } else {
@ -657,7 +661,7 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
if (componentName === 'Markdown') { if (componentName === 'Markdown') {
const { $scope } = attributes ?? {}; const { $scope } = attributes ?? {};
state.markers.insideMarkdown = typeof state.markers.insideMarkdown === 'object' ? { $scope, count: state.markers.insideMarkdown.count + 1 } : { $scope, count: 1 }; state.markers.insideMarkdown = typeof state.markers.insideMarkdown === 'object' ? { $scope, count: state.markers.insideMarkdown.count + 1 } : { $scope, count: 1 };
const keys = Object.keys(attributes).filter(attr => attr !== '$scope'); const keys = Object.keys(attributes).filter((attr) => attr !== '$scope');
if (keys.length > 0) { if (keys.length > 0) {
if (curr === 'markdown') { if (curr === 'markdown') {
await pushMarkdownToBuffer(); await pushMarkdownToBuffer();
@ -669,7 +673,7 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
} }
const { wrapper, wrapperImports } = getComponentWrapper(name, componentInfo ?? ({} as any), { astroConfig, filename }); const { wrapper, wrapperImports } = getComponentWrapper(name, componentInfo ?? ({} as any), { astroConfig, filename });
if (wrapperImports) { if (wrapperImports) {
for(let wrapperImport of wrapperImports) { for (let wrapperImport of wrapperImports) {
importStatements.add(wrapperImport); importStatements.add(wrapperImport);
} }
} }
@ -765,7 +769,7 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
if ((state.markers.insideMarkdown as Record<string, any>).count <= 0) { if ((state.markers.insideMarkdown as Record<string, any>).count <= 0) {
state.markers.insideMarkdown = false; state.markers.insideMarkdown = false;
} }
const hasAttrs = (node.attributes.filter(({ name }: Attribute) => name !== '$scope')).length > 0; const hasAttrs = node.attributes.filter(({ name }: Attribute) => name !== '$scope').length > 0;
if (hasAttrs) { if (hasAttrs) {
return; return;
} }
@ -820,7 +824,7 @@ export async function codegen(ast: Ast, { compileOptions, filename, fileID }: Co
}, },
importStatements: new Set(), importStatements: new Set(),
exportStatements: new Set(), exportStatements: new Set(),
customElementCandidates: new Map() customElementCandidates: new Map(),
}; };
const { script, createCollection } = compileModule(ast, ast.module, state, compileOptions); const { script, createCollection } = compileModule(ast, ast.module, state, compileOptions);
@ -837,6 +841,6 @@ export async function codegen(ast: Ast, { compileOptions, filename, fileID }: Co
css: state.css.length ? state.css.join('\n\n') : undefined, css: state.css.length ? state.css.join('\n\n') : undefined,
createCollection, createCollection,
hasCustomElements: Boolean(ast.meta.features & FEATURE_CUSTOM_ELEMENT), hasCustomElements: Boolean(ast.meta.features & FEATURE_CUSTOM_ELEMENT),
customElementCandidates: state.customElementCandidates customElementCandidates: state.customElementCandidates,
}; };
} }

View file

@ -116,11 +116,17 @@ import fetch from 'node-fetch';
// <script astro></script> // <script astro></script>
${result.imports.join('\n')} ${result.imports.join('\n')}
${result.hasCustomElements ? ` ${
result.hasCustomElements
? `
const __astro_element_registry = new AstroElementRegistry({ const __astro_element_registry = new AstroElementRegistry({
candidates: new Map([${Array.from(result.customElementCandidates).map(([identifier, url]) => `[${identifier}, '${url}']`).join(', ')}]) candidates: new Map([${Array.from(result.customElementCandidates)
.map(([identifier, url]) => `[${identifier}, '${url}']`)
.join(', ')}])
}); });
`.trim() : ''} `.trim()
: ''
}
// \`__render()\`: Render the contents of the Astro module. // \`__render()\`: Render the contents of the Astro module.
import { h, Fragment } from 'astro/dist/internal/h.js'; import { h, Fragment } from 'astro/dist/internal/h.js';

View file

@ -199,8 +199,8 @@ export default function transformStyles({ compileOptions, filename, fileID }: Tr
if (node.name === 'Markdown') { if (node.name === 'Markdown') {
injectScopedClassAttribute(node, scopedClass, '$scope'); injectScopedClassAttribute(node, scopedClass, '$scope');
} }
for(let attr of node.attributes) { for (let attr of node.attributes) {
if(attr.name === 'class') { if (attr.name === 'class') {
injectScopedClassAttribute(node, scopedClass, 'class'); injectScopedClassAttribute(node, scopedClass, 'class');
break; break;
} }

View file

@ -80,7 +80,7 @@ export class ConfigManager {
const r = await import(entrypoint); const r = await import(entrypoint);
return { return {
raw: r.default, raw: r.default,
options: _options options: _options,
}; };
}) })
) )
@ -106,8 +106,8 @@ export class ConfigManager {
throw new Error(`Expected the snowpackPlugin from ${name} to be a "string" but encountered "${typeof snowpackPluginName}"!`); throw new Error(`Expected the snowpackPlugin from ${name} to be a "string" but encountered "${typeof snowpackPluginName}"!`);
} }
const polyfillsNormalized = (raw.polyfills || []).map((p: string) => p.startsWith('.') ? path.join(name, p) : p); const polyfillsNormalized = (raw.polyfills || []).map((p: string) => (p.startsWith('.') ? path.join(name, p) : p));
const hydrationPolyfillsNormalized = (raw.hydrationPolyfills || []).map((p: string) => p.startsWith('.') ? path.join(name, p) : p); const hydrationPolyfillsNormalized = (raw.hydrationPolyfills || []).map((p: string) => (p.startsWith('.') ? path.join(name, p) : p));
return { return {
name, name,
@ -118,7 +118,7 @@ export class ConfigManager {
knownEntrypoints: raw.knownEntrypoints, knownEntrypoints: raw.knownEntrypoints,
external: raw.external, external: raw.external,
polyfills: polyfillsNormalized, polyfills: polyfillsNormalized,
hydrationPolyfills: hydrationPolyfillsNormalized hydrationPolyfills: hydrationPolyfillsNormalized,
}; };
}); });
@ -128,21 +128,25 @@ export class ConfigManager {
async buildSource(contents: string): Promise<string> { async buildSource(contents: string): Promise<string> {
const renderers = await this.buildRendererInstances(); const renderers = await this.buildRendererInstances();
const rendererServerPackages = renderers.map(({ server }) => server); const rendererServerPackages = renderers.map(({ server }) => server);
const rendererClientPackages = await Promise.all(renderers.filter(({client}) => client).map(({ client }) => this.resolvePackageUrl(client!))); const rendererClientPackages = await Promise.all(renderers.filter(({ client }) => client).map(({ client }) => this.resolvePackageUrl(client!)));
const rendererPolyfills = await Promise.all(renderers.map(({ polyfills }) => Promise.all(polyfills.map(src => this.resolvePackageUrl(src))))); const rendererPolyfills = await Promise.all(renderers.map(({ polyfills }) => Promise.all(polyfills.map((src) => this.resolvePackageUrl(src)))));
const rendererHydrationPolyfills = await Promise.all(renderers.map(({ hydrationPolyfills }) => Promise.all(hydrationPolyfills.map(src => this.resolvePackageUrl(src))))); const rendererHydrationPolyfills = await Promise.all(renderers.map(({ hydrationPolyfills }) => Promise.all(hydrationPolyfills.map((src) => this.resolvePackageUrl(src)))));
const result = /* js */ `${rendererServerPackages.map((pkg, i) => `import __renderer_${i} from "${pkg}";`).join('\n')} const result = /* js */ `${rendererServerPackages.map((pkg, i) => `import __renderer_${i} from "${pkg}";`).join('\n')}
import { setRenderers } from 'astro/dist/internal/__astro_component.js'; import { setRenderers } from 'astro/dist/internal/__astro_component.js';
let rendererInstances = [${renderers.map((r, i) => `{ let rendererInstances = [${renderers
.map(
(r, i) => `{
source: ${rendererClientPackages[i] ? `"${rendererClientPackages[i]}"` : 'null'}, source: ${rendererClientPackages[i] ? `"${rendererClientPackages[i]}"` : 'null'},
renderer: __renderer_${i}, renderer: __renderer_${i},
options: ${r.options ? JSON.stringify(r.options) : 'null'}, options: ${r.options ? JSON.stringify(r.options) : 'null'},
polyfills: ${JSON.stringify(rendererPolyfills[i])}, polyfills: ${JSON.stringify(rendererPolyfills[i])},
hydrationPolyfills: ${JSON.stringify(rendererHydrationPolyfills[i])} hydrationPolyfills: ${JSON.stringify(rendererHydrationPolyfills[i])}
}`).join(', ')}]; }`
)
.join(', ')}];
${contents} ${contents}
`; `;

View file

@ -22,7 +22,7 @@ const astroRendererInstance: RendererInstance = {
renderer: astro as Renderer, renderer: astro as Renderer,
options: null, options: null,
polyfills: [], polyfills: [],
hydrationPolyfills: [] hydrationPolyfills: [],
}; };
const astroHtmlRendererInstance: RendererInstance = { const astroHtmlRendererInstance: RendererInstance = {
@ -30,7 +30,7 @@ const astroHtmlRendererInstance: RendererInstance = {
renderer: astroHtml as Renderer, renderer: astroHtml as Renderer,
options: null, options: null,
polyfills: [], polyfills: [],
hydrationPolyfills: [] hydrationPolyfills: [],
}; };
let rendererInstances: RendererInstance[] = []; let rendererInstances: RendererInstance[] = [];
@ -94,14 +94,16 @@ async function generateHydrateScript({ instance, astroId, props }: HydrateScript
const { source } = instance; const { source } = instance;
let hydrationSource = ''; let hydrationSource = '';
if(instance.hydrationPolyfills.length) { if (instance.hydrationPolyfills.length) {
hydrationSource += `await Promise.all([${instance.hydrationPolyfills.map(src => `import("${src}")`).join(', ')}]);\n`; hydrationSource += `await Promise.all([${instance.hydrationPolyfills.map((src) => `import("${src}")`).join(', ')}]);\n`;
} }
hydrationSource += source ? ` hydrationSource += source
? `
const [{ ${componentExport.value}: Component }, { default: hydrate }] = await Promise.all([import("${componentUrl}"), import("${source}")]); const [{ ${componentExport.value}: Component }, { default: hydrate }] = await Promise.all([import("${componentUrl}"), import("${source}")]);
return (el, children) => hydrate(el)(Component, ${serialize(props)}, children); return (el, children) => hydrate(el)(Component, ${serialize(props)}, children);
` : ` `
: `
await import("${componentUrl}"); await import("${componentUrl}");
return () => {}; return () => {};
`; `;
@ -141,7 +143,7 @@ export const __astro_component = (Component: any, componentProps: AstroComponent
let instance = await resolveRenderer(Component, props, children); let instance = await resolveRenderer(Component, props, children);
if (!instance) { if (!instance) {
if(isCustomElementTag(Component)) { if (isCustomElementTag(Component)) {
instance = astroHtmlRendererInstance; instance = astroHtmlRendererInstance;
} else { } else {
// If the user only specifies a single renderer, but the check failed // If the user only specifies a single renderer, but the check failed
@ -156,8 +158,8 @@ export const __astro_component = (Component: any, componentProps: AstroComponent
} }
let { html } = await instance.renderer.renderToStaticMarkup(Component, props, children, instance.options); let { html } = await instance.renderer.renderToStaticMarkup(Component, props, children, instance.options);
if(instance.polyfills.length) { if (instance.polyfills.length) {
let polyfillScripts = instance.polyfills.map(src => `<script type="module" src="${src}"></script>`).join(''); let polyfillScripts = instance.polyfills.map((src) => `<script type="module" src="${src}"></script>`).join('');
html = html + polyfillScripts; html = html + polyfillScripts;
} }

View file

@ -14,9 +14,9 @@ class AstroElementRegistry {
} }
find(tagName: string) { find(tagName: string) {
for(let [module, importSpecifier] of this.candidates) { for (let [module, importSpecifier] of this.candidates) {
if(module && typeof module.tagName === 'string') { if (module && typeof module.tagName === 'string') {
if(module.tagName === tagName) { if (module.tagName === tagName) {
// Found! // Found!
return importSpecifier; return importSpecifier;
} }
@ -25,11 +25,11 @@ class AstroElementRegistry {
} }
findCached(tagName: string) { findCached(tagName: string) {
if(this.cache.has(tagName)) { if (this.cache.has(tagName)) {
return this.cache.get(tagName)!; return this.cache.get(tagName)!;
} }
let specifier = this.find(tagName); let specifier = this.find(tagName);
if(specifier) { if (specifier) {
this.cache.set(tagName, specifier); this.cache.set(tagName, specifier);
} }
return specifier; return specifier;
@ -39,10 +39,10 @@ class AstroElementRegistry {
const specifier = this.findCached(tagName); const specifier = this.findCached(tagName);
const outProps: AstroComponentProps = { const outProps: AstroComponentProps = {
...props, ...props,
componentUrl: specifier || props.componentUrl componentUrl: specifier || props.componentUrl,
}; };
return [tagName, outProps]; return [tagName, outProps];
} }
} }
export { AstroElementRegistry }; export { AstroElementRegistry };

View file

@ -3,10 +3,8 @@ import { h } from './h';
async function renderToStaticMarkup(tag: string, props: Record<string, any>, children: string) { async function renderToStaticMarkup(tag: string, props: Record<string, any>, children: string) {
const html = await h(tag, props, Promise.resolve(children)); const html = await h(tag, props, Promise.resolve(children));
return { return {
html html,
}; };
} }
export { export { renderToStaticMarkup };
renderToStaticMarkup
};

View file

@ -367,7 +367,7 @@ async function createSnowpack(astroConfig: AstroConfig, options: CreateSnowpackO
const knownEntrypoints: string[] = ['astro/dist/internal/__astro_component.js', 'astro/dist/internal/element-registry.js']; const knownEntrypoints: string[] = ['astro/dist/internal/__astro_component.js', 'astro/dist/internal/element-registry.js'];
for (const renderer of rendererInstances) { for (const renderer of rendererInstances) {
knownEntrypoints.push(renderer.server); knownEntrypoints.push(renderer.server);
if(renderer.client) { if (renderer.client) {
knownEntrypoints.push(renderer.client); knownEntrypoints.push(renderer.client);
} }
if (renderer.knownEntrypoints) { if (renderer.knownEntrypoints) {
@ -377,8 +377,8 @@ async function createSnowpack(astroConfig: AstroConfig, options: CreateSnowpackO
knownEntrypoints.push(...renderer.hydrationPolyfills); knownEntrypoints.push(...renderer.hydrationPolyfills);
} }
const external = snowpackExternals.concat([]); const external = snowpackExternals.concat([]);
for(const renderer of rendererInstances) { for (const renderer of rendererInstances) {
if(renderer.external) { if (renderer.external) {
external.push(...renderer.external); external.push(...renderer.external);
} }
} }
@ -418,7 +418,7 @@ async function createSnowpack(astroConfig: AstroConfig, options: CreateSnowpackO
packageOptions: { packageOptions: {
knownEntrypoints, knownEntrypoints,
external, external,
} },
}); });
const polyfillNode = (snowpackConfig.packageOptions as any).polyfillNode as boolean; const polyfillNode = (snowpackConfig.packageOptions as any).polyfillNode as boolean;

View file

@ -24,6 +24,6 @@ MarkdownPlugin('Can render Astro <Markdown> with plugins', async ({ runtime }) =
const $ = doc(result.contents); const $ = doc(result.contents);
assert.equal($('.toc').length, 1, 'Added a TOC'); assert.equal($('.toc').length, 1, 'Added a TOC');
assert.ok($('#hello-world').hasClass('title'), 'Added .title to h1'); assert.ok($('#hello-world').hasClass('title'), 'Added .title to h1');
}) });
MarkdownPlugin.run(); MarkdownPlugin.run();

View file

@ -138,10 +138,12 @@ StylesSSR('Astro scoped styles can be passed to child components', async ({ runt
const $ = doc(result.contents); const $ = doc(result.contents);
let scopedClass; let scopedClass;
$('style').html().replace(/outer\.(astro-[A-Za-z0-9-]+)/, (match, p1) => { $('style')
scopedClass = p1; .html()
return match; .replace(/outer\.(astro-[A-Za-z0-9-]+)/, (match, p1) => {
}); scopedClass = p1;
return match;
});
assert.match($('#passed-in').attr('class'), `outer ${scopedClass}`); assert.match($('#passed-in').attr('class'), `outer ${scopedClass}`);
}); });

View file

@ -36,10 +36,14 @@ export function setup(Suite, fixturePath, { runtimeOptions = {} } = {}) {
runtime = await createRuntime(astroConfig, { runtime = await createRuntime(astroConfig, {
logging: { level: 'error', dest: process.stderr }, logging: { level: 'error', dest: process.stderr },
...runtimeOptions, ...runtimeOptions,
}).catch(err => { createRuntimeError = err; }); }).catch((err) => {
createRuntimeError = err;
});
if(createRuntimeError) { if (createRuntimeError) {
setTimeout(() => { throw createRuntimeError }); setTimeout(() => {
throw createRuntimeError;
});
} }
context.runtime = runtime; context.runtime = runtime;

View file

@ -37,7 +37,7 @@ PreactComponent('Can export a Fragment', async ({ runtime }) => {
if (result.error) throw new Error(result.error); if (result.error) throw new Error(result.error);
const $ = doc(result.contents); const $ = doc(result.contents);
assert.equal($('body').children().length, 0, 'nothing rendered but it didn\'t throw.'); assert.equal($('body').children().length, 0, "nothing rendered but it didn't throw.");
}); });
PreactComponent.run(); PreactComponent.run();

View file

@ -1,5 +1,6 @@
export const createConfig = ({ renderers }: { renderers: string[] }) => { export const createConfig = ({ renderers }: { renderers: string[] }) => {
return [`export default { return [
`export default {
// projectRoot: '.', // Where to resolve all URLs relative to. Useful if you have a monorepo project. // projectRoot: '.', // Where to resolve all URLs relative to. Useful if you have a monorepo project.
// pages: './src/pages', // Path to Astro components, pages, and data // pages: './src/pages', // Path to Astro components, pages, and data
// dist: './dist', // When running \`astro build\`, path to final static output // dist: './dist', // When running \`astro build\`, path to final static output
@ -12,8 +13,11 @@ export const createConfig = ({ renderers }: { renderers: string[] }) => {
// port: 3000, // The port to run the dev server on. // port: 3000, // The port to run the dev server on.
// tailwindConfig: '', // Path to tailwind.config.js if used, e.g. './tailwind.config.js' // tailwindConfig: '', // Path to tailwind.config.js if used, e.g. './tailwind.config.js'
},`, },`,
` renderers: ${JSON.stringify(renderers, undefined, 2).split('\n').map((ln, i) => i !== 0 ? ` ${ln}` : ln).join('\n')},`, ` renderers: ${JSON.stringify(renderers, undefined, 2)
`}; .split('\n')
`].join('\n') .map((ln, i) => (i !== 0 ? ` ${ln}` : ln))
} .join('\n')},`,
`};
`,
].join('\n');
};

View file

@ -17,7 +17,7 @@ export default function PreactCounter({ children }) {
</div> </div>
); );
} }
` `,
}, },
'@astrojs/renderer-react': { '@astrojs/renderer-react': {
filename: `src/components/ReactCounter.jsx`, filename: `src/components/ReactCounter.jsx`,
@ -36,7 +36,7 @@ export default function ReactCounter({ children }) {
</div> </div>
); );
} }
` `,
}, },
'@astrojs/renderer-svelte': { '@astrojs/renderer-svelte': {
filename: `src/components/SvelteCounter.svelte`, filename: `src/components/SvelteCounter.svelte`,
@ -57,7 +57,7 @@ export default function ReactCounter({ children }) {
<pre>{ count }</pre> <pre>{ count }</pre>
<button on:click={add}>+</button> <button on:click={add}>+</button>
</div> </div>
` `,
}, },
'@astrojs/renderer-vue': { '@astrojs/renderer-vue': {
filename: `src/components/VueCounter.vue`, filename: `src/components/VueCounter.vue`,
@ -85,8 +85,8 @@ export default {
} }
} }
</script> </script>
` `,
} },
}; };
export const FRAMEWORKS = [ export const FRAMEWORKS = [
@ -105,5 +105,5 @@ export const FRAMEWORKS = [
{ {
title: 'Vue', title: 'Vue',
value: '@astrojs/renderer-vue', value: '@astrojs/renderer-vue',
} },
]; ];

View file

@ -66,9 +66,7 @@ export async function main() {
const hash = args.commit ? `#${args.commit}` : ''; const hash = args.commit ? `#${args.commit}` : '';
const templateTarget = options.template.includes('/') ? const templateTarget = options.template.includes('/') ? options.template : `snowpackjs/astro/examples/${options.template}`;
options.template :
`snowpackjs/astro/examples/${options.template}`;
const emitter = degit(`${templateTarget}${hash}`, { const emitter = degit(`${templateTarget}${hash}`, {
cache: false, cache: false,
@ -76,9 +74,9 @@ export async function main() {
verbose: false, verbose: false,
}); });
const selectedTemplate = TEMPLATES.find(template => template.value === options.template); const selectedTemplate = TEMPLATES.find((template) => template.value === options.template);
let renderers: string[] = []; let renderers: string[] = [];
if (selectedTemplate?.renderers === true) { if (selectedTemplate?.renderers === true) {
const result = /** @type {import('./types/internal').Options} */ await prompts([ const result = /** @type {import('./types/internal').Options} */ await prompts([
{ {
@ -91,7 +89,7 @@ export async function main() {
renderers = result.renderers; renderers = result.renderers;
} else if (selectedTemplate?.renderers && Array.isArray(selectedTemplate.renderers)) { } else if (selectedTemplate?.renderers && Array.isArray(selectedTemplate.renderers)) {
renderers = selectedTemplate.renderers; renderers = selectedTemplate.renderers;
const titles = renderers.map(renderer => FRAMEWORKS.find(item => item.value === renderer)?.title).join(', '); const titles = renderers.map((renderer) => FRAMEWORKS.find((item) => item.value === renderer)?.title).join(', ');
console.log(green(``) + bold(` Using template's default renderers`) + gray(' ') + titles); console.log(green(``) + bold(` Using template's default renderers`) + gray(' ') + titles);
} }
@ -107,47 +105,57 @@ export async function main() {
} }
// Post-process in parallel // Post-process in parallel
await Promise.all(POSTPROCESS_FILES.map(async (file) => { await Promise.all(
const fileLoc = path.resolve(path.join(cwd, file)); POSTPROCESS_FILES.map(async (file) => {
const fileLoc = path.resolve(path.join(cwd, file));
switch (file) { switch (file) {
case 'CHANGELOG.md': { case 'CHANGELOG.md': {
if (fs.existsSync(fileLoc)) { if (fs.existsSync(fileLoc)) {
await fs.promises.rm(fileLoc); await fs.promises.rm(fileLoc);
} }
break; break;
} }
case 'astro.config.mjs': { case 'astro.config.mjs': {
if (selectedTemplate?.renderers !== true) { if (selectedTemplate?.renderers !== true) {
break;
}
await fs.promises.writeFile(fileLoc, createConfig({ renderers }));
break;
}
case 'package.json': {
const packageJSON = JSON.parse(await fs.promises.readFile(fileLoc, 'utf8'));
delete packageJSON.snowpack; // delete snowpack config only needed in monorepo (can mess up projects)
// Fetch latest versions of selected renderers
const rendererEntries = (await Promise.all(
['astro', ...renderers].map((renderer: string) =>
fetch(`https://registry.npmjs.org/${renderer}/latest`)
.then((res: any) => res.json())
.then((res: any) => [renderer, `^${res['version']}`])
)
)) as any;
packageJSON.devDependencies = { ...(packageJSON.devDependencies ?? {}), ...Object.fromEntries(rendererEntries) };
await fs.promises.writeFile(fileLoc, JSON.stringify(packageJSON, undefined, 2));
break; break;
} }
await fs.promises.writeFile(fileLoc, createConfig({ renderers }));
break;
} }
case 'package.json': { })
const packageJSON = JSON.parse(await fs.promises.readFile(fileLoc, 'utf8')); );
delete packageJSON.snowpack; // delete snowpack config only needed in monorepo (can mess up projects)
// Fetch latest versions of selected renderers
const rendererEntries = await Promise.all(['astro', ...renderers].map((renderer: string) => fetch(`https://registry.npmjs.org/${renderer}/latest`).then((res: any) => res.json()).then((res: any) => [renderer, `^${res['version']}`]))) as any;
packageJSON.devDependencies = { ...packageJSON.devDependencies ?? {}, ...Object.fromEntries(rendererEntries) }
await fs.promises.writeFile(fileLoc, JSON.stringify(packageJSON, undefined, 2));
break;
}
}
}));
// Inject framework components into starter template // Inject framework components into starter template
if (selectedTemplate?.value === 'starter') { if (selectedTemplate?.value === 'starter') {
let importStatements: string[] = []; let importStatements: string[] = [];
let components: string[] = []; let components: string[] = [];
await Promise.all(renderers.map(async renderer => { await Promise.all(
const component = COUNTER_COMPONENTS[renderer as keyof typeof COUNTER_COMPONENTS]; renderers.map(async (renderer) => {
const componentName = path.basename(component.filename, path.extname(component.filename)); const component = COUNTER_COMPONENTS[renderer as keyof typeof COUNTER_COMPONENTS];
const absFileLoc = path.resolve(cwd, component.filename); const componentName = path.basename(component.filename, path.extname(component.filename));
importStatements.push(`import ${componentName} from '${component.filename.replace(/^src/, '..')}';`); const absFileLoc = path.resolve(cwd, component.filename);
components.push(`<${componentName}:visible />`); importStatements.push(`import ${componentName} from '${component.filename.replace(/^src/, '..')}';`);
await fs.promises.writeFile(absFileLoc, component.content); components.push(`<${componentName}:visible />`);
})); await fs.promises.writeFile(absFileLoc, component.content);
})
);
const pageFileLoc = path.resolve(path.join(cwd, 'src', 'pages', 'index.astro')); const pageFileLoc = path.resolve(path.join(cwd, 'src', 'pages', 'index.astro'));
const content = (await fs.promises.readFile(pageFileLoc)).toString(); const content = (await fs.promises.readFile(pageFileLoc)).toString();
@ -162,9 +170,19 @@ export async function main() {
- See https://github.com/snowpackjs/astro/blob/main/docs/core-concepts/component-hydration.md - See https://github.com/snowpackjs/astro/blob/main/docs/core-concepts/component-hydration.md
--> -->
`; `;
lines.splice(41, 0, importStatements.length > 0 ? doc.split('\n').map(ln => `${indent}${ln}`).join('\n') : '', ...components.map(ln => `${indent}${ln}`)); lines.splice(
41,
0,
importStatements.length > 0
? doc
.split('\n')
.map((ln) => `${indent}${ln}`)
.join('\n')
: '',
...components.map((ln) => `${indent}${ln}`)
);
lines.splice(3, 0, importStatements.length > 0 ? `// Framework Component Imports` : '', ...importStatements); lines.splice(3, 0, importStatements.length > 0 ? `// Framework Component Imports` : '', ...importStatements);
await fs.promises.writeFile(pageFileLoc, lines.join('\n')) await fs.promises.writeFile(pageFileLoc, lines.join('\n'));
} }
console.log(bold(green('✔') + ' Done!')); console.log(bold(green('✔') + ' Done!'));

View file

@ -7,16 +7,16 @@ export const TEMPLATES = [
{ {
title: 'Blog', title: 'Blog',
value: 'blog', value: 'blog',
renderers: ['@astrojs/renderer-preact'] renderers: ['@astrojs/renderer-preact'],
}, },
{ {
title: 'Documentation', title: 'Documentation',
value: 'docs', value: 'docs',
renderers: ['@astrojs/renderer-preact'] renderers: ['@astrojs/renderer-preact'],
}, },
{ {
title: 'Portfolio', title: 'Portfolio',
value: 'portfolio', value: 'portfolio',
renderers: ['@astrojs/renderer-preact'] renderers: ['@astrojs/renderer-preact'],
}, },
]; ];

View file

@ -10,12 +10,10 @@ async function run(outdir, template) {
}); });
} }
const testCases = [ const testCases = [['shopify', 'cassidoo/shopify-react-astro']];
['shopify', 'cassidoo/shopify-react-astro']
];
async function tests() { async function tests() {
for(let [dir, tmpl] of testCases) { for (let [dir, tmpl] of testCases) {
await run(dir, tmpl); await run(dir, tmpl);
const outPath = new URL('' + dir, FIXTURES_URL); const outPath = new URL('' + dir, FIXTURES_URL);
@ -23,7 +21,7 @@ async function tests() {
} }
} }
tests().catch(err => { tests().catch((err) => {
console.error(err); console.error(err);
process.exit(1); process.exit(1);
}); });

View file

@ -6,8 +6,4 @@ const GITHUB_SHA = process.env.GITHUB_SHA || execa.sync('git', ['rev-parse', 'HE
const FIXTURES_DIR = path.join(fileURLToPath(path.dirname(import.meta.url)), 'fixtures'); const FIXTURES_DIR = path.join(fileURLToPath(path.dirname(import.meta.url)), 'fixtures');
const FIXTURES_URL = pathToFileURL(FIXTURES_DIR + '/'); const FIXTURES_URL = pathToFileURL(FIXTURES_DIR + '/');
export { export { GITHUB_SHA, FIXTURES_DIR, FIXTURES_URL };
GITHUB_SHA,
FIXTURES_DIR,
FIXTURES_URL
};

View file

@ -28,7 +28,9 @@ export async function renderMarkdownWithFrontmatter(contents: string, opts?: Mar
export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) { export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) {
const { $: { scopedClassName = null } = {}, footnotes: useFootnotes = true, gfm: useGfm = true, remarkPlugins = [], rehypePlugins = [] } = opts ?? {}; const { $: { scopedClassName = null } = {}, footnotes: useFootnotes = true, gfm: useGfm = true, remarkPlugins = [], rehypePlugins = [] } = opts ?? {};
const { headers, rehypeCollectHeaders } = createCollectHeaders(); const { headers, rehypeCollectHeaders } = createCollectHeaders();
let parser = unified().use(markdown).use([remarkExpressions, { addResult: true }]); let parser = unified()
.use(markdown)
.use([remarkExpressions, { addResult: true }]);
if (remarkPlugins.length === 0) { if (remarkPlugins.length === 0) {
if (useGfm) { if (useGfm) {
@ -61,7 +63,12 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
let result: string; let result: string;
try { try {
const vfile = await parser.use(raw).use(rehypeCollectHeaders).use(rehypeCodeBlock()).use(rehypeStringify, { entities: { useNamedReferences: true }}).process(content); const vfile = await parser
.use(raw)
.use(rehypeCollectHeaders)
.use(rehypeCodeBlock())
.use(rehypeStringify, { entities: { useNamedReferences: true } })
.process(content);
result = vfile.contents.toString(); result = vfile.contents.toString();
} catch (err) { } catch (err) {
throw err; throw err;

View file

@ -1,5 +1,5 @@
import unified from "unified"; import unified from 'unified';
import type { Plugin, UnifiedPluginImport } from "./types"; import type { Plugin, UnifiedPluginImport } from './types';
async function importPlugin(p: string | UnifiedPluginImport): UnifiedPluginImport { async function importPlugin(p: string | UnifiedPluginImport): UnifiedPluginImport {
if (typeof p === 'string') { if (typeof p === 'string') {
@ -20,7 +20,7 @@ export function loadPlugins(items: Plugin[]): Promise<[unified.Plugin] | [unifie
} }
return importPlugin(p) return importPlugin(p)
.then((m) => resolve([m.default])) .then((m) => resolve([m.default]))
.catch((e) => reject(e)); .catch((e) => reject(e));
}); });
}); });

View file

@ -1,12 +1,12 @@
import { map } from 'unist-util-map' import { map } from 'unist-util-map';
export default function rehypeExpressions(): any { export default function rehypeExpressions(): any {
return function(node: any): any { return function (node: any): any {
return map(node, (child) => { return map(node, (child) => {
if (child.type === 'mdxTextExpression') { if (child.type === 'mdxTextExpression') {
return { type: 'text', value: `{${child.value}}` } return { type: 'text', value: `{${child.value}}` };
} }
return child; return child;
}) });
} };
} }

View file

@ -1,18 +1,18 @@
import {mdxExpression} from 'micromark-extension-mdx-expression' import { mdxExpression } from 'micromark-extension-mdx-expression';
import {mdxExpressionFromMarkdown, mdxExpressionToMarkdown} from 'mdast-util-mdx-expression' import { mdxExpressionFromMarkdown, mdxExpressionToMarkdown } from 'mdast-util-mdx-expression';
function remarkExpressions(this: any, options: any) { function remarkExpressions(this: any, options: any) {
let settings = options || {} let settings = options || {};
let data = this.data() let data = this.data();
add('micromarkExtensions', mdxExpression({})) add('micromarkExtensions', mdxExpression({}));
add('fromMarkdownExtensions', mdxExpressionFromMarkdown) add('fromMarkdownExtensions', mdxExpressionFromMarkdown);
add('toMarkdownExtensions', mdxExpressionToMarkdown) add('toMarkdownExtensions', mdxExpressionToMarkdown);
function add(field: any, value: any) { function add(field: any, value: any) {
/* istanbul ignore if - other extensions. */ /* istanbul ignore if - other extensions. */
if (data[field]) data[field].push(value) if (data[field]) data[field].push(value);
else data[field] = [value] else data[field] = [value];
} }
} }