summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig11
-rw-r--r--.prettierrc.json11
-rw-r--r--docs/astro.config.mjs18
-rw-r--r--docs/public/code.css34
-rw-r--r--docs/public/index.css366
-rw-r--r--docs/public/make-scrollable-code-focusable.js2
-rw-r--r--docs/public/theme.css182
-rw-r--r--docs/src/components/Card.astro104
-rw-r--r--docs/src/components/Footer/AvatarList.astro257
-rw-r--r--docs/src/components/Footer/Footer.astro12
-rw-r--r--docs/src/components/HeadCommon.astro49
-rw-r--r--docs/src/components/HeadSEO.astro53
-rw-r--r--docs/src/components/Header/AstroLogo.astro52
-rw-r--r--docs/src/components/Header/Header.astro305
-rw-r--r--docs/src/components/Header/LanguageSelect.css76
-rw-r--r--docs/src/components/Header/LanguageSelect.tsx196
-rw-r--r--docs/src/components/Header/Search.css100
-rw-r--r--docs/src/components/Header/Search.tsx154
-rw-r--r--docs/src/components/Header/SidebarToggle.tsx68
-rw-r--r--docs/src/components/Header/SkipToContent.astro39
-rw-r--r--docs/src/components/LeftSidebar/LeftSidebar.astro301
-rw-r--r--docs/src/components/PageContent/PageContent.astro113
-rw-r--r--docs/src/components/RightSidebar/MoreMenu.astro143
-rw-r--r--docs/src/components/RightSidebar/RightSidebar.astro40
-rw-r--r--docs/src/components/RightSidebar/TableOfContents.tsx84
-rw-r--r--docs/src/components/RightSidebar/ThemeToggleButton.css48
-rw-r--r--docs/src/components/RightSidebar/ThemeToggleButton.tsx134
-rw-r--r--docs/src/config.ts374
-rw-r--r--docs/src/layouts/MainLayout.astro220
-rw-r--r--docs/src/layouts/SplashLayout.astro78
-rw-r--r--docs/src/pages/404.astro6
-rw-r--r--docs/src/pages/de/404.astro6
-rw-r--r--docs/src/pages/de/themes.astro83
-rw-r--r--docs/src/pages/es/guides/markdown-content.astro11
-rw-r--r--docs/src/pages/es/guides/pagination.astro11
-rw-r--r--docs/src/pages/es/guides/publish-to-npm.astro11
-rw-r--r--docs/src/pages/es/guides/styling.astro11
-rw-r--r--docs/src/pages/es/reference/renderer-reference.astro15
-rw-r--r--docs/src/pages/index.astro48
-rw-r--r--docs/src/pages/ja/themes.astro85
-rw-r--r--docs/src/pages/themes.astro83
-rw-r--r--docs/src/pages/zh-TW/themes.astro83
-rw-r--r--docs/src/util.ts8
-rw-r--r--examples/blog-multiple-authors/astro.config.mjs4
-rw-r--r--examples/blog-multiple-authors/src/components/MainHead.astro40
-rw-r--r--examples/blog-multiple-authors/src/components/Nav.astro110
-rw-r--r--examples/blog-multiple-authors/src/components/Pagination.astro62
-rw-r--r--examples/blog-multiple-authors/src/components/PostPreview.astro97
-rw-r--r--examples/blog-multiple-authors/src/layouts/post.astro119
-rw-r--r--examples/blog-multiple-authors/src/pages/about.astro110
-rw-r--r--examples/blog-multiple-authors/src/pages/authors/[author].astro110
-rw-r--r--examples/blog-multiple-authors/src/pages/index.astro44
-rw-r--r--examples/blog-multiple-authors/src/pages/posts/[...page].astro108
-rw-r--r--examples/blog-multiple-authors/src/styles/global.css22
-rw-r--r--examples/blog/astro.config.mjs4
-rw-r--r--examples/blog/src/components/Author.astro18
-rw-r--r--examples/blog/src/components/BaseHead.astro9
-rw-r--r--examples/blog/src/components/BlogHeader.astro166
-rw-r--r--examples/blog/src/components/BlogPost.astro130
-rw-r--r--examples/blog/src/components/BlogPostPreview.astro77
-rw-r--r--examples/blog/src/components/Heading.astro8
-rw-r--r--examples/blog/src/components/Logo.astro128
-rw-r--r--examples/blog/src/layouts/BlogPost.astro31
-rw-r--r--examples/blog/src/pages/index.astro97
-rw-r--r--examples/blog/src/styles/blog.css342
-rw-r--r--examples/component/astro.config.mjs4
-rw-r--r--examples/component/demo/src/pages/index.astro45
-rw-r--r--examples/component/packages/my-component/Button.astro16
-rw-r--r--examples/component/packages/my-component/Heading.astro21
-rw-r--r--examples/docs/astro.config.mjs12
-rw-r--r--examples/docs/public/make-scrollable-code-focusable.js2
-rw-r--r--examples/docs/src/components/Footer/AvatarList.astro236
-rw-r--r--examples/docs/src/components/Footer/Footer.astro12
-rw-r--r--examples/docs/src/components/HeadCommon.astro30
-rw-r--r--examples/docs/src/components/HeadSEO.astro41
-rw-r--r--examples/docs/src/components/Header/AstroLogo.astro39
-rw-r--r--examples/docs/src/components/Header/Header.astro227
-rw-r--r--examples/docs/src/components/Header/LanguageSelect.css70
-rw-r--r--examples/docs/src/components/Header/LanguageSelect.tsx58
-rw-r--r--examples/docs/src/components/Header/Search.css102
-rw-r--r--examples/docs/src/components/Header/Search.tsx124
-rw-r--r--examples/docs/src/components/Header/SidebarToggle.tsx34
-rw-r--r--examples/docs/src/components/Header/SkipToContent.astro39
-rw-r--r--examples/docs/src/components/LeftSidebar/LeftSidebar.astro170
-rw-r--r--examples/docs/src/components/PageContent/PageContent.astro66
-rw-r--r--examples/docs/src/components/RightSidebar/MoreMenu.astro120
-rw-r--r--examples/docs/src/components/RightSidebar/RightSidebar.astro38
-rw-r--r--examples/docs/src/components/RightSidebar/TableOfContents.tsx66
-rw-r--r--examples/docs/src/components/RightSidebar/ThemeToggleButton.css46
-rw-r--r--examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx110
-rw-r--r--examples/docs/src/config.ts36
-rw-r--r--examples/docs/src/languages.ts4
-rw-r--r--examples/docs/src/layouts/MainLayout.astro214
-rw-r--r--examples/docs/src/pages/index.astro6
-rw-r--r--examples/docs/src/styles/code.css32
-rw-r--r--examples/docs/src/styles/index.css370
-rw-r--r--examples/docs/src/styles/theme.css176
-rw-r--r--examples/fast-build/astro.config.mjs8
-rw-r--r--examples/fast-build/src/components/Greeting.vue16
-rw-r--r--examples/fast-build/src/pages/index.astro46
-rw-r--r--examples/fast-build/src/styles/global.css2
-rw-r--r--examples/framework-alpine/astro.config.mjs4
-rw-r--r--examples/framework-alpine/src/components/Counter.astro28
-rw-r--r--examples/framework-alpine/src/pages/index.astro61
-rw-r--r--examples/framework-lit/astro.config.mjs4
-rw-r--r--examples/framework-lit/src/components/Counter.js44
-rw-r--r--examples/framework-lit/src/components/Lorem.astro89
-rw-r--r--examples/framework-lit/src/components/Test.js20
-rw-r--r--examples/framework-lit/src/pages/index.astro22
-rw-r--r--examples/framework-multiple/astro.config.mjs4
-rw-r--r--examples/framework-multiple/src/components/A.astro2
-rw-r--r--examples/framework-multiple/src/components/B.astro2
-rw-r--r--examples/framework-multiple/src/components/PreactCounter.tsx26
-rw-r--r--examples/framework-multiple/src/components/PreactSFC.tsx10
-rw-r--r--examples/framework-multiple/src/components/ReactCounter.jsx26
-rw-r--r--examples/framework-multiple/src/components/SolidCounter.tsx26
-rw-r--r--examples/framework-multiple/src/components/VueCounter.vue36
-rw-r--r--examples/framework-multiple/src/pages/index.astro71
-rw-r--r--examples/framework-multiple/src/styles/global.css18
-rw-r--r--examples/framework-preact/astro.config.mjs4
-rw-r--r--examples/framework-preact/src/components/Counter.css12
-rw-r--r--examples/framework-preact/src/components/Counter.tsx26
-rw-r--r--examples/framework-preact/src/pages/index.astro47
-rw-r--r--examples/framework-react/astro.config.mjs4
-rw-r--r--examples/framework-react/src/components/Counter.css12
-rw-r--r--examples/framework-react/src/components/Counter.jsx26
-rw-r--r--examples/framework-react/src/pages/index.astro51
-rw-r--r--examples/framework-solid/astro.config.mjs4
-rw-r--r--examples/framework-solid/src/components/Counter.css12
-rw-r--r--examples/framework-solid/src/components/Counter.jsx26
-rw-r--r--examples/framework-solid/src/pages/index.astro45
-rw-r--r--examples/framework-svelte/astro.config.mjs4
-rw-r--r--examples/framework-svelte/src/pages/index.astro48
-rw-r--r--examples/framework-vue/astro.config.mjs4
-rw-r--r--examples/framework-vue/src/components/Counter.vue48
-rw-r--r--examples/framework-vue/src/pages/index.astro48
-rw-r--r--examples/minimal/astro.config.mjs4
-rw-r--r--examples/minimal/src/pages/index.astro28
-rw-r--r--examples/portfolio-svelte/astro.config.mjs4
-rw-r--r--examples/portfolio-svelte/src/components/MainHead.astro12
-rw-r--r--examples/portfolio-svelte/src/layouts/project.astro153
-rw-r--r--examples/portfolio-svelte/src/pages/404.astro23
-rw-r--r--examples/portfolio-svelte/src/pages/about.astro99
-rw-r--r--examples/portfolio-svelte/src/pages/index.astro453
-rw-r--r--examples/portfolio-svelte/src/pages/projects.astro46
-rw-r--r--examples/portfolio-svelte/src/styles/global.scss224
-rw-r--r--examples/portfolio/astro.config.mjs4
-rw-r--r--examples/portfolio/src/components/Button/index.jsx2
-rw-r--r--examples/portfolio/src/components/Button/styles.module.scss10
-rw-r--r--examples/portfolio/src/components/Footer/index.jsx12
-rw-r--r--examples/portfolio/src/components/Footer/styles.module.scss20
-rw-r--r--examples/portfolio/src/components/MainHead.astro12
-rw-r--r--examples/portfolio/src/components/Nav/index.jsx58
-rw-r--r--examples/portfolio/src/components/Nav/styles.module.scss90
-rw-r--r--examples/portfolio/src/components/PortfolioPreview/index.jsx42
-rw-r--r--examples/portfolio/src/components/PortfolioPreview/styles.module.scss146
-rw-r--r--examples/portfolio/src/layouts/project.astro153
-rw-r--r--examples/portfolio/src/pages/404.astro23
-rw-r--r--examples/portfolio/src/pages/about.astro101
-rw-r--r--examples/portfolio/src/pages/index.astro442
-rw-r--r--examples/portfolio/src/pages/projects.astro46
-rw-r--r--examples/portfolio/src/styles/global.scss224
-rw-r--r--examples/starter/astro.config.mjs8
-rw-r--r--examples/starter/src/components/Tour.astro130
-rw-r--r--examples/starter/src/pages/index.astro76
-rw-r--r--examples/starter/src/styles/global.css34
-rw-r--r--examples/starter/src/styles/home.css50
-rw-r--r--examples/subpath/astro.config.mjs10
-rw-r--r--examples/subpath/src/components/Time.jsx6
-rw-r--r--examples/subpath/src/pages/index.astro44
-rw-r--r--examples/subpath/src/styles/main.scss6
-rw-r--r--examples/with-markdown-plugins/astro.config.mjs30
-rw-r--r--examples/with-markdown-plugins/src/layouts/main.astro56
-rw-r--r--examples/with-markdown-plugins/src/pages/about.astro22
-rw-r--r--examples/with-markdown-plugins/src/styles/global.css54
-rw-r--r--examples/with-markdown/astro.config.mjs8
-rw-r--r--examples/with-markdown/src/components/PreactCounter.tsx26
-rw-r--r--examples/with-markdown/src/components/ReactCounter.jsx26
-rw-r--r--examples/with-markdown/src/components/VueCounter.vue36
-rw-r--r--examples/with-markdown/src/layouts/main.astro20
-rw-r--r--examples/with-markdown/src/pages/external.astro16
-rw-r--r--examples/with-markdown/src/pages/index.astro52
-rw-r--r--examples/with-markdown/src/styles/global.css162
-rw-r--r--examples/with-nanostores/astro.config.mjs4
-rw-r--r--examples/with-nanostores/src/components/AdminsPreact.jsx38
-rw-r--r--examples/with-nanostores/src/components/AdminsReact.jsx38
-rw-r--r--examples/with-nanostores/src/components/AdminsSolid.jsx38
-rw-r--r--examples/with-nanostores/src/components/AdminsVue.vue40
-rw-r--r--examples/with-nanostores/src/pages/index.astro67
-rw-r--r--examples/with-nanostores/src/store/counter.js4
-rw-r--r--examples/with-nanostores/src/store/users.js38
-rw-r--r--examples/with-nanostores/src/styles/global.css34
-rw-r--r--examples/with-nanostores/src/styles/home.css34
-rw-r--r--examples/with-tailwindcss/astro.config.mjs4
-rw-r--r--examples/with-tailwindcss/postcss.config.js12
-rw-r--r--examples/with-tailwindcss/src/components/Button.astro8
-rw-r--r--examples/with-tailwindcss/src/pages/index.astro19
-rw-r--r--examples/with-tailwindcss/tailwind.config.js2
-rw-r--r--examples/with-vite-plugin-pwa/astro.config.mjs6
-rw-r--r--examples/with-vite-plugin-pwa/src/index.ts8
-rw-r--r--examples/with-vite-plugin-pwa/src/pages/index.astro22
-rw-r--r--examples/with-vite-plugin-pwa/src/vite-env.d.ts16
-rw-r--r--package.json1
-rw-r--r--packages/astro-parser/src/Stats.ts124
-rw-r--r--packages/astro-parser/src/interfaces.ts184
-rw-r--r--packages/astro-parser/src/parse/index.ts474
-rw-r--r--packages/astro-parser/src/parse/read/context.ts108
-rw-r--r--packages/astro-parser/src/parse/read/expression.ts422
-rw-r--r--packages/astro-parser/src/parse/read/script.ts94
-rw-r--r--packages/astro-parser/src/parse/read/style.ts54
-rw-r--r--packages/astro-parser/src/parse/state/codefence.ts48
-rw-r--r--packages/astro-parser/src/parse/state/codespan.ts40
-rw-r--r--packages/astro-parser/src/parse/state/fragment.ts36
-rw-r--r--packages/astro-parser/src/parse/state/mustache.ts784
-rw-r--r--packages/astro-parser/src/parse/state/setup.ts56
-rw-r--r--packages/astro-parser/src/parse/state/tag.ts902
-rw-r--r--packages/astro-parser/src/parse/state/text.ts40
-rw-r--r--packages/astro-parser/src/parse/utils/bracket.ts18
-rw-r--r--packages/astro-parser/src/parse/utils/entities.ts4062
-rw-r--r--packages/astro-parser/src/parse/utils/html.ts162
-rw-r--r--packages/astro-parser/src/parse/utils/node.ts52
-rw-r--r--packages/astro-parser/src/utils/error.ts56
-rw-r--r--packages/astro-parser/src/utils/full_char_code_at.ts8
-rw-r--r--packages/astro-parser/src/utils/fuzzymatch.ts392
-rw-r--r--packages/astro-parser/src/utils/get_code_frame.ts34
-rw-r--r--packages/astro-parser/src/utils/link.ts4
-rw-r--r--packages/astro-parser/src/utils/list.ts4
-rw-r--r--packages/astro-parser/src/utils/names.ts234
-rw-r--r--packages/astro-parser/src/utils/nodes_match.ts44
-rw-r--r--packages/astro-prism/index.mjs336
-rwxr-xr-xpackages/astro/astro.js126
-rw-r--r--packages/astro/components/Code.astro68
-rw-r--r--packages/astro/components/Debug.astro87
-rw-r--r--packages/astro/components/Markdown.astro35
-rw-r--r--packages/astro/components/Prism.astro43
-rw-r--r--packages/astro/src/@types/astro.ts464
-rw-r--r--packages/astro/src/@types/shorthash.d.ts4
-rw-r--r--packages/astro/src/cli/check.ts166
-rw-r--r--packages/astro/src/cli/index.ts246
-rw-r--r--packages/astro/src/core/build/index.ts266
-rw-r--r--packages/astro/src/core/build/internal.ts58
-rw-r--r--packages/astro/src/core/build/page-data.ts188
-rw-r--r--packages/astro/src/core/build/scan-based-build.ts98
-rw-r--r--packages/astro/src/core/build/static-build.ts292
-rw-r--r--packages/astro/src/core/build/types.d.ts6
-rw-r--r--packages/astro/src/core/config.ts228
-rw-r--r--packages/astro/src/core/create-vite.ts134
-rw-r--r--packages/astro/src/core/dev/index.ts720
-rw-r--r--packages/astro/src/core/dev/messages.ts22
-rw-r--r--packages/astro/src/core/dev/template/4xx.ts42
-rw-r--r--packages/astro/src/core/dev/template/5xx.ts28
-rw-r--r--packages/astro/src/core/dev/util.ts10
-rw-r--r--packages/astro/src/core/logger.ts216
-rw-r--r--packages/astro/src/core/preview/index.ts126
-rw-r--r--packages/astro/src/core/ssr/css.ts50
-rw-r--r--packages/astro/src/core/ssr/html.ts140
-rw-r--r--packages/astro/src/core/ssr/index.ts708
-rw-r--r--packages/astro/src/core/ssr/paginate.ts96
-rw-r--r--packages/astro/src/core/ssr/routing.ts552
-rw-r--r--packages/astro/src/core/ssr/rss.ts130
-rw-r--r--packages/astro/src/core/ssr/sitemap.ts24
-rw-r--r--packages/astro/src/core/util.ts98
-rw-r--r--packages/astro/src/runtime/client/hmr.ts60
-rw-r--r--packages/astro/src/runtime/client/idle.ts26
-rw-r--r--packages/astro/src/runtime/client/load.ts12
-rw-r--r--packages/astro/src/runtime/client/media.ts32
-rw-r--r--packages/astro/src/runtime/client/only.ts12
-rw-r--r--packages/astro/src/runtime/client/visible.ts46
-rw-r--r--packages/astro/src/runtime/server/hydration.ts176
-rw-r--r--packages/astro/src/runtime/server/index.ts622
-rw-r--r--packages/astro/src/runtime/server/metadata.ts146
-rw-r--r--packages/astro/src/runtime/server/util.ts46
-rw-r--r--packages/astro/src/vite-plugin-astro-postprocess/index.ts116
-rw-r--r--packages/astro/src/vite-plugin-astro/compile.ts156
-rw-r--r--packages/astro/src/vite-plugin-astro/index.ts190
-rw-r--r--packages/astro/src/vite-plugin-astro/query.ts52
-rw-r--r--packages/astro/src/vite-plugin-astro/styles.ts26
-rw-r--r--packages/astro/src/vite-plugin-build-css/index.ts292
-rw-r--r--packages/astro/src/vite-plugin-build-css/resolve.ts22
-rw-r--r--packages/astro/src/vite-plugin-build-html/add-rollup-input.ts64
-rw-r--r--packages/astro/src/vite-plugin-build-html/extract-assets.ts302
-rw-r--r--packages/astro/src/vite-plugin-build-html/index.ts928
-rw-r--r--packages/astro/src/vite-plugin-build-html/util.ts46
-rw-r--r--packages/astro/src/vite-plugin-config-alias/index.ts136
-rw-r--r--packages/astro/src/vite-plugin-fetch/index.ts122
-rw-r--r--packages/astro/src/vite-plugin-jsx/index.ts336
-rw-r--r--packages/astro/src/vite-plugin-markdown/index.ts100
-rw-r--r--packages/astro/test/0-css.test.js568
-rw-r--r--packages/astro/test/astro-assets.test.js100
-rw-r--r--packages/astro/test/astro-attrs.test.js102
-rw-r--r--packages/astro/test/astro-basic.test.js232
-rw-r--r--packages/astro/test/astro-children.test.js106
-rw-r--r--packages/astro/test/astro-class-list.test.js46
-rw-r--r--packages/astro/test/astro-client-only.test.js40
-rw-r--r--packages/astro/test/astro-component-code.test.js136
-rw-r--r--packages/astro/test/astro-css-bundling-import.test.js88
-rw-r--r--packages/astro/test/astro-css-bundling-nested-layouts.test.js64
-rw-r--r--packages/astro/test/astro-css-bundling.test.js94
-rw-r--r--packages/astro/test/astro-doctype.test.js94
-rw-r--r--packages/astro/test/astro-dynamic.test.js102
-rw-r--r--packages/astro/test/astro-envs.test.js78
-rw-r--r--packages/astro/test/astro-expr.test.js144
-rw-r--r--packages/astro/test/astro-fallback.test.js26
-rw-r--r--packages/astro/test/astro-get-static-paths.test.js30
-rw-r--r--packages/astro/test/astro-global.test.js102
-rw-r--r--packages/astro/test/astro-jsx.test.js54
-rw-r--r--packages/astro/test/astro-markdown-plugins.test.js86
-rw-r--r--packages/astro/test/astro-markdown.test.js218
-rw-r--r--packages/astro/test/astro-pageDirectoryUrl.test.js30
-rw-r--r--packages/astro/test/astro-pages.test.js20
-rw-r--r--packages/astro/test/astro-pagination.test.js68
-rw-r--r--packages/astro/test/astro-partial-html.test.js54
-rw-r--r--packages/astro/test/astro-public.test.js22
-rw-r--r--packages/astro/test/astro-scripts.test.js118
-rw-r--r--packages/astro/test/astro-sitemap-rss.test.js54
-rw-r--r--packages/astro/test/astro-slots.test.js218
-rw-r--r--packages/astro/test/benchmark/benchmark.js106
-rw-r--r--packages/astro/test/benchmark/build.bench.js86
-rw-r--r--packages/astro/test/benchmark/dev.bench.js82
-rw-r--r--packages/astro/test/builtins.test.js26
-rw-r--r--packages/astro/test/cli.test.js52
-rw-r--r--packages/astro/test/config-validate.test.js66
-rw-r--r--packages/astro/test/config.test.js80
-rw-r--r--packages/astro/test/custom-elements.test.js106
-rw-r--r--packages/astro/test/debug-component.test.js32
-rw-r--r--packages/astro/test/dev-routing.test.js256
-rw-r--r--packages/astro/test/errors.test.js252
-rw-r--r--packages/astro/test/fetch.test.js56
-rw-r--r--packages/astro/test/fixtures/astro-markdown-plugins/src/pages/astro.astro6
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/braces.astro9
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/code.astro8
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro12
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro30
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/external.astro12
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/post.astro6
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro22
-rw-r--r--packages/astro/test/lit-element.test.js106
-rw-r--r--packages/astro/test/markdown.test.js40
-rw-r--r--packages/astro/test/postcss.test.js80
-rw-r--r--packages/astro/test/preact-component.test.js152
-rw-r--r--packages/astro/test/react-component.test.js240
-rw-r--r--packages/astro/test/route-manifest.test.js458
-rw-r--r--packages/astro/test/sass.test.js30
-rw-r--r--packages/astro/test/slots-preact.test.js30
-rw-r--r--packages/astro/test/slots-react.test.js30
-rw-r--r--packages/astro/test/slots-solid.test.js30
-rw-r--r--packages/astro/test/slots-svelte.test.js30
-rw-r--r--packages/astro/test/slots-vue.test.js30
-rw-r--r--packages/astro/test/solid-component.test.js96
-rw-r--r--packages/astro/test/svelte-component.test.js98
-rw-r--r--packages/astro/test/test-utils.js100
-rw-r--r--packages/astro/test/vue-component.test.js98
-rwxr-xr-xpackages/create-astro/create-astro.mjs6
-rw-r--r--packages/create-astro/src/config.ts16
-rw-r--r--packages/create-astro/src/frameworks.ts80
-rw-r--r--packages/create-astro/src/index.ts352
-rw-r--r--packages/create-astro/src/templates.ts60
-rw-r--r--packages/create-astro/test/create-astro.test.js222
-rw-r--r--packages/create-astro/test/external.test.js22
-rw-r--r--packages/markdown/remark/src/index.ts120
-rw-r--r--packages/markdown/remark/src/load-plugins.ts34
-rw-r--r--packages/markdown/remark/src/rehype-collect-headers.ts50
-rw-r--r--packages/markdown/remark/src/rehype-escape.ts16
-rw-r--r--packages/markdown/remark/src/rehype-expressions.ts16
-rw-r--r--packages/markdown/remark/src/rehype-islands.ts46
-rw-r--r--packages/markdown/remark/src/rehype-jsx.ts48
-rw-r--r--packages/markdown/remark/src/remark-expressions.ts38
-rw-r--r--packages/markdown/remark/src/remark-jsx.ts38
-rw-r--r--packages/markdown/remark/src/remark-prism.ts84
-rw-r--r--packages/markdown/remark/src/remark-scoped-styles.ts20
-rw-r--r--packages/markdown/remark/src/remark-slug.ts28
-rw-r--r--packages/markdown/remark/src/remark-unwrap.ts50
-rw-r--r--packages/markdown/remark/src/types.ts14
-rw-r--r--packages/renderers/renderer-lit/client-shim.js6
-rw-r--r--packages/renderers/renderer-lit/index.js52
-rw-r--r--packages/renderers/renderer-lit/server.js86
-rw-r--r--packages/renderers/renderer-preact/compat/index.js58
-rw-r--r--packages/renderers/renderer-preact/index.js46
-rw-r--r--packages/renderers/renderer-preact/server.js38
-rw-r--r--packages/renderers/renderer-preact/static-html.js4
-rw-r--r--packages/renderers/renderer-react/client.js16
-rw-r--r--packages/renderers/renderer-react/index.js52
-rw-r--r--packages/renderers/renderer-react/server.js90
-rw-r--r--packages/renderers/renderer-react/static-html.js4
-rw-r--r--packages/renderers/renderer-solid/client.js18
-rw-r--r--packages/renderers/renderer-solid/index.js100
-rw-r--r--packages/renderers/renderer-solid/server.js36
-rw-r--r--packages/renderers/renderer-solid/static-html.js4
-rw-r--r--packages/renderers/renderer-svelte/Wrapper.svelte.ssr.js12
-rw-r--r--packages/renderers/renderer-svelte/client.js20
-rw-r--r--packages/renderers/renderer-svelte/index.js54
-rw-r--r--packages/renderers/renderer-svelte/server.js10
-rw-r--r--packages/renderers/renderer-vue/client.js18
-rw-r--r--packages/renderers/renderer-vue/index.js30
-rw-r--r--packages/renderers/renderer-vue/server.js20
-rw-r--r--packages/renderers/renderer-vue/static-html.js14
-rw-r--r--scripts/cmd/build.js106
-rw-r--r--scripts/cmd/copy.js124
-rwxr-xr-xscripts/index.js28
-rwxr-xr-xscripts/notify/index.js106
-rw-r--r--scripts/smoke/index.js36
-rw-r--r--scripts/stats/index.js124
-rw-r--r--scripts/utils/svelte-plugin.js90
-rw-r--r--www/astro.config.mjs8
-rw-r--r--www/src/components/Article.astro126
-rw-r--r--www/src/components/ArticleFooter.astro12
-rw-r--r--www/src/components/Author.astro5
-rw-r--r--www/src/components/AvatarList.astro126
-rw-r--r--www/src/components/BackArrow.astro20
-rw-r--r--www/src/components/BaseHead.astro14
-rw-r--r--www/src/components/BlockQuote.astro56
-rw-r--r--www/src/components/BlogHead.astro5
-rw-r--r--www/src/components/BlogHeader.astro217
-rw-r--r--www/src/components/BlogPost.astro186
-rw-r--r--www/src/components/BlogPostPreview.astro87
-rw-r--r--www/src/components/GithubStarButton.astro7
-rw-r--r--www/src/components/GoogleAnalytics.astro10
-rw-r--r--www/src/components/Logo.astro128
-rw-r--r--www/src/components/Main.astro24
-rw-r--r--www/src/components/MainHeader.astro270
-rw-r--r--www/src/components/Note.astro66
-rw-r--r--www/src/components/Planets.astro176
-rw-r--r--www/src/components/Shell.astro28
-rw-r--r--www/src/components/Space.astro32
-rw-r--r--www/src/components/Sponsors.astro72
-rw-r--r--www/src/components/Stars.astro245
-rw-r--r--www/src/components/Tagline.astro32
-rw-r--r--www/src/components/YouTube.astro5
-rw-r--r--www/src/components/YouTube.css88
-rw-r--r--www/src/config.ts18
-rw-r--r--www/src/layouts/Blog.astro401
-rw-r--r--www/src/pages/404.astro20
-rw-r--r--www/src/pages/blog/[slug].astro32
-rw-r--r--www/src/pages/blog/index.astro126
-rw-r--r--www/src/pages/index.astro159
-rw-r--r--www/src/scss/blog.scss322
-rw-r--r--www/src/scss/code.scss80
-rw-r--r--www/src/scss/error.scss162
-rw-r--r--www/src/scss/fonts.scss40
-rw-r--r--www/src/scss/global.scss134
440 files changed, 22124 insertions, 21742 deletions
diff --git a/.editorconfig b/.editorconfig
index 58d264c1c..0bb4ad6ec 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,9 +4,12 @@
root = true
[*]
-indent_style = space
-indent_size = 2
-end_of_line = lf
charset = utf-8
-trim_trailing_whitespace = false
+end_of_line = lf
+indent_size = 2
+indent_style = tab
insert_final_newline = true
+trim_trailing_whitespace = false
+
+[{.*,*.md,*.json,*.toml,*.yml,}]
+indent_style = space
diff --git a/.prettierrc.json b/.prettierrc.json
index 3da31c1ab..1daf43285 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -3,5 +3,14 @@
"semi": true,
"singleQuote": true,
"tabWidth": 2,
- "trailingComma": "es5"
+ "trailingComma": "es5",
+ "useTabs": true,
+ "overrides": [
+ {
+ "files": [".*", "*.json", "*.md", "*.toml", "*.yml"],
+ "options": {
+ "useTabs": false
+ }
+ }
+ ]
}
diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs
index a5299726c..94290bf17 100644
--- a/docs/astro.config.mjs
+++ b/docs/astro.config.mjs
@@ -1,12 +1,12 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- buildOptions: {
- site: 'https://docs.astro.build/',
- },
- renderers: [
- // Our main renderer for frontend components
- '@astrojs/renderer-preact',
- // Needed for Algolia search component
- '@astrojs/renderer-react',
- ],
+ buildOptions: {
+ site: 'https://docs.astro.build/',
+ },
+ renderers: [
+ // Our main renderer for frontend components
+ '@astrojs/renderer-preact',
+ // Needed for Algolia search component
+ '@astrojs/renderer-react',
+ ],
});
diff --git a/docs/public/code.css b/docs/public/code.css
index 0543b8416..3cb74322f 100644
--- a/docs/public/code.css
+++ b/docs/public/code.css
@@ -1,34 +1,34 @@
.language-css > code,
.language-sass > code,
.language-scss > code {
- color: #fd9170;
+ color: #fd9170;
}
.language-diff .token.prefix.deleted,
.language-diff .token.prefix.inserted {
- user-select: none;
+ user-select: none;
}
[class*='language-'] .namespace {
- opacity: 0.7;
+ opacity: 0.7;
}
.token.plain-text,
[class*='language-bash'] span.token,
[class*='language-shell'] span.token {
- color: hsla(var(--color-gray-90), 1);
+ color: hsla(var(--color-gray-90), 1);
}
[class*='language-bash'] span.token,
[class*='language-shell'] span.token {
- font-style: bold;
+ font-style: bold;
}
.token.prolog,
.token.comment,
[class*='language-bash'] span.token.comment,
[class*='language-shell'] span.token.comment {
- color: hsla(var(--color-gray-70), 1);
+ color: hsla(var(--color-gray-70), 1);
}
.token.selector,
@@ -38,7 +38,7 @@
.token.variable,
.token.entity,
.token.deleted {
- color: #fa5e5b;
+ color: #fa5e5b;
}
.token.boolean,
@@ -51,7 +51,7 @@
.token.hexcode,
.token.class-name,
.token.attr-name {
- color: hsla(var(--color-yellow), 1);
+ color: hsla(var(--color-yellow), 1);
}
.token.atrule,
@@ -61,41 +61,41 @@
.token.pseudo-class,
.token.pseudo-element,
.token.string {
- color: hsla(var(--color-green), 1);
+ color: hsla(var(--color-green), 1);
}
.token.symbol,
.token.function,
.token.id,
.token.important {
- color: hsla(var(--color-blue), 1);
+ color: hsla(var(--color-blue), 1);
}
.token.important,
.token.id {
- font-weight: bold;
+ font-weight: bold;
}
.token.cdata,
.token.char,
.token.property {
- color: #23b1af;
+ color: #23b1af;
}
.token.inserted {
- color: hsla(var(--color-green), 1);
+ color: hsla(var(--color-green), 1);
}
.token.keyword {
- color: #ff657c;
- font-style: italic;
+ color: #ff657c;
+ font-style: italic;
}
.token.operator {
- color: hsla(var(--color-gray-70), 1);
+ color: hsla(var(--color-gray-70), 1);
}
.token.attr-value .token.attr-equals,
.token.punctuation {
- color: hsla(var(--color-gray-80), 1);
+ color: hsla(var(--color-gray-80), 1);
}
diff --git a/docs/public/index.css b/docs/public/index.css
index e8c8759b5..25bc0e16e 100644
--- a/docs/public/index.css
+++ b/docs/public/index.css
@@ -1,46 +1,46 @@
* {
- box-sizing: border-box;
- margin: 0;
+ box-sizing: border-box;
+ margin: 0;
}
/* Global focus outline reset */
*:focus:not(:focus-visible) {
- outline: none;
+ outline: none;
}
:root {
- --user-font-scale: 1rem - 16px;
- --max-width: calc(100% - 1rem);
+ --user-font-scale: 1rem - 16px;
+ --max-width: calc(100% - 1rem);
}
@media (min-width: 50em) {
- :root {
- --max-width: 46em;
- }
+ :root {
+ --max-width: 46em;
+ }
}
body {
- display: flex;
- flex-direction: column;
- min-height: 100vh;
- font-family: var(--font-body);
- font-size: 1rem;
- font-size: clamp(0.9rem, 0.75rem + 0.375vw + var(--user-font-scale), 1rem);
- line-height: 1.5;
- max-width: 100vw;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ font-family: var(--font-body);
+ font-size: 1rem;
+ font-size: clamp(0.9rem, 0.75rem + 0.375vw + var(--user-font-scale), 1rem);
+ line-height: 1.5;
+ max-width: 100vw;
}
nav ul {
- list-style: none;
- padding: 0;
+ list-style: none;
+ padding: 0;
}
.content > section > * + * {
- margin-top: 1.25rem;
+ margin-top: 1.25rem;
}
.content > section > :first-child {
- margin-top: 0;
+ margin-top: 0;
}
/* Typography */
@@ -50,335 +50,335 @@ h3,
h4,
h5,
h6 {
- margin-bottom: 1rem;
- font-weight: bold;
- line-height: 1;
+ margin-bottom: 1rem;
+ font-weight: bold;
+ line-height: 1;
}
h1,
h2 {
- max-width: 40ch;
+ max-width: 40ch;
}
:is(h2, h3):not(:first-child) {
- margin-top: 3rem;
+ margin-top: 3rem;
}
:is(h4, h5, h6):not(:first-child) {
- margin-top: 2rem;
+ margin-top: 2rem;
}
h1 {
- font-size: 3.25rem;
- font-weight: 800;
+ font-size: 3.25rem;
+ font-weight: 800;
}
h2 {
- font-size: 2.5rem;
+ font-size: 2.5rem;
}
h3 {
- font-size: 1.75rem;
+ font-size: 1.75rem;
}
h4 {
- font-size: 1.3rem;
+ font-size: 1.3rem;
}
h5 {
- font-size: 1rem;
+ font-size: 1rem;
}
p,
.content ul {
- line-height: 1.65em;
+ line-height: 1.65em;
}
p,
.content ul {
- color: var(--theme-text-light);
+ color: var(--theme-text-light);
}
small,
.text_small {
- font-size: 0.833rem;
+ font-size: 0.833rem;
}
a {
- color: var(--theme-text-accent);
- text-underline-offset: 0.08em;
- align-items: center;
- gap: 0.5rem;
+ color: var(--theme-text-accent);
+ text-underline-offset: 0.08em;
+ align-items: center;
+ gap: 0.5rem;
}
article > section :is(ul, ol) > * + * {
- margin-top: 0.25rem;
+ margin-top: 0.25rem;
}
article > section nav :is(ul, ol) > * + * {
- margin-top: inherit;
+ margin-top: inherit;
}
article > section li > :is(p, pre, blockquote):not(:first-child) {
- margin-top: 1rem;
+ margin-top: 1rem;
}
article > section :is(ul, ol) {
- padding-left: 1em;
+ padding-left: 1em;
}
article > section nav :is(ul, ol) {
- padding-left: inherit;
+ padding-left: inherit;
}
article > section nav {
- margin-top: 1rem;
- margin-bottom: 2rem;
+ margin-top: 1rem;
+ margin-bottom: 2rem;
}
article > section ::marker {
- font-weight: bold;
- color: var(--theme-text-light);
+ font-weight: bold;
+ color: var(--theme-text-light);
}
article > section iframe {
- width: 100%;
- height: auto;
- aspect-ratio: 16 / 9;
+ width: 100%;
+ height: auto;
+ aspect-ratio: 16 / 9;
}
a > code:not([class*='language']) {
- position: relative;
- color: var(--theme-text-accent);
- background: transparent;
- text-underline-offset: var(--padding-block);
+ position: relative;
+ color: var(--theme-text-accent);
+ background: transparent;
+ text-underline-offset: var(--padding-block);
}
a > code:not([class*='language'])::before {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- display: block;
- background: var(--theme-accent);
- opacity: var(--theme-accent-opacity);
- border-radius: var(--border-radius);
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ background: var(--theme-accent);
+ opacity: var(--theme-accent-opacity);
+ border-radius: var(--border-radius);
}
a:hover,
a:focus {
- text-decoration: underline;
+ text-decoration: underline;
}
a:focus {
- outline: 2px solid currentColor;
- outline-offset: 0.25em;
+ outline: 2px solid currentColor;
+ outline-offset: 0.25em;
}
strong {
- font-weight: 600;
- color: inherit;
+ font-weight: 600;
+ color: inherit;
}
/* Supporting Content */
code {
- font-family: var(--font-mono);
- font-size: 0.85em;
+ font-family: var(--font-mono);
+ font-size: 0.85em;
}
code:not([class*='language']) {
- --border-radius: 3px;
- --padding-block: 0.2rem;
- --padding-inline: 0.4rem;
- color: var(--theme-code-inline-text);
- background-color: var(--theme-code-inline-bg);
- padding: var(--padding-block) var(--padding-inline);
- margin: calc(var(--padding-block) * -1) -0.125em;
- border-radius: var(--border-radius);
- box-shadow: 0 2px 1px 0 rgba(0, 0, 0, 0.08);
- word-break: break-word;
+ --border-radius: 3px;
+ --padding-block: 0.2rem;
+ --padding-inline: 0.4rem;
+ color: var(--theme-code-inline-text);
+ background-color: var(--theme-code-inline-bg);
+ padding: var(--padding-block) var(--padding-inline);
+ margin: calc(var(--padding-block) * -1) -0.125em;
+ border-radius: var(--border-radius);
+ box-shadow: 0 2px 1px 0 rgba(0, 0, 0, 0.08);
+ word-break: break-word;
}
pre > code:not([class*='language']) {
- background-color: transparent;
- padding: 0;
- margin: 0;
- border-radius: 0;
- color: inherit;
+ background-color: transparent;
+ padding: 0;
+ margin: 0;
+ border-radius: 0;
+ color: inherit;
}
pre > code {
- font-size: 1em;
+ font-size: 1em;
}
table,
pre {
- position: relative;
- --padding-block: 1rem;
- --padding-inline: 2rem;
- padding: var(--padding-block) var(--padding-inline);
- padding-right: calc(var(--padding-inline) * 2);
- margin-left: calc(var(--padding-inline) * -1);
- margin-right: calc(var(--padding-inline) * -1);
- font-family: var(--font-mono);
+ position: relative;
+ --padding-block: 1rem;
+ --padding-inline: 2rem;
+ padding: var(--padding-block) var(--padding-inline);
+ padding-right: calc(var(--padding-inline) * 2);
+ margin-left: calc(var(--padding-inline) * -1);
+ margin-right: calc(var(--padding-inline) * -1);
+ font-family: var(--font-mono);
- line-height: 1.5;
- font-size: 0.85em;
- overflow-y: hidden;
- overflow-x: auto;
+ line-height: 1.5;
+ font-size: 0.85em;
+ overflow-y: hidden;
+ overflow-x: auto;
}
table {
- width: 100%;
- padding: var(--padding-block) 0;
- margin: 0;
- border-collapse: collapse;
+ width: 100%;
+ padding: var(--padding-block) 0;
+ margin: 0;
+ border-collapse: collapse;
}
/* Zebra striping */
tr:nth-of-type(odd) {
- background: var(--theme-bg-hover);
+ background: var(--theme-bg-hover);
}
th {
- background: var(--color-black);
- color: var(--theme-color);
- font-weight: bold;
+ background: var(--color-black);
+ color: var(--theme-color);
+ font-weight: bold;
}
td,
th {
- padding: 6px;
- text-align: left;
+ padding: 6px;
+ text-align: left;
}
pre {
- background-color: var(--theme-code-bg);
- color: var(--theme-code-text);
+ background-color: var(--theme-code-bg);
+ color: var(--theme-code-text);
}
blockquote code:not([class*='language']) {
- background-color: var(--theme-bg);
+ background-color: var(--theme-bg);
}
@media (min-width: 37.75em) {
- pre {
- --padding-inline: 1.25rem;
- border-radius: 8px;
- margin-left: 0;
- margin-right: 0;
- }
+ pre {
+ --padding-inline: 1.25rem;
+ border-radius: 8px;
+ margin-left: 0;
+ margin-right: 0;
+ }
}
blockquote {
- margin: 2rem 0;
- padding: 1.25em 1.5rem;
- border-left: 3px solid var(--theme-text-light);
- background-color: var(--theme-bg-offset);
- border-radius: 0 0.25rem 0.25rem 0;
- line-height: 1.7;
+ margin: 2rem 0;
+ padding: 1.25em 1.5rem;
+ border-left: 3px solid var(--theme-text-light);
+ background-color: var(--theme-bg-offset);
+ border-radius: 0 0.25rem 0.25rem 0;
+ line-height: 1.7;
}
img {
- max-width: 100%;
+ max-width: 100%;
}
.flex {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
}
button {
- display: flex;
- align-items: center;
- justify-items: center;
- gap: 0.25em;
- padding: 0.33em 0.67em;
- border: 0;
- background: var(--theme-bg);
- display: flex;
- font-size: 1rem;
- align-items: center;
- gap: 0.25em;
- border-radius: 99em;
- color: var(--theme-text);
- background-color: var(--theme-bg);
+ display: flex;
+ align-items: center;
+ justify-items: center;
+ gap: 0.25em;
+ padding: 0.33em 0.67em;
+ border: 0;
+ background: var(--theme-bg);
+ display: flex;
+ font-size: 1rem;
+ align-items: center;
+ gap: 0.25em;
+ border-radius: 99em;
+ color: var(--theme-text);
+ background-color: var(--theme-bg);
}
h2.heading {
- font-size: 1rem;
- font-weight: 700;
- padding: 0.1rem 1rem;
- text-transform: uppercase;
- margin-bottom: 0.5rem;
+ font-size: 1rem;
+ font-weight: 700;
+ padding: 0.1rem 1rem;
+ text-transform: uppercase;
+ margin-bottom: 0.5rem;
}
.header-link {
- font-size: 1rem;
- padding: 0.1rem 0 0.1rem 1rem;
- border-left: 4px solid var(--theme-divider);
+ font-size: 1rem;
+ padding: 0.1rem 0 0.1rem 1rem;
+ border-left: 4px solid var(--theme-divider);
}
.header-link:hover,
.header-link:focus {
- border-left-color: var(--theme-accent);
- color: var(--theme-accent);
+ border-left-color: var(--theme-accent);
+ color: var(--theme-accent);
}
.header-link:focus-within {
- color: var(--theme-text-light);
- border-left-color: hsla(var(--color-gray-40), 1);
+ color: var(--theme-text-light);
+ border-left-color: hsla(var(--color-gray-40), 1);
}
.header-link svg {
- opacity: 0.6;
+ opacity: 0.6;
}
.header-link:hover svg {
- opacity: 0.8;
+ opacity: 0.8;
}
.header-link a {
- display: inline-flex;
- gap: 0.5em;
- width: 100%;
- padding: 0.15em 0 0.15em 0;
+ display: inline-flex;
+ gap: 0.5em;
+ width: 100%;
+ padding: 0.15em 0 0.15em 0;
}
.header-link.depth-3 {
- padding-left: 2rem;
+ padding-left: 2rem;
}
.header-link.depth-4 {
- padding-left: 3rem;
+ padding-left: 3rem;
}
.header-link a {
- font: inherit;
- color: inherit;
- text-decoration: none;
+ font: inherit;
+ color: inherit;
+ text-decoration: none;
}
/* Screenreader Only Text */
.sr-only {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- white-space: nowrap;
- border-width: 0;
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
}
.focus\:not-sr-only:focus,
.focus\:not-sr-only:focus-visible {
- position: static;
- width: auto;
- height: auto;
- padding: 0;
- margin: 0;
- overflow: visible;
- clip: auto;
- white-space: normal;
+ position: static;
+ width: auto;
+ height: auto;
+ padding: 0;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+ white-space: normal;
}
:target {
- scroll-margin: calc(var(--theme-sidebar-offset, 5rem) + 2rem) 0 2rem;
+ scroll-margin: calc(var(--theme-sidebar-offset, 5rem) + 2rem) 0 2rem;
}
diff --git a/docs/public/make-scrollable-code-focusable.js b/docs/public/make-scrollable-code-focusable.js
index 35f104923..f2b7030f7 100644
--- a/docs/public/make-scrollable-code-focusable.js
+++ b/docs/public/make-scrollable-code-focusable.js
@@ -1,3 +1,3 @@
Array.from(document.getElementsByTagName('pre')).forEach((element) => {
- element.setAttribute('tabindex', '0');
+ element.setAttribute('tabindex', '0');
});
diff --git a/docs/public/theme.css b/docs/public/theme.css
index 551ee15e0..44a933434 100644
--- a/docs/public/theme.css
+++ b/docs/public/theme.css
@@ -1,13 +1,13 @@
:root {
- --font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
- sans-serif, Apple Color Emoji, Segoe UI Emoji;
- --font-body: system-ui, var(--font-fallback);
- --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono',
- 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono',
- 'Bitstream Vera Sans Mono', 'Liberation Mono', 'Nimbus Mono L', Monaco,
- 'Courier New', Courier, monospace;
+ --font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
+ sans-serif, Apple Color Emoji, Segoe UI Emoji;
+ --font-body: system-ui, var(--font-fallback);
+ --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono',
+ 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono',
+ 'Bitstream Vera Sans Mono', 'Liberation Mono', 'Nimbus Mono L', Monaco,
+ 'Courier New', Courier, monospace;
- /*
+ /*
* Variables with --color-base prefix define
* the hue, and saturation values to be used for
* hsla colors.
@@ -18,109 +18,109 @@
*
*/
- --color-base-white: 0, 0%;
- --color-base-black: 240, 100%;
- --color-base-gray: 215, 14%;
- --color-base-blue: 212, 100%;
- --color-base-blue-dark: 212, 72%;
- --color-base-green: 158, 79%;
- --color-base-orange: 22, 100%;
- --color-base-purple: 269, 79%;
- --color-base-red: 351, 100%;
- --color-base-yellow: 41, 100%;
+ --color-base-white: 0, 0%;
+ --color-base-black: 240, 100%;
+ --color-base-gray: 215, 14%;
+ --color-base-blue: 212, 100%;
+ --color-base-blue-dark: 212, 72%;
+ --color-base-green: 158, 79%;
+ --color-base-orange: 22, 100%;
+ --color-base-purple: 269, 79%;
+ --color-base-red: 351, 100%;
+ --color-base-yellow: 41, 100%;
- /*
+ /*
* Color palettes are made using --color-base
* variables, along with a lightness value to
* define different variants.
*
*/
- --color-gray-5: var(--color-base-gray), 5%;
- --color-gray-10: var(--color-base-gray), 10%;
- --color-gray-20: var(--color-base-gray), 20%;
- --color-gray-30: var(--color-base-gray), 30%;
- --color-gray-40: var(--color-base-gray), 40%;
- --color-gray-50: var(--color-base-gray), 50%;
- --color-gray-60: var(--color-base-gray), 60%;
- --color-gray-70: var(--color-base-gray), 70%;
- --color-gray-80: var(--color-base-gray), 80%;
- --color-gray-90: var(--color-base-gray), 90%;
- --color-gray-95: var(--color-base-gray), 95%;
+ --color-gray-5: var(--color-base-gray), 5%;
+ --color-gray-10: var(--color-base-gray), 10%;
+ --color-gray-20: var(--color-base-gray), 20%;
+ --color-gray-30: var(--color-base-gray), 30%;
+ --color-gray-40: var(--color-base-gray), 40%;
+ --color-gray-50: var(--color-base-gray), 50%;
+ --color-gray-60: var(--color-base-gray), 60%;
+ --color-gray-70: var(--color-base-gray), 70%;
+ --color-gray-80: var(--color-base-gray), 80%;
+ --color-gray-90: var(--color-base-gray), 90%;
+ --color-gray-95: var(--color-base-gray), 95%;
- --color-blue: var(--color-base-blue), 61%;
- --color-blue-dark: var(--color-base-blue-dark), 39%;
- --color-green: var(--color-base-green), 42%;
- --color-orange: var(--color-base-orange), 50%;
- --color-purple: var(--color-base-purple), 54%;
- --color-red: var(--color-base-red), 54%;
- --color-yellow: var(--color-base-yellow), 59%;
+ --color-blue: var(--color-base-blue), 61%;
+ --color-blue-dark: var(--color-base-blue-dark), 39%;
+ --color-green: var(--color-base-green), 42%;
+ --color-orange: var(--color-base-orange), 50%;
+ --color-purple: var(--color-base-purple), 54%;
+ --color-red: var(--color-base-red), 54%;
+ --color-yellow: var(--color-base-yellow), 59%;
}
:root {
- color-scheme: light;
- --theme-accent: hsla(var(--color-orange), 1);
- --theme-text-accent: hsla(var(--color-orange), 1);
- --theme-accent-opacity: 0.1;
- --theme-divider: hsla(var(--color-gray-95), 1);
- --theme-text: hsla(var(--color-gray-10), 1);
- --theme-text-light: hsla(var(--color-gray-40), 1);
- /* @@@: not used anywhere */
- --theme-text-lighter: hsla(var(--color-gray-80), 1);
- --theme-bg: hsla(var(--color-base-white), 100%, 1);
- --theme-bg-hover: hsla(var(--color-gray-95), 1);
- --theme-bg-offset: hsla(var(--color-gray-90), 1);
- --theme-bg-accent: hsla(var(--color-orange), var(--theme-accent-opacity));
- --theme-code-inline-bg: hsla(var(--color-gray-95), 1);
- --theme-code-inline-text: var(--theme-text);
- --theme-code-bg: hsla(217, 19%, 27%, 1);
- --theme-code-text: hsla(var(--color-gray-95), 1);
- --theme-navbar-bg: hsla(var(--color-base-white), 100%, 1);
- --theme-navbar-height: 6rem;
- --theme-selection-color: hsla(var(--color-orange), 1);
- --theme-selection-bg: hsla(var(--color-orange), var(--theme-accent-opacity));
+ color-scheme: light;
+ --theme-accent: hsla(var(--color-orange), 1);
+ --theme-text-accent: hsla(var(--color-orange), 1);
+ --theme-accent-opacity: 0.1;
+ --theme-divider: hsla(var(--color-gray-95), 1);
+ --theme-text: hsla(var(--color-gray-10), 1);
+ --theme-text-light: hsla(var(--color-gray-40), 1);
+ /* @@@: not used anywhere */
+ --theme-text-lighter: hsla(var(--color-gray-80), 1);
+ --theme-bg: hsla(var(--color-base-white), 100%, 1);
+ --theme-bg-hover: hsla(var(--color-gray-95), 1);
+ --theme-bg-offset: hsla(var(--color-gray-90), 1);
+ --theme-bg-accent: hsla(var(--color-orange), var(--theme-accent-opacity));
+ --theme-code-inline-bg: hsla(var(--color-gray-95), 1);
+ --theme-code-inline-text: var(--theme-text);
+ --theme-code-bg: hsla(217, 19%, 27%, 1);
+ --theme-code-text: hsla(var(--color-gray-95), 1);
+ --theme-navbar-bg: hsla(var(--color-base-white), 100%, 1);
+ --theme-navbar-height: 6rem;
+ --theme-selection-color: hsla(var(--color-orange), 1);
+ --theme-selection-bg: hsla(var(--color-orange), var(--theme-accent-opacity));
}
body {
- background: var(--theme-bg);
- color: var(--theme-text);
+ background: var(--theme-bg);
+ color: var(--theme-text);
}
:root.theme-dark {
- color-scheme: dark;
- --theme-accent-opacity: 0.4;
- --theme-accent: hsla(var(--color-orange), 1);
- --theme-text-accent: hsla(var(--color-orange), 1);
- --theme-divider: hsla(var(--color-gray-10), 1);
- --theme-text: hsla(var(--color-gray-90), 1);
- --theme-text-light: hsla(var(--color-gray-80), 1);
+ color-scheme: dark;
+ --theme-accent-opacity: 0.4;
+ --theme-accent: hsla(var(--color-orange), 1);
+ --theme-text-accent: hsla(var(--color-orange), 1);
+ --theme-divider: hsla(var(--color-gray-10), 1);
+ --theme-text: hsla(var(--color-gray-90), 1);
+ --theme-text-light: hsla(var(--color-gray-80), 1);
- /* @@@: not used anywhere */
- --theme-text-lighter: hsla(var(--color-gray-40), 1);
- --theme-bg: hsla(215, 28%, 17%, 1);
- --theme-bg-hover: hsla(var(--color-gray-40), 1);
- --theme-bg-offset: hsla(var(--color-gray-5), 1);
- --theme-code-inline-bg: hsla(var(--color-gray-10), 1);
- --theme-code-inline-text: hsla(var(--color-base-white), 100%, 1);
- --theme-code-bg: hsla(var(--color-gray-5), 1);
- --theme-code-text: hsla(var(--color-base-white), 100%, 1);
- --theme-navbar-bg: hsla(215, 28%, 17%, 1);
- --theme-selection-color: hsla(var(--color-base-white), 100%, 1);
- --theme-selection-bg: hsla(var(--color-purple), var(--theme-accent-opacity));
+ /* @@@: not used anywhere */
+ --theme-text-lighter: hsla(var(--color-gray-40), 1);
+ --theme-bg: hsla(215, 28%, 17%, 1);
+ --theme-bg-hover: hsla(var(--color-gray-40), 1);
+ --theme-bg-offset: hsla(var(--color-gray-5), 1);
+ --theme-code-inline-bg: hsla(var(--color-gray-10), 1);
+ --theme-code-inline-text: hsla(var(--color-base-white), 100%, 1);
+ --theme-code-bg: hsla(var(--color-gray-5), 1);
+ --theme-code-text: hsla(var(--color-base-white), 100%, 1);
+ --theme-navbar-bg: hsla(215, 28%, 17%, 1);
+ --theme-selection-color: hsla(var(--color-base-white), 100%, 1);
+ --theme-selection-bg: hsla(var(--color-purple), var(--theme-accent-opacity));
- /* DocSearch [Algolia] */
- --docsearch-modal-background: var(--theme-bg);
- --docsearch-searchbox-focus-background: var(--theme-divider);
- --docsearch-footer-background: var(--theme-divider);
- --docsearch-text-color: var(--theme-text);
- --docsearch-hit-background: var(--theme-divider);
- --docsearch-hit-shadow: none;
- --docsearch-hit-color: var(--theme-text);
- --docsearch-footer-shadow: inset 0 2px 10px #000;
- --docsearch-modal-shadow: inset 0 0 8px #000;
+ /* DocSearch [Algolia] */
+ --docsearch-modal-background: var(--theme-bg);
+ --docsearch-searchbox-focus-background: var(--theme-divider);
+ --docsearch-footer-background: var(--theme-divider);
+ --docsearch-text-color: var(--theme-text);
+ --docsearch-hit-background: var(--theme-divider);
+ --docsearch-hit-shadow: none;
+ --docsearch-hit-color: var(--theme-text);
+ --docsearch-footer-shadow: inset 0 2px 10px #000;
+ --docsearch-modal-shadow: inset 0 0 8px #000;
}
::selection {
- color: var(--theme-selection-color);
- background-color: var(--theme-selection-bg);
+ color: var(--theme-selection-color);
+ background-color: var(--theme-selection-bg);
}
diff --git a/docs/src/components/Card.astro b/docs/src/components/Card.astro
index c024981ff..21b5deb68 100644
--- a/docs/src/components/Card.astro
+++ b/docs/src/components/Card.astro
@@ -1,52 +1,62 @@
---
-const {data, index} = Astro.props;
+const { data, index } = Astro.props;
const hasScreenshot = !!data.demo;
-const backgroundStyle = hasScreenshot ? `url('https://v1.screenshot.11ty.dev/${encodeURIComponent(data.demo)}/medium/9:16/')` : `linear-gradient(60deg, var(--theme-bg-accent), var(--theme-accent))`
+const backgroundStyle = hasScreenshot
+ ? `url('https://v1.screenshot.11ty.dev/${encodeURIComponent(
+ data.demo
+ )}/medium/9:16/')`
+ : `linear-gradient(60deg, var(--theme-bg-accent), var(--theme-accent))`;
---
+
+<article
+ class={`card ${hasScreenshot ? 'has-screenshot' : ''}`}
+ style={`background: ${backgroundStyle}; background-size: cover;`}
+>
+ {hasScreenshot && <div class="background-dimmer"></div>}
+ <div class="card-body">
+ <a href={data.github} class="card-header" target="_blank">
+ {data.name}
+ <span>{` →`}</span>
+ </a>
+ </div>
+</article>
+
<style>
- .card {
- position: relative;
- display: flex;
- flex-direction: column;
- grid-column: span 1;
- flex-grow: 1;
- height: 200px;
- justify-content: center;
- align-items: center;
- padding: 1rem;
- text-align: center;
- }
- .card-header {
- flex-grow: 1;
- font-weight: bold;
- font-size: 1.8rem;
- }
- .background-dimmer {
- position: absolute;
- top: 0;
- left: 0;
- height: 100%;
- width: 100%;
- background: linear-gradient(45deg, #0004, #000B);
- z-index: 2;
- }
- .card-body, .card-header {
- color: var(--text-color);
- }
- .card-body {
- z-index: 3;
- }
- .card.has-screenshot .card-header,
- .card.has-screenshot .card-body {
- color: white;
- }
+ .card {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ grid-column: span 1;
+ flex-grow: 1;
+ height: 200px;
+ justify-content: center;
+ align-items: center;
+ padding: 1rem;
+ text-align: center;
+ }
+ .card-header {
+ flex-grow: 1;
+ font-weight: bold;
+ font-size: 1.8rem;
+ }
+ .background-dimmer {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+ background: linear-gradient(45deg, #0004, #000b);
+ z-index: 2;
+ }
+ .card-body,
+ .card-header {
+ color: var(--text-color);
+ }
+ .card-body {
+ z-index: 3;
+ }
+ .card.has-screenshot .card-header,
+ .card.has-screenshot .card-body {
+ color: white;
+ }
</style>
-<article class={`card ${hasScreenshot ? 'has-screenshot' : ''}`} style={`background: ${backgroundStyle}; background-size: cover;`}>
- {hasScreenshot && <div class="background-dimmer"></div>}
- <div class="card-body">
- <a href={data.github} class="card-header" target="_blank">
- {data.name}
- <span>{` →`}</span>
- </a>
- </div>
-</article> \ No newline at end of file
diff --git a/docs/src/components/Footer/AvatarList.astro b/docs/src/components/Footer/AvatarList.astro
index 06ac2cd0b..5eb0c3692 100644
--- a/docs/src/components/Footer/AvatarList.astro
+++ b/docs/src/components/Footer/AvatarList.astro
@@ -2,7 +2,7 @@
// fetch all commits for just this page's path
export interface Props {
- path: string;
+ path: string;
}
const { path } = Astro.props as Props;
@@ -11,147 +11,166 @@ const url = `https://api.github.com/repos/withastro/astro/commits?path=${commitP
const commitsURL = `https://github.com/withastro/astro/commits/main/${commitPath}`;
async function getCommits(url) {
- try {
- const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN;
- if (!token) {
- throw new Error(
- 'Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.'
- );
- }
-
- const auth = `Basic ${Buffer.from(token, "binary").toString("base64")}`;
-
- const res = await fetch(url, {
- method: "GET",
- headers: {
- Authorization: auth,
- "User-Agent": "astro-docs/1.0",
- },
- });
-
- const data = await res.json();
-
- if (!res.ok) {
- throw new Error(
- `Request to fetch commits failed. Reason: ${res.statusText}
+ try {
+ const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN;
+ if (!token) {
+ throw new Error(
+ 'Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.'
+ );
+ }
+
+ const auth = `Basic ${Buffer.from(token, 'binary').toString('base64')}`;
+
+ const res = await fetch(url, {
+ method: 'GET',
+ headers: {
+ Authorization: auth,
+ 'User-Agent': 'astro-docs/1.0',
+ },
+ });
+
+ const data = await res.json();
+
+ if (!res.ok) {
+ throw new Error(
+ `Request to fetch commits failed. Reason: ${res.statusText}
Message: ${data.message}`
- );
- }
+ );
+ }
- return data;
- } catch (e) {
- console.warn(`[error] /src/components/AvatarList.astro
+ return data;
+ } catch (e) {
+ console.warn(`[error] /src/components/AvatarList.astro
${e?.message ?? e}`);
- return new Array();
- }
+ return new Array();
+ }
}
function removeDups(arr) {
- if (!arr) {
- return new Array();
- }
- let map = new Map();
-
- for (let item of arr) {
- let author = item.author;
- // Deduplicate based on author.id
- map.set(author.id, { login: author.login, id: author.id });
- }
-
- return Array.from(map.values());
+ if (!arr) {
+ return new Array();
+ }
+ let map = new Map();
+
+ for (let item of arr) {
+ let author = item.author;
+ // Deduplicate based on author.id
+ map.set(author.id, { login: author.login, id: author.id });
+ }
+
+ return Array.from(map.values());
}
const data = await getCommits(url);
const unique = removeDups(data);
const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors
const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors
-
---
+
<!-- Thanks to @5t3ph for https://smolcss.dev/#smol-avatar-list! -->
<div class="contributors">
-<ul class="avatar-list" style={`--avatar-count: ${recentContributors.length}`}>
-
-{recentContributors.map((item) => (
- <li><a href={`https://github.com/${item.login}`}><img alt={`Contributor ${item.login}`} title={`Contributor ${item.login}`} width="64" height="64" src={`https://avatars.githubusercontent.com/u/${item.id}`}/></a></li>
-
-))}
- </ul>
- {additionalContributors > 0 && <span><a href={commitsURL}>{`and ${additionalContributors} additional contributor${additionalContributors > 1 ? 's' : ''}.`}</a></span>}
- {unique.length === 0 && <a href={commitsURL}>Contributors</a>}
+ <ul
+ class="avatar-list"
+ style={`--avatar-count: ${recentContributors.length}`}
+ >
+ {recentContributors.map((item) => (
+ <li>
+ <a href={`https://github.com/${item.login}`}>
+ <img
+ alt={`Contributor ${item.login}`}
+ title={`Contributor ${item.login}`}
+ width="64"
+ height="64"
+ src={`https://avatars.githubusercontent.com/u/${item.id}`}
+ />
+ </a>
+ </li>
+ ))}
+ </ul>
+ {additionalContributors > 0 && (
+ <span>
+ <a
+ href={commitsURL}
+ >{`and ${additionalContributors} additional contributor${
+ additionalContributors > 1 ? 's' : ''
+ }.`}</a>
+ </span>
+ )}
+ {unique.length === 0 && <a href={commitsURL}>Contributors</a>}
</div>
<style>
-.avatar-list {
- --avatar-size: 2.5rem;
- --avatar-count: 3;
+ .avatar-list {
+ --avatar-size: 2.5rem;
+ --avatar-count: 3;
- display: grid;
- list-style: none;
- /* Default to displaying most of the avatar to
+ display: grid;
+ list-style: none;
+ /* Default to displaying most of the avatar to
enable easier access on touch devices, ensuring
the WCAG touch target size is met or exceeded */
- grid-template-columns: repeat(
- var(--avatar-count),
- max(44px, calc(var(--avatar-size) / 1.15))
- );
- /* `padding` matches added visual dimensions of
+ grid-template-columns: repeat(
+ var(--avatar-count),
+ max(44px, calc(var(--avatar-size) / 1.15))
+ );
+ /* `padding` matches added visual dimensions of
the `box-shadow` to help create a more accurate
computed component size */
- padding: 0.08em;
- font-size: var(--avatar-size);
-}
+ padding: 0.08em;
+ font-size: var(--avatar-size);
+ }
-@media (any-hover: hover) and (any-pointer: fine) {
- .avatar-list {
- /* We create 1 extra cell to enable the computed
+ @media (any-hover: hover) and (any-pointer: fine) {
+ .avatar-list {
+ /* We create 1 extra cell to enable the computed
width to match the final visual width */
- grid-template-columns: repeat(
- calc(var(--avatar-count) + 1),
- calc(var(--avatar-size) / 1.75)
- );
- }
-}
-
-.avatar-list li {
- width: var(--avatar-size);
- height: var(--avatar-size);
-}
-
-.avatar-list li:hover ~ li a,
-.avatar-list li:focus-within ~ li a {
- transform: translateX(33%);
-}
-
-.avatar-list img,
-.avatar-list a {
- display: block;
- border-radius: 50%;
-}
-
-.avatar-list a {
- transition: transform 180ms ease-in-out;
-}
-
-.avatar-list img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- background-color: #fff;
- box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
-}
-
-.avatar-list a:focus {
- outline: 2px solid transparent;
- /* Double-layer trick to work for dark and light backgrounds */
- box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
-}
-
-.contributors {
- display: flex;
- align-items: center;
-}
-
-.contributors > * + * {
- margin-left: .75rem;
-}
+ grid-template-columns: repeat(
+ calc(var(--avatar-count) + 1),
+ calc(var(--avatar-size) / 1.75)
+ );
+ }
+ }
+
+ .avatar-list li {
+ width: var(--avatar-size);
+ height: var(--avatar-size);
+ }
+
+ .avatar-list li:hover ~ li a,
+ .avatar-list li:focus-within ~ li a {
+ transform: translateX(33%);
+ }
+
+ .avatar-list img,
+ .avatar-list a {
+ display: block;
+ border-radius: 50%;
+ }
+
+ .avatar-list a {
+ transition: transform 180ms ease-in-out;
+ }
+
+ .avatar-list img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ background-color: #fff;
+ box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
+ }
+
+ .avatar-list a:focus {
+ outline: 2px solid transparent;
+ /* Double-layer trick to work for dark and light backgrounds */
+ box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
+ }
+
+ .contributors {
+ display: flex;
+ align-items: center;
+ }
+
+ .contributors > * + * {
+ margin-left: 0.75rem;
+ }
</style>
diff --git a/docs/src/components/Footer/Footer.astro b/docs/src/components/Footer/Footer.astro
index 48de51054..d13f832e5 100644
--- a/docs/src/components/Footer/Footer.astro
+++ b/docs/src/components/Footer/Footer.astro
@@ -4,13 +4,13 @@ const { path } = Astro.props;
---
<footer>
- <AvatarList path={path} />
+ <AvatarList {path} />
</footer>
<style>
-footer {
- margin-top: auto;
- padding: 2rem 0;
- border-top: 3px solid var(--theme-divider);
-}
+ footer {
+ margin-top: auto;
+ padding: 2rem 0;
+ border-top: 3px solid var(--theme-divider);
+ }
</style>
diff --git a/docs/src/components/HeadCommon.astro b/docs/src/components/HeadCommon.astro
index 222c17ac2..ca0782b60 100644
--- a/docs/src/components/HeadCommon.astro
+++ b/docs/src/components/HeadCommon.astro
@@ -1,10 +1,10 @@
<!-- Global Metadata -->
<meta charset="utf-8" />
-<meta name="viewport" content="width=device-width">
-<meta name="theme-color" content="#ff5e00"/>
-<link rel="icon" type="image/svg+xml" href="/favicon.svg"/>
+<meta name="viewport" content="width=device-width" />
+<meta name="theme-color" content="#ff5e00" />
+<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="alternate icon" type="image/x-icon" href="/favicon.ico" />
-<link rel="sitemap" href="/sitemap.xml"/>
+<link rel="sitemap" href="/sitemap.xml" />
<!-- Global CSS -->
<link rel="stylesheet" href="/theme.css" />
@@ -12,29 +12,38 @@
<link rel="stylesheet" href="/index.css" />
<!-- Preload Fonts -->
-<link rel="preconnect" href="https://fonts.googleapis.com">
-<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
-<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital@0;1&display=swap" rel="stylesheet">
+<link rel="preconnect" href="https://fonts.googleapis.com" />
+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
+<link
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital@0;1&display=swap"
+ rel="stylesheet"
+/>
<!-- Scrollable a11y code helper -->
-<script type="module" src="/make-scrollable-code-focusable.js" />
+<script type="module" src="/make-scrollable-code-focusable.js"></script>
<!-- This is intentionally inlined to avoid FOUC -->
<script>
- const root = document.documentElement;
- const theme = localStorage.getItem('theme');
- if (theme === 'dark' || (!theme) && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- root.classList.add('theme-dark');
- } else {
- root.classList.remove('theme-dark');
- }
+ const root = document.documentElement;
+ const theme = localStorage.getItem('theme');
+ if (
+ theme === 'dark' ||
+ (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)
+ ) {
+ root.classList.add('theme-dark');
+ } else {
+ root.classList.remove('theme-dark');
+ }
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
-<script async src="https://www.googletagmanager.com/gtag/js?id=UA-130280175-15"></script>
+<script async src="https://www.googletagmanager.com/gtag/js?id=UA-130280175-15"
+></script>
<script>
- window.dataLayer = window.dataLayer || [];
- function gtag(){dataLayer.push(arguments);}
- gtag('js', new Date());
- gtag('config', 'UA-130280175-15');
+ window.dataLayer = window.dataLayer || [];
+ function gtag() {
+ dataLayer.push(arguments);
+ }
+ gtag('js', new Date());
+ gtag('config', 'UA-130280175-15');
</script>
diff --git a/docs/src/components/HeadSEO.astro b/docs/src/components/HeadSEO.astro
index d3cd80901..6b62c9d1f 100644
--- a/docs/src/components/HeadSEO.astro
+++ b/docs/src/components/HeadSEO.astro
@@ -1,43 +1,48 @@
---
-import {SITE, OPEN_GRAPH} from '../config.ts';
+import { SITE, OPEN_GRAPH } from '../config.ts';
import { getLanguageFromURL } from '../util.ts';
export interface Props {
- content: any,
- site: any,
- canonicalURL: URL,
-};
-const {
- content = {},
- canonicalURL,
- } = Astro.props;
+ content: any;
+ site: any;
+ canonicalURL: URL;
+}
+const { content = {}, canonicalURL } = Astro.props;
const imageSrc = content?.image?.src ?? OPEN_GRAPH.image.src;
const canonicalImageSrc = new URL(imageSrc, Astro.site);
const imageAlt = content?.image?.alt ?? OPEN_GRAPH.image.alt;
const lang = canonicalURL && getLanguageFromURL(canonicalURL.pathname);
---
+
<!-- Page Metadata -->
-<link rel="canonical" href={canonicalURL}/>
+<link rel="canonical" href={canonicalURL} />
<!-- Algolia docsearch language facet -->
<meta name="docsearch:language" content={lang} />
<!-- OpenGraph Tags -->
-<meta property="og:title" content={content.title ?? SITE.title}/>
-<meta property="og:type" content="article"/>
-<meta property="og:url" content={canonicalURL}/>
-<meta property="og:locale" content={content.ogLocale ?? OPEN_GRAPH.locale}/>
-<meta property="og:image" content={canonicalImageSrc}/>
-<meta property="og:image:alt" content={imageAlt}/>
-<meta name="description" property="og:description" content={content.description ? content.description : SITE.description}/>
-<meta property="og:site_name" content={SITE.title}/>
+<meta property="og:title" content={content.title ?? SITE.title} />
+<meta property="og:type" content="article" />
+<meta property="og:url" content={canonicalURL} />
+<meta property="og:locale" content={content.ogLocale ?? OPEN_GRAPH.locale} />
+<meta property="og:image" content={canonicalImageSrc} />
+<meta property="og:image:alt" content={imageAlt} />
+<meta
+ name="description"
+ property="og:description"
+ content={content.description ? content.description : SITE.description}
+/>
+<meta property="og:site_name" content={SITE.title} />
<!-- Twitter Tags -->
-<meta name="twitter:card" content="summary_large_image"/>
-<meta name="twitter:site" content={OPEN_GRAPH.twitter}/>
-<meta name="twitter:title" content={content.title ?? SITE.title}/>
-<meta name="twitter:description" content={content.description ? content.description : SITE.description}/>
-<meta name="twitter:image" content={canonicalImageSrc}/>
-<meta name="twitter:image:alt" content={imageAlt}/>
+<meta name="twitter:card" content="summary_large_image" />
+<meta name="twitter:site" content={OPEN_GRAPH.twitter} />
+<meta name="twitter:title" content={content.title ?? SITE.title} />
+<meta
+ name="twitter:description"
+ content={content.description ? content.description : SITE.description}
+/>
+<meta name="twitter:image" content={canonicalImageSrc} />
+<meta name="twitter:image:alt" content={imageAlt} />
<!--
TODO: Add json+ld data, maybe https://schema.org/APIReference makes sense?
diff --git a/docs/src/components/Header/AstroLogo.astro b/docs/src/components/Header/AstroLogo.astro
index ff1939ad9..860b2d2b1 100644
--- a/docs/src/components/Header/AstroLogo.astro
+++ b/docs/src/components/Header/AstroLogo.astro
@@ -1,20 +1,36 @@
---
-const {size} = Astro.props;
+const { size } = Astro.props;
---
-<svg class="logo" width={size} height={size} viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
- <style>
- #flame {
- /* fill: #ff5d01; */
- fill: #3894ff;
- }
- #a {
- /* fill: #000014; */
- fill: #3894ff;
- }
- </style>
- <title>Logo</title>
- <path id="a" fill-rule="evenodd" clip-rule="evenodd"
- d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z" />
- <path id="flame" fill-rule="evenodd" clip-rule="evenodd"
- d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z" />
-</svg> \ No newline at end of file
+
+<svg
+ class="logo"
+ width={size}
+ height={size}
+ viewBox="0 0 256 256"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+>
+ <style>
+ #flame {
+ /* fill: #ff5d01; */
+ fill: #3894ff;
+ }
+ #a {
+ /* fill: #000014; */
+ fill: #3894ff;
+ }
+ </style>
+ <title>Logo</title>
+ <path
+ id="a"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
+ ></path>
+ <path
+ id="flame"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
+ ></path>
+</svg>
diff --git a/docs/src/components/Header/Header.astro b/docs/src/components/Header/Header.astro
index 06954bd7f..2196670c8 100644
--- a/docs/src/components/Header/Header.astro
+++ b/docs/src/components/Header/Header.astro
@@ -2,158 +2,183 @@
import SkipToContent from './SkipToContent.astro';
import SidebarToggle from './SidebarToggle.tsx';
import LanguageSelect from './LanguageSelect.tsx';
-import Search from "./Search.tsx";
+import Search from './Search.tsx';
import { getLanguageFromURL } from '../../util.ts';
-const {currentPage} = Astro.props;
+const { currentPage } = Astro.props;
const lang = currentPage && getLanguageFromURL(currentPage);
---
-<style>
- header {
- z-index: 11;
- height: var(--theme-navbar-height);
- width: 100%;
- background-color: var(--theme-navbar-bg);
- display: flex;
- align-items: center;
- justify-content: center;
- overflow: hidden;
- position: sticky;
- top: 0;
- }
-
- .logo {
- direction: ltr;
- display: flex;
- overflow: hidden;
- width: 30px;
- font-size: 1rem;
- flex-shrink: 0;
- font-weight: 600;
- line-height: 1;
- color: hsla(var(--color-base-white), 100%, 1);
- text-decoration: none;
- gap: 0.5em;
- z-index: -1;
- }
+<header>
+ <SkipToContent />
+ <nav class="nav-wrapper" title="Top Navigation">
+ <div class="menu-toggle">
+ <SidebarToggle client:idle />
+ </div>
+ <div class="logo flex">
+ <a href="https://astro.build/">
+ <h1 class="sr-only">Astro</h1>
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="363"
+ height="102"
+ viewBox="0 0 363 102"
+ fill="none"
+ >
+ <style>
+ .text {
+ fill: var(--theme-text);
+ }
+ .hover {
+ fill: var(--theme-accent);
+ }
+ </style>
+ <path
+ class="text"
+ fill-rule="evenodd"
+ d="M55.07 14.216l16.81 54.865a72.6 72.6 0 00-20.765-6.984L39.808 24.135a1.475 1.475 0 00-2.827.005L25.81 62.078a72.598 72.598 0 00-20.859 6.995L21.847 14.2c.998-3.243 1.497-4.865 2.47-6.066a8 8 0 013.239-2.392c1.434-.576 3.13-.576 6.524-.576h8.751c3.398 0 5.097 0 6.532.577a8 8 0 013.241 2.397c.972 1.203 1.47 2.827 2.465 6.076z"
+ clip-rule="evenodd"></path>
+ <path
+ fill="#FF5D01"
+ fill-rule="evenodd"
+ d="M54.618 71.779c-2.863 2.432-8.578 4.091-15.161 4.091-8.08 0-14.852-2.499-16.649-5.86-.642 1.926-.786 4.13-.786 5.539 0 0-.423 6.915 4.418 11.725 0-2.498 2.037-4.522 4.551-4.522 4.309 0 4.304 3.734 4.3 6.764v.27c0 4.6 2.829 8.541 6.852 10.203a9.22 9.22 0 01-.938-4.064c0-4.386 2.592-6.02 5.604-7.917 2.396-1.51 5.06-3.188 6.894-6.554a12.297 12.297 0 001.502-5.905c0-1.314-.206-2.581-.587-3.77z"
+ clip-rule="evenodd"></path>
+ <path
+ class="text"
+ d="M126.554 69c13.115 0 21.047-3.14 25.68-9.654 0 2.904.157 5.651.55 8.163h7.774c-.706-4.082-.863-6.75-.863-14.128V43.334c0-10.831-8.403-16.56-24.424-16.56-15.47 0-25.522 5.964-26.779 14.598h8.246c1.256-5.808 7.774-8.87 18.533-8.87 10.602 0 16.885 3.69 16.885 9.969v.785l-24.502 1.413c-9.974.549-13.665 1.962-16.492 4.003-2.67 1.962-4.162 5.023-4.162 8.555C107 64.683 114.696 69 126.554 69zm2.513-5.573c-9.109 0-14.135-2.119-14.135-6.357 0-4.553 3.141-6.593 14.214-7.3l23.01-1.412v1.805c0 8.241-9.66 13.264-23.089 13.264zM196.086 69c16.256 0 22.775-5.337 22.775-13.108 0-6.436-4.006-9.732-14.215-10.596l-19.083-1.49c-5.183-.393-8.088-1.884-8.088-5.102 0-4.082 4.476-6.201 14.135-6.201 10.995 0 16.727 2.198 20.497 7.064l6.361-3.061c-3.927-6.122-12.644-9.733-26.151-9.733-13.9 0-22.224 4.631-22.224 12.244 0 6.829 4.947 10.125 14.292 10.91l18.926 1.492c6.204.47 8.089 1.726 8.089 4.944 0 4.631-4.79 6.829-14.293 6.829-11.544 0-18.847-3.14-22.381-8.87l-6.204 3.376C173.312 64.918 181.715 69 196.086 69zM234.929 34.151v18.916c0 7.77 2.67 15.54 17.198 15.54 3.691 0 8.167-.706 10.131-1.57V60.68c-2.749.628-6.047 1.1-9.267 1.1-6.832 0-10.523-2.67-10.523-9.42V34.151h19.633v-5.887h-19.633V15l-7.539 3.061v10.204h-12.33v5.886h12.33zM280.823 28.265h-6.911v39.244h7.461V52.83c0-5.65 1.099-10.439 4.24-13.735 2.749-3.061 6.283-4.788 12.487-4.788 2.12 0 3.455.157 5.262.471v-7.22c-1.65-.393-3.063-.472-5.184-.472-8.402 0-15.078 4.945-17.355 12.558v-11.38zM334.807 69C351.534 69 363 60.523 363 47.887c0-12.637-11.466-21.114-28.193-21.114-16.727 0-28.193 8.477-28.193 21.114C306.614 60.523 318.08 69 334.807 69zm0-6.2c-12.329 0-20.261-5.809-20.261-14.913 0-9.105 7.932-14.913 20.261-14.913 12.251 0 20.261 5.808 20.261 14.913 0 9.104-8.01 14.912-20.261 14.912z"
+ ></path>
+ </svg>
+ </a>
+ <a href="https://docs.astro.build/">
+ <h1 class="sr-only">Docs</h1>
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="226"
+ height="102"
+ viewBox="0 0 226 102"
+ fill="none"
+ >
+ <path
+ fill="currentColor"
+ d="M25.805 68c14.688 0 24.883-8.41 24.883-21.14 0-12.786-9.62-19.756-24.653-19.756H0V68h25.805zm-14.17-33.005H24.25c8.352 0 14.17 4.09 14.17 12.039 0 8.236-5.3 13.075-14.113 13.075H11.635V34.995zM82.673 69.382c16.704 0 27.418-8.582 27.418-21.83 0-13.248-10.771-21.83-27.418-21.83-16.589 0-27.418 8.582-27.418 21.83 0 13.19 10.83 21.83 27.418 21.83zm0-8.64c-9.1 0-15.149-5.299-15.149-13.19 0-7.891 6.048-13.19 15.15-13.19 9.1 0 15.205 5.299 15.205 13.19 0 7.891-6.105 13.19-15.206 13.19zM141.497 69.382c13.306 0 22.637-5.299 25.978-14.572l-11.866-2.535c-1.67 5.415-6.393 8.295-13.709 8.295-9.216 0-15.033-5.127-15.033-13.018 0-8.006 5.702-13.018 14.918-13.018 7.43 0 12.154 3.053 13.709 8.64l12.038-2.13c-2.707-9.562-12.268-15.322-25.574-15.322-16.128 0-27.302 9.043-27.302 22.003 0 13.133 10.425 21.657 26.841 21.657zM194.94 69.382c14.745 0 23.212-5.01 23.212-14.054 0-7.603-4.665-10.944-15.955-12.096l-11.289-1.094c-5.242-.576-6.97-1.556-6.97-4.09 0-2.765 3.456-4.262 9.792-4.262 7.834 0 13.709 2.476 16.762 6.508l7.315-6.163c-5.069-5.702-13.133-8.41-23.501-8.41-13.997 0-21.888 4.781-21.888 12.903 0 7.546 4.781 11.232 14.803 12.326l12.557 1.383c4.896.518 6.624 1.555 6.624 4.09 0 3.225-3.456 4.723-10.886 4.723-8.352 0-14.688-3.226-18.087-8.007l-8.294 5.818c4.205 6.451 13.709 10.425 25.805 10.425z"
+ ></path>
+ </svg>
+ </a>
+ </div>
+ <div style="flex-grow: 1;"></div>
+ {lang && <LanguageSelect lang={lang} client:idle />}
+ <div class="search-item"><Search {lang} client:idle /></div>
+ </nav>
+</header>
- .logo a {
- padding: 0.5em 0.25em;
- margin: -0.5em -0.25em;
- }
+<style>
+ header {
+ z-index: 11;
+ height: var(--theme-navbar-height);
+ width: 100%;
+ background-color: var(--theme-navbar-bg);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ position: sticky;
+ top: 0;
+ }
- .logo svg {
- height: 40px;
- width: auto;
- display: block;
- color: var(--theme-accent);
- }
+ .logo {
+ direction: ltr;
+ display: flex;
+ overflow: hidden;
+ width: 30px;
+ font-size: 1rem;
+ flex-shrink: 0;
+ font-weight: 600;
+ line-height: 1;
+ color: hsla(var(--color-base-white), 100%, 1);
+ text-decoration: none;
+ gap: 0.5em;
+ z-index: -1;
+ }
- .logo .hover {
- opacity: 0.0;
- }
- .logo a {
- transition: transform 180ms ease-out;
- }
+ .logo a {
+ padding: 0.5em 0.25em;
+ margin: -0.5em -0.25em;
+ }
- .logo a:hover,
- .logo a:focus {
- outline: none;
- opacity: 1.0;
- transform: translateY(-2px);
- }
+ .logo svg {
+ height: 40px;
+ width: auto;
+ display: block;
+ color: var(--theme-accent);
+ }
- .logo h1 {
- font: inherit;
- color: inherit;
- margin: 0;
- }
+ .logo .hover {
+ opacity: 0;
+ }
+ .logo a {
+ transition: transform 180ms ease-out;
+ }
+ .logo a:hover,
+ .logo a:focus {
+ outline: none;
+ opacity: 1;
+ transform: translateY(-2px);
+ }
+ .logo h1 {
+ font: inherit;
+ color: inherit;
+ margin: 0;
+ }
- .nav-wrapper {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- gap: 1em;
- width: 100%;
- max-width: 82em;
- padding: 0 1rem;
- }
- @media (min-width: 50em) {
- header {
- position: static;
- padding: 2rem 0rem 0 2rem;
- }
- .logo {
- width: auto;
- margin: 0;
- z-index: 0;
- }
- .menu-toggle {
- display: none;
- }
- .logo {
- width: auto;
- }
- }
+ .nav-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 1em;
+ width: 100%;
+ max-width: 82em;
+ padding: 0 1rem;
+ }
+ @media (min-width: 50em) {
+ header {
+ position: static;
+ padding: 2rem 0rem 0 2rem;
+ }
+ .logo {
+ width: auto;
+ margin: 0;
+ z-index: 0;
+ }
+ .menu-toggle {
+ display: none;
+ }
+ .logo {
+ width: auto;
+ }
+ }
- /** Style Algolia */
-:root {
- --docsearch-primary-color: var(--theme-accent);
- --docsearch-logo-color: var(--theme-text);
-}
+ /** Style Algolia */
+ :root {
+ --docsearch-primary-color: var(--theme-accent);
+ --docsearch-logo-color: var(--theme-text);
+ }
-.search-item {
- display: none;
- position: relative;
- z-index: 10;
- flex-grow: 1;
- padding-right: 0.7rem;
- display: flex;
- max-width: 200px;
-}
-:global(.search-item > *) {
- flex-grow: 1;
-}
-@media (min-width: 50em) {
- .search-item {
- max-width: 400px;
- }
-}
+ .search-item {
+ display: none;
+ position: relative;
+ z-index: 10;
+ flex-grow: 1;
+ padding-right: 0.7rem;
+ display: flex;
+ max-width: 200px;
+ }
+ :global(.search-item > *) {
+ flex-grow: 1;
+ }
+ @media (min-width: 50em) {
+ .search-item {
+ max-width: 400px;
+ }
+ }
</style>
-<header>
- <SkipToContent />
- <nav class="nav-wrapper" title="Top Navigation">
- <div class="menu-toggle">
- <SidebarToggle client:idle/>
- </div>
- <div class="logo flex">
- <a href="https://astro.build/">
- <h1 class="sr-only">Astro</h1>
- <svg xmlns="http://www.w3.org/2000/svg" width="363" height="102" viewBox="0 0 363 102" fill="none">
- <style>
- .text {
- fill: var(--theme-text);
- }
- .hover {
- fill: var(--theme-accent);
- }
- </style>
- <path class="text" fill-rule="evenodd" d="M55.07 14.216l16.81 54.865a72.6 72.6 0 00-20.765-6.984L39.808 24.135a1.475 1.475 0 00-2.827.005L25.81 62.078a72.598 72.598 0 00-20.859 6.995L21.847 14.2c.998-3.243 1.497-4.865 2.47-6.066a8 8 0 013.239-2.392c1.434-.576 3.13-.576 6.524-.576h8.751c3.398 0 5.097 0 6.532.577a8 8 0 013.241 2.397c.972 1.203 1.47 2.827 2.465 6.076z" clip-rule="evenodd"/>
- <path fill="#FF5D01" fill-rule="evenodd" d="M54.618 71.779c-2.863 2.432-8.578 4.091-15.161 4.091-8.08 0-14.852-2.499-16.649-5.86-.642 1.926-.786 4.13-.786 5.539 0 0-.423 6.915 4.418 11.725 0-2.498 2.037-4.522 4.551-4.522 4.309 0 4.304 3.734 4.3 6.764v.27c0 4.6 2.829 8.541 6.852 10.203a9.22 9.22 0 01-.938-4.064c0-4.386 2.592-6.02 5.604-7.917 2.396-1.51 5.06-3.188 6.894-6.554a12.297 12.297 0 001.502-5.905c0-1.314-.206-2.581-.587-3.77z" clip-rule="evenodd"/>
- <path class="text" d="M126.554 69c13.115 0 21.047-3.14 25.68-9.654 0 2.904.157 5.651.55 8.163h7.774c-.706-4.082-.863-6.75-.863-14.128V43.334c0-10.831-8.403-16.56-24.424-16.56-15.47 0-25.522 5.964-26.779 14.598h8.246c1.256-5.808 7.774-8.87 18.533-8.87 10.602 0 16.885 3.69 16.885 9.969v.785l-24.502 1.413c-9.974.549-13.665 1.962-16.492 4.003-2.67 1.962-4.162 5.023-4.162 8.555C107 64.683 114.696 69 126.554 69zm2.513-5.573c-9.109 0-14.135-2.119-14.135-6.357 0-4.553 3.141-6.593 14.214-7.3l23.01-1.412v1.805c0 8.241-9.66 13.264-23.089 13.264zM196.086 69c16.256 0 22.775-5.337 22.775-13.108 0-6.436-4.006-9.732-14.215-10.596l-19.083-1.49c-5.183-.393-8.088-1.884-8.088-5.102 0-4.082 4.476-6.201 14.135-6.201 10.995 0 16.727 2.198 20.497 7.064l6.361-3.061c-3.927-6.122-12.644-9.733-26.151-9.733-13.9 0-22.224 4.631-22.224 12.244 0 6.829 4.947 10.125 14.292 10.91l18.926 1.492c6.204.47 8.089 1.726 8.089 4.944 0 4.631-4.79 6.829-14.293 6.829-11.544 0-18.847-3.14-22.381-8.87l-6.204 3.376C173.312 64.918 181.715 69 196.086 69zM234.929 34.151v18.916c0 7.77 2.67 15.54 17.198 15.54 3.691 0 8.167-.706 10.131-1.57V60.68c-2.749.628-6.047 1.1-9.267 1.1-6.832 0-10.523-2.67-10.523-9.42V34.151h19.633v-5.887h-19.633V15l-7.539 3.061v10.204h-12.33v5.886h12.33zM280.823 28.265h-6.911v39.244h7.461V52.83c0-5.65 1.099-10.439 4.24-13.735 2.749-3.061 6.283-4.788 12.487-4.788 2.12 0 3.455.157 5.262.471v-7.22c-1.65-.393-3.063-.472-5.184-.472-8.402 0-15.078 4.945-17.355 12.558v-11.38zM334.807 69C351.534 69 363 60.523 363 47.887c0-12.637-11.466-21.114-28.193-21.114-16.727 0-28.193 8.477-28.193 21.114C306.614 60.523 318.08 69 334.807 69zm0-6.2c-12.329 0-20.261-5.809-20.261-14.913 0-9.105 7.932-14.913 20.261-14.913 12.251 0 20.261 5.808 20.261 14.913 0 9.104-8.01 14.912-20.261 14.912z"/>
- </svg>
- </a>
- <a href="https://docs.astro.build/">
- <h1 class="sr-only">Docs</h1>
- <svg xmlns="http://www.w3.org/2000/svg" width="226" height="102" viewBox="0 0 226 102" fill="none">
- <path fill="currentColor" d="M25.805 68c14.688 0 24.883-8.41 24.883-21.14 0-12.786-9.62-19.756-24.653-19.756H0V68h25.805zm-14.17-33.005H24.25c8.352 0 14.17 4.09 14.17 12.039 0 8.236-5.3 13.075-14.113 13.075H11.635V34.995zM82.673 69.382c16.704 0 27.418-8.582 27.418-21.83 0-13.248-10.771-21.83-27.418-21.83-16.589 0-27.418 8.582-27.418 21.83 0 13.19 10.83 21.83 27.418 21.83zm0-8.64c-9.1 0-15.149-5.299-15.149-13.19 0-7.891 6.048-13.19 15.15-13.19 9.1 0 15.205 5.299 15.205 13.19 0 7.891-6.105 13.19-15.206 13.19zM141.497 69.382c13.306 0 22.637-5.299 25.978-14.572l-11.866-2.535c-1.67 5.415-6.393 8.295-13.709 8.295-9.216 0-15.033-5.127-15.033-13.018 0-8.006 5.702-13.018 14.918-13.018 7.43 0 12.154 3.053 13.709 8.64l12.038-2.13c-2.707-9.562-12.268-15.322-25.574-15.322-16.128 0-27.302 9.043-27.302 22.003 0 13.133 10.425 21.657 26.841 21.657zM194.94 69.382c14.745 0 23.212-5.01 23.212-14.054 0-7.603-4.665-10.944-15.955-12.096l-11.289-1.094c-5.242-.576-6.97-1.556-6.97-4.09 0-2.765 3.456-4.262 9.792-4.262 7.834 0 13.709 2.476 16.762 6.508l7.315-6.163c-5.069-5.702-13.133-8.41-23.501-8.41-13.997 0-21.888 4.781-21.888 12.903 0 7.546 4.781 11.232 14.803 12.326l12.557 1.383c4.896.518 6.624 1.555 6.624 4.09 0 3.225-3.456 4.723-10.886 4.723-8.352 0-14.688-3.226-18.087-8.007l-8.294 5.818c4.205 6.451 13.709 10.425 25.805 10.425z"/>
- </svg>
- </a>
- </div>
- <div style="flex-grow: 1;"></div>
- {lang && <LanguageSelect lang={lang} client:idle />}
- <div class="search-item"><Search lang={lang} client:idle /></div>
- </nav>
-</header>
diff --git a/docs/src/components/Header/LanguageSelect.css b/docs/src/components/Header/LanguageSelect.css
index f7b42c1e7..4c6cf6123 100644
--- a/docs/src/components/Header/LanguageSelect.css
+++ b/docs/src/components/Header/LanguageSelect.css
@@ -1,50 +1,50 @@
.language-select {
- flex-grow: 1;
- width: 48px;
- box-sizing: border-box;
- margin: 0;
- padding: 0.33em 2rem;
- overflow: visible;
- font-weight: 500;
- font-size: 1rem;
- font-family: inherit;
- line-height: inherit;
- background-color: var(--theme-bg);
- border-color: var(--theme-text-lighter);
- color: var(--theme-text-light);
- border-style: solid;
- border-width: 1px;
- border-radius: 0.25rem;
- outline: 0;
- cursor: pointer;
- transition-timing-function: ease-out;
- transition-duration: 0.2s;
- transition-property: border-color, color;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
- background-position: 97%;
- background-repeat: no-repeat;
- background-size: 1.5em 1.5em;
- -webkit-font-smoothing: antialiased;
- -webkit-appearance: none;
+ flex-grow: 1;
+ width: 48px;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0.33em 2rem;
+ overflow: visible;
+ font-weight: 500;
+ font-size: 1rem;
+ font-family: inherit;
+ line-height: inherit;
+ background-color: var(--theme-bg);
+ border-color: var(--theme-text-lighter);
+ color: var(--theme-text-light);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.25rem;
+ outline: 0;
+ cursor: pointer;
+ transition-timing-function: ease-out;
+ transition-duration: 0.2s;
+ transition-property: border-color, color;
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
+ background-position: 97%;
+ background-repeat: no-repeat;
+ background-size: 1.5em 1.5em;
+ -webkit-font-smoothing: antialiased;
+ -webkit-appearance: none;
}
.language-select-wrapper .language-select:hover,
.language-select-wrapper .language-select:focus {
- color: var(--theme-text);
- border-color: var(--theme-text-light);
+ color: var(--theme-text);
+ border-color: var(--theme-text-light);
}
.language-select-wrapper {
- color: var(--theme-text-light);
- position: relative;
+ color: var(--theme-text-light);
+ position: relative;
}
.language-select-wrapper > svg {
- position: absolute;
- top: 8px;
- left: 8px;
- pointer-events: none;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ pointer-events: none;
}
@media (min-width: 50em) {
- .language-select {
- width: 100%;
- }
+ .language-select {
+ width: 100%;
+ }
}
diff --git a/docs/src/components/Header/LanguageSelect.tsx b/docs/src/components/Header/LanguageSelect.tsx
index f88c559f2..37fef6d08 100644
--- a/docs/src/components/Header/LanguageSelect.tsx
+++ b/docs/src/components/Header/LanguageSelect.tsx
@@ -3,104 +3,104 @@ import { h } from 'preact';
import './LanguageSelect.css';
const LanguageSelect: FunctionalComponent<{ lang: string }> = ({ lang }) => {
- return (
- <div class="language-select-wrapper">
- <svg
- aria-hidden="true"
- focusable="false"
- role="img"
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 0 88.6 77.3"
- height="1.2em"
- width="1.2em"
- >
- <path
- fill="currentColor"
- d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z"
- />
- <path
- fill="currentColor"
- d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8 c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z"
- />
- </svg>
- <select
- class="language-select"
- value={lang}
- aria-label="Select language"
- onChange={(e) => {
- const newLang = e.target.value;
- if (newLang === 'en') {
- window.location.pathname = `/getting-started`;
- } else {
- window.location.pathname = `/${newLang}/getting-started`;
- }
- // TODO: Preserve the current page, if it exists:
- // const oldPathname = window.location.pathname;
- // const oldPathnameParts = oldPathname.split('/');
- // oldPathnameParts.shift();
- // if (/^[a-z]{2}$/.test(oldPathnameParts[0])) {
- // oldPathnameParts.shift();
- // }
- // if (newLang !== 'en') {
- // oldPathnameParts.unshift(newLang);
- // }
- // window.location.pathname = '/' + oldPathnameParts.join('/');
- }}
- >
- <option value="en">
- <span>English</span>
- </option>
- <option value="de">
- <span>Deutsch</span>
- </option>
- <option value="nl">
- <span>Nederlands</span>
- </option>
- <option value="pt-br">
- <span>Português do Brasil</span>
- </option>
- <option value="fi">
- <span>Suomi</span>
- </option>
- <option value="es">
- <span>Español</span>
- </option>
- <option value="zh-CN">
- <span>简体中文</span>
- </option>
- <option value="zh-TW">
- <span>正體中文</span>
- </option>
- <option value="bg">
- <span>Български</span>
- </option>
- <option value="fr">
- <span>Français</span>
- </option>
- <option value="bn">
- <span>বাংলা</span>
- </option>
- <option value="kr">
- <span>한국어</span>
- </option>
- <option value="ar">
- <span>العربية</span>
- </option>
- <option value="da">
- <span>Dansk</span>
- </option>
- <option value="ja">
- <span>日本語</span>
- </option>
- <option value="ru">
- <span>Русский</span>
- </option>
- <option value="it">
- <span>Italiano</span>
- </option>
- </select>
- </div>
- );
+ return (
+ <div class="language-select-wrapper">
+ <svg
+ aria-hidden="true"
+ focusable="false"
+ role="img"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 88.6 77.3"
+ height="1.2em"
+ width="1.2em"
+ >
+ <path
+ fill="currentColor"
+ d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z"
+ />
+ <path
+ fill="currentColor"
+ d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8 c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z"
+ />
+ </svg>
+ <select
+ class="language-select"
+ value={lang}
+ aria-label="Select language"
+ onChange={(e) => {
+ const newLang = e.target.value;
+ if (newLang === 'en') {
+ window.location.pathname = `/getting-started`;
+ } else {
+ window.location.pathname = `/${newLang}/getting-started`;
+ }
+ // TODO: Preserve the current page, if it exists:
+ // const oldPathname = window.location.pathname;
+ // const oldPathnameParts = oldPathname.split('/');
+ // oldPathnameParts.shift();
+ // if (/^[a-z]{2}$/.test(oldPathnameParts[0])) {
+ // oldPathnameParts.shift();
+ // }
+ // if (newLang !== 'en') {
+ // oldPathnameParts.unshift(newLang);
+ // }
+ // window.location.pathname = '/' + oldPathnameParts.join('/');
+ }}
+ >
+ <option value="en">
+ <span>English</span>
+ </option>
+ <option value="de">
+ <span>Deutsch</span>
+ </option>
+ <option value="nl">
+ <span>Nederlands</span>
+ </option>
+ <option value="pt-br">
+ <span>Português do Brasil</span>
+ </option>
+ <option value="fi">
+ <span>Suomi</span>
+ </option>
+ <option value="es">
+ <span>Español</span>
+ </option>
+ <option value="zh-CN">
+ <span>简体中文</span>
+ </option>
+ <option value="zh-TW">
+ <span>正體中文</span>
+ </option>
+ <option value="bg">
+ <span>Български</span>
+ </option>
+ <option value="fr">
+ <span>Français</span>
+ </option>
+ <option value="bn">
+ <span>বাংলা</span>
+ </option>
+ <option value="kr">
+ <span>한국어</span>
+ </option>
+ <option value="ar">
+ <span>العربية</span>
+ </option>
+ <option value="da">
+ <span>Dansk</span>
+ </option>
+ <option value="ja">
+ <span>日本語</span>
+ </option>
+ <option value="ru">
+ <span>Русский</span>
+ </option>
+ <option value="it">
+ <span>Italiano</span>
+ </option>
+ </select>
+ </div>
+ );
};
export default LanguageSelect;
diff --git a/docs/src/components/Header/Search.css b/docs/src/components/Header/Search.css
index 563f67bc5..42da3832c 100644
--- a/docs/src/components/Header/Search.css
+++ b/docs/src/components/Header/Search.css
@@ -1,75 +1,75 @@
/** Style Algolia */
:root {
- --docsearch-primary-color: var(--theme-accent);
- --docsearch-logo-color: var(--theme-text);
+ --docsearch-primary-color: var(--theme-accent);
+ --docsearch-logo-color: var(--theme-text);
}
.DocSearch-Modal .DocSearch-Hit a {
- box-shadow: none;
- border: 1px solid var(--theme-accent);
+ box-shadow: none;
+ border: 1px solid var(--theme-accent);
}
/** Style Search Bar */
.search-placeholder {
- flex-grow: 1;
- text-align: initial;
+ flex-grow: 1;
+ text-align: initial;
}
.search-input {
- flex-grow: 1;
- box-sizing: border-box;
- width: 100%;
- margin: 0;
- padding: 0.33em 0.5em;
- overflow: visible;
- font-weight: 500;
- font-size: 1rem;
- font-family: inherit;
- line-height: inherit;
- background-color: var(--theme-divider);
- border-color: var(--theme-divider);
- color: var(--theme-text-light);
- border-style: solid;
- border-width: 1px;
- border-radius: 0.25rem;
- outline: 0;
- cursor: pointer;
- transition-timing-function: ease-out;
- transition-duration: 0.2s;
- transition-property: border-color, color;
- -webkit-font-smoothing: antialiased;
+ flex-grow: 1;
+ box-sizing: border-box;
+ width: 100%;
+ margin: 0;
+ padding: 0.33em 0.5em;
+ overflow: visible;
+ font-weight: 500;
+ font-size: 1rem;
+ font-family: inherit;
+ line-height: inherit;
+ background-color: var(--theme-divider);
+ border-color: var(--theme-divider);
+ color: var(--theme-text-light);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.25rem;
+ outline: 0;
+ cursor: pointer;
+ transition-timing-function: ease-out;
+ transition-duration: 0.2s;
+ transition-property: border-color, color;
+ -webkit-font-smoothing: antialiased;
}
.search-input:hover,
.search-input:focus {
- color: var(--theme-text);
- border-color: var(--theme-text-light);
+ color: var(--theme-text);
+ border-color: var(--theme-text-light);
}
.search-input:hover::placeholder,
.search-input:focus::placeholder {
- color: var(--theme-text-light);
+ color: var(--theme-text-light);
}
.search-input::placeholder {
- color: var(--theme-text-light);
+ color: var(--theme-text-light);
}
.search-hint {
- padding: 3px 5px;
- display: none;
- display: none;
- align-items: center;
- justify-content: center;
- letter-spacing: 0.125em;
- font-size: 13px;
- font-family: var(--font-mono);
- pointer-events: none;
- border-color: var(--theme-text-lighter);
- color: var(--theme-text-light);
- border-style: solid;
- border-width: 1px;
- border-radius: 0.25rem;
- line-height: 14px;
+ padding: 3px 5px;
+ display: none;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ letter-spacing: 0.125em;
+ font-size: 13px;
+ font-family: var(--font-mono);
+ pointer-events: none;
+ border-color: var(--theme-text-lighter);
+ color: var(--theme-text-light);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.25rem;
+ line-height: 14px;
}
@media (min-width: 50em) {
- .search-hint {
- display: flex;
- }
+ .search-hint {
+ display: flex;
+ }
}
diff --git a/docs/src/components/Header/Search.tsx b/docs/src/components/Header/Search.tsx
index 6236b5806..8f2cfe649 100644
--- a/docs/src/components/Header/Search.tsx
+++ b/docs/src/components/Header/Search.tsx
@@ -6,88 +6,88 @@ import '@docsearch/css/dist/style.css';
import './Search.css';
const { DocSearchModal, useDocSearchKeyboardEvents } =
- (docsearch as unknown as { default: typeof docsearch }).default || docsearch;
+ (docsearch as unknown as { default: typeof docsearch }).default || docsearch;
export default function Search(props) {
- const [isOpen, setIsOpen] = useState(false);
- const searchButtonRef = useRef();
- const [initialQuery, setInitialQuery] = useState(null);
- const { lang = 'en' } = props;
+ const [isOpen, setIsOpen] = useState(false);
+ const searchButtonRef = useRef();
+ const [initialQuery, setInitialQuery] = useState(null);
+ const { lang = 'en' } = props;
- const onOpen = useCallback(() => {
- setIsOpen(true);
- }, [setIsOpen]);
+ const onOpen = useCallback(() => {
+ setIsOpen(true);
+ }, [setIsOpen]);
- const onClose = useCallback(() => {
- setIsOpen(false);
- }, [setIsOpen]);
+ const onClose = useCallback(() => {
+ setIsOpen(false);
+ }, [setIsOpen]);
- const onInput = useCallback(
- (e) => {
- setIsOpen(true);
- setInitialQuery(e.key);
- },
- [setIsOpen, setInitialQuery]
- );
+ const onInput = useCallback(
+ (e) => {
+ setIsOpen(true);
+ setInitialQuery(e.key);
+ },
+ [setIsOpen, setInitialQuery]
+ );
- useDocSearchKeyboardEvents({
- isOpen,
- onOpen,
- onClose,
- onInput,
- searchButtonRef,
- });
+ useDocSearchKeyboardEvents({
+ isOpen,
+ onOpen,
+ onClose,
+ onInput,
+ searchButtonRef,
+ });
- return (
- <>
- <button
- type="button"
- ref={searchButtonRef}
- onClick={onOpen}
- className="search-input"
- >
- <svg width="24" height="24" fill="none">
- <path
- d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
- stroke="currentColor"
- strokeWidth="2"
- strokeLinecap="round"
- strokeLinejoin="round"
- />
- </svg>
- <span className="search-placeholder">Search</span>
- <span className="search-hint">
- <span className="sr-only">Press </span>
- <kbd>/</kbd>
- <span className="sr-only"> to search</span>
- </span>
- </button>
- {isOpen &&
- createPortal(
- <DocSearchModal
- initialQuery={initialQuery}
- initialScrollY={window.scrollY}
- onClose={onClose}
- indexName="astro"
- apiKey="0f387260ad74f9cbf4353facd29c919c"
- // Set facetFilters once Astro docs have been indexed by language
- // searchParameters={{ facetFilters: [`lang:${lang}`] }}
- transformItems={(items) => {
- return items.map((item) => {
- // We transform the absolute URL into a relative URL to
- // work better on localhost, preview URLS.
- const a = document.createElement('a');
- a.href = item.url;
- const hash = a.hash === '#overview' ? '' : a.hash;
- return {
- ...item,
- url: `${a.pathname}${hash}`,
- };
- });
- }}
- />,
- document.body
- )}
- </>
- );
+ return (
+ <>
+ <button
+ type="button"
+ ref={searchButtonRef}
+ onClick={onOpen}
+ className="search-input"
+ >
+ <svg width="24" height="24" fill="none">
+ <path
+ d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
+ stroke="currentColor"
+ strokeWidth="2"
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ />
+ </svg>
+ <span className="search-placeholder">Search</span>
+ <span className="search-hint">
+ <span className="sr-only">Press </span>
+ <kbd>/</kbd>
+ <span className="sr-only"> to search</span>
+ </span>
+ </button>
+ {isOpen &&
+ createPortal(
+ <DocSearchModal
+ initialQuery={initialQuery}
+ initialScrollY={window.scrollY}
+ onClose={onClose}
+ indexName="astro"
+ apiKey="0f387260ad74f9cbf4353facd29c919c"
+ // Set facetFilters once Astro docs have been indexed by language
+ // searchParameters={{ facetFilters: [`lang:${lang}`] }}
+ transformItems={(items) => {
+ return items.map((item) => {
+ // We transform the absolute URL into a relative URL to
+ // work better on localhost, preview URLS.
+ const a = document.createElement('a');
+ a.href = item.url;
+ const hash = a.hash === '#overview' ? '' : a.hash;
+ return {
+ ...item,
+ url: `${a.pathname}${hash}`,
+ };
+ });
+ }}
+ />,
+ document.body
+ )}
+ </>
+ );
}
diff --git a/docs/src/components/Header/SidebarToggle.tsx b/docs/src/components/Header/SidebarToggle.tsx
index 605581077..2be9dee9a 100644
--- a/docs/src/components/Header/SidebarToggle.tsx
+++ b/docs/src/components/Header/SidebarToggle.tsx
@@ -3,42 +3,42 @@ import { h, Fragment } from 'preact';
import { useState, useEffect } from 'preact/hooks';
const MenuToggle: FunctionalComponent = () => {
- const [sidebarShown, setSidebarShown] = useState(false);
+ const [sidebarShown, setSidebarShown] = useState(false);
- useEffect(() => {
- const body = document.getElementsByTagName('body')[0];
- if (sidebarShown) {
- body.classList.add('mobile-sidebar-toggle');
- } else {
- body.classList.remove('mobile-sidebar-toggle');
- }
- }, [sidebarShown]);
+ useEffect(() => {
+ const body = document.getElementsByTagName('body')[0];
+ if (sidebarShown) {
+ body.classList.add('mobile-sidebar-toggle');
+ } else {
+ body.classList.remove('mobile-sidebar-toggle');
+ }
+ }, [sidebarShown]);
- return (
- <button
- type="button"
- aria-pressed={sidebarShown ? 'true' : 'false'}
- id="menu-toggle"
- onClick={() => setSidebarShown(!sidebarShown)}
- >
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="1em"
- height="1em"
- fill="none"
- viewBox="0 0 24 24"
- stroke="currentColor"
- >
- <path
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="2"
- d="M4 6h16M4 12h16M4 18h16"
- />
- </svg>
- <span className="sr-only">Toggle sidebar</span>
- </button>
- );
+ return (
+ <button
+ type="button"
+ aria-pressed={sidebarShown ? 'true' : 'false'}
+ id="menu-toggle"
+ onClick={() => setSidebarShown(!sidebarShown)}
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="1em"
+ height="1em"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke="currentColor"
+ >
+ <path
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ stroke-width="2"
+ d="M4 6h16M4 12h16M4 18h16"
+ />
+ </svg>
+ <span className="sr-only">Toggle sidebar</span>
+ </button>
+ );
};
export default MenuToggle;
diff --git a/docs/src/components/Header/SkipToContent.astro b/docs/src/components/Header/SkipToContent.astro
index 6df3a72ed..91df15b93 100644
--- a/docs/src/components/Header/SkipToContent.astro
+++ b/docs/src/components/Header/SkipToContent.astro
@@ -1,21 +1,22 @@
+<a href="#article" class="sr-only skiplink"><span>Skip to Content</span></a>
+
<style>
-.skiplink,
-.skiplink:focus,
-.skiplink:focus-visible {
- position: absolute;
- padding: 0.25em;
- font-size: larger;
- top: 0;
- left: 0;
- right: 0;
- z-index: 9;
- display: block;
- text-align: center;
- background-color: var(--theme-text-accent);
- color: var(--theme-bg);
- border-radius: 0.25em;
- outline: var(--theme-bg) solid 1px;
- outline-offset: 0;
-}
+ .skiplink,
+ .skiplink:focus,
+ .skiplink:focus-visible {
+ position: absolute;
+ padding: 0.25em;
+ font-size: larger;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 9;
+ display: block;
+ text-align: center;
+ background-color: var(--theme-text-accent);
+ color: var(--theme-bg);
+ border-radius: 0.25em;
+ outline: var(--theme-bg) solid 1px;
+ outline-offset: 0;
+ }
</style>
-<a href="#article" class="sr-only skiplink"><span>Skip to Content</span></a>
diff --git a/docs/src/components/LeftSidebar/LeftSidebar.astro b/docs/src/components/LeftSidebar/LeftSidebar.astro
index b4c2d1ed5..a37b04d53 100644
--- a/docs/src/components/LeftSidebar/LeftSidebar.astro
+++ b/docs/src/components/LeftSidebar/LeftSidebar.astro
@@ -1,153 +1,186 @@
---
import { SIDEBAR } from '../../config.ts';
-import { getLanguageFromURL, removeLeadingSlash, removeTrailingSlash } from '../../util.ts';
-const {currentPage} = Astro.props;
+import {
+ getLanguageFromURL,
+ removeLeadingSlash,
+ removeTrailingSlash,
+} from '../../util.ts';
+const { currentPage } = Astro.props;
// Get the slug w/o a leading or trailing slash
const currentPageMatch = removeLeadingSlash(removeTrailingSlash(currentPage));
const langCode = getLanguageFromURL(currentPage);
// SIDEBAR is a flat array. Group it by sections to properly render.
const sidebarSections = SIDEBAR[langCode].reduce((col, item) => {
- if (item.header) {
- col.push({...item, children: []});
- } else {
- col[col.length-1].children.push(item);
- }
- return col;
+ if (item.header) {
+ col.push({ ...item, children: [] });
+ } else {
+ col[col.length - 1].children.push(item);
+ }
+ return col;
}, []);
-
---
<nav aria-labelledby="grid-left">
- <ul class="nav-groups">
- <li>
- <div class="nav-group">
- <h2 class="sponsors-title">Sponsored by</h2>
- <div class="sponsors">
- <a href="https://www.netlify.com/" aria-label="Go to Netlify website">
- <svg class="sponsor-logo__netlify" viewBox="0 0 147 40" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><radialGradient id="netlify-gradient" cx="-779.0521" cy="1839.7205" gradientTransform="matrix(0 38.301 44.1228 0 -81154.2578 29839.2441)" gradientUnits="userSpaceOnUse" r="1.0011"><stop offset="0" stop-color="#20c6b7"/><stop offset="1" stop-color="#4d9abf"/></radialGradient><path clip-rule="evenodd" d="m53.37 12.98.12 2.2c1.4-1.7 3.24-2.55 5.53-2.55 3.95 0 5.96 2.27 6.03 6.8v12.57h-4.26v-12.32c0-1.21-.26-2.1-.78-2.68s-1.37-.87-2.55-.87c-1.72 0-3 .78-3.84 2.34v13.53h-4.26v-19.02zm24.38 19.37c-2.7 0-4.89-.85-6.57-2.56-1.68-1.7-2.52-3.98-2.52-6.81v-.53c0-1.9.36-3.59 1.1-5.09.73-1.49 1.76-2.66 3.08-3.49s2.79-1.25 4.42-1.25c2.58 0 4.58.83 5.99 2.48s2.11 3.99 2.11 7.01v1.72h-12.4c.13 1.57.65 2.81 1.57 3.73s2.07 1.37 3.46 1.37c1.95 0 3.54-.79 4.77-2.37l2.3 2.2c-.76 1.14-1.77 2.02-3.04 2.65s-2.69.94-4.27.94zm-.51-16.29c-1.17 0-2.11.41-2.83 1.23s-1.18 1.96-1.38 3.43h8.12v-.32c-.09-1.43-.47-2.51-1.14-3.24-.67-.74-1.59-1.1-2.77-1.1zm16.76-7.7v4.62h3.35v3.16h-3.35v10.62c0 .73.14 1.25.43 1.57s.8.48 1.54.48c.5 0 1-.06 1.49-.18v3.31c-.97.27-1.9.4-2.81.4-3.27 0-4.91-1.81-4.91-5.43v-10.77h-3.12v-3.16h3.12v-4.63zm11.14 23.64h-4.26v-27h4.26zm9.17 0h-4.26v-19.02h4.26zm-4.52-23.96c0-.65.21-1.2.62-1.63.42-.43 1.01-.65 1.78-.65s1.37.22 1.79.65.63.98.63 1.64c0 .64-.21 1.18-.63 1.61s-1.02.64-1.79.64-1.36-.21-1.78-.64c-.41-.44-.62-.98-.62-1.62zm10.66 23.96v-15.86h-2.89v-3.16h2.89v-1.74c0-2.11.58-3.74 1.75-4.89s2.81-1.72 4.91-1.72c.75 0 1.54.11 2.39.32l-.1 3.34c-.54-.1-1.08-.15-1.63-.14-2.04 0-3.05 1.05-3.05 3.15v1.69h3.86v3.16h-3.86v15.85zm17.87-6.12 3.86-12.9h4.54l-7.54 21.9c-1.16 3.2-3.12 4.8-5.89 4.8-.62 0-1.3-.11-2.05-.32v-3.31l.81.05c1.07 0 1.88-.2 2.43-.59.54-.39.97-1.05 1.29-1.98l.61-1.64-6.66-18.93h4.6z" fill-rule="evenodd"/><path d="m27.89 14.14-.01-.01c-.01 0-.02-.01-.02-.01-.02-.02-.03-.06-.03-.09l.77-4.73 3.62 3.63-3.77 1.6c-.01 0-.02.01-.03.01h-.02s-.01-.01-.02-.02c-.14-.16-.31-.29-.49-.38zm5.26-.29 3.88 3.88c.81.81 1.21 1.21 1.35 1.67.02.07.04.14.05.21l-9.26-3.92s-.01 0-.01-.01c-.04-.02-.08-.03-.08-.07s.04-.06.08-.07l.01-.01zm5.12 7c-.2.38-.59.77-1.25 1.43l-4.37 4.37-5.65-1.18-.03-.01c-.05-.01-.1-.02-.1-.06-.04-.47-.28-.9-.66-1.19-.02-.02-.02-.06-.01-.09v-.01l1.06-6.53v-.02c.01-.05.01-.11.06-.11.46-.06.88-.3 1.16-.67.01-.01.01-.02.03-.03.03-.01.07 0 .1.01zm-6.62 6.8-7.19 7.19 1.23-7.56v-.01c0-.01 0-.02.01-.03.01-.02.04-.03.06-.04h.01c.27-.11.51-.29.69-.52.02-.03.05-.06.09-.06h.03zm-8.71 8.71-.81.81-8.95-12.94s-.01-.01-.01-.01c-.01-.02-.03-.04-.03-.06s.01-.03.02-.04l.01-.01c.03-.04.05-.08.07-.12l.02-.03c.01-.02.03-.05.05-.06s.05-.01.07 0l9.92 2.05c.03 0 .05.02.08.03.01.01.02.03.02.04.14.53.52.97 1.03 1.17.03.01.02.05 0 .08-.01.01-.01.03-.01.05-.12.74-1.19 7.27-1.48 9.04zm-1.69 1.69c-.6.59-.95.9-1.35 1.03-.39.12-.81.12-1.21 0-.47-.15-.87-.55-1.67-1.36l-8.99-8.99 2.35-3.64c.01-.02.02-.03.04-.05s.06-.01.09 0c.54.16 1.12.13 1.64-.08.03-.01.05-.02.07 0l.03.03zm-14.09-10.19-2.06-2.06 4.07-1.74c.01 0 .02-.01.03-.01.03 0 .05.03.07.07.04.06.08.12.13.18l.01.02c.01.02 0 .03-.01.05zm-2.98-2.97-2.61-2.61c-.44-.44-.77-.77-.99-1.04l7.94 1.65h.03c.05.01.1.02.1.06 0 .05-.06.07-.11.09l-.02.01zm-4.05-5c.01-.17.04-.33.09-.5.15-.47.55-.87 1.36-1.67l3.34-3.34c1.54 2.23 3.08 4.46 4.63 6.69.03.04.06.08.03.11-.15.16-.29.34-.4.53-.01.02-.03.05-.05.06-.01.01-.03 0-.04 0zm5.68-6.4 4.49-4.49c.42.19 1.96.83 3.33 1.41 1.04.44 1.99.84 2.29.97.03.01.06.02.07.05.01.02 0 .04 0 .06-.14.66.05 1.35.52 1.83.03.03 0 .07-.03.11l-.01.02-4.56 7.06c-.01.02-.02.04-.04.05s-.06.01-.09 0c-.18-.05-.36-.07-.54-.07-.16 0-.34.03-.52.06-.02 0-.04.01-.05 0-.02-.01-.03-.03-.05-.05zm5.4-5.4 5.81-5.81c.81-.81 1.21-1.21 1.67-1.36.39-.12.81-.12 1.21 0 .47.15.87.55 1.67 1.36l1.26 1.26-4.14 6.4c-.01.02-.02.03-.04.05s-.06.01-.09 0c-.66-.2-1.38-.06-1.92.37-.03.03-.07.01-.1 0-.53-.24-4.73-2.01-5.33-2.27zm12.5-3.67 3.82 3.82-.92 5.7v.02c0 .01 0 .03-.01.04-.01.02-.03.02-.05.03-.2.06-.38.15-.55.27-.01.01-.01.01-.02.02s-.02.02-.04.02c-.01 0-.03 0-.04-.01l-5.82-2.47-.01-.01c-.04-.02-.08-.03-.08-.07-.03-.32-.14-.64-.31-.91-.03-.05-.06-.09-.03-.14zm-3.93 8.6 5.45 2.31c.03.01.06.03.08.06.01.02.01.04 0 .06-.02.08-.03.17-.03.26v.15c0 .04-.04.05-.08.07h-.01c-.86.37-12.13 5.17-12.15 5.17s-.03 0-.05-.02c-.03-.03 0-.07.03-.11 0-.01.01-.01.01-.02l4.48-6.94.01-.01c.03-.04.06-.09.1-.09l.05.01c.1.01.19.03.28.03.68 0 1.31-.33 1.69-.9.01-.02.02-.03.03-.04.04-.01.08 0 .11.01zm-6.25 9.19 12.28-5.24s.02 0 .03.02c.07.07.12.11.18.15l.03.02c.02.01.05.03.05.06v.02l-1.05 6.46v.03c-.01.05-.01.11-.06.11-.57.04-1.08.36-1.37.85v.01c-.01.02-.03.05-.05.06s-.05.01-.07 0l-9.79-2.02c-.02-.02-.16-.53-.18-.53z" fill="url(#netlify-gradient)"/></svg>
- </a>
- </div>
- </div>
- </li>
- {sidebarSections.map(section => (
- <li>
- <div class="nav-group">
- <h2 class="nav-group-title">{section.text}</h2>
- <ul>
- {section.children.map(child => (
- <li class="nav-link"><a href={`${Astro.site.pathname}${child.link}`} aria-current={`${currentPageMatch === child.link ? 'page' : 'false'}`}>{child.text}</a></li>
- ))}
- </ul>
- </div>
- </li>
- ))}
- </ul>
+ <ul class="nav-groups">
+ <li>
+ <div class="nav-group">
+ <h2 class="sponsors-title">Sponsored by</h2>
+ <div class="sponsors">
+ <a href="https://www.netlify.com/" aria-label="Go to Netlify website">
+ <svg
+ class="sponsor-logo__netlify"
+ viewBox="0 0 147 40"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ ><radialGradient
+ id="netlify-gradient"
+ cx="-779.0521"
+ cy="1839.7205"
+ gradientTransform="matrix(0 38.301 44.1228 0 -81154.2578 29839.2441)"
+ gradientUnits="userSpaceOnUse"
+ r="1.0011"
+ ><stop offset="0" stop-color="#20c6b7"></stop><stop
+ offset="1"
+ stop-color="#4d9abf"></stop></radialGradient
+ ><path
+ clip-rule="evenodd"
+ d="m53.37 12.98.12 2.2c1.4-1.7 3.24-2.55 5.53-2.55 3.95 0 5.96 2.27 6.03 6.8v12.57h-4.26v-12.32c0-1.21-.26-2.1-.78-2.68s-1.37-.87-2.55-.87c-1.72 0-3 .78-3.84 2.34v13.53h-4.26v-19.02zm24.38 19.37c-2.7 0-4.89-.85-6.57-2.56-1.68-1.7-2.52-3.98-2.52-6.81v-.53c0-1.9.36-3.59 1.1-5.09.73-1.49 1.76-2.66 3.08-3.49s2.79-1.25 4.42-1.25c2.58 0 4.58.83 5.99 2.48s2.11 3.99 2.11 7.01v1.72h-12.4c.13 1.57.65 2.81 1.57 3.73s2.07 1.37 3.46 1.37c1.95 0 3.54-.79 4.77-2.37l2.3 2.2c-.76 1.14-1.77 2.02-3.04 2.65s-2.69.94-4.27.94zm-.51-16.29c-1.17 0-2.11.41-2.83 1.23s-1.18 1.96-1.38 3.43h8.12v-.32c-.09-1.43-.47-2.51-1.14-3.24-.67-.74-1.59-1.1-2.77-1.1zm16.76-7.7v4.62h3.35v3.16h-3.35v10.62c0 .73.14 1.25.43 1.57s.8.48 1.54.48c.5 0 1-.06 1.49-.18v3.31c-.97.27-1.9.4-2.81.4-3.27 0-4.91-1.81-4.91-5.43v-10.77h-3.12v-3.16h3.12v-4.63zm11.14 23.64h-4.26v-27h4.26zm9.17 0h-4.26v-19.02h4.26zm-4.52-23.96c0-.65.21-1.2.62-1.63.42-.43 1.01-.65 1.78-.65s1.37.22 1.79.65.63.98.63 1.64c0 .64-.21 1.18-.63 1.61s-1.02.64-1.79.64-1.36-.21-1.78-.64c-.41-.44-.62-.98-.62-1.62zm10.66 23.96v-15.86h-2.89v-3.16h2.89v-1.74c0-2.11.58-3.74 1.75-4.89s2.81-1.72 4.91-1.72c.75 0 1.54.11 2.39.32l-.1 3.34c-.54-.1-1.08-.15-1.63-.14-2.04 0-3.05 1.05-3.05 3.15v1.69h3.86v3.16h-3.86v15.85zm17.87-6.12 3.86-12.9h4.54l-7.54 21.9c-1.16 3.2-3.12 4.8-5.89 4.8-.62 0-1.3-.11-2.05-.32v-3.31l.81.05c1.07 0 1.88-.2 2.43-.59.54-.39.97-1.05 1.29-1.98l.61-1.64-6.66-18.93h4.6z"
+ fill-rule="evenodd"></path><path
+ d="m27.89 14.14-.01-.01c-.01 0-.02-.01-.02-.01-.02-.02-.03-.06-.03-.09l.77-4.73 3.62 3.63-3.77 1.6c-.01 0-.02.01-.03.01h-.02s-.01-.01-.02-.02c-.14-.16-.31-.29-.49-.38zm5.26-.29 3.88 3.88c.81.81 1.21 1.21 1.35 1.67.02.07.04.14.05.21l-9.26-3.92s-.01 0-.01-.01c-.04-.02-.08-.03-.08-.07s.04-.06.08-.07l.01-.01zm5.12 7c-.2.38-.59.77-1.25 1.43l-4.37 4.37-5.65-1.18-.03-.01c-.05-.01-.1-.02-.1-.06-.04-.47-.28-.9-.66-1.19-.02-.02-.02-.06-.01-.09v-.01l1.06-6.53v-.02c.01-.05.01-.11.06-.11.46-.06.88-.3 1.16-.67.01-.01.01-.02.03-.03.03-.01.07 0 .1.01zm-6.62 6.8-7.19 7.19 1.23-7.56v-.01c0-.01 0-.02.01-.03.01-.02.04-.03.06-.04h.01c.27-.11.51-.29.69-.52.02-.03.05-.06.09-.06h.03zm-8.71 8.71-.81.81-8.95-12.94s-.01-.01-.01-.01c-.01-.02-.03-.04-.03-.06s.01-.03.02-.04l.01-.01c.03-.04.05-.08.07-.12l.02-.03c.01-.02.03-.05.05-.06s.05-.01.07 0l9.92 2.05c.03 0 .05.02.08.03.01.01.02.03.02.04.14.53.52.97 1.03 1.17.03.01.02.05 0 .08-.01.01-.01.03-.01.05-.12.74-1.19 7.27-1.48 9.04zm-1.69 1.69c-.6.59-.95.9-1.35 1.03-.39.12-.81.12-1.21 0-.47-.15-.87-.55-1.67-1.36l-8.99-8.99 2.35-3.64c.01-.02.02-.03.04-.05s.06-.01.09 0c.54.16 1.12.13 1.64-.08.03-.01.05-.02.07 0l.03.03zm-14.09-10.19-2.06-2.06 4.07-1.74c.01 0 .02-.01.03-.01.03 0 .05.03.07.07.04.06.08.12.13.18l.01.02c.01.02 0 .03-.01.05zm-2.98-2.97-2.61-2.61c-.44-.44-.77-.77-.99-1.04l7.94 1.65h.03c.05.01.1.02.1.06 0 .05-.06.07-.11.09l-.02.01zm-4.05-5c.01-.17.04-.33.09-.5.15-.47.55-.87 1.36-1.67l3.34-3.34c1.54 2.23 3.08 4.46 4.63 6.69.03.04.06.08.03.11-.15.16-.29.34-.4.53-.01.02-.03.05-.05.06-.01.01-.03 0-.04 0zm5.68-6.4 4.49-4.49c.42.19 1.96.83 3.33 1.41 1.04.44 1.99.84 2.29.97.03.01.06.02.07.05.01.02 0 .04 0 .06-.14.66.05 1.35.52 1.83.03.03 0 .07-.03.11l-.01.02-4.56 7.06c-.01.02-.02.04-.04.05s-.06.01-.09 0c-.18-.05-.36-.07-.54-.07-.16 0-.34.03-.52.06-.02 0-.04.01-.05 0-.02-.01-.03-.03-.05-.05zm5.4-5.4 5.81-5.81c.81-.81 1.21-1.21 1.67-1.36.39-.12.81-.12 1.21 0 .47.15.87.55 1.67 1.36l1.26 1.26-4.14 6.4c-.01.02-.02.03-.04.05s-.06.01-.09 0c-.66-.2-1.38-.06-1.92.37-.03.03-.07.01-.1 0-.53-.24-4.73-2.01-5.33-2.27zm12.5-3.67 3.82 3.82-.92 5.7v.02c0 .01 0 .03-.01.04-.01.02-.03.02-.05.03-.2.06-.38.15-.55.27-.01.01-.01.01-.02.02s-.02.02-.04.02c-.01 0-.03 0-.04-.01l-5.82-2.47-.01-.01c-.04-.02-.08-.03-.08-.07-.03-.32-.14-.64-.31-.91-.03-.05-.06-.09-.03-.14zm-3.93 8.6 5.45 2.31c.03.01.06.03.08.06.01.02.01.04 0 .06-.02.08-.03.17-.03.26v.15c0 .04-.04.05-.08.07h-.01c-.86.37-12.13 5.17-12.15 5.17s-.03 0-.05-.02c-.03-.03 0-.07.03-.11 0-.01.01-.01.01-.02l4.48-6.94.01-.01c.03-.04.06-.09.1-.09l.05.01c.1.01.19.03.28.03.68 0 1.31-.33 1.69-.9.01-.02.02-.03.03-.04.04-.01.08 0 .11.01zm-6.25 9.19 12.28-5.24s.02 0 .03.02c.07.07.12.11.18.15l.03.02c.02.01.05.03.05.06v.02l-1.05 6.46v.03c-.01.05-.01.11-.06.11-.57.04-1.08.36-1.37.85v.01c-.01.02-.03.05-.05.06s-.05.01-.07 0l-9.79-2.02c-.02-.02-.16-.53-.18-.53z"
+ fill="url(#netlify-gradient)"></path></svg
+ >
+ </a>
+ </div>
+ </div>
+ </li>
+ {sidebarSections.map((section) => (
+ <li>
+ <div class="nav-group">
+ <h2 class="nav-group-title">{section.text}</h2>
+ <ul>
+ {section.children.map((child) => (
+ <li class="nav-link">
+ <a
+ href={`${Astro.site.pathname}${child.link}`}
+ aria-current={`${
+ currentPageMatch === child.link ? 'page' : 'false'
+ }`}
+ >
+ {child.text}
+ </a>
+ </li>
+ ))}
+ </ul>
+ </div>
+ </li>
+ ))}
+ </ul>
</nav>
<script>
- window.addEventListener('DOMContentLoaded', (event) => {
- var target = document.querySelector('[aria-current="page"]');
- if (target && (target.offsetTop > (window.innerHeight - 100))) {
- document.querySelector('.nav-groups').scrollTop = target.offsetTop;
- }
- });
+ window.addEventListener('DOMContentLoaded', (event) => {
+ var target = document.querySelector('[aria-current="page"]');
+ if (target && target.offsetTop > window.innerHeight - 100) {
+ document.querySelector('.nav-groups').scrollTop = target.offsetTop;
+ }
+ });
</script>
<style lang="scss">
- nav {
- width: 100%;
- margin-right: 1rem;
- }
- .nav-groups {
- height: 100%;
- padding: 2rem 0;
- overflow-x: visible;
- overflow-y: auto;
- max-height: 100vh;
-
- > li + li {
- margin-top: 1.75rem;
- }
-
- > :first-child {
- padding-top: var(--doc-padding);
- }
-
- > :last-child {
- padding-bottom: 2rem;
- margin-bottom: var(--theme-navbar-height);
- }
-
- @media (min-width: 50em) {
- padding: 0;
- }
- }
-
- .nav-group-title {
- font-size: 1.0rem;
- font-weight: 700;
- padding: 0.1rem 1rem;
- text-transform: uppercase;
- margin-bottom: 0.5rem;
- }
-
- .nav-link a {
- font-size: 1.0rem;
- margin: 1px;
- padding: 0.3rem 1rem;
- font: inherit;
- color: inherit;
- text-decoration: none;
- display: block;
-
- &:hover,
- &:focus {
- background-color: var(--theme-bg-hover);
- }
-
- &[aria-current="page"] {
- color: var(--theme-text-accent);
- background-color: var(--theme-bg-accent);
- font-weight: 600;
- }
- }
-
- :global(:root.theme-dark) .nav-link a[aria-current="page"] {
- color: hsla(var(--color-base-white), 100%, 1);
- }
-
- .sponsors {
- display: grid;
- padding-left: 1rem;
- padding-top: 0.25rem;
- margin-bottom: -0.375rem; // logo overshoot creates extra perceived space
- grid-gap: 0.5rem;
- grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
-
- svg {
- color: var(--theme-text);
- fill: currentColor;
- }
- }
-
- .sponsor-logo__netlify {
- width: 90px;
- }
-
- .sponsor-logo__vercel {
- width: 90px;
- }
- :global(:root.theme-dark .sponsors-title) {
- color: hsl(var(--color-base-gray), 75%);
- }
- .sponsors-title {
- color: hsl(var(--color-base-gray), 25%);
- font-size: 0.8em;
- font-weight: 300;
- letter-spacing: 0.0625em;
- margin: 0 0 0.5rem;
- padding-left: 1rem;
- text-transform: uppercase;
- }
+ nav {
+ width: 100%;
+ margin-right: 1rem;
+ }
+ .nav-groups {
+ height: 100%;
+ padding: 2rem 0;
+ overflow-x: visible;
+ overflow-y: auto;
+ max-height: 100vh;
+
+ > li + li {
+ margin-top: 1.75rem;
+ }
+
+ > :first-child {
+ padding-top: var(--doc-padding);
+ }
+
+ > :last-child {
+ padding-bottom: 2rem;
+ margin-bottom: var(--theme-navbar-height);
+ }
+
+ @media (min-width: 50em) {
+ padding: 0;
+ }
+ }
+
+ .nav-group-title {
+ font-size: 1rem;
+ font-weight: 700;
+ padding: 0.1rem 1rem;
+ text-transform: uppercase;
+ margin-bottom: 0.5rem;
+ }
+
+ .nav-link a {
+ font-size: 1rem;
+ margin: 1px;
+ padding: 0.3rem 1rem;
+ font: inherit;
+ color: inherit;
+ text-decoration: none;
+ display: block;
+
+ &:hover,
+ &:focus {
+ background-color: var(--theme-bg-hover);
+ }
+
+ &[aria-current='page'] {
+ color: var(--theme-text-accent);
+ background-color: var(--theme-bg-accent);
+ font-weight: 600;
+ }
+ }
+
+ :global(:root.theme-dark) .nav-link a[aria-current='page'] {
+ color: hsla(var(--color-base-white), 100%, 1);
+ }
+
+ .sponsors {
+ display: grid;
+ padding-left: 1rem;
+ padding-top: 0.25rem;
+ margin-bottom: -0.375rem; // logo overshoot creates extra perceived space
+ grid-gap: 0.5rem;
+ grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
+
+ svg {
+ color: var(--theme-text);
+ fill: currentColor;
+ }
+ }
+
+ .sponsor-logo__netlify {
+ width: 90px;
+ }
+
+ .sponsor-logo__vercel {
+ width: 90px;
+ }
+ :global(:root.theme-dark .sponsors-title) {
+ color: hsl(var(--color-base-gray), 75%);
+ }
+ .sponsors-title {
+ color: hsl(var(--color-base-gray), 25%);
+ font-size: 0.8em;
+ font-weight: 300;
+ letter-spacing: 0.0625em;
+ margin: 0 0 0.5rem;
+ padding-left: 1rem;
+ text-transform: uppercase;
+ }
</style>
diff --git a/docs/src/components/PageContent/PageContent.astro b/docs/src/components/PageContent/PageContent.astro
index 91ea90d62..da9939e50 100644
--- a/docs/src/components/PageContent/PageContent.astro
+++ b/docs/src/components/PageContent/PageContent.astro
@@ -1,59 +1,78 @@
---
import MoreMenu from '../RightSidebar/MoreMenu.astro';
import TableOfContents from '../RightSidebar/TableOfContents.tsx';
-import {getLanguageFromURL} from '../../util.ts';
-import {SIDEBAR} from '../../config.ts';
-const {content, githubEditUrl, currentPage} = Astro.props;
+import { getLanguageFromURL } from '../../util.ts';
+import { SIDEBAR } from '../../config.ts';
+const { content, githubEditUrl, currentPage } = Astro.props;
const title = content.title;
const headers = content.astro?.headers;
const langCode = getLanguageFromURL(currentPage);
-const links = SIDEBAR[langCode].filter(x => x.link && typeof x.header === 'undefined');
+const links = SIDEBAR[langCode].filter(
+ (x) => x.link && typeof x.header === 'undefined'
+);
// handle cases with a trailing slash or not
-const index = links.findIndex(x => `/${x.link}/` === currentPage || `/${x.link}` === currentPage);
-const next = index !== -1 ? (index === links.length - 1 ? null : links[index + 1]) : null;
+const index = links.findIndex(
+ (x) => `/${x.link}/` === currentPage || `/${x.link}` === currentPage
+);
+const next =
+ index !== -1 ? (index === links.length - 1 ? null : links[index + 1]) : null;
const previous = index !== -1 ? (index === 0 ? null : links[index - 1]) : null;
---
+
+<article id="article" class="content">
+ <section class="main-section">
+ <h1 class="content-title" id="overview">{title}</h1>
+ {headers && (
+ <nav class="block sm:hidden">
+ <TableOfContents client:media="(max-width: 50em)" headers={headers} />
+ </nav>
+ )}
+ <slot />
+ </section>
+ <nav class="block sm:hidden">
+ <MoreMenu editHref={githubEditUrl} />
+ </nav>
+ {(previous || next) && (
+ <aside>
+ {previous && (
+ <div>
+ Previous Article:{' '}
+ <a rel="prev" href={new URL(previous.link, Astro.site).pathname}>
+ {previous.text}
+ </a>
+ </div>
+ )}
+ {next && (
+ <div>
+ Next Article:{' '}
+ <a rel="next" href={new URL(next.link, Astro.site).pathname}>
+ {next.text}
+ </a>
+ </div>
+ )}
+ </aside>
+ )}
+</article>
+
<style>
- .content {
- padding: 0;
- max-width: 75ch;
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- }
- .content > section {
- margin-bottom: 4rem;
- }
- .block {
- display: block;
- }
+ .content {
+ padding: 0;
+ max-width: 75ch;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ }
+ .content > section {
+ margin-bottom: 4rem;
+ }
+ .block {
+ display: block;
+ }
- @media (min-width: 50em) {
- .sm\:hidden {
- display: none;
- }
- }
+ @media (min-width: 50em) {
+ .sm\:hidden {
+ display: none;
+ }
+ }
</style>
-<article id="article" class="content">
- <section class="main-section">
- <h1 class="content-title" id="overview">{title}</h1>
- {headers && <nav class="block sm:hidden">
- <TableOfContents client:media="(max-width: 50em)" headers={headers}/>
- </nav>}
- <slot />
- </section>
- <nav class="block sm:hidden">
- <MoreMenu editHref={githubEditUrl}/>
- </nav>
- {
- (previous || next) && <aside>
- {
- previous && <div>Previous Article: <a rel="prev" href={new URL(previous.link, Astro.site).pathname}>{previous.text}</a></div>
- }
- {
- next && <div>Next Article: <a rel="next" href={new URL(next.link, Astro.site).pathname}>{next.text}</a></div>
- }
- </aside>
- }
-</article> \ No newline at end of file
diff --git a/docs/src/components/RightSidebar/MoreMenu.astro b/docs/src/components/RightSidebar/MoreMenu.astro
index 1b39cff10..981a1a9b8 100644
--- a/docs/src/components/RightSidebar/MoreMenu.astro
+++ b/docs/src/components/RightSidebar/MoreMenu.astro
@@ -1,68 +1,91 @@
---
import ThemeToggleButton from './ThemeToggleButton.tsx';
-const {editHref} = Astro.props;
+const { editHref } = Astro.props;
---
-<style>
- .edit-on-github {
- text-decoration: none;
- font: inherit;
- color: inherit;
- font-size: 1rem;
- }
-</style>
+
<h2 class="heading">More</h2>
<ul>
- <li class={`header-link depth-2`}>
- <a class="edit-on-github" href={editHref} target="_blank">
- <svg
- aria-hidden="true"
- focusable="false"
- data-prefix="fas"
- data-icon="pen"
- class="svg-inline--fa fa-pen fa-w-16"
- role="img"
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 0 512 512"
- height="1em"
- width="1em"
- >
- <path
- fill="currentColor"
- d="M290.74 93.24l128.02 128.02-277.99 277.99-114.14 12.6C11.35 513.54-1.56 500.62.14 485.34l12.7-114.22 277.9-277.88zm207.2-19.06l-60.11-60.11c-18.75-18.75-49.16-18.75-67.91 0l-56.55 56.55 128.02 128.02 56.55-56.55c18.75-18.76 18.75-49.16 0-67.91z"
- ></path>
- </svg>
- <span>Edit this page</span>
- </a>
- </li>
- <li class={`header-link depth-2`}>
- <a href="https://github.com/withastro/astro/blob/main/CONTRIBUTING.md#translations" target="_blank">
- <svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88.6 77.3" height="1.24em" width="1.24em" style="margin: -2px;"> <path fill="currentColor" d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z" /> <path fill="currentColor" d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8 c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z" /> </svg>
- <span>Translate this page</span>
- </a>
- </li>
- <li class={`header-link depth-2`}>
- <a href="https://astro.build/chat" target="_blank">
- <svg
- aria-hidden="true"
- focusable="false"
- data-prefix="fas"
- data-icon="comment-alt"
- class="svg-inline--fa fa-comment-alt fa-w-16"
- role="img"
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 0 512 512"
- height="1em"
- width="1em"
- >
- <path
- fill="currentColor"
- d="M448 0H64C28.7 0 0 28.7 0 64v288c0 35.3 28.7 64 64 64h96v84c0 9.8 11.2 15.5 19.1 9.7L304 416h144c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64z"
- ></path>
- </svg>
- <span>Join our community</span>
- </a>
- </li>
+ <li class={`header-link depth-2`}>
+ <a class="edit-on-github" href={editHref} target="_blank">
+ <svg
+ aria-hidden="true"
+ focusable="false"
+ data-prefix="fas"
+ data-icon="pen"
+ class="svg-inline--fa fa-pen fa-w-16"
+ role="img"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 512 512"
+ height="1em"
+ width="1em"
+ >
+ <path
+ fill="currentColor"
+ d="M290.74 93.24l128.02 128.02-277.99 277.99-114.14 12.6C11.35 513.54-1.56 500.62.14 485.34l12.7-114.22 277.9-277.88zm207.2-19.06l-60.11-60.11c-18.75-18.75-49.16-18.75-67.91 0l-56.55 56.55 128.02 128.02 56.55-56.55c18.75-18.76 18.75-49.16 0-67.91z"
+ ></path>
+ </svg>
+ <span>Edit this page</span>
+ </a>
+ </li>
+ <li class={`header-link depth-2`}>
+ <a
+ href="https://github.com/withastro/astro/blob/main/CONTRIBUTING.md#translations"
+ target="_blank"
+ >
+ <svg
+ aria-hidden="true"
+ focusable="false"
+ role="img"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 88.6 77.3"
+ height="1.24em"
+ width="1.24em"
+ style="margin: -2px;"
+ >
+ <path
+ fill="currentColor"
+ d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z"
+ ></path>
+ <path
+ fill="currentColor"
+ d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8 c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z"
+ ></path>
+ </svg>
+ <span>Translate this page</span>
+ </a>
+ </li>
+ <li class={`header-link depth-2`}>
+ <a href="https://astro.build/chat" target="_blank">
+ <svg
+ aria-hidden="true"
+ focusable="false"
+ data-prefix="fas"
+ data-icon="comment-alt"
+ class="svg-inline--fa fa-comment-alt fa-w-16"
+ role="img"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 512 512"
+ height="1em"
+ width="1em"
+ >
+ <path
+ fill="currentColor"
+ d="M448 0H64C28.7 0 0 28.7 0 64v288c0 35.3 28.7 64 64 64h96v84c0 9.8 11.2 15.5 19.1 9.7L304 416h144c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64z"
+ ></path>
+ </svg>
+ <span>Join our community</span>
+ </a>
+ </li>
</ul>
<div style="margin: 2rem 0; text-align: center;">
- <ThemeToggleButton client:visible />
+ <ThemeToggleButton client:visible />
</div>
+
+<style>
+ .edit-on-github {
+ text-decoration: none;
+ font: inherit;
+ color: inherit;
+ font-size: 1rem;
+ }
+</style>
diff --git a/docs/src/components/RightSidebar/RightSidebar.astro b/docs/src/components/RightSidebar/RightSidebar.astro
index 5415d4cef..f447f4408 100644
--- a/docs/src/components/RightSidebar/RightSidebar.astro
+++ b/docs/src/components/RightSidebar/RightSidebar.astro
@@ -1,25 +1,29 @@
---
import TableOfContents from './TableOfContents.tsx';
import MoreMenu from './MoreMenu.astro';
-const {content, githubEditUrl} = Astro.props;
+const { content, githubEditUrl } = Astro.props;
const headers = content.astro?.headers;
---
+
+<nav class="sidebar-nav" aria-labelledby="grid-right">
+ <div class="sidebar-nav-inner">
+ {headers && (
+ <TableOfContents client:media="(min-width: 50em)" headers={headers} />
+ )}
+ <MoreMenu editHref={githubEditUrl} />
+ </div>
+</nav>
+
<style>
- .sidebar-nav {
- width: 100%;
- position: sticky;
- top: 0;
- }
- .sidebar-nav-inner {
- height: 100%;
- padding: 0;
- padding-top: var(--doc-padding);
- overflow: auto;
- }
+ .sidebar-nav {
+ width: 100%;
+ position: sticky;
+ top: 0;
+ }
+ .sidebar-nav-inner {
+ height: 100%;
+ padding: 0;
+ padding-top: var(--doc-padding);
+ overflow: auto;
+ }
</style>
-<nav class="sidebar-nav" aria-labelledby="grid-right">
- <div class="sidebar-nav-inner">
- {headers && <TableOfContents client:media="(min-width: 50em)" headers={headers} />}
- <MoreMenu editHref={githubEditUrl} />
- </div>
-</nav> \ No newline at end of file
diff --git a/docs/src/components/RightSidebar/TableOfContents.tsx b/docs/src/components/RightSidebar/TableOfContents.tsx
index 803b05568..64ed93ba1 100644
--- a/docs/src/components/RightSidebar/TableOfContents.tsx
+++ b/docs/src/components/RightSidebar/TableOfContents.tsx
@@ -3,53 +3,53 @@ import { h, Fragment } from 'preact';
import { useState, useEffect, useRef } from 'preact/hooks';
const TableOfContents: FunctionalComponent<{ headers: any[] }> = ({
- headers = [],
+ headers = [],
}) => {
- const itemOffsets = useRef([]);
- const [activeId, setActiveId] = useState<string>(undefined);
+ const itemOffsets = useRef([]);
+ const [activeId, setActiveId] = useState<string>(undefined);
- useEffect(() => {
- const getItemOffsets = () => {
- const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
- itemOffsets.current = Array.from(titles).map((title) => ({
- id: title.id,
- topOffset: title.getBoundingClientRect().top + window.scrollY,
- }));
- };
+ useEffect(() => {
+ const getItemOffsets = () => {
+ const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
+ itemOffsets.current = Array.from(titles).map((title) => ({
+ id: title.id,
+ topOffset: title.getBoundingClientRect().top + window.scrollY,
+ }));
+ };
- getItemOffsets();
- window.addEventListener('resize', getItemOffsets);
+ getItemOffsets();
+ window.addEventListener('resize', getItemOffsets);
- return () => {
- window.removeEventListener('resize', getItemOffsets);
- };
- }, []);
+ return () => {
+ window.removeEventListener('resize', getItemOffsets);
+ };
+ }, []);
- return (
- <>
- <h2 class="heading">On this page</h2>
- <ul>
- <li
- class={`header-link depth-2 ${
- activeId === 'overview' ? 'active' : ''
- }`.trim()}
- >
- <a href="#overview">Overview</a>
- </li>
- {headers
- .filter(({ depth }) => depth > 1 && depth < 4)
- .map((header) => (
- <li
- class={`header-link depth-${header.depth} ${
- activeId === header.slug ? 'active' : ''
- }`.trim()}
- >
- <a href={`#${header.slug}`}>{header.text}</a>
- </li>
- ))}
- </ul>
- </>
- );
+ return (
+ <>
+ <h2 class="heading">On this page</h2>
+ <ul>
+ <li
+ class={`header-link depth-2 ${
+ activeId === 'overview' ? 'active' : ''
+ }`.trim()}
+ >
+ <a href="#overview">Overview</a>
+ </li>
+ {headers
+ .filter(({ depth }) => depth > 1 && depth < 4)
+ .map((header) => (
+ <li
+ class={`header-link depth-${header.depth} ${
+ activeId === header.slug ? 'active' : ''
+ }`.trim()}
+ >
+ <a href={`#${header.slug}`}>{header.text}</a>
+ </li>
+ ))}
+ </ul>
+ </>
+ );
};
export default TableOfContents;
diff --git a/docs/src/components/RightSidebar/ThemeToggleButton.css b/docs/src/components/RightSidebar/ThemeToggleButton.css
index b0f1b535b..d8cd7c2ac 100644
--- a/docs/src/components/RightSidebar/ThemeToggleButton.css
+++ b/docs/src/components/RightSidebar/ThemeToggleButton.css
@@ -1,38 +1,38 @@
.theme-toggle {
- display: inline-flex;
- align-items: center;
- gap: 0.25em;
- padding: 0.33em 0.67em;
- border-radius: 99em;
- background-color: var(--theme-code-inline-bg);
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25em;
+ padding: 0.33em 0.67em;
+ border-radius: 99em;
+ background-color: var(--theme-code-inline-bg);
}
.theme-toggle > label:focus-within {
- outline: 2px solid transparent;
- box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
+ outline: 2px solid transparent;
+ box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
}
.theme-toggle > label {
- color: var(--theme-code-inline-text);
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0.5;
- cursor: pointer;
+ color: var(--theme-code-inline-text);
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0.5;
+ cursor: pointer;
}
.theme-toggle .checked {
- color: var(--theme-accent);
- opacity: 1;
+ color: var(--theme-accent);
+ opacity: 1;
}
input[name='theme-toggle'] {
- position: absolute;
- opacity: 0;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: -1;
+ position: absolute;
+ opacity: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: -1;
}
diff --git a/docs/src/components/RightSidebar/ThemeToggleButton.tsx b/docs/src/components/RightSidebar/ThemeToggleButton.tsx
index 68927fad2..3cdc5bc0c 100644
--- a/docs/src/components/RightSidebar/ThemeToggleButton.tsx
+++ b/docs/src/components/RightSidebar/ThemeToggleButton.tsx
@@ -6,78 +6,78 @@ import './ThemeToggleButton.css';
const themes = ['light', 'dark'];
const icons = [
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="20"
- height="20"
- viewBox="0 0 20 20"
- fill="currentColor"
- >
- <path
- fill-rule="evenodd"
- d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
- clip-rule="evenodd"
- />
- </svg>,
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="20"
- height="20"
- viewBox="0 0 20 20"
- fill="currentColor"
- >
- <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
- </svg>,
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="20"
+ height="20"
+ viewBox="0 0 20 20"
+ fill="currentColor"
+ >
+ <path
+ fill-rule="evenodd"
+ d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
+ clip-rule="evenodd"
+ />
+ </svg>,
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="20"
+ height="20"
+ viewBox="0 0 20 20"
+ fill="currentColor"
+ >
+ <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
+ </svg>,
];
function ThemeToggle() {
- const [theme, setTheme] = useState(() => {
- if (import.meta.env.SSR) {
- return undefined;
- }
- if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
- return localStorage.getItem('theme');
- }
- if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return 'dark';
- }
- return 'light';
- });
+ const [theme, setTheme] = useState(() => {
+ if (import.meta.env.SSR) {
+ return undefined;
+ }
+ if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
+ return localStorage.getItem('theme');
+ }
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
+ return 'dark';
+ }
+ return 'light';
+ });
- useEffect(() => {
- const root = document.documentElement;
- if (theme === 'light') {
- root.classList.remove('theme-dark');
- } else {
- root.classList.add('theme-dark');
- }
- }, [theme]);
+ useEffect(() => {
+ const root = document.documentElement;
+ if (theme === 'light') {
+ root.classList.remove('theme-dark');
+ } else {
+ root.classList.add('theme-dark');
+ }
+ }, [theme]);
- return (
- <div class="theme-toggle">
- {themes.map((t, i) => {
- const icon = icons[i];
- const checked = t === theme;
- return (
- <label class={checked ? 'checked' : ''}>
- {icon}
- <input
- type="radio"
- name="theme-toggle"
- checked={checked}
- value={t}
- title={`Use ${t} theme`}
- aria-label={`Use ${t} theme`}
- onChange={() => {
- localStorage.setItem('theme', t);
- setTheme(t);
- }}
- />
- </label>
- );
- })}
- </div>
- );
+ return (
+ <div class="theme-toggle">
+ {themes.map((t, i) => {
+ const icon = icons[i];
+ const checked = t === theme;
+ return (
+ <label class={checked ? 'checked' : ''}>
+ {icon}
+ <input
+ type="radio"
+ name="theme-toggle"
+ checked={checked}
+ value={t}
+ title={`Use ${t} theme`}
+ aria-label={`Use ${t} theme`}
+ onChange={() => {
+ localStorage.setItem('theme', t);
+ setTheme(t);
+ }}
+ />
+ </label>
+ );
+ })}
+ </div>
+ );
}
export default ThemeToggle;
diff --git a/docs/src/config.ts b/docs/src/config.ts
index 57cfada7d..88e41aad4 100644
--- a/docs/src/config.ts
+++ b/docs/src/config.ts
@@ -1,204 +1,204 @@
export const SIDEBAR = {
- en: [
- { text: 'Setup', header: true },
- { text: 'Getting Started', link: 'getting-started' },
- { text: 'Quickstart', link: 'quick-start' },
- { text: 'Installation', link: 'installation' },
- { text: 'Themes', link: 'themes' },
- { text: 'Astro vs. X', link: 'comparing-astro-vs-other-tools' },
- { text: 'Migrate to v0.21', link: 'migration/0.21.0' },
+ en: [
+ { text: 'Setup', header: true },
+ { text: 'Getting Started', link: 'getting-started' },
+ { text: 'Quickstart', link: 'quick-start' },
+ { text: 'Installation', link: 'installation' },
+ { text: 'Themes', link: 'themes' },
+ { text: 'Astro vs. X', link: 'comparing-astro-vs-other-tools' },
+ { text: 'Migrate to v0.21', link: 'migration/0.21.0' },
- { text: 'Basics', header: true },
- { text: 'Project Structure', link: 'core-concepts/project-structure' },
- { text: 'Component Syntax', link: 'core-concepts/astro-components' },
- { text: 'Pages', link: 'core-concepts/astro-pages' },
- { text: 'Layouts', link: 'core-concepts/layouts' },
- { text: 'Routing', link: 'core-concepts/routing' },
- { text: 'Partial Hydration', link: 'core-concepts/component-hydration' },
+ { text: 'Basics', header: true },
+ { text: 'Project Structure', link: 'core-concepts/project-structure' },
+ { text: 'Component Syntax', link: 'core-concepts/astro-components' },
+ { text: 'Pages', link: 'core-concepts/astro-pages' },
+ { text: 'Layouts', link: 'core-concepts/layouts' },
+ { text: 'Routing', link: 'core-concepts/routing' },
+ { text: 'Partial Hydration', link: 'core-concepts/component-hydration' },
- { text: 'Guides', header: true },
- { text: 'Styling & CSS', link: 'guides/styling' },
- { text: 'Markdown', link: 'guides/markdown-content' },
- { text: 'Debugging', link: 'guides/debugging' },
- { text: 'Data Fetching', link: 'guides/data-fetching' },
- { text: 'Pagination', link: 'guides/pagination' },
- { text: 'RSS', link: 'guides/rss' },
- { text: 'Supported Imports', link: 'guides/imports' },
- { text: 'Aliases', link: 'guides/aliases' },
- { text: 'Environment Variables', link: 'guides/environment-variables' },
- { text: 'Deploy to the web', link: 'guides/deploy' },
- { text: 'Publish to npm', link: 'guides/publish-to-npm' },
+ { text: 'Guides', header: true },
+ { text: 'Styling & CSS', link: 'guides/styling' },
+ { text: 'Markdown', link: 'guides/markdown-content' },
+ { text: 'Debugging', link: 'guides/debugging' },
+ { text: 'Data Fetching', link: 'guides/data-fetching' },
+ { text: 'Pagination', link: 'guides/pagination' },
+ { text: 'RSS', link: 'guides/rss' },
+ { text: 'Supported Imports', link: 'guides/imports' },
+ { text: 'Aliases', link: 'guides/aliases' },
+ { text: 'Environment Variables', link: 'guides/environment-variables' },
+ { text: 'Deploy to the web', link: 'guides/deploy' },
+ { text: 'Publish to npm', link: 'guides/publish-to-npm' },
- { text: 'Reference', header: true },
- { text: 'Built-In Components', link: 'reference/builtin-components' },
- { text: 'API Reference', link: 'reference/api-reference' },
- { text: 'CLI Reference', link: 'reference/cli-reference' },
- {
- text: 'Configuration Reference',
- link: 'reference/configuration-reference',
- },
- { text: 'Renderer Reference', link: 'reference/renderer-reference' },
- ],
- de: [
- { text: 'Einrichtung', header: true },
- { text: 'Erste Schritte', link: 'de/getting-started' },
- { text: 'Schnellstart', link: 'de/quick-start' },
- { text: 'Installation', link: 'de/installation' },
- { text: 'Vorlagen', link: 'de/themes' },
- { text: 'Astro vs. X', link: 'de/comparing-astro-vs-other-tools' },
- { text: 'Umstellung auf v0.21', link: 'de/migration/0.21.0' },
+ { text: 'Reference', header: true },
+ { text: 'Built-In Components', link: 'reference/builtin-components' },
+ { text: 'API Reference', link: 'reference/api-reference' },
+ { text: 'CLI Reference', link: 'reference/cli-reference' },
+ {
+ text: 'Configuration Reference',
+ link: 'reference/configuration-reference',
+ },
+ { text: 'Renderer Reference', link: 'reference/renderer-reference' },
+ ],
+ de: [
+ { text: 'Einrichtung', header: true },
+ { text: 'Erste Schritte', link: 'de/getting-started' },
+ { text: 'Schnellstart', link: 'de/quick-start' },
+ { text: 'Installation', link: 'de/installation' },
+ { text: 'Vorlagen', link: 'de/themes' },
+ { text: 'Astro vs. X', link: 'de/comparing-astro-vs-other-tools' },
+ { text: 'Umstellung auf v0.21', link: 'de/migration/0.21.0' },
- { text: 'Grundlagen', header: true },
- { text: 'Projektstruktur', link: 'de/core-concepts/project-structure' },
- { text: 'Astro-Komponenten', link: 'de/core-concepts/astro-components' },
- { text: 'Astro-Seiten', link: 'de/core-concepts/astro-pages' },
- { text: 'Layouts', link: 'de/core-concepts/layouts' },
- { text: 'Routing', link: 'de/core-concepts/routing' },
- { text: 'Partial Hydration', link: 'de/core-concepts/component-hydration' },
+ { text: 'Grundlagen', header: true },
+ { text: 'Projektstruktur', link: 'de/core-concepts/project-structure' },
+ { text: 'Astro-Komponenten', link: 'de/core-concepts/astro-components' },
+ { text: 'Astro-Seiten', link: 'de/core-concepts/astro-pages' },
+ { text: 'Layouts', link: 'de/core-concepts/layouts' },
+ { text: 'Routing', link: 'de/core-concepts/routing' },
+ { text: 'Partial Hydration', link: 'de/core-concepts/component-hydration' },
- { text: 'Anleitungen', header: true },
- { text: 'Styling & CSS', link: 'de/guides/styling' },
+ { text: 'Anleitungen', header: true },
+ { text: 'Styling & CSS', link: 'de/guides/styling' },
- { text: 'Referenz', header: true },
- ],
- nl: [
- { text: 'Welkom', header: true },
- { text: 'Beginnen', link: 'nl/getting-started' },
- { text: 'Snel start', link: 'nl/quick-start' },
- ],
- fi: [
- { text: 'Tervetuloa', header: true },
- { text: 'Aloittaminen', link: 'fi/getting-started' },
- { text: 'Pika-aloitus', link: 'fi/quick-start' },
- { text: 'Asennus', link: 'fi/installation' },
- ],
- es: [
- { text: 'Configuración', header: true },
- { text: 'Empezando', link: 'es/getting-started' },
- { text: 'Comienzo rápido', link: 'es/quick-start' },
- { text: 'Instalación', link: 'es/installation' },
- { text: 'Astro vs. X', link: 'es/comparing-astro-vs-other-tools' },
+ { text: 'Referenz', header: true },
+ ],
+ nl: [
+ { text: 'Welkom', header: true },
+ { text: 'Beginnen', link: 'nl/getting-started' },
+ { text: 'Snel start', link: 'nl/quick-start' },
+ ],
+ fi: [
+ { text: 'Tervetuloa', header: true },
+ { text: 'Aloittaminen', link: 'fi/getting-started' },
+ { text: 'Pika-aloitus', link: 'fi/quick-start' },
+ { text: 'Asennus', link: 'fi/installation' },
+ ],
+ es: [
+ { text: 'Configuración', header: true },
+ { text: 'Empezando', link: 'es/getting-started' },
+ { text: 'Comienzo rápido', link: 'es/quick-start' },
+ { text: 'Instalación', link: 'es/installation' },
+ { text: 'Astro vs. X', link: 'es/comparing-astro-vs-other-tools' },
- { text: 'Fundamentos', header: true },
- {
- text: 'Estructura del Proyecto',
- link: 'es/core-concepts/project-structure',
- },
- {
- text: 'Sintaxis del Componente',
- link: 'es/core-concepts/astro-components',
- },
- { text: 'Páginas', link: 'es/core-concepts/astro-pages' },
- { text: 'Maquetas', link: 'es/core-concepts/layouts' },
- { text: 'Enrutamiento', link: 'es/core-concepts/routing' },
- {
- text: 'Hidratación parcial',
- link: 'es/core-concepts/component-hydration',
- },
+ { text: 'Fundamentos', header: true },
+ {
+ text: 'Estructura del Proyecto',
+ link: 'es/core-concepts/project-structure',
+ },
+ {
+ text: 'Sintaxis del Componente',
+ link: 'es/core-concepts/astro-components',
+ },
+ { text: 'Páginas', link: 'es/core-concepts/astro-pages' },
+ { text: 'Maquetas', link: 'es/core-concepts/layouts' },
+ { text: 'Enrutamiento', link: 'es/core-concepts/routing' },
+ {
+ text: 'Hidratación parcial',
+ link: 'es/core-concepts/component-hydration',
+ },
- { text: 'Guías', header: true },
- { text: 'Estilo y CSS', link: 'es/guides/styling' },
- { text: 'Markdown', link: 'es/guides/markdown-content' },
- { text: 'Depuración', link: 'es/guides/debugging' },
- { text: 'Obtención de datos', link: 'es/guides/data-fetching' },
- { text: 'Paginación', link: 'es/guides/pagination' },
- { text: 'RSS', link: 'es/guides/rss' },
- { text: 'Importaciones admitidas', link: 'es/guides/imports' },
- { text: 'Alias', link: 'es/guides/aliases' },
- { text: 'Desplegar en la web', link: 'es/guides/deploy' },
- { text: 'Publicar en npm', link: 'es/guides/publish-to-npm' },
+ { text: 'Guías', header: true },
+ { text: 'Estilo y CSS', link: 'es/guides/styling' },
+ { text: 'Markdown', link: 'es/guides/markdown-content' },
+ { text: 'Depuración', link: 'es/guides/debugging' },
+ { text: 'Obtención de datos', link: 'es/guides/data-fetching' },
+ { text: 'Paginación', link: 'es/guides/pagination' },
+ { text: 'RSS', link: 'es/guides/rss' },
+ { text: 'Importaciones admitidas', link: 'es/guides/imports' },
+ { text: 'Alias', link: 'es/guides/aliases' },
+ { text: 'Desplegar en la web', link: 'es/guides/deploy' },
+ { text: 'Publicar en npm', link: 'es/guides/publish-to-npm' },
- { text: 'Referencia', header: true },
- {
- text: 'Componentes incorporados',
- link: 'es/reference/builtin-components',
- },
- { text: 'Referencia de API', link: 'es/reference/api-reference' },
- { text: 'Referencia de CLI', link: 'es/reference/cli-reference' },
- {
- text: 'Referencia de configuración',
- link: 'es/reference/configuration-reference',
- },
- {
- text: 'Referencia de renderizador',
- link: 'es/reference/renderer-reference',
- },
- ],
- 'zh-CN': [
- { text: '起步', header: true },
- { text: '入门指南', link: 'zh-CN/getting-started' },
- { text: '快速入门', link: 'zh-CN/quick-start' },
- { text: '安装指南', link: 'zh-CN/installation' },
- { text: '模板样例', link: 'zh-CN/examples' },
- {
- text: 'Astro 对比其他框架',
- link: 'zh-CN/comparing-astro-vs-other-tools',
- },
- ],
- 'zh-TW': [
- { text: '設定', header: true },
- { text: '新手上路', link: 'zh-TW/getting-started' },
- { text: '快速開始', link: 'zh-TW/quick-start' },
- { text: '安裝', link: 'zh-TW/installation' },
- { text: '佈景主題', link: 'zh-TW/themes' },
- ],
- bg: [
- { text: 'Главни', header: true },
- { text: 'Започваме!', link: 'bg/getting-started' },
- ],
- fr: [
- { text: 'Bienvenue', header: true },
- { text: 'Bien démarrer', link: 'fr/getting-started' },
- { text: 'Démarrage rapide', link: 'fr/quick-start' },
- { text: 'Installation', link: 'fr/installation' },
- ],
- bn: [
- { text: 'সেটআপ', header: true },
- { text: 'শুরু করুন', link: 'bn/getting-started' },
- ],
- kr: [
- { text: '환영합니다', header: true },
- { text: '시작하기', link: 'kr/getting-started' },
- ],
- ar: [
- { text: 'التهيئة', header: true },
- { text: 'باشر البدأ', link: 'ar/getting-started' },
- ],
- da: [
- { text: 'Velkommen', header: true },
- { text: 'Introduktion', link: 'da/getting-started' },
- ],
- ja: [
- { text: 'セットアップ', header: true },
- { text: 'はじめに', link: 'ja/getting-started' },
- { text: 'クイックスタート', link: 'ja/quick-start' },
- { text: 'インストール', link: 'ja/installation' },
- { text: 'テーマ', link: 'ja/themes' },
- { text: 'Astro vs. X', link: 'ja/comparing-astro-vs-other-tools' },
- ],
- ru: [
- { text: 'Введение', header: true },
- { text: 'Начало работы', link: 'ru/getting-started' },
- { text: 'Быстрый старт', link: 'ru/quick-start' },
- ],
- it: [
- { text: 'Impostare', header: true },
- { text: 'Come iniziare', link: 'it/getting-started' },
- ],
+ { text: 'Referencia', header: true },
+ {
+ text: 'Componentes incorporados',
+ link: 'es/reference/builtin-components',
+ },
+ { text: 'Referencia de API', link: 'es/reference/api-reference' },
+ { text: 'Referencia de CLI', link: 'es/reference/cli-reference' },
+ {
+ text: 'Referencia de configuración',
+ link: 'es/reference/configuration-reference',
+ },
+ {
+ text: 'Referencia de renderizador',
+ link: 'es/reference/renderer-reference',
+ },
+ ],
+ 'zh-CN': [
+ { text: '起步', header: true },
+ { text: '入门指南', link: 'zh-CN/getting-started' },
+ { text: '快速入门', link: 'zh-CN/quick-start' },
+ { text: '安装指南', link: 'zh-CN/installation' },
+ { text: '模板样例', link: 'zh-CN/examples' },
+ {
+ text: 'Astro 对比其他框架',
+ link: 'zh-CN/comparing-astro-vs-other-tools',
+ },
+ ],
+ 'zh-TW': [
+ { text: '設定', header: true },
+ { text: '新手上路', link: 'zh-TW/getting-started' },
+ { text: '快速開始', link: 'zh-TW/quick-start' },
+ { text: '安裝', link: 'zh-TW/installation' },
+ { text: '佈景主題', link: 'zh-TW/themes' },
+ ],
+ bg: [
+ { text: 'Главни', header: true },
+ { text: 'Започваме!', link: 'bg/getting-started' },
+ ],
+ fr: [
+ { text: 'Bienvenue', header: true },
+ { text: 'Bien démarrer', link: 'fr/getting-started' },
+ { text: 'Démarrage rapide', link: 'fr/quick-start' },
+ { text: 'Installation', link: 'fr/installation' },
+ ],
+ bn: [
+ { text: 'সেটআপ', header: true },
+ { text: 'শুরু করুন', link: 'bn/getting-started' },
+ ],
+ kr: [
+ { text: '환영합니다', header: true },
+ { text: '시작하기', link: 'kr/getting-started' },
+ ],
+ ar: [
+ { text: 'التهيئة', header: true },
+ { text: 'باشر البدأ', link: 'ar/getting-started' },
+ ],
+ da: [
+ { text: 'Velkommen', header: true },
+ { text: 'Introduktion', link: 'da/getting-started' },
+ ],
+ ja: [
+ { text: 'セットアップ', header: true },
+ { text: 'はじめに', link: 'ja/getting-started' },
+ { text: 'クイックスタート', link: 'ja/quick-start' },
+ { text: 'インストール', link: 'ja/installation' },
+ { text: 'テーマ', link: 'ja/themes' },
+ { text: 'Astro vs. X', link: 'ja/comparing-astro-vs-other-tools' },
+ ],
+ ru: [
+ { text: 'Введение', header: true },
+ { text: 'Начало работы', link: 'ru/getting-started' },
+ { text: 'Быстрый старт', link: 'ru/quick-start' },
+ ],
+ it: [
+ { text: 'Impostare', header: true },
+ { text: 'Come iniziare', link: 'it/getting-started' },
+ ],
};
export const SITE = {
- title: 'Astro Documentation',
- description: 'Build faster websites with less client-side Javascript.',
+ title: 'Astro Documentation',
+ description: 'Build faster websites with less client-side Javascript.',
};
export const OPEN_GRAPH = {
- locale: 'en_US',
- image: {
- src: '/default-og-image.png?v=1',
- alt:
- 'astro logo on a starry expanse of space,' +
- ' with a purple saturn-like planet floating in the right foreground',
- },
- twitter: 'astrodotbuild',
+ locale: 'en_US',
+ image: {
+ src: '/default-og-image.png?v=1',
+ alt:
+ 'astro logo on a starry expanse of space,' +
+ ' with a purple saturn-like planet floating in the right foreground',
+ },
+ twitter: 'astrodotbuild',
};
diff --git a/docs/src/layouts/MainLayout.astro b/docs/src/layouts/MainLayout.astro
index 12cdedd85..b439b139f 100644
--- a/docs/src/layouts/MainLayout.astro
+++ b/docs/src/layouts/MainLayout.astro
@@ -1,122 +1,126 @@
---
-import HeadCommon from "../components/HeadCommon.astro";
-import HeadSEO from "../components/HeadSEO.astro";
+import HeadCommon from '../components/HeadCommon.astro';
+import HeadSEO from '../components/HeadSEO.astro';
import Header from '../components/Header/Header.astro';
import Footer from '../components/Footer/Footer.astro';
import PageContent from '../components/PageContent/PageContent.astro';
import LeftSidebar from '../components/LeftSidebar/LeftSidebar.astro';
import RightSidebar from '../components/RightSidebar/RightSidebar.astro';
-import { SITE } from "../config.ts";
+import { SITE } from '../config.ts';
-const { content = {}, hideRightSidebar = false} = Astro.props;
+const { content = {}, hideRightSidebar = false } = Astro.props;
const currentPage = Astro.request.url.pathname;
-const currentFile = `src/pages${currentPage.replace(/\/$/, "")}.md`;
+const currentFile = `src/pages${currentPage.replace(/\/$/, '')}.md`;
const githubEditUrl = `https://github.com/withastro/astro/blob/main/docs/${currentFile}`;
-const formatTitle = (content, SITE) => content.title ? `${content.title} 🚀 ${SITE.title}` : SITE.title;
+const formatTitle = (content, SITE) =>
+ content.title ? `${content.title} 🚀 ${SITE.title}` : SITE.title;
---
+
<html dir={content.dir ?? 'ltr'} lang={content.lang ?? 'en-us'} class="initial">
- <head>
- <HeadCommon />
- <HeadSEO {content} canonicalURL={Astro.request.canonicalURL} />
- <title>{formatTitle(content, SITE)}</title>
- <style>
- body {
- width: 100%;
- display: grid;
- grid-template-rows: var(--theme-navbar-height) 1fr;
- --gutter: 0.5rem;
- --doc-padding: 2rem;
- }
- .layout {
- display: grid;
- grid-auto-flow: column;
- grid-template-columns:
- minmax(var(--gutter), 1fr)
- minmax(0, var(--max-width))
- minmax(var(--gutter), 1fr);
- overflow-x: hidden;
- }
- .layout :global(> *) {
- width: 100%;
- height: 100%;
- }
- .grid-sidebar {
- height: 100vh;
- position: sticky;
- top: 0;
- padding: 0;
- }
- #grid-left {
- position: fixed;
- background-color: var(--theme-bg);
- z-index: 10;
- display: none;
- }
- #grid-main {
- padding: var(--doc-padding) var(--gutter);
- grid-column: 2;
- display: flex;
- flex-direction: column;
- height: 100%;
- }
- #grid-right {
- display: none;
- }
- :global(.mobile-sidebar-toggle) {
- overflow: hidden;
- }
- :global(.mobile-sidebar-toggle #grid-left) {
- display: block;
- top: 2rem;
- }
- @media (min-width: 50em) {
- .layout {
- overflow: initial;
- grid-template-columns:
- 20rem
- minmax(0, var(--max-width));
- gap: 1em;
- }
- #grid-left {
- display: flex;
- padding-left: 2rem;
- position: sticky;
- grid-column: 1;
- }
- }
+ <head>
+ <HeadCommon />
+ <HeadSEO {content} canonicalURL={Astro.request.canonicalURL} />
+ <title>{formatTitle(content, SITE)}</title>
+ <style>
+ body {
+ width: 100%;
+ display: grid;
+ grid-template-rows: var(--theme-navbar-height) 1fr;
+ --gutter: 0.5rem;
+ --doc-padding: 2rem;
+ }
+ .layout {
+ display: grid;
+ grid-auto-flow: column;
+ grid-template-columns:
+ minmax(var(--gutter), 1fr)
+ minmax(0, var(--max-width))
+ minmax(var(--gutter), 1fr);
+ overflow-x: hidden;
+ }
+ .layout :global(> *) {
+ width: 100%;
+ height: 100%;
+ }
+ .grid-sidebar {
+ height: 100vh;
+ position: sticky;
+ top: 0;
+ padding: 0;
+ }
+ #grid-left {
+ position: fixed;
+ background-color: var(--theme-bg);
+ z-index: 10;
+ display: none;
+ }
+ #grid-main {
+ padding: var(--doc-padding) var(--gutter);
+ grid-column: 2;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ }
+ #grid-right {
+ display: none;
+ }
+ :global(.mobile-sidebar-toggle) {
+ overflow: hidden;
+ }
+ :global(.mobile-sidebar-toggle #grid-left) {
+ display: block;
+ top: 2rem;
+ }
+ @media (min-width: 50em) {
+ .layout {
+ overflow: initial;
+ grid-template-columns:
+ 20rem
+ minmax(0, var(--max-width));
+ gap: 1em;
+ }
+ #grid-left {
+ display: flex;
+ padding-left: 2rem;
+ position: sticky;
+ grid-column: 1;
+ }
+ }
- @media (min-width: 72em) {
- .layout {
- grid-template-columns:
- 20rem
- minmax(0, var(--max-width))
- 18rem;
- padding-left: 0;
- padding-right: 0;
- margin: 0 auto;
- }
- #grid-right {
- grid-column: 3;
- display: flex;
- }
- }
- </style>
- </head>
+ @media (min-width: 72em) {
+ .layout {
+ grid-template-columns:
+ 20rem
+ minmax(0, var(--max-width))
+ 18rem;
+ padding-left: 0;
+ padding-right: 0;
+ margin: 0 auto;
+ }
+ #grid-right {
+ grid-column: 3;
+ display: flex;
+ }
+ }
+ </style>
+ </head>
- <body>
- <Header currentPage={currentPage} />
- <main class="layout">
- <aside id="grid-left" class="grid-sidebar" title="Site Navigation">
- <LeftSidebar currentPage={currentPage} />
- </aside>
- <div id="grid-main">
- <PageContent content={content} githubEditUrl={githubEditUrl} currentPage={currentPage}>
- <slot />
- </PageContent>
- </div>
- <aside id="grid-right" class="grid-sidebar" title="Table of Contents">
- {!hideRightSidebar && <RightSidebar content={content} githubEditUrl={githubEditUrl} />}
- </aside>
- </main>
- </body>
+ <body>
+ <Header {currentPage} />
+ <main class="layout">
+ <aside id="grid-left" class="grid-sidebar" title="Site Navigation">
+ <LeftSidebar {currentPage} />
+ </aside>
+ <div id="grid-main">
+ <PageContent {content} {githubEditUrl} {currentPage}>
+ <slot />
+ </PageContent>
+ </div>
+ <aside id="grid-right" class="grid-sidebar" title="Table of Contents">
+ {!hideRightSidebar && (
+ <RightSidebar content={content} githubEditUrl={githubEditUrl} />
+ )}
+ </aside>
+ </main>
+ </body>
</html>
diff --git a/docs/src/layouts/SplashLayout.astro b/docs/src/layouts/SplashLayout.astro
index d2b00e9bc..7e4ea55b6 100644
--- a/docs/src/layouts/SplashLayout.astro
+++ b/docs/src/layouts/SplashLayout.astro
@@ -1,48 +1,48 @@
---
-import HeadCommon from "../components/HeadCommon.astro";
+import HeadCommon from '../components/HeadCommon.astro';
import Header from '../components/Header/Header.astro';
-import { SITE } from "../config.ts";
+import { SITE } from '../config.ts';
const { title } = Astro.props;
---
<html dir="ltr" lang="en-us" class="initial">
- <head>
- <HeadCommon />
- <title>{`${title} 🚀 ${SITE.title}`}</title>
- <style>
- body {
- width: 100%;
- display: grid;
- grid-template-rows: var(--theme-navbar-height) 1fr;
- --gutter: 0.5rem;
- --doc-padding: 2rem;
- }
- .layout {
- display: grid;
- grid-auto-flow: column;
- grid-template-columns:
- minmax(var(--gutter), 1fr)
- minmax(0, var(--max-width))
- minmax(var(--gutter), 1fr);
- overflow-x: hidden;
- }
- article {
- padding: var(--doc-padding) var(--gutter);
- grid-column: 2;
- display: flex;
- flex-direction: column;
- height: 100%;
- }
- </style>
- </head>
+ <head>
+ <HeadCommon />
+ <title>{`${title} 🚀 ${SITE.title}`}</title>
+ <style>
+ body {
+ width: 100%;
+ display: grid;
+ grid-template-rows: var(--theme-navbar-height) 1fr;
+ --gutter: 0.5rem;
+ --doc-padding: 2rem;
+ }
+ .layout {
+ display: grid;
+ grid-auto-flow: column;
+ grid-template-columns:
+ minmax(var(--gutter), 1fr)
+ minmax(0, var(--max-width))
+ minmax(var(--gutter), 1fr);
+ overflow-x: hidden;
+ }
+ article {
+ padding: var(--doc-padding) var(--gutter);
+ grid-column: 2;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ }
+ </style>
+ </head>
- <body>
- <Header />
- <main class="layout splash-layout">
- <article>
- <slot />
- </article>
- </main>
- </body>
+ <body>
+ <Header />
+ <main class="layout splash-layout">
+ <article>
+ <slot />
+ </article>
+ </main>
+ </body>
</html>
diff --git a/docs/src/pages/404.astro b/docs/src/pages/404.astro
index 242cbf594..8a0bfbb5b 100644
--- a/docs/src/pages/404.astro
+++ b/docs/src/pages/404.astro
@@ -3,7 +3,7 @@ import SplashLayout from '../layouts/SplashLayout.astro';
---
<SplashLayout title="Not Found">
- <h1>404</h1>
- <p>This page isn't in our solar system.</p>
- <a href="/">Take me home.</a>
+ <h1>404</h1>
+ <p>This page isn't in our solar system.</p>
+ <a href="/">Take me home.</a>
</SplashLayout>
diff --git a/docs/src/pages/de/404.astro b/docs/src/pages/de/404.astro
index fe950b41d..7107cc49a 100644
--- a/docs/src/pages/de/404.astro
+++ b/docs/src/pages/de/404.astro
@@ -3,7 +3,7 @@ import SplashLayout from '../../layouts/SplashLayout.astro';
---
<SplashLayout title="Nicht gefunden">
- <h1>404</h1>
- <p>Diese Seite befindet sich nicht in unserem Sonnensystem.</p>
- <a href="/">Bring mich nach Hause.</a>
+ <h1>404</h1>
+ <p>Diese Seite befindet sich nicht in unserem Sonnensystem.</p>
+ <a href="/">Bring mich nach Hause.</a>
</SplashLayout>
diff --git a/docs/src/pages/de/themes.astro b/docs/src/pages/de/themes.astro
index 81db14520..b33fe3c03 100644
--- a/docs/src/pages/de/themes.astro
+++ b/docs/src/pages/de/themes.astro
@@ -5,48 +5,49 @@ import { Markdown } from 'astro/components';
import themes from '../../data/themes.json';
import components from '../../data/components.json';
---
-<Layout content={{title: 'Vorlagen'}} hideRightSidebar>
- <style>
- .card-grid {
- display: grid;
- grid-column-gap: 15px;
- grid-row-gap: 15px;
- grid-auto-flow: dense;
- grid-template-columns: repeat(auto-fit,minmax(300px,1fr))
- }
- </style>
- <Markdown>
- ## Vorgestellte Vorlagen
- </Markdown>
- <div class="card-grid">
- {themes.featured.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## Offizielle Vorlagen
- Astro pflegt verschiedene offizielle Vorlagen für Dokumentationssites, Portfolios und mehr.
- </Markdown>
- <div class="card-grid">
- {themes.official.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## Vorlagen aus der Community
+<Layout content={{ title: 'Vorlagen' }} hideRightSidebar>
+ <style>
+ .card-grid {
+ display: grid;
+ grid-column-gap: 15px;
+ grid-row-gap: 15px;
+ grid-auto-flow: dense;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ }
+ </style>
+ <Markdown>
+ ## Vorgestellte Vorlagen
+ </Markdown>
+ <div class="card-grid">
+ {themes.featured.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## Offizielle Vorlagen
- Sieh dir einige von unserer Community entwickelte Vorlagen an!
- </Markdown>
- <div class="card-grid">
- {themes.community.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## Vorgestellte Packages
+ Astro pflegt verschiedene offizielle Vorlagen für Dokumentationssites, Portfolios und mehr.
+ </Markdown>
+ <div class="card-grid">
+ {themes.official.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## Vorlagen aus der Community
- Unser Package-Ökosystem wächst stetig! Sieh dir die hier vorgestellten Packages unserer Community an. Durchsuche unsere vollständige Sammlung [auf npm.](https://www.npmjs.com/search?q=keywords%3Aastro-component)
- </Markdown>
- <div class="card-grid">
- {components.community.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- > Möchtest du deine eigene Arbeit hier sehen? [Teile sie in Discord!](https://astro.build/chat)
- Wir teilen hier regelmäßig unsere Favoriten aus dem #showcase-Channel.
- </Markdown>
+ Sieh dir einige von unserer Community entwickelte Vorlagen an!
+ </Markdown>
+ <div class="card-grid">
+ {themes.community.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## Vorgestellte Packages
+
+ Unser Package-Ökosystem wächst stetig! Sieh dir die hier vorgestellten Packages unserer Community an. Durchsuche unsere vollständige Sammlung [auf npm.](https://www.npmjs.com/search?q=keywords%3Aastro-component)
+ </Markdown>
+ <div class="card-grid">
+ {components.community.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ > Möchtest du deine eigene Arbeit hier sehen? [Teile sie in Discord!](https://astro.build/chat)
+ > Wir teilen hier regelmäßig unsere Favoriten aus dem #showcase-Channel.
+ </Markdown>
</Layout>
diff --git a/docs/src/pages/es/guides/markdown-content.astro b/docs/src/pages/es/guides/markdown-content.astro
index 562511baf..ca2c5574d 100644
--- a/docs/src/pages/es/guides/markdown-content.astro
+++ b/docs/src/pages/es/guides/markdown-content.astro
@@ -4,10 +4,9 @@ import MainLayout from '~/layouts/MainLayout.astro';
const [content] = Astro.fetchContent('/src/pages/guides/markdown-content.md');
---
-<MainLayout content="{content}">
- <Markdown>
- > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
- </Markdown>
- {content.astro.html}
+<MainLayout {content}>
+ <Markdown>
+ > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
+ </Markdown>
+ {content.astro.html}
</MainLayout>
-
diff --git a/docs/src/pages/es/guides/pagination.astro b/docs/src/pages/es/guides/pagination.astro
index 65c0fe3c4..ad7682695 100644
--- a/docs/src/pages/es/guides/pagination.astro
+++ b/docs/src/pages/es/guides/pagination.astro
@@ -4,10 +4,9 @@ import MainLayout from '~/layouts/MainLayout.astro';
const [content] = Astro.fetchContent('/src/pages/guides/pagination.md');
---
-<MainLayout content="{content}">
- <Markdown>
- > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
- </Markdown>
- {content.astro.html}
+<MainLayout {content}>
+ <Markdown>
+ > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
+ </Markdown>
+ {content.astro.html}
</MainLayout>
-
diff --git a/docs/src/pages/es/guides/publish-to-npm.astro b/docs/src/pages/es/guides/publish-to-npm.astro
index a1ffac93c..d745b8b7c 100644
--- a/docs/src/pages/es/guides/publish-to-npm.astro
+++ b/docs/src/pages/es/guides/publish-to-npm.astro
@@ -4,10 +4,9 @@ import MainLayout from '~/layouts/MainLayout.astro';
const [content] = Astro.fetchContent('/src/pages/guides/publish-to-npm.md');
---
-<MainLayout content="{content}">
- <Markdown>
- > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
- </Markdown>
- {content.astro.html}
+<MainLayout {content}>
+ <Markdown>
+ > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
+ </Markdown>
+ {content.astro.html}
</MainLayout>
-
diff --git a/docs/src/pages/es/guides/styling.astro b/docs/src/pages/es/guides/styling.astro
index d15e6298f..b153798c3 100644
--- a/docs/src/pages/es/guides/styling.astro
+++ b/docs/src/pages/es/guides/styling.astro
@@ -4,10 +4,9 @@ import MainLayout from '~/layouts/MainLayout.astro';
const [content] = Astro.fetchContent('/src/pages/guides/styling.md');
---
-<MainLayout content="{content}">
- <Markdown>
- > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
- </Markdown>
- {content.astro.html}
+<MainLayout {content}>
+ <Markdown>
+ > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
+ </Markdown>
+ {content.astro.html}
</MainLayout>
-
diff --git a/docs/src/pages/es/reference/renderer-reference.astro b/docs/src/pages/es/reference/renderer-reference.astro
index f3c703a60..131b95ab4 100644
--- a/docs/src/pages/es/reference/renderer-reference.astro
+++ b/docs/src/pages/es/reference/renderer-reference.astro
@@ -1,13 +1,14 @@
---
import { Markdown } from 'astro/components';
import MainLayout from '~/layouts/MainLayout.astro';
-const [content] = Astro.fetchContent('/src/pages/reference/renderer-reference.md');
+const [content] = Astro.fetchContent(
+ '/src/pages/reference/renderer-reference.md'
+);
---
-<MainLayout content="{content}">
- <Markdown>
- > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
- </Markdown>
- {content.astro.html}
+<MainLayout {content}>
+ <Markdown>
+ > Esta página todavía no está disponible en Español. Se muestra la versión en inglés.
+ </Markdown>
+ {content.astro.html}
</MainLayout>
-
diff --git a/docs/src/pages/index.astro b/docs/src/pages/index.astro
index 74bf80c9c..ed0831cbd 100644
--- a/docs/src/pages/index.astro
+++ b/docs/src/pages/index.astro
@@ -3,19 +3,37 @@ import Layout from '../layouts/MainLayout.astro';
---
<script>
- // WIP: trigger a client-side redirect based on the browser language.
- // A vercel.json redirect is enforced in production, so no user should ever see this page.
- // Remove the vercel.json redirect when this is ready.
- const KNOWN_LANGUAGES = ['bg', 'de','en','es','fi','nl','pt-br','zh-CN','zh-TW', 'fr', 'kr', 'da', 'ja'];
- let newLangWithRegion = (window.navigator.userLanguage || window.navigator.language || 'en-US').substr(0, 5);
- let newLang = newLangWithRegion.substr(0, 2);
- if (newLang === 'en') {
- window.location.pathname = '/getting-started';
- } else if (KNOWN_LANGUAGES.includes(newLangWithRegion)) {
- window.location.pathname = '/' + newLangWithRegion + '/getting-started';
- } else if (KNOWN_LANGUAGES.includes(newLang)) {
- window.location.pathname = '/' + newLang + '/getting-started';
- } else {
- window.location.pathname = '/getting-started';
- }
+ // WIP: trigger a client-side redirect based on the browser language.
+ // A vercel.json redirect is enforced in production, so no user should ever see this page.
+ // Remove the vercel.json redirect when this is ready.
+ const KNOWN_LANGUAGES = [
+ 'bg',
+ 'de',
+ 'en',
+ 'es',
+ 'fi',
+ 'nl',
+ 'pt-br',
+ 'zh-CN',
+ 'zh-TW',
+ 'fr',
+ 'kr',
+ 'da',
+ 'ja',
+ ];
+ let newLangWithRegion = (
+ window.navigator.userLanguage ||
+ window.navigator.language ||
+ 'en-US'
+ ).substr(0, 5);
+ let newLang = newLangWithRegion.substr(0, 2);
+ if (newLang === 'en') {
+ window.location.pathname = '/getting-started';
+ } else if (KNOWN_LANGUAGES.includes(newLangWithRegion)) {
+ window.location.pathname = '/' + newLangWithRegion + '/getting-started';
+ } else if (KNOWN_LANGUAGES.includes(newLang)) {
+ window.location.pathname = '/' + newLang + '/getting-started';
+ } else {
+ window.location.pathname = '/getting-started';
+ }
</script>
diff --git a/docs/src/pages/ja/themes.astro b/docs/src/pages/ja/themes.astro
index 56810054d..5a64cf826 100644
--- a/docs/src/pages/ja/themes.astro
+++ b/docs/src/pages/ja/themes.astro
@@ -1,52 +1,53 @@
---
import Layout from '../../layouts/MainLayout.astro';
import Card from '../../components/Card.astro';
-import {Markdown} from 'astro/components';
+import { Markdown } from 'astro/components';
import themes from '../../data/themes.json';
import components from '../../data/components.json';
---
-<Layout content={{title: 'テーマ'}} hideRightSidebar>
- <style>
- .card-grid {
- display: grid;
- grid-column-gap: 15px;
- grid-row-gap: 15px;
- grid-auto-flow: dense;
- grid-template-columns: repeat(auto-fit,minmax(300px,1fr))
- }
- </style>
- <Markdown>
- ## 注目のテーマ
- </Markdown>
- <div class="card-grid">
- {themes.featured.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## 公式テーマ
- Astroでは、ドキュメントサイトやポートフォリオなど、いくつかの公式テーマを用意しています。
- </Markdown>
- <div class="card-grid">
- {themes.official.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## コミュニティテーマ
+<Layout content={{ title: 'テーマ' }} hideRightSidebar>
+ <style>
+ .card-grid {
+ display: grid;
+ grid-column-gap: 15px;
+ grid-row-gap: 15px;
+ grid-auto-flow: dense;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ }
+ </style>
+ <Markdown>
+ ## 注目のテーマ
+ </Markdown>
+ <div class="card-grid">
+ {themes.featured.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## 公式テーマ
- コミュニティが開発したテーマをご覧ください。
- </Markdown>
- <div class="card-grid">
- {themes.community.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## 注目のパッケージ
+ Astro では、ドキュメントサイトやポートフォリオなど、いくつかの公式テーマを用意しています。
+ </Markdown>
+ <div class="card-grid">
+ {themes.official.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## コミュニティテーマ
- 私たちのパッケージエコシステムは成長し続けています。注目のコミュニティパッケージをご覧ください。コレクション全体は[npm](https://www.npmjs.com/search?q=keywords%3Aastro-component)で検索できます。
- </Markdown>
- <div class="card-grid">
- {components.community.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- > 自分のテーマを紹介したい場合は、[Discordでシェアしてください!](https://astro.build/chat)
- `#showcase` チャンネルに投稿されたお気に入りの作品をよくピックアップしています。
- </Markdown>
+ コミュニティが開発したテーマをご覧ください。
+ </Markdown>
+ <div class="card-grid">
+ {themes.community.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## 注目のパッケージ
+
+ 私たちのパッケージエコシステムは成長し続けています。注目のコミュニティパッケージをご覧ください。コレクション全体は[npm](https://www.npmjs.com/search?q=keywords%3Aastro-component)で検索できます。
+ </Markdown>
+ <div class="card-grid">
+ {components.community.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ > 自分のテーマを紹介したい場合は、[Discord でシェアしてください!](https://astro.build/chat)
+ > `#showcase` チャンネルに投稿されたお気に入りの作品をよくピックアップしています。
+ </Markdown>
</Layout>
diff --git a/docs/src/pages/themes.astro b/docs/src/pages/themes.astro
index 697335298..a8a84a48a 100644
--- a/docs/src/pages/themes.astro
+++ b/docs/src/pages/themes.astro
@@ -5,48 +5,49 @@ import { Markdown } from 'astro/components';
import themes from '../data/themes.json';
import components from '../data/components.json';
---
-<Layout content={{title: 'Themes'}} hideRightSidebar>
- <style>
- .card-grid {
- display: grid;
- grid-column-gap: 15px;
- grid-row-gap: 15px;
- grid-auto-flow: dense;
- grid-template-columns: repeat(auto-fit,minmax(300px,1fr))
- }
- </style>
- <Markdown>
- ## Featured Theme
- </Markdown>
- <div class="card-grid">
- {themes.featured.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## Official Themes
- Astro maintains several official themes for documentation sites, portfolios, and more.
- </Markdown>
- <div class="card-grid">
- {themes.official.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## Community Themes
+<Layout content={{ title: 'Themes' }} hideRightSidebar>
+ <style>
+ .card-grid {
+ display: grid;
+ grid-column-gap: 15px;
+ grid-row-gap: 15px;
+ grid-auto-flow: dense;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ }
+ </style>
+ <Markdown>
+ ## Featured Theme
+ </Markdown>
+ <div class="card-grid">
+ {themes.featured.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## Official Themes
- Checkout some themes developed by our community!
- </Markdown>
- <div class="card-grid">
- {themes.community.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## Featured Packages
+ Astro maintains several official themes for documentation sites, portfolios, and more.
+ </Markdown>
+ <div class="card-grid">
+ {themes.official.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## Community Themes
- Our package ecosystem is growing! Check out these featured community packages. Search the entire collection [on npm.](https://www.npmjs.com/search?q=keywords%3Aastro-component)
- </Markdown>
- <div class="card-grid">
- {components.community.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- > Want to see your own work featured? [Share it to Discord!](https://astro.build/chat)
- We'll often take our favorites from the `#showcase` channel and post them here.
- </Markdown>
+ Checkout some themes developed by our community!
+ </Markdown>
+ <div class="card-grid">
+ {themes.community.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## Featured Packages
+
+ Our package ecosystem is growing! Check out these featured community packages. Search the entire collection [on npm.](https://www.npmjs.com/search?q=keywords%3Aastro-component)
+ </Markdown>
+ <div class="card-grid">
+ {components.community.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ > Want to see your own work featured? [Share it to Discord!](https://astro.build/chat)
+ > We'll often take our favorites from the `#showcase` channel and post them here.
+ </Markdown>
</Layout>
diff --git a/docs/src/pages/zh-TW/themes.astro b/docs/src/pages/zh-TW/themes.astro
index 7f4a8fa34..c1908fbc8 100644
--- a/docs/src/pages/zh-TW/themes.astro
+++ b/docs/src/pages/zh-TW/themes.astro
@@ -5,48 +5,49 @@ import { Markdown } from 'astro/components';
import themes from '../../data/themes.json';
import components from '../../data/components.json';
---
-<Layout content={{title: '佈景主題'}} hideRightSidebar>
- <style>
- .card-grid {
- display: grid;
- grid-column-gap: 15px;
- grid-row-gap: 15px;
- grid-auto-flow: dense;
- grid-template-columns: repeat(auto-fit,minmax(300px,1fr))
- }
- </style>
- <Markdown>
- ## 精選佈景主題
- </Markdown>
- <div class="card-grid">
- {themes.featured.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## 官方佈景主題
- Astro 維護的文件網站、作品集⋯等官方佈景主題。
- </Markdown>
- <div class="card-grid">
- {themes.official.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## 社群佈景主題
+<Layout content={{ title: '佈景主題' }} hideRightSidebar>
+ <style>
+ .card-grid {
+ display: grid;
+ grid-column-gap: 15px;
+ grid-row-gap: 15px;
+ grid-auto-flow: dense;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ }
+ </style>
+ <Markdown>
+ ## 精選佈景主題
+ </Markdown>
+ <div class="card-grid">
+ {themes.featured.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## 官方佈景主題
- 趕緊來看看社群開發的佈景主題!
- </Markdown>
- <div class="card-grid">
- {themes.community.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- ## 精選套件
+ Astro 維護的文件網站、作品集 ⋯ 等官方佈景主題。
+ </Markdown>
+ <div class="card-grid">
+ {themes.official.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## 社群佈景主題
- 我們的套件生態持續成長!所有精選社群套件都可以在 [npm](https://www.npmjs.com/search?q=keywords%3Aastro-component) 發掘。
- </Markdown>
- <div class="card-grid">
- {components.community.map((item)=>(<Card data={item} />))}
- </div>
- <Markdown>
- > 想要讓自己的作品成為精選嗎?[在 Discord 分享!](https://astro.build/chat)
- 我們常在 `#showcase` 頻道取材,把深受喜愛的在這裡發布。
- </Markdown>
+ 趕緊來看看社群開發的佈景主題!
+ </Markdown>
+ <div class="card-grid">
+ {themes.community.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ ## 精選套件
+
+ 我們的套件生態持續成長!所有精選社群套件都可以在 [npm](https://www.npmjs.com/search?q=keywords%3Aastro-component) 發掘。
+ </Markdown>
+ <div class="card-grid">
+ {components.community.map((item) => <Card data={item} />)}
+ </div>
+ <Markdown>
+ > 想要讓自己的作品成為精選嗎?[在 Discord 分享!](https://astro.build/chat)
+ > 我們常在 `#showcase` 頻道取材,把深受喜愛的在這裡發布。
+ </Markdown>
</Layout>
diff --git a/docs/src/util.ts b/docs/src/util.ts
index 269373735..c20b0713e 100644
--- a/docs/src/util.ts
+++ b/docs/src/util.ts
@@ -1,14 +1,14 @@
export function getLanguageFromURL(pathname: string) {
- const langCodeMatch = pathname.match(/\/([a-z]{2}-?[A-Z]{0,2})\//);
- return langCodeMatch ? langCodeMatch[1] : 'en';
+ const langCodeMatch = pathname.match(/\/([a-z]{2}-?[A-Z]{0,2})\//);
+ return langCodeMatch ? langCodeMatch[1] : 'en';
}
/** Remove \ and / from beginning of string */
export function removeLeadingSlash(path: string) {
- return path.replace(/^[/\\]+/, '');
+ return path.replace(/^[/\\]+/, '');
}
/** Remove \ and / from end of string */
export function removeTrailingSlash(path: string) {
- return path.replace(/[/\\]+$/, '');
+ return path.replace(/[/\\]+$/, '');
}
diff --git a/examples/blog-multiple-authors/astro.config.mjs b/examples/blog-multiple-authors/astro.config.mjs
index 68499b3fa..a1516f292 100644
--- a/examples/blog-multiple-authors/astro.config.mjs
+++ b/examples/blog-multiple-authors/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Preact renderer to support Preact JSX components.
- renderers: ['@astrojs/renderer-preact'],
+ // Enable the Preact renderer to support Preact JSX components.
+ renderers: ['@astrojs/renderer-preact'],
});
diff --git a/examples/blog-multiple-authors/src/components/MainHead.astro b/examples/blog-multiple-authors/src/components/MainHead.astro
index 0bf1ec4b0..ffdc0f1c7 100644
--- a/examples/blog-multiple-authors/src/components/MainHead.astro
+++ b/examples/blog-multiple-authors/src/components/MainHead.astro
@@ -1,45 +1,45 @@
---
export interface Props {
- title: string;
- description: string;
- image?: string;
- type?: string;
- next?: string;
- prev?: string;
- canonicalURL?: string | URL;
+ title: string;
+ description: string;
+ image?: string;
+ type?: string;
+ next?: string;
+ prev?: string;
+ canonicalURL?: string | URL;
}
const { title, description, image, type, next, prev, canonicalURL } = Astro.props as Props;
---
<!-- Common -->
-<meta charset="UTF-8">
+<meta charset="UTF-8" />
<title>{title}</title>
-<meta name="description" content={description}>
-<link rel="preconnect" href="https://fonts.gstatic.com">
-<link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
+<meta name="description" content={description} />
+<link rel="preconnect" href="https://fonts.gstatic.com" />
+<link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href={Astro.resolve('../styles/global.css')} />
<!-- Sitemap -->
-<link rel="sitemap" href="/sitemap.xml">
+<link rel="sitemap" href="/sitemap.xml" />
<!-- RSS -->
-<link rel="alternate" type="application/rss+xml" href="/feed/posts.xml">
+<link rel="alternate" type="application/rss+xml" href="/feed/posts.xml" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<!-- SEO -->
-<link rel="canonical" href={canonicalURL}>
+<link rel="canonical" href={canonicalURL} />
{next && <link rel="next" aria-label="Previous Page" href={new URL(next, canonicalURL).href}>}
{prev && <link rel="prev" aria-label="Next Page" href={new URL(prev, canonicalURL).href}>}
<!-- OpenGraph -->
-<meta property="og:title" content={title}>
-<meta property="og:description" content={description}>
+<meta property="og:title" content={title} />
+<meta property="og:description" content={description} />
{image && (<meta property="og:image" content={new URL(image, canonicalURL)}>)}
<!-- Twitter -->
-<meta name="twitter:card" content={image ? 'summary_large_image' : 'summary'}>
-<meta name="twitter:site" content="@astro">
-<meta name="twitter:title" content={title}>
-<meta name="twitter:description" content={description}>
+<meta name="twitter:card" content={image ? 'summary_large_image' : 'summary'} />
+<meta name="twitter:site" content="@astro" />
+<meta name="twitter:title" content={title} />
+<meta name="twitter:description" content={description} />
{image && (<meta name="twitter:image" content={image}>)}
diff --git a/examples/blog-multiple-authors/src/components/Nav.astro b/examples/blog-multiple-authors/src/components/Nav.astro
index 04a537f87..a2a5fc3a3 100644
--- a/examples/blog-multiple-authors/src/components/Nav.astro
+++ b/examples/blog-multiple-authors/src/components/Nav.astro
@@ -1,63 +1,63 @@
---
export interface Props {
- title: string;
+ title: string;
}
const { title } = Astro.props;
---
-<style lang="scss">
-.header {
- text-align: center;
-
- @media (min-width: 600px) {
- display: flex;
- align-items: center;
- padding: 2rem;
- }
-}
-
-.title {
- margin: 0;
- font-size: 1.2em;
- letter-spacing: -0.03em;
- font-weight: 400;
- margin-right: 1em;
-}
-
-.nav {
- text-align: center;
-
- @media (min-width: 600px) {
- display: flex;
- }
-}
-
-ul {
- list-style: none;
- margin: 0;
- padding: 0;
-}
-
-li {
- margin: 0;
-}
-
-a {
- display: block;
- font-size: 1.2em;
- letter-spacing: -0.02em;
- margin-left: 0.75em;
- margin-right: 0.75em;
-}
-</style>
-
<nav class="header">
- <h1 class="title">Don’s Blog</h1>
- <ul class="nav">
- <li><a href="/">Home</a></li>
- <li><a href="/posts">All Posts</a></li>
- <li><a href="/authors/don">Author: Don</a></li>
- <li><a href="/authors/sancho">Author: Sancho</a></li>
- <li><a href="/about">About</a></li>
- </ul>
+ <h1 class="title">Don’s Blog</h1>
+ <ul class="nav">
+ <li><a href="/">Home</a></li>
+ <li><a href="/posts">All Posts</a></li>
+ <li><a href="/authors/don">Author: Don</a></li>
+ <li><a href="/authors/sancho">Author: Sancho</a></li>
+ <li><a href="/about">About</a></li>
+ </ul>
</nav>
+
+<style lang="scss">
+ .header {
+ text-align: center;
+
+ @media (min-width: 600px) {
+ display: flex;
+ align-items: center;
+ padding: 2rem;
+ }
+ }
+
+ .title {
+ margin: 0;
+ font-size: 1.2em;
+ letter-spacing: -0.03em;
+ font-weight: 400;
+ margin-right: 1em;
+ }
+
+ .nav {
+ text-align: center;
+
+ @media (min-width: 600px) {
+ display: flex;
+ }
+ }
+
+ ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+
+ li {
+ margin: 0;
+ }
+
+ a {
+ display: block;
+ font-size: 1.2em;
+ letter-spacing: -0.02em;
+ margin-left: 0.75em;
+ margin-right: 0.75em;
+ }
+</style>
diff --git a/examples/blog-multiple-authors/src/components/Pagination.astro b/examples/blog-multiple-authors/src/components/Pagination.astro
index 401931c07..8cc3941f6 100644
--- a/examples/blog-multiple-authors/src/components/Pagination.astro
+++ b/examples/blog-multiple-authors/src/components/Pagination.astro
@@ -1,44 +1,44 @@
---
export interface Props {
- prevUrl: string;
- nextUrl: string;
+ prevUrl: string;
+ nextUrl: string;
}
const { prevUrl, nextUrl } = Astro.props;
---
+<div class="wrapper">
+ <nav class="nav">
+ <a class="prev" href={prevUrl || '#'} aria-label="Previous Page">Prev</a>
+ <a class="next" href={nextUrl || '#'} aria-label="Next Page">Next</a>
+ </nav>
+</div>
+
<style lang="scss">
-.nav {
- display: flex;
- margin-right: auto;
- margin-left: auto;
- padding-top: 4rem;
- padding-bottom: 4rem;
-}
+ .nav {
+ display: flex;
+ margin-right: auto;
+ margin-left: auto;
+ padding-top: 4rem;
+ padding-bottom: 4rem;
+ }
-.prev,
-.next {
- display: block;
- text-transform: uppercase;
- font-size: 0.8em;
+ .prev,
+ .next {
+ display: block;
+ text-transform: uppercase;
+ font-size: 0.8em;
- &[href="#"] {
- display: none;
- }
-}
+ &[href='#'] {
+ display: none;
+ }
+ }
-.prev {
- margin-right: auto;
-}
+ .prev {
+ margin-right: auto;
+ }
-.next {
- margin-left: auto;
-}
+ .next {
+ margin-left: auto;
+ }
</style>
-
-<div class="wrapper">
- <nav class="nav">
- <a class="prev" href={prevUrl || '#'} aria-label="Previous Page">Prev</a>
- <a class="next" href={nextUrl || '#'} aria-label="Next Page">Next</a>
- </nav>
-</div>
diff --git a/examples/blog-multiple-authors/src/components/PostPreview.astro b/examples/blog-multiple-authors/src/components/PostPreview.astro
index 19e1362e5..81e80ba6c 100644
--- a/examples/blog-multiple-authors/src/components/PostPreview.astro
+++ b/examples/blog-multiple-authors/src/components/PostPreview.astro
@@ -1,66 +1,65 @@
---
export interface Props {
- post: any;
- author: string;
+ post: any;
+ author: string;
}
const { post, author } = Astro.props;
function formatDate(date) {
- return new Date(date).toUTCString().replace(/(\d\d\d\d) .*/, '$1'); // remove everything after YYYY
+ return new Date(date).toUTCString().replace(/(\d\d\d\d) .*/, '$1'); // remove everything after YYYY
}
---
+<article class="post">
+ <div class="data">
+ <h2>{post.title}</h2>
+ <a class="author" href={`/authors/${post.author}`}>{author.name}</a>
+ <time class="date" datetime={post.date}>{formatDate(post.date)}</time>
+ <p class="description">
+ {post.description}
+ <a class="link" href={post.url} aria-label={`Read ${post.title}`}>Read</a>
+ </p>
+ </div>
+</article>
+
<style lang="scss">
-.post {
- padding-top: 6rem;
- padding-bottom: 6rem;
- border-bottom: 1px solid rgba(black, 0.25);
- text-align: center;
-}
+ .post {
+ padding-top: 6rem;
+ padding-bottom: 6rem;
+ border-bottom: 1px solid rgba(black, 0.25);
+ text-align: center;
+ }
-.author {
- text-transform: uppercase;
-}
+ .author {
+ text-transform: uppercase;
+ }
-.date {
- font-style: italic;
-}
+ .date {
+ font-style: italic;
+ }
-.description {
- font-size: 1.25em;
-}
+ .description {
+ font-size: 1.25em;
+ }
-.link {
- text-transform: uppercase;
- font-size: 0.8em;
- margin-left: 1em;
-}
+ .link {
+ text-transform: uppercase;
+ font-size: 0.8em;
+ margin-left: 1em;
+ }
-h2 {
- font-weight: 700;
- font-size: 2.75em;
- line-height: 1;
- letter-spacing: -0.04em;
- margin-top: 0;
- margin-bottom: 0;
-}
+ h2 {
+ font-weight: 700;
+ font-size: 2.75em;
+ line-height: 1;
+ letter-spacing: -0.04em;
+ margin-top: 0;
+ margin-bottom: 0;
+ }
-time {
- display: block;
- margin-top: 0.25rem;
- margin-bottom: 0.5em;
-}
+ time {
+ display: block;
+ margin-top: 0.25rem;
+ margin-bottom: 0.5em;
+ }
</style>
-
-<article class="post">
-
- <div class="data">
- <h2>{post.title}</h2>
- <a class="author" href={`/authors/${post.author}`}>{author.name}</a>
- <time class="date" datetime={post.date}>{formatDate(post.date)}</time>
- <p class="description">
- {post.description}
- <a class="link" href={post.url} aria-label={`Read ${post.title}`}>Read</a>
- </p>
- </div>
-</article>
diff --git a/examples/blog-multiple-authors/src/layouts/post.astro b/examples/blog-multiple-authors/src/layouts/post.astro
index 64ad9fabd..fd8fcefa6 100644
--- a/examples/blog-multiple-authors/src/layouts/post.astro
+++ b/examples/blog-multiple-authors/src/layouts/post.astro
@@ -7,73 +7,72 @@ const { content } = Astro.props;
let canonicalURL = Astro.request.canonicalURL;
---
-<html lang={ content.lang || 'en' }>
- <head>
- <title>{content.title}</title>
- <MainHead title={content.title} description={content.description} image={content.image} canonicalURL={canonicalURL} />
- <style lang="scss">
- .title {
- margin-top: 4rem;
- margin-bottom: 4rem;
- font-size: 3em;
- letter-spacing: -0.04em;
- text-align: center;
- }
+<html lang={content.lang || 'en'}>
+ <head>
+ <title>{content.title}</title>
+ <MainHead title={content.title} description={content.description} image={content.image} {canonicalURL} />
+ <style lang="scss">
+ .title {
+ margin-top: 4rem;
+ margin-bottom: 4rem;
+ font-size: 3em;
+ letter-spacing: -0.04em;
+ text-align: center;
+ }
- .description {
- margin-bottom: 4rem;
- font-size: 1.4em;
- font-weight: 400;
- text-align: justify;
- text-transform: uppercase;
- }
+ .description {
+ margin-bottom: 4rem;
+ font-size: 1.4em;
+ font-weight: 400;
+ text-align: justify;
+ text-transform: uppercase;
+ }
- .img {
- display: block;
- width: 100%;
- height: auto;
- }
+ .img {
+ display: block;
+ width: 100%;
+ height: auto;
+ }
- .article {
- margin-top: 4rem;
- margin-bottom: 6rem;
+ .article {
+ margin-top: 4rem;
+ margin-bottom: 6rem;
- :global(p) {
- font-size: 1.3em;
- line-height: 2;
- margin-top: 2em;
- margin-bottom: 2em;
- }
- }
+ :global(p) {
+ font-size: 1.3em;
+ line-height: 2;
+ margin-top: 2em;
+ margin-bottom: 2em;
+ }
+ }
- .posts {
- text-transform: uppercase;
- }
+ .posts {
+ text-transform: uppercase;
+ }
- .footer {
- margin-top: 6rem;
- padding-bottom: 6rem;
- text-align: center;
- }
- </style>
- </head>
+ .footer {
+ margin-top: 6rem;
+ padding-bottom: 6rem;
+ text-align: center;
+ }
+ </style>
+ </head>
- <body>
- <Nav />
+ <body>
+ <Nav />
- <main class="wrapper">
- <h2 class="title">{content.title}</h2>
- <p class="description">{content.description}</p>
- <img class="img" src={content.image} alt="">
- <article class="article">
- <slot />
- </article>
- <footer class="footer">
- <a class="posts" href="/posts">All Posts</a>
- </footer>
- </main>
+ <main class="wrapper">
+ <h2 class="title">{content.title}</h2>
+ <p class="description">{content.description}</p>
+ <img class="img" src={content.image} alt="" />
+ <article class="article">
+ <slot />
+ </article>
+ <footer class="footer">
+ <a class="posts" href="/posts">All Posts</a>
+ </footer>
+ </main>
- <footer>
- </footer>
- </body>
+ <footer></footer>
+ </body>
</html>
diff --git a/examples/blog-multiple-authors/src/pages/about.astro b/examples/blog-multiple-authors/src/pages/about.astro
index 69124a3fa..ad101368b 100644
--- a/examples/blog-multiple-authors/src/pages/about.astro
+++ b/examples/blog-multiple-authors/src/pages/about.astro
@@ -2,65 +2,69 @@
import MainHead from '../components/MainHead.astro';
import Nav from '../components/Nav.astro';
-let title = "About";
-let description = "About page of an example blog on Astro";
+let title = 'About';
+let description = 'About page of an example blog on Astro';
let canonicalURL = Astro.request.canonicalURL;
---
-<html lang="en">
- <head>
- <MainHead
- title={title}
- description={description}
- canonicalURL={canonicalURL}
- />
- <style lang="scss">
-
- .text {
- padding-bottom: 6rem;
- p {
- font-size: 1.2em;
- line-height: 2;
- margin-top: 2em;
- margin-bottom: 2em;
- }
- }
+<html lang="en">
+ <head>
+ <MainHead {title} {description} {canonicalURL} />
+ <style lang="scss">
+ .text {
+ padding-bottom: 6rem;
- .hero {
- display: block;
- height: 16rem;
- overflow: hidden;
- margin: 4rem 0;
+ p {
+ font-size: 1.2em;
+ line-height: 2;
+ margin-top: 2em;
+ margin-bottom: 2em;
+ }
+ }
- &-img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- }
+ .hero {
+ display: block;
+ height: 16rem;
+ overflow: hidden;
+ margin: 4rem 0;
- .title {
- font-size: 3em;
- letter-spacing: -0.04em;
- margin-top: 2rem;
- margin-bottom: 0;
- text-align: center;
- }
- </style>
- </head>
- <body>
- <Nav title={title} />
+ &-img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ }
- <main class="wrapper">
- <h2 class="title">{title}</h2>
- <div class="hero">
- <img class="hero-img" src="/images/chapter-01.jpg" alt="">
- </div>
- <div class="text">
- <p>The book cover and spine above and the images which follow were not part of the original Ormsby translation—they are taken from the 1880 edition of J. W. Clark, illustrated by Gustave Doré. Clark in his edition states that, “The English text of ‘Don Quixote’ adopted in this edition is that of Jarvis, with occasional corrections from Motteaux.”</p>
- <p>See in the introduction below John Ormsby’s critique of both the Jarvis and Motteaux translations. It has been elected in the present Project Gutenberg edition to attach the famous engravings of Gustave Doré to the Ormsby translation instead of the Jarvis/Motteaux. The detail of many of the Doré engravings can be fully appreciated only by utilizing the “Full Size” button to expand them to their original dimensions. Ormsby in his Preface has criticized the fanciful nature of Doré’s illustrations; others feel these woodcuts and steel engravings well match Quixote’s dreams.</p>
- </div>
- </main>
+ .title {
+ font-size: 3em;
+ letter-spacing: -0.04em;
+ margin-top: 2rem;
+ margin-bottom: 0;
+ text-align: center;
+ }
+ </style>
+ </head>
+ <body>
+ <Nav {title} />
- </body>
+ <main class="wrapper">
+ <h2 class="title">{title}</h2>
+ <div class="hero">
+ <img class="hero-img" src="/images/chapter-01.jpg" alt="" />
+ </div>
+ <div class="text">
+ <p>
+ The book cover and spine above and the images which follow were not part of the original Ormsby translation—they are taken from the 1880 edition of J. W. Clark,
+ illustrated by Gustave Doré. Clark in his edition states that, “The English text of ‘Don Quixote’ adopted in this edition is that of Jarvis, with occasional corrections
+ from Motteaux.”
+ </p>
+ <p>
+ See in the introduction below John Ormsby’s critique of both the Jarvis and Motteaux translations. It has been elected in the present Project Gutenberg edition to attach
+ the famous engravings of Gustave Doré to the Ormsby translation instead of the Jarvis/Motteaux. The detail of many of the Doré engravings can be fully appreciated only by
+ utilizing the “Full Size” button to expand them to their original dimensions. Ormsby in his Preface has criticized the fanciful nature of Doré’s illustrations; others
+ feel these woodcuts and steel engravings well match Quixote’s dreams.
+ </p>
+ </div>
+ </main>
+ </body>
</html>
diff --git a/examples/blog-multiple-authors/src/pages/authors/[author].astro b/examples/blog-multiple-authors/src/pages/authors/[author].astro
index a4dbeb129..21aab27a5 100644
--- a/examples/blog-multiple-authors/src/pages/authors/[author].astro
+++ b/examples/blog-multiple-authors/src/pages/authors/[author].astro
@@ -1,5 +1,4 @@
---
-
import MainHead from '../../components/MainHead.astro';
import Nav from '../../components/Nav.astro';
import PostPreview from '../../components/PostPreview.astro';
@@ -7,16 +6,16 @@ import Pagination from '../../components/Pagination.astro';
import authorData from '../../data/authors.json';
export function getStaticPaths() {
- const allPosts = Astro.fetchContent<MarkdownFrontmatter>('../post/*.md');
- let allAuthorsUnique = [...new Set(allPosts.map(p => p.author))];
- return allAuthorsUnique.map(author => ({params: {author}, props: {allPosts}}));
+ const allPosts = Astro.fetchContent<MarkdownFrontmatter>('../post/*.md');
+ let allAuthorsUnique = [...new Set(allPosts.map((p) => p.author))];
+ return allAuthorsUnique.map((author) => ({ params: { author }, props: { allPosts } }));
}
interface MarkdownFrontmatter {
- date: number;
- description: string;
- title: string;
- author: string;
+ date: number;
+ description: string;
+ title: string;
+ author: string;
}
const { allPosts } = Astro.props;
@@ -25,65 +24,58 @@ const title = 'Don’s Blog';
const description = 'An example blog on Astro';
/** filter posts by author, sort by date */
-const posts = allPosts
- .filter((post) => post.author === params.author)
- .sort((a, b) => new Date(b.date).valueOf() - new Date(a.date).valueOf());
+const posts = allPosts.filter((post) => post.author === params.author).sort((a, b) => new Date(b.date).valueOf() - new Date(a.date).valueOf());
const author = authorData[posts[0].author];
---
<html lang="en">
- <head>
- <title>{title}</title>
- <MainHead
- title={title}
- description={description}
- image={posts[0].image}
- canonicalURL={canonicalURL.toString()}
- />
+ <head>
+ <title>{title}</title>
+ <MainHead {title} {description} image={posts[0].image} canonicalURL={canonicalURL.toString()} />
- <style lang="scss">
- .title {
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 3em;
- letter-spacing: -0.04em;
- margin-top: 2rem;
- margin-bottom: 0;
- }
+ <style lang="scss">
+ .title {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 3em;
+ letter-spacing: -0.04em;
+ margin-top: 2rem;
+ margin-bottom: 0;
+ }
- .avatar {
- width: 1em;
- height: 1em;
- margin-right: 0.5em;
- border-radius: 50%;
- overflow:hidden;
+ .avatar {
+ width: 1em;
+ height: 1em;
+ margin-right: 0.5em;
+ border-radius: 50%;
+ overflow: hidden;
- &-img {
- display: block;
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- }
+ &-img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ }
- .count {
- font-size: 1em;
- display: block;
- text-align: center;
- }
- </style>
- </head>
+ .count {
+ font-size: 1em;
+ display: block;
+ text-align: center;
+ }
+ </style>
+ </head>
- <body>
- <Nav title={title} />
+ <body>
+ <Nav {title} />
- <main class="wrapper">
- <h2 class="title">
- <div class="avatar"><img class="avatar-img" src={author.image} alt="" /></div>
- {author.name}
- </h2>
- {posts.map((post) => <PostPreview post={post} author={author} />)}
- </main>
- </body>
+ <main class="wrapper">
+ <h2 class="title">
+ <div class="avatar"><img class="avatar-img" src={author.image} alt="" /></div>
+ {author.name}
+ </h2>
+ {posts.map((post) => <PostPreview post={post} author={author} />)}
+ </main>
+ </body>
</html>
diff --git a/examples/blog-multiple-authors/src/pages/index.astro b/examples/blog-multiple-authors/src/pages/index.astro
index 5c18a87f0..8ad01c190 100644
--- a/examples/blog-multiple-authors/src/pages/index.astro
+++ b/examples/blog-multiple-authors/src/pages/index.astro
@@ -7,9 +7,9 @@ import Pagination from '../components/Pagination.astro';
import authorData from '../data/authors.json';
interface MarkdownFrontmatter {
- date: number;
- image: string;
- author: string;
+ date: number;
+ image: string;
+ author: string;
}
// Component Script:
@@ -28,26 +28,22 @@ let firstPage = allPosts.slice(0, 2);
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <title>{title}</title>
- <MainHead
- title={title}
- description={description}
- image={allPosts[0].image}
- canonicalURL={canonicalURL}
- />
- </head>
-
- <body>
- <Nav title={title} />
-
- <main class="wrapper">
- {allPosts.map((post) => <PostPreview post={post} author={authorData[post.author]} />)}
- </main>
-
- <footer>
- <Pagination prevUrl="/posts" nextUrl="/posts/2" />
- </footer>
- </body>
+ <head>
+ <title>{title}</title>
+ <MainHead {title} {description} image={allPosts[0].image} {canonicalURL} />
+ </head>
+
+ <body>
+ <Nav {title} />
+
+ <main class="wrapper">
+ {allPosts.map((post) => <PostPreview post={post} author={authorData[post.author]} />)}
+ </main>
+
+ <footer>
+ <Pagination prevUrl="/posts" nextUrl="/posts/2" />
+ </footer>
+ </body>
</html>
diff --git a/examples/blog-multiple-authors/src/pages/posts/[...page].astro b/examples/blog-multiple-authors/src/pages/posts/[...page].astro
index b615d762f..d0f95ce5b 100644
--- a/examples/blog-multiple-authors/src/pages/posts/[...page].astro
+++ b/examples/blog-multiple-authors/src/pages/posts/[...page].astro
@@ -5,26 +5,26 @@ import PostPreview from '../../components/PostPreview.astro';
import Pagination from '../../components/Pagination.astro';
import authorData from '../../data/authors.json';
-export async function getStaticPaths({paginate, rss}) {
- const allPosts = Astro.fetchContent<MarkdownFrontmatter>('../post/*.md');
- const sortedPosts = allPosts.sort((a, b) => new Date(b.date).valueOf() - new Date(a.date).valueOf());
+export async function getStaticPaths({ paginate, rss }) {
+ const allPosts = Astro.fetchContent<MarkdownFrontmatter>('../post/*.md');
+ const sortedPosts = allPosts.sort((a, b) => new Date(b.date).valueOf() - new Date(a.date).valueOf());
- // Generate an RSS feed from this collection of posts.
- // NOTE: This is disabled by default, since it requires `buildOptions.site` to be set in your "astro.config.mjs" file.
- // rss({
- // title: 'Don’s Blog',
- // description: 'An example blog on Astro',
- // customData: `<language>en-us</language>`,
- // items: sortedPosts.map(item => ({
- // title: item.title,
- // description: item.description,
- // link: item.url,
- // pubDate: item.date,
- // })),
- // });
+ // Generate an RSS feed from this collection of posts.
+ // NOTE: This is disabled by default, since it requires `buildOptions.site` to be set in your "astro.config.mjs" file.
+ // rss({
+ // title: 'Don’s Blog',
+ // description: 'An example blog on Astro',
+ // customData: `<language>en-us</language>`,
+ // items: sortedPosts.map(item => ({
+ // title: item.title,
+ // description: item.description,
+ // link: item.url,
+ // pubDate: item.date,
+ // })),
+ // });
- // Return a paginated collection of paths for all posts
- return paginate(sortedPosts, {pageSize: 1});
+ // Return a paginated collection of paths for all posts
+ return paginate(sortedPosts, { pageSize: 1 });
}
// page
@@ -34,55 +34,47 @@ let canonicalURL = Astro.request.canonicalURL;
// collection
interface MarkdownFrontmatter {
- date: number;
- description: string;
- title: string;
+ date: number;
+ description: string;
+ title: string;
}
-
const { page } = Astro.props;
---
<html lang="en">
- <head>
- <title>{title}</title>
- <MainHead
- title={title}
- description={description}
- image={page.data[0].image}
- canonicalURL={canonicalURL.toString()}
- prev={page.url.prev}
- next={page.url.next}
- />
+ <head>
+ <title>{title}</title>
+ <MainHead {title} {description} image={page.data[0].image} canonicalURL={canonicalURL.toString()} prev={page.url.prev} next={page.url.next} />
- <style lang="scss">
- .title {
- font-size: 3em;
- letter-spacing: -0.04em;
- margin-top: 2rem;
- margin-bottom: 0;
- text-align: center;
- }
+ <style lang="scss">
+ .title {
+ font-size: 3em;
+ letter-spacing: -0.04em;
+ margin-top: 2rem;
+ margin-bottom: 0;
+ text-align: center;
+ }
- .count {
- font-size: 1em;
- display: block;
- text-align: center;
- }
- </style>
- </head>
+ .count {
+ font-size: 1em;
+ display: block;
+ text-align: center;
+ }
+ </style>
+ </head>
- <body>
- <Nav title={title} />
+ <body>
+ <Nav {title} />
- <main class="wrapper">
- <h2 class="title">All Posts</h2>
- <small class="count">{page.start + 1}–{page.end + 1} of {page.total}</small>
- {page.data.map((post) => <PostPreview post={post} author={authorData[post.author]} />)}
- </main>
+ <main class="wrapper">
+ <h2 class="title">All Posts</h2>
+ <small class="count">{page.start + 1}–{page.end + 1} of {page.total}</small>
+ {page.data.map((post) => <PostPreview post={post} author={authorData[post.author]} />)}
+ </main>
- <footer>
- <Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
- </footer>
- </body>
+ <footer>
+ <Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
+ </footer>
+ </body>
</html>
diff --git a/examples/blog-multiple-authors/src/styles/global.css b/examples/blog-multiple-authors/src/styles/global.css
index d5891f6b4..408d2b9de 100644
--- a/examples/blog-multiple-authors/src/styles/global.css
+++ b/examples/blog-multiple-authors/src/styles/global.css
@@ -1,25 +1,25 @@
body {
- font-family: 'Spectral', serif;
- line-height: 1.4;
+ font-family: 'Spectral', serif;
+ line-height: 1.4;
}
p {
- line-height: 2;
+ line-height: 2;
}
a {
- color: crimson;
+ color: crimson;
}
img {
- max-width: 100%;
- height: auto;
+ max-width: 100%;
+ height: auto;
}
.wrapper {
- max-width: 60rem;
- margin-left: auto;
- margin-right: auto;
- padding-left: 2rem;
- padding-right: 2rem;
+ max-width: 60rem;
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: 2rem;
+ padding-right: 2rem;
}
diff --git a/examples/blog/astro.config.mjs b/examples/blog/astro.config.mjs
index 68499b3fa..a1516f292 100644
--- a/examples/blog/astro.config.mjs
+++ b/examples/blog/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Preact renderer to support Preact JSX components.
- renderers: ['@astrojs/renderer-preact'],
+ // Enable the Preact renderer to support Preact JSX components.
+ renderers: ['@astrojs/renderer-preact'],
});
diff --git a/examples/blog/src/components/Author.astro b/examples/blog/src/components/Author.astro
index 87cb48c45..3b29e0e76 100644
--- a/examples/blog/src/components/Author.astro
+++ b/examples/blog/src/components/Author.astro
@@ -1,16 +1,18 @@
---
export interface Props {
- name: string;
- href: string;
+ name: string;
+ href: string;
}
const { name, href } = Astro.props;
---
-<style>
- .author {
- margin-bottom: 0.75rem;
- }
-</style>
+
<div class="author">
- <p><a href={href}>{name}</a></p>
+ <p><a {href}>{name}</a></p>
</div>
+
+<style>
+ .author {
+ margin-bottom: 0.75rem;
+ }
+</style>
diff --git a/examples/blog/src/components/BaseHead.astro b/examples/blog/src/components/BaseHead.astro
index a5e199a29..e5745e196 100644
--- a/examples/blog/src/components/BaseHead.astro
+++ b/examples/blog/src/components/BaseHead.astro
@@ -1,8 +1,8 @@
---
export interface Props {
- title: string;
- description: string;
- permalink: string;
+ title: string;
+ description: string;
+ permalink: string;
}
const { title, description, permalink } = Astro.props;
---
@@ -12,7 +12,6 @@ const { title, description, permalink } = Astro.props;
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
-
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
@@ -34,4 +33,4 @@ const { title, description, permalink } = Astro.props;
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com" />
-<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&family=IBM+Plex+Sans:wght@400;700&display=swap">
+<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&family=IBM+Plex+Sans:wght@400;700&display=swap" />
diff --git a/examples/blog/src/components/BlogHeader.astro b/examples/blog/src/components/BlogHeader.astro
index 92d0cbfb1..4d32e87d8 100644
--- a/examples/blog/src/components/BlogHeader.astro
+++ b/examples/blog/src/components/BlogHeader.astro
@@ -1,90 +1,90 @@
+<header class="wrapper">
+ <article>
+ <h1>
+ <a href="/">
+ <svg class="logo" width="32" height="32" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <style>
+ #flame {
+ fill: #ff5d01;
+ }
+ #a {
+ fill: #000014;
+ }
+ </style>
+ <title>Logo</title>
+ <path
+ id="a"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
+ ></path>
+ <path
+ id="flame"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
+ ></path>
+ </svg>
+ <span>My Blog</span>
+ </a>
+ </h1>
+ </article>
+</header>
+
<style>
-header {
- padding-top: 1rem;
- padding-bottom: 1rem;
- height: 5rem;
-}
-article {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-.header-subitem {
- display: flex;
- flex-grow: 0;
- gap: 0.5em;
- align-items: center;
- justify-content: center;
- color: var(--theme-text-lighter);
- font-size: initial;
- padding: 0.5rem;
-}
-.header-subitem:hover {
- color: var(--theme-accent);
-}
-.header-subitem svg {
- width: 1.5rem;
- height: 1.5rem;
-}
+ header {
+ padding-top: 1rem;
+ padding-bottom: 1rem;
+ height: 5rem;
+ }
+ article {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+ .header-subitem {
+ display: flex;
+ flex-grow: 0;
+ gap: 0.5em;
+ align-items: center;
+ justify-content: center;
+ color: var(--theme-text-lighter);
+ font-size: initial;
+ padding: 0.5rem;
+ }
+ .header-subitem:hover {
+ color: var(--theme-accent);
+ }
+ .header-subitem svg {
+ width: 1.5rem;
+ height: 1.5rem;
+ }
-@media (max-width: 32em) {
- .header-subitem {
- display: none;
- }
-}
+ @media (max-width: 32em) {
+ .header-subitem {
+ display: none;
+ }
+ }
-h1 {
- margin: 0;
- font-size: 1.5rem;
- max-width: 100%;
- display: flex;
- flex-grow: 1;
-}
+ h1 {
+ margin: 0;
+ font-size: 1.5rem;
+ max-width: 100%;
+ display: flex;
+ flex-grow: 1;
+ }
-.logo {
- transform: translateY(0.25rem);
-}
+ .logo {
+ transform: translateY(0.25rem);
+ }
-svg {
- width: 2.5rem;
- height: 2.5rem;
-}
+ svg {
+ width: 2.5rem;
+ height: 2.5rem;
+ }
-h1 a {
- text-decoration: none;
- display: inline-flex;
-}
+ h1 a {
+ text-decoration: none;
+ display: inline-flex;
+ }
</style>
-
-<header class="wrapper">
- <article>
- <h1>
- <a href="/">
- <svg class="logo" width="32" height="32" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
- <style>
- #flame {
- fill: #ff5d01;
- }
- #a {
- fill: #000014;
- }
- </style>
- <title>Logo</title>
- <path
- id="a"
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
- />
- <path
- id="flame"
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
- />
- </svg>
- <span>My Blog</span>
- </a>
- </h1>
- </article>
-</header>
diff --git a/examples/blog/src/components/BlogPost.astro b/examples/blog/src/components/BlogPost.astro
index 9f7ac9efe..2dba32c3d 100644
--- a/examples/blog/src/components/BlogPost.astro
+++ b/examples/blog/src/components/BlogPost.astro
@@ -2,87 +2,87 @@
import Author from './Author.astro';
export interface Props {
- title: string;
- author: string;
- publishDate: string;
- heroImage: string;
- alt: string;
+ title: string;
+ author: string;
+ publishDate: string;
+ heroImage: string;
+ alt: string;
}
const { title, author, publishDate, heroImage, alt } = Astro.props;
---
<div class="layout">
- <article class="content">
- <div>
- <header>
- {heroImage && <img width="720" height="420" class="hero-image" loading="lazy" src={heroImage} alt={alt} />}
- <p class="publish-date">{publishDate}</p>
- <h1 class="title">{title}</h1>
- <Author name="@FredKSchott" href="https://twitter.com/FredKSchott" />
- </header>
- <main>
- <slot />
- </main>
- </div>
- </article>
+ <article class="content">
+ <div>
+ <header>
+ {heroImage && <img width="720" height="420" class="hero-image" loading="lazy" src={heroImage} alt={alt} />}
+ <p class="publish-date">{publishDate}</p>
+ <h1 class="title">{title}</h1>
+ <Author name="@FredKSchott" href="https://twitter.com/FredKSchott" />
+ </header>
+ <main>
+ <slot />
+ </main>
+ </div>
+ </article>
</div>
<style>
-.hero-image {
- width: 100vw;
- object-fit: cover;
- object-position: center;
- margin-top: 2rem;
- margin-bottom: 4rem;
- max-width: 1280px;
-}
+ .hero-image {
+ width: 100vw;
+ object-fit: cover;
+ object-position: center;
+ margin-top: 2rem;
+ margin-bottom: 4rem;
+ max-width: 1280px;
+ }
-@media (max-width: 50em) {
- .hero-image {
- height: 260px;
- margin-top: 0;
- margin-bottom: 2rem;
- }
-}
+ @media (max-width: 50em) {
+ .hero-image {
+ height: 260px;
+ margin-top: 0;
+ margin-bottom: 2rem;
+ }
+ }
-.content {
- margin-bottom: 8rem;
-}
+ .content {
+ margin-bottom: 8rem;
+ }
-.content :global(main > * + *) {
- margin-top: 1rem;
-}
+ .content :global(main > * + *) {
+ margin-top: 1rem;
+ }
-.content :global(h2) {
- margin-top: 4rem;
-}
+ .content :global(h2) {
+ margin-top: 4rem;
+ }
-header {
- display: flex;
- flex-direction: column;
- text-align: center;
- align-items: center;
- justify-content: center;
+ header {
+ display: flex;
+ flex-direction: column;
+ text-align: center;
+ align-items: center;
+ justify-content: center;
- padding-bottom: 2rem;
- margin-bottom: 2rem;
- border-bottom: 4px solid var(--theme-divider);
-}
+ padding-bottom: 2rem;
+ margin-bottom: 2rem;
+ border-bottom: 4px solid var(--theme-divider);
+ }
-.title,
-.author,
-.publish-date {
- margin: 0;
-}
+ .title,
+ .author,
+ .publish-date {
+ margin: 0;
+ }
-.publish-date,
-.author {
- color: var(--theme-text-lighter);
-}
+ .publish-date,
+ .author {
+ color: var(--theme-text-lighter);
+ }
-.title {
- font-size: 2.25rem;
- font-weight: 700;
-}
+ .title {
+ font-size: 2.25rem;
+ font-weight: 700;
+ }
</style>
diff --git a/examples/blog/src/components/BlogPostPreview.astro b/examples/blog/src/components/BlogPostPreview.astro
index 96d1190c7..4841d3a65 100644
--- a/examples/blog/src/components/BlogPostPreview.astro
+++ b/examples/blog/src/components/BlogPostPreview.astro
@@ -1,54 +1,55 @@
---
export interface Props {
- post: any;
+ post: any;
}
const { post } = Astro.props;
---
+
<article class="post-preview">
- <header>
- <p class="publish-date">{post.publishDate}</p>
- <a href={post.url}><h1 class="title">{post.title}</h1></a>
- </header>
- <p>{post.description}</p>
- <a href={post.url}>Read more</a>
+ <header>
+ <p class="publish-date">{post.publishDate}</p>
+ <a href={post.url}><h1 class="title">{post.title}</h1></a>
+ </header>
+ <p>{post.description}</p>
+ <a href={post.url}>Read more</a>
</article>
<style>
-.content :global(main > * + *) {
- margin-top: 1rem;
-}
+ .content :global(main > * + *) {
+ margin-top: 1rem;
+ }
-.post-preview {
- padding-bottom: 2rem;
- margin-bottom: 2rem;
- border-bottom: 4px solid var(--theme-divider);
-}
+ .post-preview {
+ padding-bottom: 2rem;
+ margin-bottom: 2rem;
+ border-bottom: 4px solid var(--theme-divider);
+ }
-header {
- align-items: flex-start;
- display: flex;
- flex-direction: column;
- justify-content: center;
- padding-bottom: 2rem;
- text-align: left;
-}
+ header {
+ align-items: flex-start;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ padding-bottom: 2rem;
+ text-align: left;
+ }
-.title,
-.author,
-.publish-date {
- margin: 0;
-}
+ .title,
+ .author,
+ .publish-date {
+ margin: 0;
+ }
-.publish-date,
-.author {
- font-size: 1.25rem;
- color: var(--theme-text-lighter);
-}
+ .publish-date,
+ .author {
+ font-size: 1.25rem;
+ color: var(--theme-text-lighter);
+ }
-.title {
- font-size: 2.25rem;
- font-weight: 700;
- color: var(--theme-text);
-}
+ .title {
+ font-size: 2.25rem;
+ font-weight: 700;
+ color: var(--theme-text);
+ }
</style>
diff --git a/examples/blog/src/components/Heading.astro b/examples/blog/src/components/Heading.astro
index f1805aaf7..fec49b538 100644
--- a/examples/blog/src/components/Heading.astro
+++ b/examples/blog/src/components/Heading.astro
@@ -1,9 +1,9 @@
<h1>
- <slot/>
+ <slot />
</h1>
<style>
- h1 {
- color: red;
- }
+ h1 {
+ color: red;
+ }
</style>
diff --git a/examples/blog/src/components/Logo.astro b/examples/blog/src/components/Logo.astro
index 7926dab4d..a92927536 100644
--- a/examples/blog/src/components/Logo.astro
+++ b/examples/blog/src/components/Logo.astro
@@ -1,56 +1,86 @@
<svg class="logo" width="158" height="170" viewBox="0 0 158 170" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path fill-rule="evenodd" clip-rule="evenodd" d="M96.5039 9.46441C97.4758 10.671 97.9714 12.2991 98.9626 15.5553L120.617 86.6902C112.611 82.5368 103.907 79.5413 94.7281 77.9252L80.6289 30.2798C80.3982 29.5002 79.6822 28.9654 78.8692 28.9654C78.0541 28.9654 77.3367 29.503 77.1079 30.2853L63.1795 77.9011C53.9579 79.51 45.2146 82.5109 37.1741 86.6793L58.9347 15.5388C59.929 12.2882 60.4262 10.6629 61.3981 9.45854C62.2562 8.39532 63.3723 7.56959 64.64 7.06003C66.076 6.48285 67.7756 6.48285 71.1749 6.48285H86.7174C90.1211 6.48285 91.823 6.48285 93.2603 7.06124C94.5291 7.575 95.6459 8.39925 96.5039 9.46441Z" fill="white" />
- <path fill-rule="evenodd" clip-rule="evenodd" d="M99.0951 90.0755C95.5253 93.1279 88.4002 95.2097 80.1929 95.2097C70.1197 95.2097 61.6767 92.0737 59.4363 87.8561C58.6354 90.2733 58.4558 93.0397 58.4558 94.8069C58.4558 94.8069 57.9281 103.485 63.9636 109.52C63.9636 106.386 66.5042 103.846 69.6381 103.846C75.0097 103.846 75.0036 108.532 74.9987 112.334C74.9986 112.448 74.9984 112.561 74.9984 112.673C74.9984 118.444 78.5255 123.391 83.5416 125.477C82.7924 123.936 82.3721 122.205 82.3721 120.377C82.3721 114.873 85.6034 112.823 89.3588 110.441C92.3469 108.546 95.6668 106.441 97.9548 102.217C99.1486 100.013 99.8265 97.4893 99.8265 94.8069C99.8265 93.1573 99.5702 91.5676 99.0951 90.0755Z" fill="#FF5D01" />
- <path fill-rule="evenodd" clip-rule="evenodd" d="M99.0951 90.0755C95.5253 93.1279 88.4002 95.2097 80.1929 95.2097C70.1197 95.2097 61.6767 92.0737 59.4363 87.8561C58.6354 90.2733 58.4558 93.0397 58.4558 94.8069C58.4558 94.8069 57.9281 103.485 63.9636 109.52C63.9636 106.386 66.5042 103.846 69.6381 103.846C75.0097 103.846 75.0036 108.532 74.9987 112.334C74.9986 112.448 74.9984 112.561 74.9984 112.673C74.9984 118.444 78.5255 123.391 83.5416 125.477C82.7924 123.936 82.3721 122.205 82.3721 120.377C82.3721 114.873 85.6034 112.823 89.3588 110.441C92.3469 108.546 95.6668 106.441 97.9548 102.217C99.1486 100.013 99.8265 97.4893 99.8265 94.8069C99.8265 93.1573 99.5702 91.5676 99.0951 90.0755Z" fill="url(#paint1_linear)" />
- <path d="M11.9957 169.024C20.0117 169.024 24.8597 167.104 27.6917 163.12C27.6917 164.896 27.7877 166.576 28.0277 168.112H32.7797C32.3477 165.616 32.2517 163.984 32.2517 159.472V153.328C32.2517 146.704 27.1157 143.2 17.3237 143.2C7.8677 143.2 1.7237 146.848 0.955701 152.128H5.9957C6.7637 148.576 10.7477 146.704 17.3237 146.704C23.8037 146.704 27.6437 148.96 27.6437 152.8V153.28L12.6677 154.144C6.5717 154.48 4.3157 155.344 2.5877 156.592C0.955701 157.792 0.0437012 159.664 0.0437012 161.824C0.0437012 166.384 4.7477 169.024 11.9957 169.024ZM13.5317 165.616C7.9637 165.616 4.8917 164.32 4.8917 161.728C4.8917 158.944 6.8117 157.696 13.5797 157.264L27.6437 156.4V157.504C27.6437 162.544 21.7397 165.616 13.5317 165.616Z" fill="white" />
- <path d="M55.9352 169.024C65.8712 169.024 69.8552 165.76 69.8552 161.008C69.8552 157.072 67.4072 155.056 61.1672 154.528L49.5032 153.616C46.3352 153.376 44.5592 152.464 44.5592 150.496C44.5592 148 47.2952 146.704 53.1992 146.704C59.9192 146.704 63.4232 148.048 65.7272 151.024L69.6152 149.152C67.2152 145.408 61.8872 143.2 53.6312 143.2C45.1352 143.2 40.0472 146.032 40.0472 150.688C40.0472 154.864 43.0712 156.88 48.7832 157.36L60.3512 158.272C64.1432 158.56 65.2952 159.328 65.2952 161.296C65.2952 164.128 62.3672 165.472 56.5592 165.472C49.5032 165.472 45.0392 163.552 42.8792 160.048L39.0872 162.112C42.0152 166.528 47.1512 169.024 55.9352 169.024Z" fill="white" />
- <path d="M79.6765 147.712V159.28C79.6765 164.032 81.3085 168.784 90.1885 168.784C92.4445 168.784 95.1805 168.352 96.3805 167.824V163.936C94.7005 164.32 92.6845 164.608 90.7165 164.608C86.5405 164.608 84.2845 162.976 84.2845 158.848V147.712H96.2845V144.112H84.2845V136L79.6765 137.872V144.112H72.1404V147.712H79.6765Z" fill="white" />
- <path d="M107.728 144.112H103.504V168.112H108.064V159.136C108.064 155.68 108.736 152.752 110.656 150.736C112.336 148.864 114.496 147.808 118.288 147.808C119.584 147.808 120.4 147.904 121.504 148.096V143.68C120.496 143.44 119.632 143.392 118.336 143.392C113.2 143.392 109.12 146.416 107.728 151.072V144.112Z" fill="white" />
- <path d="M140.724 169.024C150.948 169.024 157.956 163.84 157.956 156.112C157.956 148.384 150.948 143.2 140.724 143.2C130.5 143.2 123.492 148.384 123.492 156.112C123.492 163.84 130.5 169.024 140.724 169.024ZM140.724 165.232C133.188 165.232 128.34 161.68 128.34 156.112C128.34 150.544 133.188 146.992 140.724 146.992C148.212 146.992 153.108 150.544 153.108 156.112C153.108 161.68 148.212 165.232 140.724 165.232Z" fill="white" />
- <defs>
- <linearGradient id="paint1_linear" x1="115.168" y1="65.245" x2="94.0326" y2="109.491" gradientUnits="userSpaceOnUse">
- <stop stop-color="#FF1639" />
- <stop offset="1" stop-color="#FF1639" stop-opacity="0" />
- </linearGradient>
- </defs>
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M96.5039 9.46441C97.4758 10.671 97.9714 12.2991 98.9626 15.5553L120.617 86.6902C112.611 82.5368 103.907 79.5413 94.7281 77.9252L80.6289 30.2798C80.3982 29.5002 79.6822 28.9654 78.8692 28.9654C78.0541 28.9654 77.3367 29.503 77.1079 30.2853L63.1795 77.9011C53.9579 79.51 45.2146 82.5109 37.1741 86.6793L58.9347 15.5388C59.929 12.2882 60.4262 10.6629 61.3981 9.45854C62.2562 8.39532 63.3723 7.56959 64.64 7.06003C66.076 6.48285 67.7756 6.48285 71.1749 6.48285H86.7174C90.1211 6.48285 91.823 6.48285 93.2603 7.06124C94.5291 7.575 95.6459 8.39925 96.5039 9.46441Z"
+ fill="white"
+ />
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M99.0951 90.0755C95.5253 93.1279 88.4002 95.2097 80.1929 95.2097C70.1197 95.2097 61.6767 92.0737 59.4363 87.8561C58.6354 90.2733 58.4558 93.0397 58.4558 94.8069C58.4558 94.8069 57.9281 103.485 63.9636 109.52C63.9636 106.386 66.5042 103.846 69.6381 103.846C75.0097 103.846 75.0036 108.532 74.9987 112.334C74.9986 112.448 74.9984 112.561 74.9984 112.673C74.9984 118.444 78.5255 123.391 83.5416 125.477C82.7924 123.936 82.3721 122.205 82.3721 120.377C82.3721 114.873 85.6034 112.823 89.3588 110.441C92.3469 108.546 95.6668 106.441 97.9548 102.217C99.1486 100.013 99.8265 97.4893 99.8265 94.8069C99.8265 93.1573 99.5702 91.5676 99.0951 90.0755Z"
+ fill="#FF5D01"
+ />
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M99.0951 90.0755C95.5253 93.1279 88.4002 95.2097 80.1929 95.2097C70.1197 95.2097 61.6767 92.0737 59.4363 87.8561C58.6354 90.2733 58.4558 93.0397 58.4558 94.8069C58.4558 94.8069 57.9281 103.485 63.9636 109.52C63.9636 106.386 66.5042 103.846 69.6381 103.846C75.0097 103.846 75.0036 108.532 74.9987 112.334C74.9986 112.448 74.9984 112.561 74.9984 112.673C74.9984 118.444 78.5255 123.391 83.5416 125.477C82.7924 123.936 82.3721 122.205 82.3721 120.377C82.3721 114.873 85.6034 112.823 89.3588 110.441C92.3469 108.546 95.6668 106.441 97.9548 102.217C99.1486 100.013 99.8265 97.4893 99.8265 94.8069C99.8265 93.1573 99.5702 91.5676 99.0951 90.0755Z"
+ fill="url(#paint1_linear)"
+ />
+ <path
+ d="M11.9957 169.024C20.0117 169.024 24.8597 167.104 27.6917 163.12C27.6917 164.896 27.7877 166.576 28.0277 168.112H32.7797C32.3477 165.616 32.2517 163.984 32.2517 159.472V153.328C32.2517 146.704 27.1157 143.2 17.3237 143.2C7.8677 143.2 1.7237 146.848 0.955701 152.128H5.9957C6.7637 148.576 10.7477 146.704 17.3237 146.704C23.8037 146.704 27.6437 148.96 27.6437 152.8V153.28L12.6677 154.144C6.5717 154.48 4.3157 155.344 2.5877 156.592C0.955701 157.792 0.0437012 159.664 0.0437012 161.824C0.0437012 166.384 4.7477 169.024 11.9957 169.024ZM13.5317 165.616C7.9637 165.616 4.8917 164.32 4.8917 161.728C4.8917 158.944 6.8117 157.696 13.5797 157.264L27.6437 156.4V157.504C27.6437 162.544 21.7397 165.616 13.5317 165.616Z"
+ fill="white"
+ />
+ <path
+ d="M55.9352 169.024C65.8712 169.024 69.8552 165.76 69.8552 161.008C69.8552 157.072 67.4072 155.056 61.1672 154.528L49.5032 153.616C46.3352 153.376 44.5592 152.464 44.5592 150.496C44.5592 148 47.2952 146.704 53.1992 146.704C59.9192 146.704 63.4232 148.048 65.7272 151.024L69.6152 149.152C67.2152 145.408 61.8872 143.2 53.6312 143.2C45.1352 143.2 40.0472 146.032 40.0472 150.688C40.0472 154.864 43.0712 156.88 48.7832 157.36L60.3512 158.272C64.1432 158.56 65.2952 159.328 65.2952 161.296C65.2952 164.128 62.3672 165.472 56.5592 165.472C49.5032 165.472 45.0392 163.552 42.8792 160.048L39.0872 162.112C42.0152 166.528 47.1512 169.024 55.9352 169.024Z"
+ fill="white"
+ />
+ <path
+ d="M79.6765 147.712V159.28C79.6765 164.032 81.3085 168.784 90.1885 168.784C92.4445 168.784 95.1805 168.352 96.3805 167.824V163.936C94.7005 164.32 92.6845 164.608 90.7165 164.608C86.5405 164.608 84.2845 162.976 84.2845 158.848V147.712H96.2845V144.112H84.2845V136L79.6765 137.872V144.112H72.1404V147.712H79.6765Z"
+ fill="white"
+ />
+ <path
+ d="M107.728 144.112H103.504V168.112H108.064V159.136C108.064 155.68 108.736 152.752 110.656 150.736C112.336 148.864 114.496 147.808 118.288 147.808C119.584 147.808 120.4 147.904 121.504 148.096V143.68C120.496 143.44 119.632 143.392 118.336 143.392C113.2 143.392 109.12 146.416 107.728 151.072V144.112Z"
+ fill="white"
+ />
+ <path
+ d="M140.724 169.024C150.948 169.024 157.956 163.84 157.956 156.112C157.956 148.384 150.948 143.2 140.724 143.2C130.5 143.2 123.492 148.384 123.492 156.112C123.492 163.84 130.5 169.024 140.724 169.024ZM140.724 165.232C133.188 165.232 128.34 161.68 128.34 156.112C128.34 150.544 133.188 146.992 140.724 146.992C148.212 146.992 153.108 150.544 153.108 156.112C153.108 161.68 148.212 165.232 140.724 165.232Z"
+ fill="white"
+ />
+ <defs>
+ <linearGradient id="paint1_linear" x1="115.168" y1="65.245" x2="94.0326" y2="109.491" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#FF1639" />
+ <stop offset="1" stop-color="#FF1639" stop-opacity="0" />
+ </linearGradient>
+ </defs>
</svg>
<style lang="scss">
- .logo {
- margin: 2rem auto;
- }
+ .logo {
+ margin: 2rem auto;
+ }
- .title {
- font-family: var(--font-sans);
- font-size: 1rem;
- }
- .title svg {
- margin-right: -100%;
- }
- .title svg text {
- font-size: 16px;
- font-family: var(--font-sans);
- }
- .title svg text.span {
- fill: white;
- font-size: 16.2px;
- transform: translate(0, 18px);
- }
- .title svg text.em {
- fill: var(--color-green);
- transform: translate(0, 36px);
- }
+ .title {
+ font-family: var(--font-sans);
+ font-size: 1rem;
+ }
+ .title svg {
+ margin-right: -100%;
+ }
+ .title svg text {
+ font-size: 16px;
+ font-family: var(--font-sans);
+ }
+ .title svg text.span {
+ fill: white;
+ font-size: 16.2px;
+ transform: translate(0, 18px);
+ }
+ .title svg text.em {
+ fill: var(--color-green);
+ transform: translate(0, 36px);
+ }
- @media (min-width: 40em) {
- .title svg {
- margin-right: 0;
- margin-bottom: -40px;
- }
- .title svg text.span {
- font-size: 16px;
- }
- .title svg text.em {
- transform: translate(190px, 18px);
- }
- }
+ @media (min-width: 40em) {
+ .title svg {
+ margin-right: 0;
+ margin-bottom: -40px;
+ }
+ .title svg text.span {
+ font-size: 16px;
+ }
+ .title svg text.em {
+ transform: translate(190px, 18px);
+ }
+ }
</style>
diff --git a/examples/blog/src/layouts/BlogPost.astro b/examples/blog/src/layouts/BlogPost.astro
index b227d5222..e6bb0a6c7 100644
--- a/examples/blog/src/layouts/BlogPost.astro
+++ b/examples/blog/src/layouts/BlogPost.astro
@@ -3,23 +3,22 @@ import BaseHead from '../components/BaseHead.astro';
import BlogHeader from '../components/BlogHeader.astro';
import BlogPost from '../components/BlogPost.astro';
-const {content} = Astro.props;
-const {title, description, publishDate, author, heroImage, permalink, alt} = content;
+const { content } = Astro.props;
+const { title, description, publishDate, author, heroImage, permalink, alt } = content;
---
-<html lang={ content.lang || 'en' }>
- <head>
- <BaseHead title={title} description={description} permalink={permalink} />
- <link rel="stylesheet" href={Astro.resolve('../styles/blog.css')} />
- </head>
+<html lang={content.lang || 'en'}>
+ <head>
+ <BaseHead {title} {description} {permalink} />
+ <link rel="stylesheet" href={Astro.resolve('../styles/blog.css')} />
+ </head>
- <body>
- <BlogHeader />
- <div class="wrapper">
- <BlogPost title={title} author={author} heroImage={heroImage} publishDate={publishDate} alt={alt}>
- <slot />
- </BlogPost>
- </div>
- </body>
+ <body>
+ <BlogHeader />
+ <div class="wrapper">
+ <BlogPost {title} {author} {heroImage} {publishDate} {alt}>
+ <slot />
+ </BlogPost>
+ </div>
+ </body>
</html>
-
diff --git a/examples/blog/src/pages/index.astro b/examples/blog/src/pages/index.astro
index cd839d382..9a2178ed0 100644
--- a/examples/blog/src/pages/index.astro
+++ b/examples/blog/src/pages/index.astro
@@ -5,7 +5,7 @@ import BlogHeader from '../components/BlogHeader.astro';
import BlogPostPreview from '../components/BlogPostPreview.astro';
interface MarkdownFrontmatter {
- publishDate: number;
+ publishDate: number;
}
// Component Script:
@@ -24,59 +24,60 @@ allPosts = allPosts.sort((a, b) => new Date(b.publishDate).valueOf() - new Date(
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <BaseHead title={title} description={description} permalink={permalink} />
- <link rel="stylesheet" href={Astro.resolve('../styles/blog.css')} />
+ <head>
+ <BaseHead {title} {description} {permalink} />
+ <link rel="stylesheet" href={Astro.resolve('../styles/blog.css')} />
- <style>
- header {
- width: 100%;
- height: 100%;
- background-color: var(--theme-bg-offset);
- display: flex;
- align-items: center;
- justify-content: center;
- }
+ <style>
+ header {
+ width: 100%;
+ height: 100%;
+ background-color: var(--theme-bg-offset);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
- .content {
- margin-top: 4rem;
- margin-bottom: 8rem;
- }
+ .content {
+ margin-top: 4rem;
+ margin-bottom: 8rem;
+ }
- .content :global(main > * + *) {
- margin-top: 1rem;
- }
+ .content :global(main > * + *) {
+ margin-top: 1rem;
+ }
- .intro {
- padding-bottom: 4rem;
- margin-bottom: 2rem;
- border-bottom: 4px solid var(--theme-divider);
- }
+ .intro {
+ padding-bottom: 4rem;
+ margin-bottom: 2rem;
+ border-bottom: 4px solid var(--theme-divider);
+ }
- .intro > * {
- margin: 0;
- }
+ .intro > * {
+ margin: 0;
+ }
- .latest {
- font-size: 2.5rem;
- font-weight: 700;
- }
- </style>
- </head>
+ .latest {
+ font-size: 2.5rem;
+ font-weight: 700;
+ }
+ </style>
+ </head>
- <body>
- <BlogHeader />
- <div class="wrapper">
- <main class="content">
- <section class="intro">
- <h1 class="latest">{title}</h1>
- <p>{description}</p>
- </section>
- <section aria-label="Blog post list">
- {allPosts.map(p => <BlogPostPreview post={p} />)}
- </section>
- </main>
- </div>
- </body>
+ <body>
+ <BlogHeader />
+ <div class="wrapper">
+ <main class="content">
+ <section class="intro">
+ <h1 class="latest">{title}</h1>
+ <p>{description}</p>
+ </section>
+ <section aria-label="Blog post list">
+ {allPosts.map((p) => <BlogPostPreview post={p} />)}
+ </section>
+ </main>
+ </div>
+ </body>
</html>
diff --git a/examples/blog/src/styles/blog.css b/examples/blog/src/styles/blog.css
index 319cdbc6f..f086aea0c 100644
--- a/examples/blog/src/styles/blog.css
+++ b/examples/blog/src/styles/blog.css
@@ -1,284 +1,284 @@
:root {
- --font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
- --font-body: 'IBM Plex Sans', var(--font-fallback);
- --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
- 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
-
- --color-white: #fff;
- --color-black: #000014;
-
- --color-gray-50: #f9fafb;
- --color-gray-100: #f3f4f6;
- --color-gray-200: #e5e7eb;
- --color-gray-300: #d1d5db;
- --color-gray-400: #9ca3af;
- --color-gray-500: #6b7280;
- --color-gray-600: #4b5563;
- --color-gray-700: #374151;
- --color-gray-800: #1f2937;
- --color-gray-900: #111827;
-
- --color-blue: #3894ff;
- --color-blue-rgb: 56, 148, 255;
- --color-green: #17c083;
- --color-green-rgb: 23, 192, 131;
- --color-orange: #ff5d01;
- --color-orange-rgb: 255, 93, 1;
- --color-purple: #882de7;
- --color-purple-rgb: 136, 45, 231;
- --color-red: #ff1639;
- --color-red-rgb: 255, 22, 57;
- --color-yellow: #ffbe2d;
- --color-yellow-rgb: 255, 190, 45;
+ --font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
+ --font-body: 'IBM Plex Sans', var(--font-fallback);
+ --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
+ 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
+
+ --color-white: #fff;
+ --color-black: #000014;
+
+ --color-gray-50: #f9fafb;
+ --color-gray-100: #f3f4f6;
+ --color-gray-200: #e5e7eb;
+ --color-gray-300: #d1d5db;
+ --color-gray-400: #9ca3af;
+ --color-gray-500: #6b7280;
+ --color-gray-600: #4b5563;
+ --color-gray-700: #374151;
+ --color-gray-800: #1f2937;
+ --color-gray-900: #111827;
+
+ --color-blue: #3894ff;
+ --color-blue-rgb: 56, 148, 255;
+ --color-green: #17c083;
+ --color-green-rgb: 23, 192, 131;
+ --color-orange: #ff5d01;
+ --color-orange-rgb: 255, 93, 1;
+ --color-purple: #882de7;
+ --color-purple-rgb: 136, 45, 231;
+ --color-red: #ff1639;
+ --color-red-rgb: 255, 22, 57;
+ --color-yellow: #ffbe2d;
+ --color-yellow-rgb: 255, 190, 45;
}
:root {
- color-scheme: light;
- --theme-accent: var(--color-orange);
- --theme-accent-rgb: var(--color-orange-rgb);
- --theme-accent-opacity: 0.1;
- --theme-divider: var(--color-gray-100);
- --theme-text: var(--color-gray-800);
- --theme-text-light: var(--color-gray-600);
- --theme-text-lighter: var(--color-gray-400);
- --theme-bg: var(--color-white);
- --theme-bg-offset: var(--color-gray-100);
- --theme-bg-accent: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
- --theme-code-inline-bg: var(--color-gray-100);
- --theme-code-text: var(--color-gray-100);
- --theme-code-bg: var(--color-gray-700);
+ color-scheme: light;
+ --theme-accent: var(--color-orange);
+ --theme-accent-rgb: var(--color-orange-rgb);
+ --theme-accent-opacity: 0.1;
+ --theme-divider: var(--color-gray-100);
+ --theme-text: var(--color-gray-800);
+ --theme-text-light: var(--color-gray-600);
+ --theme-text-lighter: var(--color-gray-400);
+ --theme-bg: var(--color-white);
+ --theme-bg-offset: var(--color-gray-100);
+ --theme-bg-accent: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
+ --theme-code-inline-bg: var(--color-gray-100);
+ --theme-code-text: var(--color-gray-100);
+ --theme-code-bg: var(--color-gray-700);
}
body {
- background: var(--theme-bg);
- color: var(--theme-text);
+ background: var(--theme-bg);
+ color: var(--theme-text);
}
:root.theme-dark {
- color-scheme: dark;
- --theme-accent-opacity: 0.3;
- --theme-divider: var(--color-gray-900);
- --theme-text: var(--color-gray-200);
- --theme-text-light: var(--color-gray-400);
- --theme-text-lighter: var(--color-gray-600);
- --theme-bg: var(--color-black);
- --theme-bg-offset: var(--color-gray-900);
- --theme-code-inline-bg: var(--color-gray-800);
- --theme-code-text: var(--color-gray-200);
- --theme-code-bg: var(--color-gray-900);
+ color-scheme: dark;
+ --theme-accent-opacity: 0.3;
+ --theme-divider: var(--color-gray-900);
+ --theme-text: var(--color-gray-200);
+ --theme-text-light: var(--color-gray-400);
+ --theme-text-lighter: var(--color-gray-600);
+ --theme-bg: var(--color-black);
+ --theme-bg-offset: var(--color-gray-900);
+ --theme-code-inline-bg: var(--color-gray-800);
+ --theme-code-text: var(--color-gray-200);
+ --theme-code-bg: var(--color-gray-900);
}
::selection {
- color: var(--theme-accent);
- background-color: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
+ color: var(--theme-accent);
+ background-color: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
}
* {
- box-sizing: border-box;
- margin: 0;
+ box-sizing: border-box;
+ margin: 0;
}
:root {
- --user-font-scale: 1rem - 16px;
- --max-width: calc(100% - 2rem);
+ --user-font-scale: 1rem - 16px;
+ --max-width: calc(100% - 2rem);
}
@media (min-width: 50em) {
- :root {
- --max-width: 40em;
- }
+ :root {
+ --max-width: 40em;
+ }
}
body {
- font-family: var(--font-body);
- font-size: 1rem;
- font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
- line-height: 1.625;
+ font-family: var(--font-body);
+ font-size: 1rem;
+ font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
+ line-height: 1.625;
}
.wrapper {
- margin-left: auto;
- margin-right: auto;
- max-width: 65em;
- padding-left: 2rem;
- padding-right: 2rem;
- width: 100%;
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 65em;
+ padding-left: 2rem;
+ padding-right: 2rem;
+ width: 100%;
}
nav ul {
- list-style: none;
- padding: 0;
+ list-style: none;
+ padding: 0;
}
/* Typography */
:is(h1, h2, h3, h4, h5, h6) {
- margin-bottom: 1.38rem;
- font-weight: 400;
- line-height: 1.3;
+ margin-bottom: 1.38rem;
+ font-weight: 400;
+ line-height: 1.3;
}
:is(h1, h2) {
- max-width: 40ch;
+ max-width: 40ch;
}
:is(h2, h3):not(:first-child) {
- margin-top: 3rem;
+ margin-top: 3rem;
}
h1 {
- font-size: clamp(2.488rem, 1.924rem + 1.41vw, 3.052rem);
+ font-size: clamp(2.488rem, 1.924rem + 1.41vw, 3.052rem);
}
h2 {
- font-size: clamp(2.074rem, 1.707rem + 0.9175vw, 2.441rem);
+ font-size: clamp(2.074rem, 1.707rem + 0.9175vw, 2.441rem);
}
h3 {
- font-size: clamp(1.728rem, 1.503rem + 0.5625vw, 1.953rem);
+ font-size: clamp(1.728rem, 1.503rem + 0.5625vw, 1.953rem);
}
h4 {
- font-size: clamp(1.44rem, 1.317rem + 0.3075vw, 1.563rem);
+ font-size: clamp(1.44rem, 1.317rem + 0.3075vw, 1.563rem);
}
h5 {
- font-size: clamp(1.2rem, 1.15rem + 0.125vw, 1.25rem);
+ font-size: clamp(1.2rem, 1.15rem + 0.125vw, 1.25rem);
}
p {
- color: var(--theme-text-light);
+ color: var(--theme-text-light);
}
small,
.text_small {
- font-size: 0.833rem;
+ font-size: 0.833rem;
}
a {
- color: var(--theme-accent);
- font-weight: 400;
- text-underline-offset: 0.08em;
- text-decoration: none;
- align-items: center;
- gap: 0.5rem;
+ color: var(--theme-accent);
+ font-weight: 400;
+ text-underline-offset: 0.08em;
+ text-decoration: none;
+ align-items: center;
+ gap: 0.5rem;
}
a > code:not([class*='language']) {
- position: relative;
- color: var(--theme-accent);
- background: transparent;
- text-underline-offset: var(--padding-block);
+ position: relative;
+ color: var(--theme-accent);
+ background: transparent;
+ text-underline-offset: var(--padding-block);
}
a > code:not([class*='language'])::before {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- display: block;
- background: var(--theme-accent);
- opacity: var(--theme-accent-opacity);
- border-radius: var(--border-radius);
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ background: var(--theme-accent);
+ opacity: var(--theme-accent-opacity);
+ border-radius: var(--border-radius);
}
a:hover,
a:focus {
- text-decoration: underline;
+ text-decoration: underline;
}
a:focus {
- outline: 2px solid currentColor;
- outline-offset: 0.25em;
+ outline: 2px solid currentColor;
+ outline-offset: 0.25em;
}
strong {
- font-weight: 600;
- color: inherit;
+ font-weight: 600;
+ color: inherit;
}
/* Supporting Content */
code:not([class*='language']) {
- --border-radius: 3px;
- --padding-block: 0.2rem;
- --padding-inline: 0.33rem;
+ --border-radius: 3px;
+ --padding-block: 0.2rem;
+ --padding-inline: 0.33rem;
- font-family: var(--font-mono);
- font-size: 0.85em;
- color: inherit;
- background-color: var(--theme-code-inline-bg);
- padding: var(--padding-block) var(--padding-inline);
- margin: calc(var(--padding-block) * -1) -0.125em;
- border-radius: var(--border-radius);
- word-break: break-word;
+ font-family: var(--font-mono);
+ font-size: 0.85em;
+ color: inherit;
+ background-color: var(--theme-code-inline-bg);
+ padding: var(--padding-block) var(--padding-inline);
+ margin: calc(var(--padding-block) * -1) -0.125em;
+ border-radius: var(--border-radius);
+ word-break: break-word;
}
pre > code:not([class*='language']) {
- background-color: transparent;
- padding: 0;
- margin: 0;
- border-radius: 0;
- color: inherit;
+ background-color: transparent;
+ padding: 0;
+ margin: 0;
+ border-radius: 0;
+ color: inherit;
}
pre {
- position: relative;
- background-color: var(--theme-code-bg);
- color: var(--theme-code-text);
- --padding-block: 1rem;
- --padding-inline: 2rem;
- padding: var(--padding-block) var(--padding-inline);
- padding-right: calc(var(--padding-inline) * 2);
- margin-left: calc(50vw - var(--padding-inline));
- transform: translateX(-50vw);
-
- line-height: 1.414;
- width: calc(100vw + (var(--padding-inline) * 2));
- max-width: calc(100% + (var(--padding-inline) * 2));
- overflow-y: hidden;
- overflow-x: auto;
+ position: relative;
+ background-color: var(--theme-code-bg);
+ color: var(--theme-code-text);
+ --padding-block: 1rem;
+ --padding-inline: 2rem;
+ padding: var(--padding-block) var(--padding-inline);
+ padding-right: calc(var(--padding-inline) * 2);
+ margin-left: calc(50vw - var(--padding-inline));
+ transform: translateX(-50vw);
+
+ line-height: 1.414;
+ width: calc(100vw + (var(--padding-inline) * 2));
+ max-width: calc(100% + (var(--padding-inline) * 2));
+ overflow-y: hidden;
+ overflow-x: auto;
}
@media (min-width: 37.75em) {
- pre {
- --padding-inline: 1.25rem;
- border-radius: 8px;
- }
+ pre {
+ --padding-inline: 1.25rem;
+ border-radius: 8px;
+ }
}
.flex {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
}
img.cover {
- width: 100%;
- max-height: 50vh;
- object-fit: cover;
+ width: 100%;
+ max-height: 50vh;
+ object-fit: cover;
}
blockquote {
- font-size: 1.5rem;
- --padding-block: 1rem;
- --padding-inline: 1.25rem;
- --color: var(--theme-divider);
+ font-size: 1.5rem;
+ --padding-block: 1rem;
+ --padding-inline: 1.25rem;
+ --color: var(--theme-divider);
- display: flex;
- flex-direction: column;
+ display: flex;
+ flex-direction: column;
- padding: var(--padding-block) var(--padding-inline);
- margin-left: calc(var(--padding-inline) * -1);
- margin-right: calc(var(--padding-inline) * -1);
+ padding: var(--padding-block) var(--padding-inline);
+ margin-left: calc(var(--padding-inline) * -1);
+ margin-right: calc(var(--padding-inline) * -1);
- background: transparent;
- border-left: calc(var(--padding-inline) / 2) solid var(--color);
- border-radius: 0;
+ background: transparent;
+ border-left: calc(var(--padding-inline) / 2) solid var(--color);
+ border-radius: 0;
}
blockquote .source {
- font-weight: 500;
- color: var(--color);
- font-size: 1rem;
+ font-weight: 500;
+ color: var(--color);
+ font-size: 1rem;
}
diff --git a/examples/component/astro.config.mjs b/examples/component/astro.config.mjs
index 94846abde..d68cea82c 100644
--- a/examples/component/astro.config.mjs
+++ b/examples/component/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Comment out "renderers: []" to enable Astro's default component support.
- renderers: [],
+ // Comment out "renderers: []" to enable Astro's default component support.
+ renderers: [],
});
diff --git a/examples/component/demo/src/pages/index.astro b/examples/component/demo/src/pages/index.astro
index 96ca814e3..211c2f5e3 100644
--- a/examples/component/demo/src/pages/index.astro
+++ b/examples/component/demo/src/pages/index.astro
@@ -1,29 +1,24 @@
---
-import * as Component from '@example/my-component'
+import * as Component from '@example/my-component';
---
-<html lang="en">
-
-<head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width" />
- <title>Welcome to Astro</title>
- <style global>
- h {
- display: block;
- font-size: 2em;
- font-weight: bold;
- margin-block: 0.67em;
- }
- </style>
-</head>
-<body>
- <Component.Heading>
- Welcome to Astro
- </Component.Heading>
- <Component.Button>
- Plain Button
- </Component.Button>
-</body>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <title>Welcome to Astro</title>
+ <style global>
+ h {
+ display: block;
+ font-size: 2em;
+ font-weight: bold;
+ margin-block: 0.67em;
+ }
+ </style>
+ </head>
-</html> \ No newline at end of file
+ <body>
+ <Component.Heading>Welcome to Astro</Component.Heading>
+ <Component.Button>Plain Button</Component.Button>
+ </body>
+</html>
diff --git a/examples/component/packages/my-component/Button.astro b/examples/component/packages/my-component/Button.astro
index d853628a1..5f32ce4e8 100644
--- a/examples/component/packages/my-component/Button.astro
+++ b/examples/component/packages/my-component/Button.astro
@@ -1,15 +1,13 @@
---
export interface Props extends Record<any, any> {
- type?: string
+ type?: string;
}
-const {
- type,
- ...props
-} = {
- ...Astro.props
-} as Props
+const { type, ...props } = {
+ ...Astro.props,
+} as Props;
-props.type = type || 'button'
+props.type = type || 'button';
---
-<button {...props}><slot /></button> \ No newline at end of file
+
+<button {...props}><slot /></button>
diff --git a/examples/component/packages/my-component/Heading.astro b/examples/component/packages/my-component/Heading.astro
index f27e74b3d..813c0c11b 100644
--- a/examples/component/packages/my-component/Heading.astro
+++ b/examples/component/packages/my-component/Heading.astro
@@ -1,18 +1,15 @@
---
export interface Props extends Record<any, any> {
- level?: number | string
- role?: string
+ level?: number | string;
+ role?: string;
}
-const {
- level,
- role,
- ...props
-} = {
- ...Astro.props
-} as Props
+const { level, role, ...props } = {
+ ...Astro.props,
+} as Props;
-props.role = role || 'heading'
-props['aria-level'] = level || '1'
+props.role = role || 'heading';
+props['aria-level'] = level || '1';
---
-<h {...props}><slot /></h> \ No newline at end of file
+
+<h {...props}><slot /></h>
diff --git a/examples/docs/astro.config.mjs b/examples/docs/astro.config.mjs
index 120106f3e..075ab81fe 100644
--- a/examples/docs/astro.config.mjs
+++ b/examples/docs/astro.config.mjs
@@ -8,10 +8,10 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- renderers: [
- // Enable the Preact renderer to support Preact JSX components.
- '@astrojs/renderer-preact',
- // Enable the React renderer, for the Algolia search component
- '@astrojs/renderer-react',
- ],
+ renderers: [
+ // Enable the Preact renderer to support Preact JSX components.
+ '@astrojs/renderer-preact',
+ // Enable the React renderer, for the Algolia search component
+ '@astrojs/renderer-react',
+ ],
});
diff --git a/examples/docs/public/make-scrollable-code-focusable.js b/examples/docs/public/make-scrollable-code-focusable.js
index 35f104923..f2b7030f7 100644
--- a/examples/docs/public/make-scrollable-code-focusable.js
+++ b/examples/docs/public/make-scrollable-code-focusable.js
@@ -1,3 +1,3 @@
Array.from(document.getElementsByTagName('pre')).forEach((element) => {
- element.setAttribute('tabindex', '0');
+ element.setAttribute('tabindex', '0');
});
diff --git a/examples/docs/src/components/Footer/AvatarList.astro b/examples/docs/src/components/Footer/AvatarList.astro
index 429676990..c57ee2eb1 100644
--- a/examples/docs/src/components/Footer/AvatarList.astro
+++ b/examples/docs/src/components/Footer/AvatarList.astro
@@ -1,151 +1,149 @@
---
// fetch all commits for just this page's path
-const path = "docs/" + Astro.props.path;
+const path = 'docs/' + Astro.props.path;
const url = `https://api.github.com/repos/withastro/astro/commits?path=${path}`;
const commitsURL = `https://github.com/withastro/astro/commits/main/${path}`;
async function getCommits(url) {
- try {
- const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN;
- if (!token) {
- throw new Error(
- 'Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.'
- );
- }
-
- const auth = `Basic ${Buffer.from(token, "binary").toString("base64")}`;
-
- const res = await fetch(url, {
- method: "GET",
- headers: {
- Authorization: auth,
- "User-Agent": "astro-docs/1.0",
- },
- });
-
- const data = await res.json();
-
- if (!res.ok) {
- throw new Error(
- `Request to fetch commits failed. Reason: ${res.statusText}
+ try {
+ const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN;
+ if (!token) {
+ throw new Error('Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.');
+ }
+
+ const auth = `Basic ${Buffer.from(token, 'binary').toString('base64')}`;
+
+ const res = await fetch(url, {
+ method: 'GET',
+ headers: {
+ Authorization: auth,
+ 'User-Agent': 'astro-docs/1.0',
+ },
+ });
+
+ const data = await res.json();
+
+ if (!res.ok) {
+ throw new Error(
+ `Request to fetch commits failed. Reason: ${res.statusText}
Message: ${data.message}`
- );
- }
+ );
+ }
- return data;
- } catch (e) {
- console.warn(`[error] /src/components/AvatarList.astro
+ return data;
+ } catch (e) {
+ console.warn(`[error] /src/components/AvatarList.astro
${e?.message ?? e}`);
- return new Array();
- }
+ return new Array();
+ }
}
function removeDups(arr) {
- if (!arr) {
- return new Array();
- }
- let map = new Map();
-
- for (let item of arr) {
- let author = item.author;
- // Deduplicate based on author.id
- map.set(author.id, { login: author.login, id: author.id });
- }
-
- return Array.from(map.values());
+ if (!arr) {
+ return new Array();
+ }
+ let map = new Map();
+
+ for (let item of arr) {
+ let author = item.author;
+ // Deduplicate based on author.id
+ map.set(author.id, { login: author.login, id: author.id });
+ }
+
+ return Array.from(map.values());
}
const data = await getCommits(url);
const unique = removeDups(data);
const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors
const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors
-
---
+
<!-- Thanks to @5t3ph for https://smolcss.dev/#smol-avatar-list! -->
<div class="contributors">
-<ul class="avatar-list" style={`--avatar-count: ${recentContributors.length}`}>
-
-{recentContributors.map((item) => (
- <li><a href={`https://github.com/${item.login}`}><img alt={`Contributor ${item.login}`} title={`Contributor ${item.login}`} width="64" height="64" src={`https://avatars.githubusercontent.com/u/${item.id}`}/></a></li>
-
-))}
- </ul>
- {additionalContributors > 0 && <span><a href={commitsURL}>{`and ${additionalContributors} additional contributor${additionalContributors > 1 ? 's' : ''}.`}</a></span>}
- {unique.length === 0 && <a href={commitsURL}>Contributors</a>}
+ <ul class="avatar-list" style={`--avatar-count: ${recentContributors.length}`}>
+ {recentContributors.map((item) => (
+ <li>
+ <a href={`https://github.com/${item.login}`}>
+ <img alt={`Contributor ${item.login}`} title={`Contributor ${item.login}`} width="64" height="64" src={`https://avatars.githubusercontent.com/u/${item.id}`} />
+ </a>
+ </li>
+ ))}
+ </ul>
+ {additionalContributors > 0 && (
+ <span>
+ <a href={commitsURL}>{`and ${additionalContributors} additional contributor${additionalContributors > 1 ? 's' : ''}.`}</a>
+ </span>
+ )}
+ {unique.length === 0 && <a href={commitsURL}>Contributors</a>}
</div>
<style>
-.avatar-list {
- --avatar-size: 2.5rem;
- --avatar-count: 3;
+ .avatar-list {
+ --avatar-size: 2.5rem;
+ --avatar-count: 3;
- display: grid;
- list-style: none;
- /* Default to displaying most of the avatar to
+ display: grid;
+ list-style: none;
+ /* Default to displaying most of the avatar to
enable easier access on touch devices, ensuring
the WCAG touch target size is met or exceeded */
- grid-template-columns: repeat(
- var(--avatar-count),
- max(44px, calc(var(--avatar-size) / 1.15))
- );
- /* `padding` matches added visual dimensions of
+ grid-template-columns: repeat(var(--avatar-count), max(44px, calc(var(--avatar-size) / 1.15)));
+ /* `padding` matches added visual dimensions of
the `box-shadow` to help create a more accurate
computed component size */
- padding: 0.08em;
- font-size: var(--avatar-size);
-}
+ padding: 0.08em;
+ font-size: var(--avatar-size);
+ }
-@media (any-hover: hover) and (any-pointer: fine) {
- .avatar-list {
- /* We create 1 extra cell to enable the computed
+ @media (any-hover: hover) and (any-pointer: fine) {
+ .avatar-list {
+ /* We create 1 extra cell to enable the computed
width to match the final visual width */
- grid-template-columns: repeat(
- calc(var(--avatar-count) + 1),
- calc(var(--avatar-size) / 1.75)
- );
- }
-}
-
-.avatar-list li {
- width: var(--avatar-size);
- height: var(--avatar-size);
-}
-
-.avatar-list li:hover ~ li a,
-.avatar-list li:focus-within ~ li a {
- transform: translateX(33%);
-}
-
-.avatar-list img,
-.avatar-list a {
- display: block;
- border-radius: 50%;
-}
-
-.avatar-list a {
- transition: transform 180ms ease-in-out;
-}
-
-.avatar-list img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- background-color: #fff;
- box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
-}
-
-.avatar-list a:focus {
- outline: 2px solid transparent;
- /* Double-layer trick to work for dark and light backgrounds */
- box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
-}
-
-.contributors {
- display: flex;
- align-items: center;
-}
-
-.contributors > * + * {
- margin-left: .75rem;
-}
+ grid-template-columns: repeat(calc(var(--avatar-count) + 1), calc(var(--avatar-size) / 1.75));
+ }
+ }
+
+ .avatar-list li {
+ width: var(--avatar-size);
+ height: var(--avatar-size);
+ }
+
+ .avatar-list li:hover ~ li a,
+ .avatar-list li:focus-within ~ li a {
+ transform: translateX(33%);
+ }
+
+ .avatar-list img,
+ .avatar-list a {
+ display: block;
+ border-radius: 50%;
+ }
+
+ .avatar-list a {
+ transition: transform 180ms ease-in-out;
+ }
+
+ .avatar-list img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ background-color: #fff;
+ box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
+ }
+
+ .avatar-list a:focus {
+ outline: 2px solid transparent;
+ /* Double-layer trick to work for dark and light backgrounds */
+ box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
+ }
+
+ .contributors {
+ display: flex;
+ align-items: center;
+ }
+
+ .contributors > * + * {
+ margin-left: 0.75rem;
+ }
</style>
diff --git a/examples/docs/src/components/Footer/Footer.astro b/examples/docs/src/components/Footer/Footer.astro
index 48de51054..d13f832e5 100644
--- a/examples/docs/src/components/Footer/Footer.astro
+++ b/examples/docs/src/components/Footer/Footer.astro
@@ -4,13 +4,13 @@ const { path } = Astro.props;
---
<footer>
- <AvatarList path={path} />
+ <AvatarList {path} />
</footer>
<style>
-footer {
- margin-top: auto;
- padding: 2rem 0;
- border-top: 3px solid var(--theme-divider);
-}
+ footer {
+ margin-top: auto;
+ padding: 2rem 0;
+ border-top: 3px solid var(--theme-divider);
+ }
</style>
diff --git a/examples/docs/src/components/HeadCommon.astro b/examples/docs/src/components/HeadCommon.astro
index fd24a81d8..289aa23aa 100644
--- a/examples/docs/src/components/HeadCommon.astro
+++ b/examples/docs/src/components/HeadCommon.astro
@@ -1,11 +1,11 @@
<!-- Global Metadata -->
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width">
+<meta charset="utf-8" />
+<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="alternate icon" type="image/x-icon" href="/favicon.ico" />
-<link rel="sitemap" href="/sitemap.xml"/>
+<link rel="sitemap" href="/sitemap.xml" />
<!-- Global CSS -->
<link rel="stylesheet" href={Astro.resolve('../styles/theme.css')} />
@@ -13,22 +13,22 @@
<link rel="stylesheet" href={Astro.resolve('../styles/index.css')} />
<!-- Preload Fonts -->
-<link rel="preconnect" href="https://fonts.googleapis.com">
-<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
-<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital@0;1&display=swap" rel="stylesheet">
+<link rel="preconnect" href="https://fonts.googleapis.com" />
+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
+<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital@0;1&display=swap" rel="stylesheet" />
<!-- Scrollable a11y code helper -->
-<script type="module" src="/make-scrollable-code-focusable.js" />
+<script type="module" src="/make-scrollable-code-focusable.js"></script>
<!-- This is intentionally inlined to avoid FOUC -->
<script>
- const root = document.documentElement;
- const theme = localStorage.getItem('theme');
- if (theme === 'dark' || (!theme) && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- root.classList.add('theme-dark');
- } else {
- root.classList.remove('theme-dark');
- }
+ const root = document.documentElement;
+ const theme = localStorage.getItem('theme');
+ if (theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
+ root.classList.add('theme-dark');
+ } else {
+ root.classList.remove('theme-dark');
+ }
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
@@ -38,4 +38,4 @@
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-TEL60V1WM9');
-</script> --> \ No newline at end of file
+</script> -->
diff --git a/examples/docs/src/components/HeadSEO.astro b/examples/docs/src/components/HeadSEO.astro
index 3add09b7d..003872bc9 100644
--- a/examples/docs/src/components/HeadSEO.astro
+++ b/examples/docs/src/components/HeadSEO.astro
@@ -1,36 +1,37 @@
---
-import {SITE, OPEN_GRAPH} from '../config.ts';
+import { SITE, OPEN_GRAPH } from '../config.ts';
export interface Props {
- content: any,
- site: any,
- canonicalURL: URL | string,
-};
+ content: any;
+ site: any;
+ canonicalURL: URL | string;
+}
const { content = {}, canonicalURL } = Astro.props;
const formattedContentTitle = content.title ? `${content.title} 🚀 ${SITE.title}` : SITE.title;
const imageSrc = content?.image?.src ?? OPEN_GRAPH.image.src;
const canonicalImageSrc = new URL(imageSrc, Astro.site);
const imageAlt = content?.image?.alt ?? OPEN_GRAPH.image.alt;
---
+
<!-- Page Metadata -->
-<link rel="canonical" href={canonicalURL}/>
+<link rel="canonical" href={canonicalURL} />
<!-- OpenGraph Tags -->
-<meta property="og:title" content={formattedContentTitle}/>
-<meta property="og:type" content="article"/>
-<meta property="og:url" content={canonicalURL}/>
-<meta property="og:locale" content={content.ogLocale ?? SITE.defaultLanguage}/>
-<meta property="og:image" content={canonicalImageSrc}/>
-<meta property="og:image:alt" content={imageAlt}/>
-<meta name="description" property="og:description" content={content.description ? content.description : SITE.description}/>
-<meta property="og:site_name" content={SITE.title}/>
+<meta property="og:title" content={formattedContentTitle} />
+<meta property="og:type" content="article" />
+<meta property="og:url" content={canonicalURL} />
+<meta property="og:locale" content={content.ogLocale ?? SITE.defaultLanguage} />
+<meta property="og:image" content={canonicalImageSrc} />
+<meta property="og:image:alt" content={imageAlt} />
+<meta name="description" property="og:description" content={content.description ? content.description : SITE.description} />
+<meta property="og:site_name" content={SITE.title} />
<!-- Twitter Tags -->
-<meta name="twitter:card" content="summary_large_image"/>
-<meta name="twitter:site" content={OPEN_GRAPH.twitter}/>
-<meta name="twitter:title" content={formattedContentTitle}/>
-<meta name="twitter:description" content={content.description ? content.description : SITE.description}/>
-<meta name="twitter:image" content={canonicalImageSrc}/>
-<meta name="twitter:image:alt" content={imageAlt}/>
+<meta name="twitter:card" content="summary_large_image" />
+<meta name="twitter:site" content={OPEN_GRAPH.twitter} />
+<meta name="twitter:title" content={formattedContentTitle} />
+<meta name="twitter:description" content={content.description ? content.description : SITE.description} />
+<meta name="twitter:image" content={canonicalImageSrc} />
+<meta name="twitter:image:alt" content={imageAlt} />
<!--
TODO: Add json+ld data, maybe https://schema.org/APIReference makes sense?
diff --git a/examples/docs/src/components/Header/AstroLogo.astro b/examples/docs/src/components/Header/AstroLogo.astro
index 124865f1b..7d6891dcb 100644
--- a/examples/docs/src/components/Header/AstroLogo.astro
+++ b/examples/docs/src/components/Header/AstroLogo.astro
@@ -1,18 +1,27 @@
---
-const {size} = Astro.props;
+const { size } = Astro.props;
---
+
<svg class="logo" width={size} height={size} viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
- <style>
- #flame {
- fill: var(--theme-text-accent);
- }
- #a {
- fill: var(--theme-text-accent);
- }
- </style>
- <title>Logo</title>
- <path id="a" fill-rule="evenodd" clip-rule="evenodd"
- d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z" />
- <path id="flame" fill-rule="evenodd" clip-rule="evenodd"
- d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z" />
-</svg> \ No newline at end of file
+ <style>
+ #flame {
+ fill: var(--theme-text-accent);
+ }
+ #a {
+ fill: var(--theme-text-accent);
+ }
+ </style>
+ <title>Logo</title>
+ <path
+ id="a"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
+ ></path>
+ <path
+ id="flame"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
+ ></path>
+</svg>
diff --git a/examples/docs/src/components/Header/Header.astro b/examples/docs/src/components/Header/Header.astro
index a6c82d7bb..46677b481 100644
--- a/examples/docs/src/components/Header/Header.astro
+++ b/examples/docs/src/components/Header/Header.astro
@@ -5,128 +5,133 @@ import AstroLogo from './AstroLogo.astro';
import SkipToContent from './SkipToContent.astro';
import SidebarToggle from './SidebarToggle.tsx';
import LanguageSelect from './LanguageSelect.tsx';
-import Search from "./Search.tsx";
+import Search from './Search.tsx';
-const {currentPage} = Astro.props;
+const { currentPage } = Astro.props;
const lang = currentPage && getLanguageFromURL(currentPage);
---
-<style>
- header {
- z-index: 11;
- height: var(--theme-navbar-height);
- width: 100%;
- background-color: var(--theme-navbar-bg);
- display: flex;
- align-items: center;
- justify-content: center;
- overflow: hidden;
- position: sticky;
- top: 0;
- }
+<header>
+ <SkipToContent />
+ <nav class="nav-wrapper" title="Top Navigation">
+ <div class="menu-toggle">
+ <SidebarToggle client:idle />
+ </div>
+ <div class="logo flex">
+ <AstroLogo size={40} />
+ <a href="/">
+ <h1>Documentation</h1>
+ </a>
+ </div>
+ <div style="flex-grow: 1;"></div>
+ {KNOWN_LANGUAGE_CODES.length > 1 && <LanguageSelect lang={lang} client:idle />}
+ {CONFIG.ALGOLIA && (
+ <div class="search-item">
+ <Search client:idle />
+ </div>
+ )}
+ </nav>
+</header>
- .logo {
- display: flex;
- overflow: hidden;
- width: 30px;
- font-size: 2rem;
- flex-shrink: 0;
- font-weight: 600;
- line-height: 1;
- color: hsla(var(--color-base-white), 100%, 1);
- gap: 0.25em;
- z-index: -1;
- }
+<style>
+ header {
+ z-index: 11;
+ height: var(--theme-navbar-height);
+ width: 100%;
+ background-color: var(--theme-navbar-bg);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ position: sticky;
+ top: 0;
+ }
- .logo a {
- padding: 0.5em 0.25em;
- margin: -0.5em -0.25em;
- text-decoration: none;
- font-weight: bold;
- }
+ .logo {
+ display: flex;
+ overflow: hidden;
+ width: 30px;
+ font-size: 2rem;
+ flex-shrink: 0;
+ font-weight: 600;
+ line-height: 1;
+ color: hsla(var(--color-base-white), 100%, 1);
+ gap: 0.25em;
+ z-index: -1;
+ }
- .logo a {
- transition: color 100ms ease-out;
- color: var(--theme-text);
- }
+ .logo a {
+ padding: 0.5em 0.25em;
+ margin: -0.5em -0.25em;
+ text-decoration: none;
+ font-weight: bold;
+ }
- .logo a:hover,
- .logo a:focus {
- color: var(--theme-text-accent);
- }
+ .logo a {
+ transition: color 100ms ease-out;
+ color: var(--theme-text);
+ }
- .logo h1 {
- font: inherit;
- color: inherit;
- margin: 0;
- }
+ .logo a:hover,
+ .logo a:focus {
+ color: var(--theme-text-accent);
+ }
- .nav-wrapper {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- gap: 1em;
- width: 100%;
- max-width: 82em;
- padding: 0 1rem;
- }
+ .logo h1 {
+ font: inherit;
+ color: inherit;
+ margin: 0;
+ }
- @media (min-width: 50em) {
- header {
- position: static;
- padding: 2rem 0rem;
- }
- .logo {
- width: auto;
- margin: 0;
- z-index: 0;
- }
- .menu-toggle {
- display: none;
- }
- .logo {
- width: auto;
- }
- }
+ .nav-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 1em;
+ width: 100%;
+ max-width: 82em;
+ padding: 0 1rem;
+ }
- /** Style Algolia */
- :root {
- --docsearch-primary-color: var(--theme-accent);
- --docsearch-logo-color: var(--theme-text);
- }
+ @media (min-width: 50em) {
+ header {
+ position: static;
+ padding: 2rem 0rem;
+ }
+ .logo {
+ width: auto;
+ margin: 0;
+ z-index: 0;
+ }
+ .menu-toggle {
+ display: none;
+ }
+ .logo {
+ width: auto;
+ }
+ }
- .search-item {
- display: none;
- position: relative;
- z-index: 10;
- flex-grow: 1;
- padding-right: 0.7rem;
- display: flex;
- max-width: 200px;
- }
- :global(.search-item > *) {
- flex-grow: 1;
- }
- @media (min-width: 50em) {
- .search-item {
- max-width: 400px;
- }
- }
+ /** Style Algolia */
+ :root {
+ --docsearch-primary-color: var(--theme-accent);
+ --docsearch-logo-color: var(--theme-text);
+ }
+
+ .search-item {
+ display: none;
+ position: relative;
+ z-index: 10;
+ flex-grow: 1;
+ padding-right: 0.7rem;
+ display: flex;
+ max-width: 200px;
+ }
+ :global(.search-item > *) {
+ flex-grow: 1;
+ }
+ @media (min-width: 50em) {
+ .search-item {
+ max-width: 400px;
+ }
+ }
</style>
-<header>
- <SkipToContent />
- <nav class="nav-wrapper" title="Top Navigation">
- <div class="menu-toggle">
- <SidebarToggle client:idle/>
- </div>
- <div class="logo flex">
- <AstroLogo size={40} />
- <a href="/">
- <h1>Documentation</h1>
- </a>
- </div>
- <div style="flex-grow: 1;"></div>
- {KNOWN_LANGUAGE_CODES.length > 1 && <LanguageSelect lang={lang} client:idle />}
- {CONFIG.ALGOLIA && <div class="search-item"><Search client:idle /></div>}
- </nav>
-</header> \ No newline at end of file
diff --git a/examples/docs/src/components/Header/LanguageSelect.css b/examples/docs/src/components/Header/LanguageSelect.css
index 4e878714b..9e0ae7ce1 100644
--- a/examples/docs/src/components/Header/LanguageSelect.css
+++ b/examples/docs/src/components/Header/LanguageSelect.css
@@ -1,47 +1,47 @@
.language-select {
- flex-grow: 1;
- width: 48px;
- box-sizing: border-box;
- margin: 0;
- padding: 0.33em 0.5em;
- overflow: visible;
- font-weight: 500;
- font-size: 1rem;
- font-family: inherit;
- line-height: inherit;
- background-color: var(--theme-bg);
- border-color: var(--theme-text-lighter);
- color: var(--theme-text-light);
- border-style: solid;
- border-width: 1px;
- border-radius: 0.25rem;
- outline: 0;
- cursor: pointer;
- transition-timing-function: ease-out;
- transition-duration: 0.2s;
- transition-property: border-color, color;
- -webkit-font-smoothing: antialiased;
- padding-left: 30px;
- padding-right: 1rem;
+ flex-grow: 1;
+ width: 48px;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0.33em 0.5em;
+ overflow: visible;
+ font-weight: 500;
+ font-size: 1rem;
+ font-family: inherit;
+ line-height: inherit;
+ background-color: var(--theme-bg);
+ border-color: var(--theme-text-lighter);
+ color: var(--theme-text-light);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.25rem;
+ outline: 0;
+ cursor: pointer;
+ transition-timing-function: ease-out;
+ transition-duration: 0.2s;
+ transition-property: border-color, color;
+ -webkit-font-smoothing: antialiased;
+ padding-left: 30px;
+ padding-right: 1rem;
}
.language-select-wrapper .language-select:hover,
.language-select-wrapper .language-select:focus {
- color: var(--theme-text);
- border-color: var(--theme-text-light);
+ color: var(--theme-text);
+ border-color: var(--theme-text-light);
}
.language-select-wrapper {
- color: var(--theme-text-light);
- position: relative;
+ color: var(--theme-text-light);
+ position: relative;
}
.language-select-wrapper > svg {
- position: absolute;
- top: 7px;
- left: 10px;
- pointer-events: none;
+ position: absolute;
+ top: 7px;
+ left: 10px;
+ pointer-events: none;
}
@media (min-width: 50em) {
- .language-select {
- width: 100%;
- }
+ .language-select {
+ width: 100%;
+ }
}
diff --git a/examples/docs/src/components/Header/LanguageSelect.tsx b/examples/docs/src/components/Header/LanguageSelect.tsx
index 8b9807fe8..7fd3af229 100644
--- a/examples/docs/src/components/Header/LanguageSelect.tsx
+++ b/examples/docs/src/components/Header/LanguageSelect.tsx
@@ -4,35 +4,35 @@ import './LanguageSelect.css';
import { KNOWN_LANGUAGES, langPathRegex } from '../../languages';
const LanguageSelect: FunctionalComponent<{ lang: string }> = ({ lang }) => {
- return (
- <div class="language-select-wrapper">
- <svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88.6 77.3" height="1.2em" width="1.2em">
- <path fill="currentColor" d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z" />
- <path
- fill="currentColor"
- d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8 c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z"
- />
- </svg>
- <select
- class="language-select"
- value={lang}
- onChange={(e) => {
- const newLang = e.target.value;
- let actualDest = window.location.pathname.replace(langPathRegex, '/');
- if (actualDest == '/') actualDest = `/introduction`;
- window.location.pathname = '/' + newLang + actualDest;
- }}
- >
- {Object.keys(KNOWN_LANGUAGES).map((key) => {
- return (
- <option value={KNOWN_LANGUAGES[key]}>
- <span>{key}</span>
- </option>
- );
- })}
- </select>
- </div>
- );
+ return (
+ <div class="language-select-wrapper">
+ <svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88.6 77.3" height="1.2em" width="1.2em">
+ <path fill="currentColor" d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z" />
+ <path
+ fill="currentColor"
+ d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8 c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z"
+ />
+ </svg>
+ <select
+ class="language-select"
+ value={lang}
+ onChange={(e) => {
+ const newLang = e.target.value;
+ let actualDest = window.location.pathname.replace(langPathRegex, '/');
+ if (actualDest == '/') actualDest = `/introduction`;
+ window.location.pathname = '/' + newLang + actualDest;
+ }}
+ >
+ {Object.keys(KNOWN_LANGUAGES).map((key) => {
+ return (
+ <option value={KNOWN_LANGUAGES[key]}>
+ <span>{key}</span>
+ </option>
+ );
+ })}
+ </select>
+ </div>
+ );
};
export default LanguageSelect;
diff --git a/examples/docs/src/components/Header/Search.css b/examples/docs/src/components/Header/Search.css
index 2056c2c8f..9a0c7f303 100644
--- a/examples/docs/src/components/Header/Search.css
+++ b/examples/docs/src/components/Header/Search.css
@@ -1,69 +1,69 @@
/** Style Algolia */
:root {
- --docsearch-primary-color: var(--theme-accent);
- --docsearch-logo-color: var(--theme-text);
+ --docsearch-primary-color: var(--theme-accent);
+ --docsearch-logo-color: var(--theme-text);
}
.search-input {
- flex-grow: 1;
- box-sizing: border-box;
- width: 100%;
- margin: 0;
- padding: 0.33em 0.5em;
- overflow: visible;
- font-weight: 500;
- font-size: 1rem;
- font-family: inherit;
- line-height: inherit;
- background-color: var(--theme-divider);
- border-color: var(--theme-divider);
- color: var(--theme-text-light);
- border-style: solid;
- border-width: 1px;
- border-radius: 0.25rem;
- outline: 0;
- cursor: pointer;
- transition-timing-function: ease-out;
- transition-duration: 0.2s;
- transition-property: border-color, color;
- -webkit-font-smoothing: antialiased;
+ flex-grow: 1;
+ box-sizing: border-box;
+ width: 100%;
+ margin: 0;
+ padding: 0.33em 0.5em;
+ overflow: visible;
+ font-weight: 500;
+ font-size: 1rem;
+ font-family: inherit;
+ line-height: inherit;
+ background-color: var(--theme-divider);
+ border-color: var(--theme-divider);
+ color: var(--theme-text-light);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.25rem;
+ outline: 0;
+ cursor: pointer;
+ transition-timing-function: ease-out;
+ transition-duration: 0.2s;
+ transition-property: border-color, color;
+ -webkit-font-smoothing: antialiased;
}
.search-input:hover,
.search-input:focus {
- color: var(--theme-text);
- border-color: var(--theme-text-light);
+ color: var(--theme-text);
+ border-color: var(--theme-text-light);
}
.search-input:hover::placeholder,
.search-input:focus::placeholder {
- color: var(--theme-text-light);
+ color: var(--theme-text-light);
}
.search-input::placeholder {
- color: var(--theme-text-light);
+ color: var(--theme-text-light);
}
.search-hint {
- position: absolute;
- top: 7px;
- right: 19px;
- padding: 3px 5px;
- display: none;
- display: none;
- align-items: center;
- justify-content: center;
- letter-spacing: 0.125em;
- font-size: 13px;
- font-family: var(--font-mono);
- pointer-events: none;
- border-color: var(--theme-text-lighter);
- color: var(--theme-text-light);
- border-style: solid;
- border-width: 1px;
- border-radius: 0.25rem;
- line-height: 14px;
+ position: absolute;
+ top: 7px;
+ right: 19px;
+ padding: 3px 5px;
+ display: none;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ letter-spacing: 0.125em;
+ font-size: 13px;
+ font-family: var(--font-mono);
+ pointer-events: none;
+ border-color: var(--theme-text-lighter);
+ color: var(--theme-text-light);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.25rem;
+ line-height: 14px;
}
@media (min-width: 50em) {
- .search-hint {
- display: flex;
- }
+ .search-hint {
+ display: flex;
+ }
}
/* ------------------------------------------------------------ *\
@@ -71,6 +71,6 @@
\* ------------------------------------------------------------ */
.DocSearch-Modal .DocSearch-Hit a {
- box-shadow: none;
- border: 1px solid var(--theme-accent);
+ box-shadow: none;
+ border: 1px solid var(--theme-accent);
}
diff --git a/examples/docs/src/components/Header/Search.tsx b/examples/docs/src/components/Header/Search.tsx
index 158f6fe29..cfee83d04 100644
--- a/examples/docs/src/components/Header/Search.tsx
+++ b/examples/docs/src/components/Header/Search.tsx
@@ -7,71 +7,71 @@ import '@docsearch/css/dist/style.css';
import './Search.css';
export default function Search() {
- const [isOpen, setIsOpen] = useState(false);
- const searchButtonRef = useRef();
- const [initialQuery, setInitialQuery] = useState(null);
+ const [isOpen, setIsOpen] = useState(false);
+ const searchButtonRef = useRef();
+ const [initialQuery, setInitialQuery] = useState(null);
- const onOpen = useCallback(() => {
- setIsOpen(true);
- }, [setIsOpen]);
+ const onOpen = useCallback(() => {
+ setIsOpen(true);
+ }, [setIsOpen]);
- const onClose = useCallback(() => {
- setIsOpen(false);
- }, [setIsOpen]);
+ const onClose = useCallback(() => {
+ setIsOpen(false);
+ }, [setIsOpen]);
- const onInput = useCallback(
- (e) => {
- setIsOpen(true);
- setInitialQuery(e.key);
- },
- [setIsOpen, setInitialQuery]
- );
+ const onInput = useCallback(
+ (e) => {
+ setIsOpen(true);
+ setInitialQuery(e.key);
+ },
+ [setIsOpen, setInitialQuery]
+ );
- useDocSearchKeyboardEvents({
- isOpen,
- onOpen,
- onClose,
- onInput,
- searchButtonRef,
- });
+ useDocSearchKeyboardEvents({
+ isOpen,
+ onOpen,
+ onClose,
+ onInput,
+ searchButtonRef,
+ });
- return (
- <>
- <button type="button" ref={searchButtonRef} onClick={onOpen} className="search-input">
- <svg width="24" height="24" fill="none">
- <path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
- </svg>
- <span>Search</span>
- <span className="search-hint">
- <span className="sr-only">Press </span>
- <kbd>/</kbd>
- <span className="sr-only"> to search</span>
- </span>
- </button>
- {isOpen &&
- createPortal(
- <DocSearchModal
- initialQuery={initialQuery}
- initialScrollY={window.scrollY}
- onClose={onClose}
- indexName={(CONFIG as any).ALGOLIA.indexName}
- apiKey={(CONFIG as any).ALGOLIA.apiKey}
- transformItems={(items) => {
- return items.map((item) => {
- // We transform the absolute URL into a relative URL to
- // work better on localhost, preview URLS.
- const a = document.createElement('a');
- a.href = item.url;
- const hash = a.hash === '#overview' ? '' : a.hash;
- return {
- ...item,
- url: `${a.pathname}${hash}`,
- };
- });
- }}
- />,
- document.body
- )}
- </>
- );
+ return (
+ <>
+ <button type="button" ref={searchButtonRef} onClick={onOpen} className="search-input">
+ <svg width="24" height="24" fill="none">
+ <path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
+ </svg>
+ <span>Search</span>
+ <span className="search-hint">
+ <span className="sr-only">Press </span>
+ <kbd>/</kbd>
+ <span className="sr-only"> to search</span>
+ </span>
+ </button>
+ {isOpen &&
+ createPortal(
+ <DocSearchModal
+ initialQuery={initialQuery}
+ initialScrollY={window.scrollY}
+ onClose={onClose}
+ indexName={(CONFIG as any).ALGOLIA.indexName}
+ apiKey={(CONFIG as any).ALGOLIA.apiKey}
+ transformItems={(items) => {
+ return items.map((item) => {
+ // We transform the absolute URL into a relative URL to
+ // work better on localhost, preview URLS.
+ const a = document.createElement('a');
+ a.href = item.url;
+ const hash = a.hash === '#overview' ? '' : a.hash;
+ return {
+ ...item,
+ url: `${a.pathname}${hash}`,
+ };
+ });
+ }}
+ />,
+ document.body
+ )}
+ </>
+ );
}
diff --git a/examples/docs/src/components/Header/SidebarToggle.tsx b/examples/docs/src/components/Header/SidebarToggle.tsx
index 97fece6b2..90b180461 100644
--- a/examples/docs/src/components/Header/SidebarToggle.tsx
+++ b/examples/docs/src/components/Header/SidebarToggle.tsx
@@ -3,25 +3,25 @@ import { h, Fragment } from 'preact';
import { useState, useEffect } from 'preact/hooks';
const MenuToggle: FunctionalComponent = () => {
- const [sidebarShown, setSidebarShown] = useState(false);
+ const [sidebarShown, setSidebarShown] = useState(false);
- useEffect(() => {
- const body = document.getElementsByTagName('body')[0];
- if (sidebarShown) {
- body.classList.add('mobile-sidebar-toggle');
- } else {
- body.classList.remove('mobile-sidebar-toggle');
- }
- }, [sidebarShown]);
+ useEffect(() => {
+ const body = document.getElementsByTagName('body')[0];
+ if (sidebarShown) {
+ body.classList.add('mobile-sidebar-toggle');
+ } else {
+ body.classList.remove('mobile-sidebar-toggle');
+ }
+ }, [sidebarShown]);
- return (
- <button type="button" aria-pressed={sidebarShown ? 'true' : 'false'} id="menu-toggle" onClick={() => setSidebarShown(!sidebarShown)}>
- <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
- </svg>
- <span className="sr-only">Toggle sidebar</span>
- </button>
- );
+ return (
+ <button type="button" aria-pressed={sidebarShown ? 'true' : 'false'} id="menu-toggle" onClick={() => setSidebarShown(!sidebarShown)}>
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
+ </svg>
+ <span className="sr-only">Toggle sidebar</span>
+ </button>
+ );
};
export default MenuToggle;
diff --git a/examples/docs/src/components/Header/SkipToContent.astro b/examples/docs/src/components/Header/SkipToContent.astro
index 6df3a72ed..91df15b93 100644
--- a/examples/docs/src/components/Header/SkipToContent.astro
+++ b/examples/docs/src/components/Header/SkipToContent.astro
@@ -1,21 +1,22 @@
+<a href="#article" class="sr-only skiplink"><span>Skip to Content</span></a>
+
<style>
-.skiplink,
-.skiplink:focus,
-.skiplink:focus-visible {
- position: absolute;
- padding: 0.25em;
- font-size: larger;
- top: 0;
- left: 0;
- right: 0;
- z-index: 9;
- display: block;
- text-align: center;
- background-color: var(--theme-text-accent);
- color: var(--theme-bg);
- border-radius: 0.25em;
- outline: var(--theme-bg) solid 1px;
- outline-offset: 0;
-}
+ .skiplink,
+ .skiplink:focus,
+ .skiplink:focus-visible {
+ position: absolute;
+ padding: 0.25em;
+ font-size: larger;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 9;
+ display: block;
+ text-align: center;
+ background-color: var(--theme-text-accent);
+ color: var(--theme-bg);
+ border-radius: 0.25em;
+ outline: var(--theme-bg) solid 1px;
+ outline-offset: 0;
+ }
</style>
-<a href="#article" class="sr-only skiplink"><span>Skip to Content</span></a>
diff --git a/examples/docs/src/components/LeftSidebar/LeftSidebar.astro b/examples/docs/src/components/LeftSidebar/LeftSidebar.astro
index e979dc80e..dd7b34e0f 100644
--- a/examples/docs/src/components/LeftSidebar/LeftSidebar.astro
+++ b/examples/docs/src/components/LeftSidebar/LeftSidebar.astro
@@ -1,109 +1,111 @@
---
import { getLanguageFromURL } from '../../languages.ts';
import { SIDEBAR } from '../../config.ts';
-const {currentPage} = Astro.props;
+const { currentPage } = Astro.props;
const currentPageMatch = currentPage.slice(1);
const langCode = getLanguageFromURL(currentPage);
-// SIDEBAR is a flat array. Group it by sections to properly render.
+// SIDEBAR is a flat array. Group it by sections to properly render.
const sidebarSections = SIDEBAR[langCode].reduce((col, item) => {
- if (item.header) {
- col.push({...item, children: []});
- } else {
- col[col.length-1].children.push(item);
- }
- return col;
+ if (item.header) {
+ col.push({ ...item, children: [] });
+ } else {
+ col[col.length - 1].children.push(item);
+ }
+ return col;
}, []);
-
---
<nav aria-labelledby="grid-left">
- <ul class="nav-groups">
- {sidebarSections.map(section => (
- <li>
- <div class="nav-group">
- <h2 class="nav-group-title">{section.text}</h2>
- <ul>
- {section.children.map(child => (
- <li class="nav-link"><a href={`${Astro.site.pathname}${child.link}`} aria-current={`${currentPageMatch === child.link ? 'page' : 'false'}`}>{child.text}</a></li>
- ))}
- </ul>
- </div>
- </li>
- ))}
- </ul>
+ <ul class="nav-groups">
+ {sidebarSections.map((section) => (
+ <li>
+ <div class="nav-group">
+ <h2 class="nav-group-title">{section.text}</h2>
+ <ul>
+ {section.children.map((child) => (
+ <li class="nav-link">
+ <a href={`${Astro.site.pathname}${child.link}`} aria-current={`${currentPageMatch === child.link ? 'page' : 'false'}`}>
+ {child.text}
+ </a>
+ </li>
+ ))}
+ </ul>
+ </div>
+ </li>
+ ))}
+ </ul>
</nav>
<script>
- window.addEventListener('DOMContentLoaded', (event) => {
- var target = document.querySelector('[aria-current="page"]');
- if (target && (target.offsetTop > (window.innerHeight - 100))) {
- document.querySelector('.nav-groups').scrollTop = target.offsetTop;
- }
- });
+ window.addEventListener('DOMContentLoaded', (event) => {
+ var target = document.querySelector('[aria-current="page"]');
+ if (target && target.offsetTop > window.innerHeight - 100) {
+ document.querySelector('.nav-groups').scrollTop = target.offsetTop;
+ }
+ });
</script>
<style>
- nav {
- width: 100%;
- margin-right: 1rem;
- }
- .nav-groups {
- height: 100%;
- padding: 2rem 0;
- overflow-x: visible;
- overflow-y: auto;
- max-height: 100vh;
- }
-
- .nav-groups > li + li {
- margin-top: 2rem;
- }
+ nav {
+ width: 100%;
+ margin-right: 1rem;
+ }
+ .nav-groups {
+ height: 100%;
+ padding: 2rem 0;
+ overflow-x: visible;
+ overflow-y: auto;
+ max-height: 100vh;
+ }
- .nav-groups > :first-child {
- padding-top: var(--doc-padding);
- }
+ .nav-groups > li + li {
+ margin-top: 2rem;
+ }
- .nav-groups > :last-child {
- padding-bottom: 2rem;
- margin-bottom: var(--theme-navbar-height);
- }
+ .nav-groups > :first-child {
+ padding-top: var(--doc-padding);
+ }
- .nav-group-title {
- font-size: 1.0rem;
- font-weight: 700;
- padding: 0.1rem 1rem;
- text-transform: uppercase;
- margin-bottom: 0.5rem;
- }
+ .nav-groups > :last-child {
+ padding-bottom: 2rem;
+ margin-bottom: var(--theme-navbar-height);
+ }
- .nav-link a {
- font-size: 1.0rem;
- margin: 1px;
- padding: 0.3rem 1rem;
- font: inherit;
- color: inherit;
- text-decoration: none;
- display: block;
- }
- .nav-link a:hover,
- .nav-link a:focus {
- background-color: var(--theme-bg-hover);
- }
+ .nav-group-title {
+ font-size: 1rem;
+ font-weight: 700;
+ padding: 0.1rem 1rem;
+ text-transform: uppercase;
+ margin-bottom: 0.5rem;
+ }
- .nav-link a[aria-current="page"] {
- color: var(--theme-text-accent);
- background-color: var(--theme-bg-accent);
- font-weight: 600;
- }
+ .nav-link a {
+ font-size: 1rem;
+ margin: 1px;
+ padding: 0.3rem 1rem;
+ font: inherit;
+ color: inherit;
+ text-decoration: none;
+ display: block;
+ }
+ .nav-link a:hover,
+ .nav-link a:focus {
+ background-color: var(--theme-bg-hover);
+ }
- :global(:root.theme-dark) .nav-link a[aria-current="page"] {
- color: hsla(var(--color-base-white), 100%, 1);
- }
+ .nav-link a[aria-current='page'] {
+ color: var(--theme-text-accent);
+ background-color: var(--theme-bg-accent);
+ font-weight: 600;
+ }
- @media (min-width: 50em) {
- .nav-groups {
- padding: 0;
- }
- }
+ :global(:root.theme-dark) .nav-link a[aria-current='page'] {
+ color: hsla(var(--color-base-white), 100%, 1);
+ }
+ @media (min-width: 50em) {
+ .nav-groups {
+ padding: 0;
+ }
+ }
</style>
diff --git a/examples/docs/src/components/PageContent/PageContent.astro b/examples/docs/src/components/PageContent/PageContent.astro
index 4d904852a..32db5cd58 100644
--- a/examples/docs/src/components/PageContent/PageContent.astro
+++ b/examples/docs/src/components/PageContent/PageContent.astro
@@ -2,41 +2,43 @@
import MoreMenu from '../RightSidebar/MoreMenu.astro';
import TableOfContents from '../RightSidebar/TableOfContents.tsx';
-const {content, githubEditUrl} = Astro.props;
+const { content, githubEditUrl } = Astro.props;
const title = content.title;
const headers = content.astro.headers;
---
+
+<article id="article" class="content">
+ <section class="main-section">
+ <h1 class="content-title" id="overview">{title}</h1>
+ <nav class="block sm:hidden">
+ <TableOfContents client:media="(max-width: 50em)" {headers} />
+ </nav>
+ <slot />
+ </section>
+ <nav class="block sm:hidden">
+ <MoreMenu editHref={githubEditUrl} />
+ </nav>
+</article>
+
<style>
- .content {
- padding: 0;
- max-width: 75ch;
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- }
- .content > section {
- margin-bottom: 4rem;
- }
- .block {
- display: block;
- }
+ .content {
+ padding: 0;
+ max-width: 75ch;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ }
+ .content > section {
+ margin-bottom: 4rem;
+ }
+ .block {
+ display: block;
+ }
- @media (min-width: 50em) {
- .sm\:hidden {
- display: none;
- }
- }
+ @media (min-width: 50em) {
+ .sm\:hidden {
+ display: none;
+ }
+ }
</style>
-<article id="article" class="content">
- <section class="main-section">
- <h1 class="content-title" id="overview">{title}</h1>
- <nav class="block sm:hidden">
- <TableOfContents client:media="(max-width: 50em)" headers={headers} />
- </nav>
- <slot />
- </section>
- <nav class="block sm:hidden">
- <MoreMenu editHref={githubEditUrl}/>
- </nav>
-</article> \ No newline at end of file
diff --git a/examples/docs/src/components/RightSidebar/MoreMenu.astro b/examples/docs/src/components/RightSidebar/MoreMenu.astro
index c822aaee9..649e02c29 100644
--- a/examples/docs/src/components/RightSidebar/MoreMenu.astro
+++ b/examples/docs/src/components/RightSidebar/MoreMenu.astro
@@ -1,68 +1,70 @@
---
import ThemeToggleButton from './ThemeToggleButton.tsx';
import * as CONFIG from '../../config';
-const {editHref} = Astro.props;
-const showMoreSection = (CONFIG.COMMUNITY_INVITE_URL || editHref);
+const { editHref } = Astro.props;
+const showMoreSection = CONFIG.COMMUNITY_INVITE_URL || editHref;
---
-<style>
- .edit-on-github {
- text-decoration: none;
- font: inherit;
- color: inherit;
- font-size: 1rem;
- }
-</style>
+
{showMoreSection && <h2 class="heading">More</h2>}
<ul>
- {editHref &&
- <li class={`header-link depth-2`}>
- <a class="edit-on-github" href={editHref} target="_blank">
- <svg
- aria-hidden="true"
- focusable="false"
- data-prefix="fas"
- data-icon="pen"
- class="svg-inline--fa fa-pen fa-w-16"
- role="img"
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 0 512 512"
- height="1em"
- width="1em"
- >
- <path
- fill="currentColor"
- d="M290.74 93.24l128.02 128.02-277.99 277.99-114.14 12.6C11.35 513.54-1.56 500.62.14 485.34l12.7-114.22 277.9-277.88zm207.2-19.06l-60.11-60.11c-18.75-18.75-49.16-18.75-67.91 0l-56.55 56.55 128.02 128.02 56.55-56.55c18.75-18.76 18.75-49.16 0-67.91z"
- ></path>
- </svg>
- <span>Edit this page</span>
- </a>
- </li>
- }
- {CONFIG.COMMUNITY_INVITE_URL &&
- <li class={`header-link depth-2`}>
- <a href={CONFIG.COMMUNITY_INVITE_URL} target="_blank">
- <svg
- aria-hidden="true"
- focusable="false"
- data-prefix="fas"
- data-icon="comment-alt"
- class="svg-inline--fa fa-comment-alt fa-w-16"
- role="img"
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 0 512 512"
- height="1em"
- width="1em"
- >
- <path
- fill="currentColor"
- d="M448 0H64C28.7 0 0 28.7 0 64v288c0 35.3 28.7 64 64 64h96v84c0 9.8 11.2 15.5 19.1 9.7L304 416h144c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64z"
- ></path>
- </svg>
- <span>Join our community</span>
- </a>
- </li>
- }
+ {editHref && (
+ <li class={`header-link depth-2`}>
+ <a class="edit-on-github" href={editHref} target="_blank">
+ <svg
+ aria-hidden="true"
+ focusable="false"
+ data-prefix="fas"
+ data-icon="pen"
+ class="svg-inline--fa fa-pen fa-w-16"
+ role="img"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 512 512"
+ height="1em"
+ width="1em"
+ >
+ <path
+ fill="currentColor"
+ d="M290.74 93.24l128.02 128.02-277.99 277.99-114.14 12.6C11.35 513.54-1.56 500.62.14 485.34l12.7-114.22 277.9-277.88zm207.2-19.06l-60.11-60.11c-18.75-18.75-49.16-18.75-67.91 0l-56.55 56.55 128.02 128.02 56.55-56.55c18.75-18.76 18.75-49.16 0-67.91z"
+ ></path>
+ </svg>
+ <span>Edit this page</span>
+ </a>
+ </li>
+ )}
+ {CONFIG.COMMUNITY_INVITE_URL && (
+ <li class={`header-link depth-2`}>
+ <a href={CONFIG.COMMUNITY_INVITE_URL} target="_blank">
+ <svg
+ aria-hidden="true"
+ focusable="false"
+ data-prefix="fas"
+ data-icon="comment-alt"
+ class="svg-inline--fa fa-comment-alt fa-w-16"
+ role="img"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 512 512"
+ height="1em"
+ width="1em"
+ >
+ <path
+ fill="currentColor"
+ d="M448 0H64C28.7 0 0 28.7 0 64v288c0 35.3 28.7 64 64 64h96v84c0 9.8 11.2 15.5 19.1 9.7L304 416h144c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64z"
+ ></path>
+ </svg>
+ <span>Join our community</span>
+ </a>
+ </li>
+ )}
</ul>
<div style="margin: 2rem 0; text-align: center;">
- <ThemeToggleButton client:visible />
+ <ThemeToggleButton client:visible />
</div>
+
+<style>
+ .edit-on-github {
+ text-decoration: none;
+ font: inherit;
+ color: inherit;
+ font-size: 1rem;
+ }
+</style>
diff --git a/examples/docs/src/components/RightSidebar/RightSidebar.astro b/examples/docs/src/components/RightSidebar/RightSidebar.astro
index 5e0a86b78..a0b5779dc 100644
--- a/examples/docs/src/components/RightSidebar/RightSidebar.astro
+++ b/examples/docs/src/components/RightSidebar/RightSidebar.astro
@@ -1,25 +1,27 @@
---
import TableOfContents from './TableOfContents.tsx';
import MoreMenu from './MoreMenu.astro';
-const {content, githubEditUrl} = Astro.props;
+const { content, githubEditUrl } = Astro.props;
const headers = content.astro.headers;
---
+
+<nav class="sidebar-nav" aria-labelledby="grid-right">
+ <div class="sidebar-nav-inner">
+ <TableOfContents client:media="(min-width: 50em)" {headers} />
+ <MoreMenu editHref={githubEditUrl} />
+ </div>
+</nav>
+
<style>
- .sidebar-nav {
- width: 100%;
- position: sticky;
- top: 0;
- }
- .sidebar-nav-inner {
- height: 100%;
- padding: 0;
- padding-top: var(--doc-padding);
- overflow: auto;
- }
+ .sidebar-nav {
+ width: 100%;
+ position: sticky;
+ top: 0;
+ }
+ .sidebar-nav-inner {
+ height: 100%;
+ padding: 0;
+ padding-top: var(--doc-padding);
+ overflow: auto;
+ }
</style>
-<nav class="sidebar-nav" aria-labelledby="grid-right">
- <div class="sidebar-nav-inner">
- <TableOfContents client:media="(min-width: 50em)" headers={headers} />
- <MoreMenu editHref={githubEditUrl} />
- </div>
-</nav> \ No newline at end of file
diff --git a/examples/docs/src/components/RightSidebar/TableOfContents.tsx b/examples/docs/src/components/RightSidebar/TableOfContents.tsx
index d8ea998d4..578d2aa98 100644
--- a/examples/docs/src/components/RightSidebar/TableOfContents.tsx
+++ b/examples/docs/src/components/RightSidebar/TableOfContents.tsx
@@ -3,43 +3,43 @@ import { h, Fragment } from 'preact';
import { useState, useEffect, useRef } from 'preact/hooks';
const TableOfContents: FunctionalComponent<{ headers: any[] }> = ({ headers = [] }) => {
- const itemOffsets = useRef([]);
- const [activeId, setActiveId] = useState<string>(undefined);
+ const itemOffsets = useRef([]);
+ const [activeId, setActiveId] = useState<string>(undefined);
- useEffect(() => {
- const getItemOffsets = () => {
- const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
- itemOffsets.current = Array.from(titles).map((title) => ({
- id: title.id,
- topOffset: title.getBoundingClientRect().top + window.scrollY,
- }));
- };
+ useEffect(() => {
+ const getItemOffsets = () => {
+ const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
+ itemOffsets.current = Array.from(titles).map((title) => ({
+ id: title.id,
+ topOffset: title.getBoundingClientRect().top + window.scrollY,
+ }));
+ };
- getItemOffsets();
- window.addEventListener('resize', getItemOffsets);
+ getItemOffsets();
+ window.addEventListener('resize', getItemOffsets);
- return () => {
- window.removeEventListener('resize', getItemOffsets);
- };
- }, []);
+ return () => {
+ window.removeEventListener('resize', getItemOffsets);
+ };
+ }, []);
- return (
- <>
- <h2 class="heading">On this page</h2>
- <ul>
- <li class={`header-link depth-2 ${activeId === 'overview' ? 'active' : ''}`.trim()}>
- <a href="#overview">Overview</a>
- </li>
- {headers
- .filter(({ depth }) => depth > 1 && depth < 4)
- .map((header) => (
- <li class={`header-link depth-${header.depth} ${activeId === header.slug ? 'active' : ''}`.trim()}>
- <a href={`#${header.slug}`}>{header.text}</a>
- </li>
- ))}
- </ul>
- </>
- );
+ return (
+ <>
+ <h2 class="heading">On this page</h2>
+ <ul>
+ <li class={`header-link depth-2 ${activeId === 'overview' ? 'active' : ''}`.trim()}>
+ <a href="#overview">Overview</a>
+ </li>
+ {headers
+ .filter(({ depth }) => depth > 1 && depth < 4)
+ .map((header) => (
+ <li class={`header-link depth-${header.depth} ${activeId === header.slug ? 'active' : ''}`.trim()}>
+ <a href={`#${header.slug}`}>{header.text}</a>
+ </li>
+ ))}
+ </ul>
+ </>
+ );
};
export default TableOfContents;
diff --git a/examples/docs/src/components/RightSidebar/ThemeToggleButton.css b/examples/docs/src/components/RightSidebar/ThemeToggleButton.css
index 7de231d1b..dc5ba46d9 100644
--- a/examples/docs/src/components/RightSidebar/ThemeToggleButton.css
+++ b/examples/docs/src/components/RightSidebar/ThemeToggleButton.css
@@ -1,37 +1,37 @@
.theme-toggle {
- display: inline-flex;
- align-items: center;
- gap: 0.25em;
- padding: 0.33em 0.67em;
- border-radius: 99em;
- background-color: var(--theme-code-inline-bg);
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25em;
+ padding: 0.33em 0.67em;
+ border-radius: 99em;
+ background-color: var(--theme-code-inline-bg);
}
.theme-toggle > label:focus-within {
- outline: 2px solid transparent;
- box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
+ outline: 2px solid transparent;
+ box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
}
.theme-toggle > label {
- color: var(--theme-code-inline-text);
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0.5;
+ color: var(--theme-code-inline-text);
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0.5;
}
.theme-toggle .checked {
- color: var(--theme-accent);
- opacity: 1;
+ color: var(--theme-accent);
+ opacity: 1;
}
input[name='theme-toggle'] {
- position: absolute;
- opacity: 0;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: -1;
+ position: absolute;
+ opacity: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: -1;
}
diff --git a/examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx b/examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx
index 75ea775f4..6bdf45f02 100644
--- a/examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx
+++ b/examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx
@@ -6,66 +6,66 @@ import './ThemeToggleButton.css';
const themes = ['light', 'dark'];
const icons = [
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
- <path
- fillRule="evenodd"
- d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
- clipRule="evenodd"
- />
- </svg>,
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
- <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
- </svg>,
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
+ <path
+ fillRule="evenodd"
+ d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
+ clipRule="evenodd"
+ />
+ </svg>,
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
+ <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
+ </svg>,
];
const ThemeToggle: FunctionalComponent = () => {
- const [theme, setTheme] = useState(() => {
- if (import.meta.env.SSR) {
- return undefined;
- }
- if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
- return localStorage.getItem('theme');
- }
- if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return 'dark';
- }
- return 'light';
- });
+ const [theme, setTheme] = useState(() => {
+ if (import.meta.env.SSR) {
+ return undefined;
+ }
+ if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
+ return localStorage.getItem('theme');
+ }
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
+ return 'dark';
+ }
+ return 'light';
+ });
- useEffect(() => {
- const root = document.documentElement;
- if (theme === 'light') {
- root.classList.remove('theme-dark');
- } else {
- root.classList.add('theme-dark');
- }
- }, [theme]);
+ useEffect(() => {
+ const root = document.documentElement;
+ if (theme === 'light') {
+ root.classList.remove('theme-dark');
+ } else {
+ root.classList.add('theme-dark');
+ }
+ }, [theme]);
- return (
- <div class="theme-toggle">
- {themes.map((t, i) => {
- const icon = icons[i];
- const checked = t === theme;
- return (
- <label className={checked ? ' checked' : ''}>
- {icon}
- <input
- type="radio"
- name="theme-toggle"
- checked={checked}
- value={t}
- title={`Use ${t} theme`}
- aria-label={`Use ${t} theme`}
- onChange={() => {
- localStorage.setItem('theme', t);
- setTheme(t);
- }}
- />
- </label>
- );
- })}
- </div>
- );
+ return (
+ <div class="theme-toggle">
+ {themes.map((t, i) => {
+ const icon = icons[i];
+ const checked = t === theme;
+ return (
+ <label className={checked ? ' checked' : ''}>
+ {icon}
+ <input
+ type="radio"
+ name="theme-toggle"
+ checked={checked}
+ value={t}
+ title={`Use ${t} theme`}
+ aria-label={`Use ${t} theme`}
+ onChange={() => {
+ localStorage.setItem('theme', t);
+ setTheme(t);
+ }}
+ />
+ </label>
+ );
+ })}
+ </div>
+ );
};
export default ThemeToggle;
diff --git a/examples/docs/src/config.ts b/examples/docs/src/config.ts
index ae4321f48..5953dd97a 100644
--- a/examples/docs/src/config.ts
+++ b/examples/docs/src/config.ts
@@ -1,19 +1,19 @@
export const SITE = {
- title: 'Your Documentation Website',
- description: 'Your website description.',
- defaultLanguage: 'en_US',
+ title: 'Your Documentation Website',
+ description: 'Your website description.',
+ defaultLanguage: 'en_US',
};
export const OPEN_GRAPH = {
- image: {
- src: 'https://github.com/withastro/astro/blob/main/assets/social/banner.jpg?raw=true',
- alt: 'astro logo on a starry expanse of space,' + ' with a purple saturn-like planet floating in the right foreground',
- },
- twitter: 'astrodotbuild',
+ image: {
+ src: 'https://github.com/withastro/astro/blob/main/assets/social/banner.jpg?raw=true',
+ alt: 'astro logo on a starry expanse of space,' + ' with a purple saturn-like planet floating in the right foreground',
+ },
+ twitter: 'astrodotbuild',
};
export const KNOWN_LANGUAGES = {
- English: 'en',
+ English: 'en',
};
// Uncomment this to add an "Edit this page" button to every page of documentation.
@@ -30,14 +30,14 @@ export const KNOWN_LANGUAGES = {
// }
export const SIDEBAR = {
- en: [
- { text: '', header: true },
- { text: 'Section Header', header: true },
- { text: 'Introduction', link: 'en/introduction' },
- { text: 'Page 2', link: 'en/page-2' },
- { text: 'Page 3', link: 'en/page-3' },
+ en: [
+ { text: '', header: true },
+ { text: 'Section Header', header: true },
+ { text: 'Introduction', link: 'en/introduction' },
+ { text: 'Page 2', link: 'en/page-2' },
+ { text: 'Page 3', link: 'en/page-3' },
- { text: 'Another Section', header: true },
- { text: 'Page 4', link: 'en/page-4' },
- ],
+ { text: 'Another Section', header: true },
+ { text: 'Page 4', link: 'en/page-4' },
+ ],
};
diff --git a/examples/docs/src/languages.ts b/examples/docs/src/languages.ts
index 069413fd2..ffc680954 100644
--- a/examples/docs/src/languages.ts
+++ b/examples/docs/src/languages.ts
@@ -5,6 +5,6 @@ export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES);
export const langPathRegex = /\/([a-z]{2}-?[A-Z]{0,2})\//;
export function getLanguageFromURL(pathname: string) {
- const langCodeMatch = pathname.match(langPathRegex);
- return langCodeMatch ? langCodeMatch[1] : 'en';
+ const langCodeMatch = pathname.match(langPathRegex);
+ return langCodeMatch ? langCodeMatch[1] : 'en';
}
diff --git a/examples/docs/src/layouts/MainLayout.astro b/examples/docs/src/layouts/MainLayout.astro
index cdadebe61..92e0bcf44 100644
--- a/examples/docs/src/layouts/MainLayout.astro
+++ b/examples/docs/src/layouts/MainLayout.astro
@@ -1,122 +1,122 @@
---
-import HeadCommon from "../components/HeadCommon.astro";
-import HeadSEO from "../components/HeadSEO.astro";
+import HeadCommon from '../components/HeadCommon.astro';
+import HeadSEO from '../components/HeadSEO.astro';
import Header from '../components/Header/Header.astro';
import Footer from '../components/Footer/Footer.astro';
import PageContent from '../components/PageContent/PageContent.astro';
import LeftSidebar from '../components/LeftSidebar/LeftSidebar.astro';
import RightSidebar from '../components/RightSidebar/RightSidebar.astro';
-import * as CONFIG from "../config";
+import * as CONFIG from '../config';
const { content = {} } = Astro.props;
const currentPage = Astro.request.url.pathname;
-const currentFile = `src/pages${currentPage.replace(/\/$/, "")}.md`;
-const githubEditUrl = CONFIG.GITHUB_EDIT_URL && (CONFIG.GITHUB_EDIT_URL + currentFile);
+const currentFile = `src/pages${currentPage.replace(/\/$/, '')}.md`;
+const githubEditUrl = CONFIG.GITHUB_EDIT_URL && CONFIG.GITHUB_EDIT_URL + currentFile;
---
<html dir={content.dir ?? 'ltr'} lang={content.lang ?? 'en-us'} class="initial">
- <head>
- <HeadCommon />
- <HeadSEO {content} canonicalURL={Astro.request.canonicalURL} />
- <title>{content.title ? `${content.title} 🚀 ${CONFIG.SITE.title}` : CONFIG.SITE.title}</title>
- <style>
- body {
- width: 100%;
- display: grid;
- grid-template-rows: var(--theme-navbar-height) 1fr;
- --gutter: 0.5rem;
- --doc-padding: 2rem;
- }
- .layout {
- display: grid;
- grid-auto-flow: column;
- grid-template-columns:
- minmax(var(--gutter), 1fr)
- minmax(0, var(--max-width))
- minmax(var(--gutter), 1fr);
- overflow-x: hidden;
- }
- .layout :global(> *) {
- width: 100%;
- height: 100%;
- }
- .grid-sidebar {
- height: 100vh;
- position: sticky;
- top: 0;
- padding: 0;
- }
- #grid-left {
- position: fixed;
- background-color: var(--theme-bg);
- z-index: 10;
- display: none;
- }
- #grid-main {
- padding: var(--doc-padding) var(--gutter);
- grid-column: 2;
- display: flex;
- flex-direction: column;
- height: 100%;
- }
- #grid-right {
- display: none;
- }
- :global(.mobile-sidebar-toggle) {
- overflow: hidden;
- }
- :global(.mobile-sidebar-toggle) #grid-left {
- display: block;
- top: 2rem;
- }
- @media (min-width: 50em) {
- .layout {
- overflow: initial;
- grid-template-columns:
- 20rem
- minmax(0, var(--max-width));
- gap: 1em;
- }
- #grid-left {
- display: flex;
- padding-left: 2rem;
- position: sticky;
- grid-column: 1;
- }
- }
+ <head>
+ <HeadCommon />
+ <HeadSEO {content} canonicalURL={Astro.request.canonicalURL} />
+ <title>{content.title ? `${content.title} 🚀 ${CONFIG.SITE.title}` : CONFIG.SITE.title}</title>
+ <style>
+ body {
+ width: 100%;
+ display: grid;
+ grid-template-rows: var(--theme-navbar-height) 1fr;
+ --gutter: 0.5rem;
+ --doc-padding: 2rem;
+ }
+ .layout {
+ display: grid;
+ grid-auto-flow: column;
+ grid-template-columns:
+ minmax(var(--gutter), 1fr)
+ minmax(0, var(--max-width))
+ minmax(var(--gutter), 1fr);
+ overflow-x: hidden;
+ }
+ .layout :global(> *) {
+ width: 100%;
+ height: 100%;
+ }
+ .grid-sidebar {
+ height: 100vh;
+ position: sticky;
+ top: 0;
+ padding: 0;
+ }
+ #grid-left {
+ position: fixed;
+ background-color: var(--theme-bg);
+ z-index: 10;
+ display: none;
+ }
+ #grid-main {
+ padding: var(--doc-padding) var(--gutter);
+ grid-column: 2;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ }
+ #grid-right {
+ display: none;
+ }
+ :global(.mobile-sidebar-toggle) {
+ overflow: hidden;
+ }
+ :global(.mobile-sidebar-toggle) #grid-left {
+ display: block;
+ top: 2rem;
+ }
+ @media (min-width: 50em) {
+ .layout {
+ overflow: initial;
+ grid-template-columns:
+ 20rem
+ minmax(0, var(--max-width));
+ gap: 1em;
+ }
+ #grid-left {
+ display: flex;
+ padding-left: 2rem;
+ position: sticky;
+ grid-column: 1;
+ }
+ }
- @media (min-width: 72em) {
- .layout {
- grid-template-columns:
- 20rem
- minmax(0, var(--max-width))
- 18rem;
- padding-left: 0;
- padding-right: 0;
- margin: 0 auto;
- }
- #grid-right {
- grid-column: 3;
- display: flex;
- }
- }
- </style>
- </head>
+ @media (min-width: 72em) {
+ .layout {
+ grid-template-columns:
+ 20rem
+ minmax(0, var(--max-width))
+ 18rem;
+ padding-left: 0;
+ padding-right: 0;
+ margin: 0 auto;
+ }
+ #grid-right {
+ grid-column: 3;
+ display: flex;
+ }
+ }
+ </style>
+ </head>
- <body>
- <Header currentPage={currentPage} />
- <main class="layout">
- <aside id="grid-left" class="grid-sidebar" title="Site Navigation">
- <LeftSidebar currentPage={currentPage} />
- </aside>
- <div id="grid-main">
- <PageContent content={content} githubEditUrl={githubEditUrl}>
- <slot />
- </PageContent>
- </div>
- <aside id="grid-right" class="grid-sidebar" title="Table of Contents">
- <RightSidebar content={content} githubEditUrl={githubEditUrl} />
- </aside>
- </main>
- </body>
+ <body>
+ <Header {currentPage} />
+ <main class="layout">
+ <aside id="grid-left" class="grid-sidebar" title="Site Navigation">
+ <LeftSidebar {currentPage} />
+ </aside>
+ <div id="grid-main">
+ <PageContent {content} {githubEditUrl}>
+ <slot />
+ </PageContent>
+ </div>
+ <aside id="grid-right" class="grid-sidebar" title="Table of Contents">
+ <RightSidebar {content} {githubEditUrl} />
+ </aside>
+ </main>
+ </body>
</html>
diff --git a/examples/docs/src/pages/index.astro b/examples/docs/src/pages/index.astro
index 7f660ada0..4ce893162 100644
--- a/examples/docs/src/pages/index.astro
+++ b/examples/docs/src/pages/index.astro
@@ -1,5 +1,5 @@
<script>
- // Redirect your homepage to the first page of documentation.
- // If you have a landing page, remove this script and add it here!
- window.location.pathname = `/en/introduction`;
+ // Redirect your homepage to the first page of documentation.
+ // If you have a landing page, remove this script and add it here!
+ window.location.pathname = `/en/introduction`;
</script>
diff --git a/examples/docs/src/styles/code.css b/examples/docs/src/styles/code.css
index 3fbb26626..b4275adab 100644
--- a/examples/docs/src/styles/code.css
+++ b/examples/docs/src/styles/code.css
@@ -1,29 +1,29 @@
.language-css > code,
.language-sass > code,
.language-scss > code {
- color: #fd9170;
+ color: #fd9170;
}
[class*='language-'] .namespace {
- opacity: 0.7;
+ opacity: 0.7;
}
.token.plain-text,
[class*='language-bash'] span.token,
[class*='language-shell'] span.token {
- color: hsla(var(--color-gray-90), 1);
+ color: hsla(var(--color-gray-90), 1);
}
[class*='language-bash'] span.token,
[class*='language-shell'] span.token {
- font-style: bold;
+ font-style: bold;
}
.token.prolog,
.token.comment,
[class*='language-bash'] span.token.comment,
[class*='language-shell'] span.token.comment {
- color: hsla(var(--color-gray-70), 1);
+ color: hsla(var(--color-gray-70), 1);
}
.token.selector,
@@ -33,7 +33,7 @@
.token.variable,
.token.entity,
.token.deleted {
- color: #fa5e5b;
+ color: #fa5e5b;
}
.token.boolean,
@@ -46,7 +46,7 @@
.token.hexcode,
.token.class-name,
.token.attr-name {
- color: hsla(var(--color-yellow), 1);
+ color: hsla(var(--color-yellow), 1);
}
.token.atrule,
@@ -56,41 +56,41 @@
.token.pseudo-class,
.token.pseudo-element,
.token.string {
- color: hsla(var(--color-green), 1);
+ color: hsla(var(--color-green), 1);
}
.token.symbol,
.token.function,
.token.id,
.token.important {
- color: hsla(var(--color-blue), 1);
+ color: hsla(var(--color-blue), 1);
}
.token.important,
.token.id {
- font-weight: bold;
+ font-weight: bold;
}
.token.cdata,
.token.char,
.token.property {
- color: #23b1af;
+ color: #23b1af;
}
.token.inserted {
- color: hsla(var(--color-green), 1);
+ color: hsla(var(--color-green), 1);
}
.token.keyword {
- color: #ff657c;
- font-style: italic;
+ color: #ff657c;
+ font-style: italic;
}
.token.operator {
- color: hsla(var(--color-gray-70), 1);
+ color: hsla(var(--color-gray-70), 1);
}
.token.attr-value .token.attr-equals,
.token.punctuation {
- color: hsla(var(--color-gray-80), 1);
+ color: hsla(var(--color-gray-80), 1);
}
diff --git a/examples/docs/src/styles/index.css b/examples/docs/src/styles/index.css
index e1e2e96c6..ad0a5adf7 100644
--- a/examples/docs/src/styles/index.css
+++ b/examples/docs/src/styles/index.css
@@ -1,46 +1,46 @@
* {
- box-sizing: border-box;
- margin: 0;
+ box-sizing: border-box;
+ margin: 0;
}
/* Global focus outline reset */
*:focus:not(:focus-visible) {
- outline: none;
+ outline: none;
}
:root {
- --user-font-scale: 1rem - 16px;
- --max-width: calc(100% - 1rem);
+ --user-font-scale: 1rem - 16px;
+ --max-width: calc(100% - 1rem);
}
@media (min-width: 50em) {
- :root {
- --max-width: 46em;
- }
+ :root {
+ --max-width: 46em;
+ }
}
body {
- display: flex;
- flex-direction: column;
- min-height: 100vh;
- font-family: var(--font-body);
- font-size: 1rem;
- font-size: clamp(0.9rem, 0.75rem + 0.375vw + var(--user-font-scale), 1rem);
- line-height: 1.5;
- max-width: 100vw;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ font-family: var(--font-body);
+ font-size: 1rem;
+ font-size: clamp(0.9rem, 0.75rem + 0.375vw + var(--user-font-scale), 1rem);
+ line-height: 1.5;
+ max-width: 100vw;
}
nav ul {
- list-style: none;
- padding: 0;
+ list-style: none;
+ padding: 0;
}
.content > section > * + * {
- margin-top: 1.25rem;
+ margin-top: 1.25rem;
}
.content > section > :first-child {
- margin-top: 0;
+ margin-top: 0;
}
/* Typography */
@@ -50,339 +50,339 @@ h3,
h4,
h5,
h6 {
- margin-bottom: 1rem;
- font-weight: bold;
- line-height: 1;
+ margin-bottom: 1rem;
+ font-weight: bold;
+ line-height: 1;
}
h1,
h2 {
- max-width: 40ch;
+ max-width: 40ch;
}
:is(h2, h3):not(:first-child) {
- margin-top: 3rem;
+ margin-top: 3rem;
}
:is(h4, h5, h6):not(:first-child) {
- margin-top: 2rem;
+ margin-top: 2rem;
}
h1 {
- font-size: 3.25rem;
- font-weight: 800;
+ font-size: 3.25rem;
+ font-weight: 800;
}
h2 {
- font-size: 2.5rem;
+ font-size: 2.5rem;
}
h3 {
- font-size: 1.75rem;
+ font-size: 1.75rem;
}
h4 {
- font-size: 1.3rem;
+ font-size: 1.3rem;
}
h5 {
- font-size: 1rem;
+ font-size: 1rem;
}
p {
- line-height: 1.65em;
+ line-height: 1.65em;
}
.content ul {
- line-height: 1.1em;
+ line-height: 1.1em;
}
p,
.content ul {
- color: var(--theme-text-light);
+ color: var(--theme-text-light);
}
small,
.text_small {
- font-size: 0.833rem;
+ font-size: 0.833rem;
}
a {
- color: var(--theme-text-accent);
- font-weight: 400;
- text-underline-offset: 0.08em;
- align-items: center;
- gap: 0.5rem;
+ color: var(--theme-text-accent);
+ font-weight: 400;
+ text-underline-offset: 0.08em;
+ align-items: center;
+ gap: 0.5rem;
}
article > section :is(ul, ol) > * + * {
- margin-top: 0.75rem;
+ margin-top: 0.75rem;
}
article > section nav :is(ul, ol) > * + * {
- margin-top: inherit;
+ margin-top: inherit;
}
article > section li > :is(p, pre, blockquote):not(:first-child) {
- margin-top: 1rem;
+ margin-top: 1rem;
}
article > section :is(ul, ol) {
- padding-left: 1em;
+ padding-left: 1em;
}
article > section nav :is(ul, ol) {
- padding-left: inherit;
+ padding-left: inherit;
}
article > section nav {
- margin-top: 1rem;
- margin-bottom: 2rem;
+ margin-top: 1rem;
+ margin-bottom: 2rem;
}
article > section ::marker {
- font-weight: bold;
- color: var(--theme-text-light);
+ font-weight: bold;
+ color: var(--theme-text-light);
}
article > section iframe {
- width: 100%;
- height: auto;
- aspect-ratio: 16 / 9;
+ width: 100%;
+ height: auto;
+ aspect-ratio: 16 / 9;
}
a > code:not([class*='language']) {
- position: relative;
- color: var(--theme-text-accent);
- background: transparent;
- text-underline-offset: var(--padding-block);
+ position: relative;
+ color: var(--theme-text-accent);
+ background: transparent;
+ text-underline-offset: var(--padding-block);
}
a > code:not([class*='language'])::before {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- display: block;
- background: var(--theme-accent);
- opacity: var(--theme-accent-opacity);
- border-radius: var(--border-radius);
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ background: var(--theme-accent);
+ opacity: var(--theme-accent-opacity);
+ border-radius: var(--border-radius);
}
a:hover,
a:focus {
- text-decoration: underline;
+ text-decoration: underline;
}
a:focus {
- outline: 2px solid currentColor;
- outline-offset: 0.25em;
+ outline: 2px solid currentColor;
+ outline-offset: 0.25em;
}
strong {
- font-weight: 600;
- color: inherit;
+ font-weight: 600;
+ color: inherit;
}
/* Supporting Content */
code {
- font-family: var(--font-mono);
- font-size: 0.85em;
+ font-family: var(--font-mono);
+ font-size: 0.85em;
}
code:not([class*='language']) {
- --border-radius: 3px;
- --padding-block: 0.2rem;
- --padding-inline: 0.4rem;
- color: var(--theme-code-inline-text);
- background-color: var(--theme-code-inline-bg);
- padding: var(--padding-block) var(--padding-inline);
- margin: calc(var(--padding-block) * -1) -0.125em;
- border-radius: var(--border-radius);
- box-shadow: 0 2px 1px 0 rgba(0, 0, 0, 0.08);
- word-break: break-word;
+ --border-radius: 3px;
+ --padding-block: 0.2rem;
+ --padding-inline: 0.4rem;
+ color: var(--theme-code-inline-text);
+ background-color: var(--theme-code-inline-bg);
+ padding: var(--padding-block) var(--padding-inline);
+ margin: calc(var(--padding-block) * -1) -0.125em;
+ border-radius: var(--border-radius);
+ box-shadow: 0 2px 1px 0 rgba(0, 0, 0, 0.08);
+ word-break: break-word;
}
pre > code:not([class*='language']) {
- background-color: transparent;
- padding: 0;
- margin: 0;
- border-radius: 0;
- color: inherit;
+ background-color: transparent;
+ padding: 0;
+ margin: 0;
+ border-radius: 0;
+ color: inherit;
}
pre > code {
- font-size: 1em;
+ font-size: 1em;
}
table,
pre {
- position: relative;
- --padding-block: 1rem;
- --padding-inline: 2rem;
- padding: var(--padding-block) var(--padding-inline);
- padding-right: calc(var(--padding-inline) * 2);
- margin-left: calc(var(--padding-inline) * -1);
- margin-right: calc(var(--padding-inline) * -1);
- font-family: var(--font-mono);
+ position: relative;
+ --padding-block: 1rem;
+ --padding-inline: 2rem;
+ padding: var(--padding-block) var(--padding-inline);
+ padding-right: calc(var(--padding-inline) * 2);
+ margin-left: calc(var(--padding-inline) * -1);
+ margin-right: calc(var(--padding-inline) * -1);
+ font-family: var(--font-mono);
- line-height: 1.5;
- font-size: 0.85em;
- overflow-y: hidden;
- overflow-x: auto;
+ line-height: 1.5;
+ font-size: 0.85em;
+ overflow-y: hidden;
+ overflow-x: auto;
}
table {
- width: 100%;
- padding: var(--padding-block) 0;
- margin: 0;
- border-collapse: collapse;
+ width: 100%;
+ padding: var(--padding-block) 0;
+ margin: 0;
+ border-collapse: collapse;
}
/* Zebra striping */
tr:nth-of-type(odd) {
- background: var(--theme-bg-hover);
+ background: var(--theme-bg-hover);
}
th {
- background: var(--color-black);
- color: var(--theme-color);
- font-weight: bold;
+ background: var(--color-black);
+ color: var(--theme-color);
+ font-weight: bold;
}
td,
th {
- padding: 6px;
- text-align: left;
+ padding: 6px;
+ text-align: left;
}
pre {
- background-color: var(--theme-code-bg);
- color: var(--theme-code-text);
+ background-color: var(--theme-code-bg);
+ color: var(--theme-code-text);
}
blockquote code:not([class*='language']) {
- background-color: var(--theme-bg);
+ background-color: var(--theme-bg);
}
@media (min-width: 37.75em) {
- pre {
- --padding-inline: 1.25rem;
- border-radius: 8px;
- margin-left: 0;
- margin-right: 0;
- }
+ pre {
+ --padding-inline: 1.25rem;
+ border-radius: 8px;
+ margin-left: 0;
+ margin-right: 0;
+ }
}
blockquote {
- margin: 2rem 0;
- padding: 1.25em 1.5rem;
- border-left: 3px solid var(--theme-text-light);
- background-color: var(--theme-bg-offset);
- border-radius: 0 0.25rem 0.25rem 0;
- line-height: 1.7;
+ margin: 2rem 0;
+ padding: 1.25em 1.5rem;
+ border-left: 3px solid var(--theme-text-light);
+ background-color: var(--theme-bg-offset);
+ border-radius: 0 0.25rem 0.25rem 0;
+ line-height: 1.7;
}
img {
- max-width: 100%;
+ max-width: 100%;
}
.flex {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
}
button {
- display: flex;
- align-items: center;
- justify-items: center;
- gap: 0.25em;
- padding: 0.33em 0.67em;
- border: 0;
- background: var(--theme-bg);
- display: flex;
- font-size: 1rem;
- align-items: center;
- gap: 0.25em;
- border-radius: 99em;
- color: var(--theme-text);
- background-color: var(--theme-bg);
+ display: flex;
+ align-items: center;
+ justify-items: center;
+ gap: 0.25em;
+ padding: 0.33em 0.67em;
+ border: 0;
+ background: var(--theme-bg);
+ display: flex;
+ font-size: 1rem;
+ align-items: center;
+ gap: 0.25em;
+ border-radius: 99em;
+ color: var(--theme-text);
+ background-color: var(--theme-bg);
}
h2.heading {
- font-size: 1rem;
- font-weight: 700;
- padding: 0.1rem 1rem;
- text-transform: uppercase;
- margin-bottom: 0.5rem;
+ font-size: 1rem;
+ font-weight: 700;
+ padding: 0.1rem 1rem;
+ text-transform: uppercase;
+ margin-bottom: 0.5rem;
}
.header-link {
- font-size: 1rem;
- padding: 0.1rem 0 0.1rem 1rem;
- border-left: 4px solid var(--theme-divider);
+ font-size: 1rem;
+ padding: 0.1rem 0 0.1rem 1rem;
+ border-left: 4px solid var(--theme-divider);
}
.header-link:hover,
.header-link:focus {
- border-left-color: var(--theme-accent);
- color: var(--theme-accent);
+ border-left-color: var(--theme-accent);
+ color: var(--theme-accent);
}
.header-link:focus-within {
- color: var(--theme-text-light);
- border-left-color: hsla(var(--color-gray-40), 1);
+ color: var(--theme-text-light);
+ border-left-color: hsla(var(--color-gray-40), 1);
}
.header-link svg {
- opacity: 0.6;
+ opacity: 0.6;
}
.header-link:hover svg {
- opacity: 0.8;
+ opacity: 0.8;
}
.header-link a {
- display: inline-flex;
- gap: 0.5em;
- width: 100%;
- padding: 0.15em 0 0.15em 0;
+ display: inline-flex;
+ gap: 0.5em;
+ width: 100%;
+ padding: 0.15em 0 0.15em 0;
}
.header-link.depth-3 {
- padding-left: 2rem;
+ padding-left: 2rem;
}
.header-link.depth-4 {
- padding-left: 3rem;
+ padding-left: 3rem;
}
.header-link a {
- font: inherit;
- color: inherit;
- text-decoration: none;
+ font: inherit;
+ color: inherit;
+ text-decoration: none;
}
/* Screenreader Only Text */
.sr-only {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- white-space: nowrap;
- border-width: 0;
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
}
.focus\:not-sr-only:focus,
.focus\:not-sr-only:focus-visible {
- position: static;
- width: auto;
- height: auto;
- padding: 0;
- margin: 0;
- overflow: visible;
- clip: auto;
- white-space: normal;
+ position: static;
+ width: auto;
+ height: auto;
+ padding: 0;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+ white-space: normal;
}
:target {
- scroll-margin: calc(var(--theme-sidebar-offset, 5rem) + 2rem) 0 2rem;
+ scroll-margin: calc(var(--theme-sidebar-offset, 5rem) + 2rem) 0 2rem;
}
diff --git a/examples/docs/src/styles/theme.css b/examples/docs/src/styles/theme.css
index 1bd29c2f6..830bed8dd 100644
--- a/examples/docs/src/styles/theme.css
+++ b/examples/docs/src/styles/theme.css
@@ -1,10 +1,10 @@
:root {
- --font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
- --font-body: system-ui, var(--font-fallback);
- --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
- 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
+ --font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
+ --font-body: system-ui, var(--font-fallback);
+ --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
+ 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
- /*
+ /*
* Variables with --color-base prefix define
* the hue, and saturation values to be used for
* hsla colors.
@@ -15,109 +15,109 @@
*
*/
- --color-base-white: 0, 0%;
- --color-base-black: 240, 100%;
- --color-base-gray: 215, 14%;
- --color-base-blue: 212, 100%;
- --color-base-blue-dark: 212, 72%;
- --color-base-green: 158, 79%;
- --color-base-orange: 22, 100%;
- --color-base-purple: 269, 79%;
- --color-base-red: 351, 100%;
- --color-base-yellow: 41, 100%;
+ --color-base-white: 0, 0%;
+ --color-base-black: 240, 100%;
+ --color-base-gray: 215, 14%;
+ --color-base-blue: 212, 100%;
+ --color-base-blue-dark: 212, 72%;
+ --color-base-green: 158, 79%;
+ --color-base-orange: 22, 100%;
+ --color-base-purple: 269, 79%;
+ --color-base-red: 351, 100%;
+ --color-base-yellow: 41, 100%;
- /*
+ /*
* Color palettes are made using --color-base
* variables, along with a lightness value to
* define different variants.
*
*/
- --color-gray-5: var(--color-base-gray), 5%;
- --color-gray-10: var(--color-base-gray), 10%;
- --color-gray-20: var(--color-base-gray), 20%;
- --color-gray-30: var(--color-base-gray), 30%;
- --color-gray-40: var(--color-base-gray), 40%;
- --color-gray-50: var(--color-base-gray), 50%;
- --color-gray-60: var(--color-base-gray), 60%;
- --color-gray-70: var(--color-base-gray), 70%;
- --color-gray-80: var(--color-base-gray), 80%;
- --color-gray-90: var(--color-base-gray), 90%;
- --color-gray-95: var(--color-base-gray), 95%;
+ --color-gray-5: var(--color-base-gray), 5%;
+ --color-gray-10: var(--color-base-gray), 10%;
+ --color-gray-20: var(--color-base-gray), 20%;
+ --color-gray-30: var(--color-base-gray), 30%;
+ --color-gray-40: var(--color-base-gray), 40%;
+ --color-gray-50: var(--color-base-gray), 50%;
+ --color-gray-60: var(--color-base-gray), 60%;
+ --color-gray-70: var(--color-base-gray), 70%;
+ --color-gray-80: var(--color-base-gray), 80%;
+ --color-gray-90: var(--color-base-gray), 90%;
+ --color-gray-95: var(--color-base-gray), 95%;
- --color-blue: var(--color-base-blue), 61%;
- --color-blue-dark: var(--color-base-blue-dark), 39%;
- --color-green: var(--color-base-green), 42%;
- --color-orange: var(--color-base-orange), 50%;
- --color-purple: var(--color-base-purple), 54%;
- --color-red: var(--color-base-red), 54%;
- --color-yellow: var(--color-base-yellow), 59%;
+ --color-blue: var(--color-base-blue), 61%;
+ --color-blue-dark: var(--color-base-blue-dark), 39%;
+ --color-green: var(--color-base-green), 42%;
+ --color-orange: var(--color-base-orange), 50%;
+ --color-purple: var(--color-base-purple), 54%;
+ --color-red: var(--color-base-red), 54%;
+ --color-yellow: var(--color-base-yellow), 59%;
}
:root {
- color-scheme: light;
- --theme-accent: hsla(var(--color-blue), 1);
- --theme-text-accent: hsla(var(--color-blue), 1);
- --theme-accent-opacity: 0.15;
- --theme-divider: hsla(var(--color-gray-95), 1);
- --theme-text: hsla(var(--color-gray-10), 1);
- --theme-text-light: hsla(var(--color-gray-40), 1);
- /* @@@: not used anywhere */
- --theme-text-lighter: hsla(var(--color-gray-80), 1);
- --theme-bg: hsla(var(--color-base-white), 100%, 1);
- --theme-bg-hover: hsla(var(--color-gray-95), 1);
- --theme-bg-offset: hsla(var(--color-gray-90), 1);
- --theme-bg-accent: hsla(var(--color-blue), var(--theme-accent-opacity));
- --theme-code-inline-bg: hsla(var(--color-gray-95), 1);
- --theme-code-inline-text: var(--theme-text);
- --theme-code-bg: hsla(217, 19%, 27%, 1);
- --theme-code-text: hsla(var(--color-gray-95), 1);
- --theme-navbar-bg: hsla(var(--color-base-white), 100%, 1);
- --theme-navbar-height: 6rem;
- --theme-selection-color: hsla(var(--color-blue), 1);
- --theme-selection-bg: hsla(var(--color-blue), var(--theme-accent-opacity));
+ color-scheme: light;
+ --theme-accent: hsla(var(--color-blue), 1);
+ --theme-text-accent: hsla(var(--color-blue), 1);
+ --theme-accent-opacity: 0.15;
+ --theme-divider: hsla(var(--color-gray-95), 1);
+ --theme-text: hsla(var(--color-gray-10), 1);
+ --theme-text-light: hsla(var(--color-gray-40), 1);
+ /* @@@: not used anywhere */
+ --theme-text-lighter: hsla(var(--color-gray-80), 1);
+ --theme-bg: hsla(var(--color-base-white), 100%, 1);
+ --theme-bg-hover: hsla(var(--color-gray-95), 1);
+ --theme-bg-offset: hsla(var(--color-gray-90), 1);
+ --theme-bg-accent: hsla(var(--color-blue), var(--theme-accent-opacity));
+ --theme-code-inline-bg: hsla(var(--color-gray-95), 1);
+ --theme-code-inline-text: var(--theme-text);
+ --theme-code-bg: hsla(217, 19%, 27%, 1);
+ --theme-code-text: hsla(var(--color-gray-95), 1);
+ --theme-navbar-bg: hsla(var(--color-base-white), 100%, 1);
+ --theme-navbar-height: 6rem;
+ --theme-selection-color: hsla(var(--color-blue), 1);
+ --theme-selection-bg: hsla(var(--color-blue), var(--theme-accent-opacity));
}
body {
- background: var(--theme-bg);
- color: var(--theme-text);
+ background: var(--theme-bg);
+ color: var(--theme-text);
}
:root.theme-dark {
- color-scheme: dark;
- --theme-accent-opacity: 0.15;
- --theme-accent: hsla(var(--color-blue), 1);
- --theme-text-accent: hsla(var(--color-blue), 1);
- --theme-divider: hsla(var(--color-gray-10), 1);
- --theme-text: hsla(var(--color-gray-90), 1);
- --theme-text-light: hsla(var(--color-gray-80), 1);
+ color-scheme: dark;
+ --theme-accent-opacity: 0.15;
+ --theme-accent: hsla(var(--color-blue), 1);
+ --theme-text-accent: hsla(var(--color-blue), 1);
+ --theme-divider: hsla(var(--color-gray-10), 1);
+ --theme-text: hsla(var(--color-gray-90), 1);
+ --theme-text-light: hsla(var(--color-gray-80), 1);
- /* @@@: not used anywhere */
- --theme-text-lighter: hsla(var(--color-gray-40), 1);
- --theme-bg: hsla(215, 28%, 17%, 1);
- --theme-bg-hover: hsla(var(--color-gray-40), 1);
- --theme-bg-offset: hsla(var(--color-gray-5), 1);
- --theme-code-inline-bg: hsla(var(--color-gray-10), 1);
- --theme-code-inline-text: hsla(var(--color-base-white), 100%, 1);
- --theme-code-bg: hsla(var(--color-gray-5), 1);
- --theme-code-text: hsla(var(--color-base-white), 100%, 1);
- --theme-navbar-bg: hsla(215, 28%, 17%, 1);
- --theme-selection-color: hsla(var(--color-base-white), 100%, 1);
- --theme-selection-bg: hsla(var(--color-purple), var(--theme-accent-opacity));
+ /* @@@: not used anywhere */
+ --theme-text-lighter: hsla(var(--color-gray-40), 1);
+ --theme-bg: hsla(215, 28%, 17%, 1);
+ --theme-bg-hover: hsla(var(--color-gray-40), 1);
+ --theme-bg-offset: hsla(var(--color-gray-5), 1);
+ --theme-code-inline-bg: hsla(var(--color-gray-10), 1);
+ --theme-code-inline-text: hsla(var(--color-base-white), 100%, 1);
+ --theme-code-bg: hsla(var(--color-gray-5), 1);
+ --theme-code-text: hsla(var(--color-base-white), 100%, 1);
+ --theme-navbar-bg: hsla(215, 28%, 17%, 1);
+ --theme-selection-color: hsla(var(--color-base-white), 100%, 1);
+ --theme-selection-bg: hsla(var(--color-purple), var(--theme-accent-opacity));
- /* DocSearch [Algolia] */
- --docsearch-modal-background: var(--theme-bg);
- --docsearch-searchbox-focus-background: var(--theme-divider);
- --docsearch-footer-background: var(--theme-divider);
- --docsearch-text-color: var(--theme-text);
- --docsearch-hit-background: var(--theme-divider);
- --docsearch-hit-shadow: none;
- --docsearch-hit-color: var(--theme-text);
- --docsearch-footer-shadow: inset 0 2px 10px #000;
- --docsearch-modal-shadow: inset 0 0 8px #000;
+ /* DocSearch [Algolia] */
+ --docsearch-modal-background: var(--theme-bg);
+ --docsearch-searchbox-focus-background: var(--theme-divider);
+ --docsearch-footer-background: var(--theme-divider);
+ --docsearch-text-color: var(--theme-text);
+ --docsearch-hit-background: var(--theme-divider);
+ --docsearch-hit-shadow: none;
+ --docsearch-hit-color: var(--theme-text);
+ --docsearch-footer-shadow: inset 0 2px 10px #000;
+ --docsearch-modal-shadow: inset 0 0 8px #000;
}
::selection {
- color: var(--theme-selection-color);
- background-color: var(--theme-selection-bg);
+ color: var(--theme-selection-color);
+ background-color: var(--theme-selection-bg);
}
diff --git a/examples/fast-build/astro.config.mjs b/examples/fast-build/astro.config.mjs
index 38a4a1140..f40bc1cca 100644
--- a/examples/fast-build/astro.config.mjs
+++ b/examples/fast-build/astro.config.mjs
@@ -2,8 +2,8 @@ import { imagetools } from 'vite-imagetools';
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- renderers: ['@astrojs/renderer-vue'],
- vite: {
- plugins: [imagetools()],
- },
+ renderers: ['@astrojs/renderer-vue'],
+ vite: {
+ plugins: [imagetools()],
+ },
});
diff --git a/examples/fast-build/src/components/Greeting.vue b/examples/fast-build/src/components/Greeting.vue
index 69fa4fbca..a94f586bf 100644
--- a/examples/fast-build/src/components/Greeting.vue
+++ b/examples/fast-build/src/components/Greeting.vue
@@ -1,20 +1,20 @@
<script>
export default {
- data() {
- return {
- greeting: 'Hello World!',
- };
- },
+ data() {
+ return {
+ greeting: 'Hello World!',
+ };
+ },
};
</script>
<template>
- <p class="greeting">{{ greeting }}</p>
+ <p class="greeting">{{ greeting }}</p>
</template>
<style>
.greeting {
- color: red;
- font-weight: bold;
+ color: red;
+ font-weight: bold;
}
</style>
diff --git a/examples/fast-build/src/pages/index.astro b/examples/fast-build/src/pages/index.astro
index b5b9785da..2bdadbf5b 100644
--- a/examples/fast-build/src/pages/index.astro
+++ b/examples/fast-build/src/pages/index.astro
@@ -5,28 +5,30 @@ import Greeting from '../components/Greeting.vue';
---
<html>
-<head>
- <title>Demo app</title>
- <style>
- h1 { color: salmon; }
- </style>
-</head>
-<body>
- <section>
- <h1>Images</h1>
+ <head>
+ <title>Demo app</title>
+ <style>
+ h1 {
+ color: salmon;
+ }
+ </style>
+ </head>
+ <body>
+ <section>
+ <h1>Images</h1>
- <h2>Imported in JS</h2>
- <img src={imgUrl} />
- </section>
+ <h2>Imported in JS</h2>
+ <img src={imgUrl} />
+ </section>
- <section>
- <h1>Component CSS</h1>
- <Greeting />
- </section>
+ <section>
+ <h1>Component CSS</h1>
+ <Greeting />
+ </section>
- <section>
- <h1>ImageTools</h1>
- <img src={grayscaleUrl} />
- </section>
-</body>
-</html> \ No newline at end of file
+ <section>
+ <h1>ImageTools</h1>
+ <img src={grayscaleUrl} />
+ </section>
+ </body>
+</html>
diff --git a/examples/fast-build/src/styles/global.css b/examples/fast-build/src/styles/global.css
index 9f52e094e..ab5ca9bfe 100644
--- a/examples/fast-build/src/styles/global.css
+++ b/examples/fast-build/src/styles/global.css
@@ -1,3 +1,3 @@
body {
- background: lightcoral;
+ background: lightcoral;
}
diff --git a/examples/framework-alpine/astro.config.mjs b/examples/framework-alpine/astro.config.mjs
index 015eeff7c..05fc24b01 100644
--- a/examples/framework-alpine/astro.config.mjs
+++ b/examples/framework-alpine/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // No renderers are needed for AlpineJS support, just use Astro components!
- renderers: [],
+ // No renderers are needed for AlpineJS support, just use Astro components!
+ renderers: [],
});
diff --git a/examples/framework-alpine/src/components/Counter.astro b/examples/framework-alpine/src/components/Counter.astro
index 24140ca16..4d0b62a60 100644
--- a/examples/framework-alpine/src/components/Counter.astro
+++ b/examples/framework-alpine/src/components/Counter.astro
@@ -6,24 +6,24 @@ const { initialCount = 0 } = Astro.props;
---
<div class="counter" x-data=`{ count: ${initialCount} }`>
- <button x-on:click="count--">-</button>
- <pre x-text="count">{ initialCount }</pre>
- <button x-on:click="count++">+</button>
+ <button x-on:click="count--">-</button>
+ <pre x-text="count">{ initialCount }</pre>
+ <button x-on:click="count++">+</button>
</div>
<div class="counter-message">
- <slot />
+ <slot />
</div>
<style>
-.counter {
- display: grid;
- font-size: 2em;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- margin-top: 2em;
- place-items: center;
-}
-.counter-message {
- text-align: center;
-}
+ .counter {
+ display: grid;
+ font-size: 2em;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ margin-top: 2em;
+ place-items: center;
+ }
+ .counter-message {
+ text-align: center;
+ }
</style>
diff --git a/examples/framework-alpine/src/pages/index.astro b/examples/framework-alpine/src/pages/index.astro
index 3046c0aa6..c72e41187 100644
--- a/examples/framework-alpine/src/pages/index.astro
+++ b/examples/framework-alpine/src/pages/index.astro
@@ -1,40 +1,41 @@
---
// Component Imports
-import Counter from '../components/Counter.astro'
+import Counter from '../components/Counter.astro';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <style>
- html,
- body {
- font-family: system-ui;
- margin: 0;
- }
- body {
- padding: 2rem;
- }
- </style>
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <style>
+ html,
+ body {
+ font-family: system-ui;
+ margin: 0;
+ }
+ body {
+ padding: 2rem;
+ }
+ </style>
- <!-- Be sure to include AlpineJS -->
- <script src="//unpkg.com/alpinejs" defer></script>
- </head>
- <body>
- <main>
- <!-- Note: no `client:load` necessary since AlpineJS is always included -->
- <Counter>
- <h1>Hello, AlpineJS!</h1>
- </Counter>
+ <!-- Be sure to include AlpineJS -->
+ <script src="//unpkg.com/alpinejs" defer></script>
+ </head>
+ <body>
+ <main>
+ <!-- Note: no `client:load` necessary since AlpineJS is always included -->
+ <Counter>
+ <h1>Hello, AlpineJS!</h1>
+ </Counter>
- <!-- Note: pass props to Astro components to initialize Alpine with a certain state -->
- <Counter initialCount={5}>
- <h2>Use Astro to pass in server-side props</h2>
- </Counter>
- </main>
- </body>
+ <!-- Note: pass props to Astro components to initialize Alpine with a certain state -->
+ <Counter initialCount={5}>
+ <h2>Use Astro to pass in server-side props</h2>
+ </Counter>
+ </main>
+ </body>
</html>
diff --git a/examples/framework-lit/astro.config.mjs b/examples/framework-lit/astro.config.mjs
index 6a053bcc5..0e5346799 100644
--- a/examples/framework-lit/astro.config.mjs
+++ b/examples/framework-lit/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the lit renderer to support LitHTML components and templates.
- renderers: ['@astrojs/renderer-lit'],
+ // Enable the lit renderer to support LitHTML components and templates.
+ renderers: ['@astrojs/renderer-lit'],
});
diff --git a/examples/framework-lit/src/components/Counter.js b/examples/framework-lit/src/components/Counter.js
index 063d338e4..35fa8dbbb 100644
--- a/examples/framework-lit/src/components/Counter.js
+++ b/examples/framework-lit/src/components/Counter.js
@@ -3,32 +3,32 @@ import { LitElement, html } from 'lit';
export const tagName = 'my-counter';
class Counter extends LitElement {
- static get properties() {
- return {
- count: {
- type: Number,
- },
- };
- }
+ static get properties() {
+ return {
+ count: {
+ type: Number,
+ },
+ };
+ }
- constructor() {
- super();
- this.count = 0;
- }
+ constructor() {
+ super();
+ this.count = 0;
+ }
- increment() {
- this.count++;
- }
+ increment() {
+ this.count++;
+ }
- render() {
- return html`
- <div>
- <p>Count: ${this.count}</p>
+ render() {
+ return html`
+ <div>
+ <p>Count: ${this.count}</p>
- <button type="button" @click=${this.increment}>Increment</button>
- </div>
- `;
- }
+ <button type="button" @click=${this.increment}>Increment</button>
+ </div>
+ `;
+ }
}
customElements.define(tagName, Counter);
diff --git a/examples/framework-lit/src/components/Lorem.astro b/examples/framework-lit/src/components/Lorem.astro
index 1b03755e3..b4040980e 100644
--- a/examples/framework-lit/src/components/Lorem.astro
+++ b/examples/framework-lit/src/components/Lorem.astro
@@ -1,31 +1,58 @@
-
-<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi quam arcu, rhoncus et dui at, volutpat viverra augue. Suspendisse placerat libero tellus, ut consequat ligula rutrum id. Vestibulum lectus libero, viverra in lacus eget, porttitor tincidunt leo. Integer sit amet turpis et felis fringilla lacinia in id nibh. Proin vitae dapibus odio. Mauris ornare eget urna id volutpat. Duis tellus nisi, hendrerit id sodales in, rutrum a quam. Proin tempor velit turpis, et tempor lacus sagittis in. Sed congue mauris quis nibh posuere, nec semper lacus auctor. Morbi sit amet enim sit amet arcu ullamcorper sollicitudin. Donec dignissim posuere tincidunt. Donec ultrices quam nec orci venenatis suscipit.
-
-Maecenas sapien quam, pretium sit amet ullamcorper at, vulputate sit amet urna. Suspendisse potenti. Integer in sapien turpis. Nulla accumsan viverra diam, quis convallis magna finibus eget. Integer sed eros bibendum, consequat velit sit amet, tincidunt orci. Mauris varius id metus in fringilla. Vestibulum dignissim massa eget erat luctus, ac congue mauris pellentesque. In et tempor dolor. Cras blandit congue lorem at facilisis. Aenean vel lacinia quam. Pellentesque luctus metus ut scelerisque efficitur.
-
-Mauris laoreet sodales libero eget luctus. Proin at congue dui, a cursus risus. Pellentesque lorem sem, rhoncus fermentum arcu ut, euismod fermentum ligula. Nullam eu orci posuere, laoreet leo in, commodo dolor. Fusce at felis elementum, commodo justo at, placerat justo. Nam feugiat scelerisque arcu, ut fermentum tellus elementum in. Sed ut vulputate ante.
-
-Morbi cursus arcu quis odio convallis egestas. Donec vulputate vestibulum dolor eget tristique. Nullam tempor semper augue, vitae lobortis neque tempor ac. Pellentesque massa leo, congue id ligula auctor, sollicitudin pharetra lorem. Curabitur a lacus porttitor, venenatis est quis, mattis velit. Fusce hendrerit lobortis mi ac efficitur. Mauris ornare, lorem sed varius faucibus, nisi dui pretium urna, sit amet lacinia nibh ligula in ipsum. Phasellus gravida, metus eget ornare ultrices, dolor ipsum consectetur erat, ac aliquet eros metus sed lectus. Nullam eleifend posuere rhoncus. Curabitur semper ligula vel ante posuere, at blandit orci accumsan.
-
-Vivamus accumsan metus in lorem laoreet, a luctus arcu tempus. Donec posuere sollicitudin nulla at vulputate. Nulla condimentum imperdiet purus, et lobortis ligula iaculis in. Donec suscipit viverra neque, ut elementum eros lacinia ut. Fusce at odio enim. Donec rutrum lectus sit amet est auctor, ac rhoncus lorem imperdiet. Curabitur commodo ex est, non tempus massa pulvinar nec. Sed fermentum, lectus eget ultricies luctus, enim sem sodales quam, sed laoreet tortor sem feugiat nisi. Morbi molestie vehicula viverra. Integer accumsan mi in orci ultrices posuere.
-
-Integer mi quam, faucibus et aliquet imperdiet, ornare ac ex. Nunc mattis molestie nisi, eu venenatis nibh vehicula at. Aliquam ut elit consectetur, finibus lorem sed, condimentum sapien. Praesent fermentum iaculis orci, vitae tincidunt est viverra nec. Morbi semper turpis sed lectus ornare tristique. Sed congue dui ex. Maecenas orci ligula, imperdiet sit amet accumsan et, finibus a velit. Ut vitae blandit eros. Nam gravida nec ipsum non volutpat.
-
-Integer quam metus, porttitor id ante sed, rutrum porta quam. Aenean at mattis ante. Morbi id libero eget risus sagittis gravida. Proin consequat sapien a dignissim posuere. Ut luctus sed metus ut elementum. Mauris tincidunt condimentum risus at bibendum. Aenean a sapien justo. Morbi vel neque in eros venenatis scelerisque vitae nec justo. Vestibulum lacinia, dui eu sollicitudin ornare, est elit vestibulum arcu, nec ultrices augue turpis in massa. Duis commodo lectus sed est posuere, et mollis nisi dapibus. Sed id ultrices arcu.
-
-Praesent tempor sodales aliquet. Donec suscipit ipsum eu odio cursus, quis sodales metus sodales. Nunc vestibulum massa at felis ullamcorper cursus. Pellentesque facilisis ante ut lectus vulputate vestibulum. Nullam pharetra felis ac lacus sodales, vel suscipit metus faucibus. Donec facilisis imperdiet risus, in volutpat odio tincidunt a. Aliquam vitae leo lorem. Proin scelerisque efficitur velit, vel cursus ipsum accumsan id. Morbi nibh nulla, pretium quis venenatis et, pharetra et sapien. Cras lobortis, massa sit amet blandit pulvinar, mi magna condimentum ex, quis commodo ipsum est quis metus. Maecenas pulvinar, leo sit amet congue pulvinar, neque magna ultrices mi, et rhoncus massa sapien quis libero. Etiam a nunc et ipsum faucibus pretium. Nulla facilisi. Nunc nec dolor velit. In semper semper mi non condimentum. Pellentesque vehicula volutpat odio, a semper sem porta a.
-
-In sit amet lectus rutrum, sollicitudin augue auctor, maximus quam. Mauris congue, nisl non fermentum iaculis, leo erat interdum lorem, quis bibendum arcu eros et elit. Fusce tortor ante, gravida a arcu in, lacinia finibus ante. Phasellus facilisis lectus vitae sapien feugiat laoreet. Curabitur ultricies libero sit amet condimentum suscipit. Duis at vestibulum mi. Suspendisse at neque augue. Duis ornare a mauris id efficitur. Suspendisse in dui nec dolor dignissim venenatis. Curabitur a magna turpis. Aliquam at commodo tellus.
-
-In id sem interdum, suscipit felis at, mattis velit. Proin accumsan sodales felis a lacinia. Curabitur at magna a massa varius maximus. Vestibulum in auctor ante. Donec aliquam tortor sed nulla rutrum, et egestas mi efficitur. Sed viverra quam tellus, quis vulputate felis ultrices sed. Mauris sagittis, neque quis laoreet gravida, nisi est ultrices mi, at tempus nunc justo non dui. Suspendisse porttitor tortor nulla, eget luctus quam finibus id. Proin sodales eros mollis tellus euismod luctus a eu mi. Quisque consectetur iaculis nibh, at mollis tellus volutpat eu. Aenean a nulla vel lectus rhoncus aliquam. Donec vitae lacinia neque. Donec non lectus eget sem finibus ultrices vel nec felis.
-
-Proin fringilla mi a leo rhoncus aliquam sit amet quis augue. Duis congue ligula at est suscipit fringilla. Proin aliquam erat ut consequat dapibus. Suspendisse non nisi orci. Donec ac erat vel libero egestas laoreet. Nullam felis odio, tincidunt eget eleifend a, porttitor eu nisi. Suspendisse tristique eros at dolor scelerisque hendrerit. Etiam id dignissim lectus. Fusce lacinia metus eu risus placerat, et eleifend nunc ultrices. Ut gravida a dui sed volutpat. Sed semper quis erat sed ornare. Pellentesque sapien sem, fermentum vel nunc at, auctor posuere nisl. Maecenas aliquet lobortis leo. Vivamus tellus urna, dignissim consectetur sapien vitae, hendrerit varius sem. Nunc dictum tristique fermentum. Duis eu suscipit odio.
-
-Curabitur quis egestas neque. Fusce eu fringilla orci, vitae euismod sapien. Donec sit amet iaculis urna. Phasellus maximus nisl in libero bibendum volutpat. Nulla at vehicula lorem. Phasellus varius, elit ac suscipit pretium, turpis ipsum porttitor lectus, vitae ullamcorper orci velit ut ligula. Proin mollis, orci vel commodo auctor, sapien ipsum vulputate enim, sit amet aliquam nulla sapien ut sapien. Proin tincidunt ex non massa aliquet, quis aliquam nulla egestas. Maecenas mollis turpis dapibus, dignissim lectus tincidunt, egestas ligula. Suspendisse in lobortis purus. Sed tellus tellus, mollis eget tempor sed, interdum ut lectus. Nulla sed ex efficitur, porta dui cursus, tristique elit. Maecenas tincidunt tortor vitae massa laoreet ultricies. Mauris ac elit vitae orci eleifend ornare non eu ligula. Curabitur venenatis nulla ut neque tristique, non tincidunt justo pretium. Suspendisse mattis semper dui, eget vestibulum risus elementum sed.
-
-In consequat nisi sit amet nulla euismod, at convallis tortor tincidunt. Aliquam hendrerit venenatis risus in interdum. Duis ullamcorper imperdiet elit sit amet blandit. Mauris placerat lacinia velit id pharetra. Nam nec iaculis dui. Etiam odio mi, fringilla in rutrum in, viverra quis tellus. Aliquam egestas mauris id nisi facilisis, in laoreet nibh malesuada. Ut eu dui laoreet, venenatis tellus ac, feugiat mauris. Nunc in velit laoreet, venenatis tellus quis, blandit dolor. Nulla ultrices et neque id placerat. Nulla eu interdum nulla. Aliquam molestie enim quis rutrum finibus. Nulla bibendum orci vel scelerisque posuere. Praesent quis magna molestie, luctus tortor tincidunt, gravida neque.
-
-Quisque et ligula eget magna viverra interdum at a sapien. Mauris ornare efficitur nunc sed vulputate. Praesent laoreet mollis tincidunt. Vestibulum id arcu vulputate, eleifend enim vel, accumsan turpis. Morbi faucibus convallis tellus, semper laoreet justo lacinia nec. Sed sodales ligula consectetur dui rhoncus, et convallis metus accumsan. Sed ullamcorper non ex sit amet ultricies. Donec finibus nulla nec blandit porttitor. Etiam aliquam quis leo a imperdiet. Cras at lobortis est. In convallis semper enim, ac porta ligula fringilla at. Donec augue est, facilisis et odio sit amet, viverra ullamcorper nisl. Ut porta velit nec sem lacinia, sit amet mollis magna auctor. Nulla lobortis lacinia mauris nec sagittis. Suspendisse rutrum ex vel nisi interdum hendrerit et ut purus.
-
-Sed consectetur sodales nibh eget tempus. Aenean egestas luctus viverra. Integer fermentum tincidunt tellus, nec rhoncus velit hendrerit vitae. Proin quis neque porttitor, scelerisque risus gravida, volutpat sem. Fusce nec ex rhoncus, tempor libero nec, pellentesque ex. Integer quis iaculis purus. Nullam vitae imperdiet orci. Sed sit amet eros condimentum, scelerisque turpis facilisis, dignissim ante. Proin quis tristique lacus, sed sagittis nisl. Cras pharetra ultrices purus, sed ullamcorper nisi fringilla eu. Praesent risus turpis, auctor in fringilla a, fringilla eu dolor. Phasellus auctor tristique enim, eleifend molestie diam venenatis ut. Mauris dapibus, enim eget pharetra semper, nulla dui porttitor mi, auctor hendrerit augue nulla quis urna. Aliquam in cursus justo.
-</p> \ No newline at end of file
+<p>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi quam arcu, rhoncus et dui at, volutpat viverra augue. Suspendisse placerat libero tellus, ut consequat ligula
+ rutrum id. Vestibulum lectus libero, viverra in lacus eget, porttitor tincidunt leo. Integer sit amet turpis et felis fringilla lacinia in id nibh. Proin vitae dapibus odio.
+ Mauris ornare eget urna id volutpat. Duis tellus nisi, hendrerit id sodales in, rutrum a quam. Proin tempor velit turpis, et tempor lacus sagittis in. Sed congue mauris quis nibh
+ posuere, nec semper lacus auctor. Morbi sit amet enim sit amet arcu ullamcorper sollicitudin. Donec dignissim posuere tincidunt. Donec ultrices quam nec orci venenatis suscipit.
+ Maecenas sapien quam, pretium sit amet ullamcorper at, vulputate sit amet urna. Suspendisse potenti. Integer in sapien turpis. Nulla accumsan viverra diam, quis convallis magna
+ finibus eget. Integer sed eros bibendum, consequat velit sit amet, tincidunt orci. Mauris varius id metus in fringilla. Vestibulum dignissim massa eget erat luctus, ac congue
+ mauris pellentesque. In et tempor dolor. Cras blandit congue lorem at facilisis. Aenean vel lacinia quam. Pellentesque luctus metus ut scelerisque efficitur. Mauris laoreet
+ sodales libero eget luctus. Proin at congue dui, a cursus risus. Pellentesque lorem sem, rhoncus fermentum arcu ut, euismod fermentum ligula. Nullam eu orci posuere, laoreet leo
+ in, commodo dolor. Fusce at felis elementum, commodo justo at, placerat justo. Nam feugiat scelerisque arcu, ut fermentum tellus elementum in. Sed ut vulputate ante. Morbi cursus
+ arcu quis odio convallis egestas. Donec vulputate vestibulum dolor eget tristique. Nullam tempor semper augue, vitae lobortis neque tempor ac. Pellentesque massa leo, congue id
+ ligula auctor, sollicitudin pharetra lorem. Curabitur a lacus porttitor, venenatis est quis, mattis velit. Fusce hendrerit lobortis mi ac efficitur. Mauris ornare, lorem sed
+ varius faucibus, nisi dui pretium urna, sit amet lacinia nibh ligula in ipsum. Phasellus gravida, metus eget ornare ultrices, dolor ipsum consectetur erat, ac aliquet eros metus
+ sed lectus. Nullam eleifend posuere rhoncus. Curabitur semper ligula vel ante posuere, at blandit orci accumsan. Vivamus accumsan metus in lorem laoreet, a luctus arcu tempus.
+ Donec posuere sollicitudin nulla at vulputate. Nulla condimentum imperdiet purus, et lobortis ligula iaculis in. Donec suscipit viverra neque, ut elementum eros lacinia ut. Fusce
+ at odio enim. Donec rutrum lectus sit amet est auctor, ac rhoncus lorem imperdiet. Curabitur commodo ex est, non tempus massa pulvinar nec. Sed fermentum, lectus eget ultricies
+ luctus, enim sem sodales quam, sed laoreet tortor sem feugiat nisi. Morbi molestie vehicula viverra. Integer accumsan mi in orci ultrices posuere. Integer mi quam, faucibus et
+ aliquet imperdiet, ornare ac ex. Nunc mattis molestie nisi, eu venenatis nibh vehicula at. Aliquam ut elit consectetur, finibus lorem sed, condimentum sapien. Praesent fermentum
+ iaculis orci, vitae tincidunt est viverra nec. Morbi semper turpis sed lectus ornare tristique. Sed congue dui ex. Maecenas orci ligula, imperdiet sit amet accumsan et, finibus a
+ velit. Ut vitae blandit eros. Nam gravida nec ipsum non volutpat. Integer quam metus, porttitor id ante sed, rutrum porta quam. Aenean at mattis ante. Morbi id libero eget risus
+ sagittis gravida. Proin consequat sapien a dignissim posuere. Ut luctus sed metus ut elementum. Mauris tincidunt condimentum risus at bibendum. Aenean a sapien justo. Morbi vel
+ neque in eros venenatis scelerisque vitae nec justo. Vestibulum lacinia, dui eu sollicitudin ornare, est elit vestibulum arcu, nec ultrices augue turpis in massa. Duis commodo
+ lectus sed est posuere, et mollis nisi dapibus. Sed id ultrices arcu. Praesent tempor sodales aliquet. Donec suscipit ipsum eu odio cursus, quis sodales metus sodales. Nunc
+ vestibulum massa at felis ullamcorper cursus. Pellentesque facilisis ante ut lectus vulputate vestibulum. Nullam pharetra felis ac lacus sodales, vel suscipit metus faucibus.
+ Donec facilisis imperdiet risus, in volutpat odio tincidunt a. Aliquam vitae leo lorem. Proin scelerisque efficitur velit, vel cursus ipsum accumsan id. Morbi nibh nulla, pretium
+ quis venenatis et, pharetra et sapien. Cras lobortis, massa sit amet blandit pulvinar, mi magna condimentum ex, quis commodo ipsum est quis metus. Maecenas pulvinar, leo sit amet
+ congue pulvinar, neque magna ultrices mi, et rhoncus massa sapien quis libero. Etiam a nunc et ipsum faucibus pretium. Nulla facilisi. Nunc nec dolor velit. In semper semper mi
+ non condimentum. Pellentesque vehicula volutpat odio, a semper sem porta a. In sit amet lectus rutrum, sollicitudin augue auctor, maximus quam. Mauris congue, nisl non fermentum
+ iaculis, leo erat interdum lorem, quis bibendum arcu eros et elit. Fusce tortor ante, gravida a arcu in, lacinia finibus ante. Phasellus facilisis lectus vitae sapien feugiat
+ laoreet. Curabitur ultricies libero sit amet condimentum suscipit. Duis at vestibulum mi. Suspendisse at neque augue. Duis ornare a mauris id efficitur. Suspendisse in dui nec
+ dolor dignissim venenatis. Curabitur a magna turpis. Aliquam at commodo tellus. In id sem interdum, suscipit felis at, mattis velit. Proin accumsan sodales felis a lacinia.
+ Curabitur at magna a massa varius maximus. Vestibulum in auctor ante. Donec aliquam tortor sed nulla rutrum, et egestas mi efficitur. Sed viverra quam tellus, quis vulputate
+ felis ultrices sed. Mauris sagittis, neque quis laoreet gravida, nisi est ultrices mi, at tempus nunc justo non dui. Suspendisse porttitor tortor nulla, eget luctus quam finibus
+ id. Proin sodales eros mollis tellus euismod luctus a eu mi. Quisque consectetur iaculis nibh, at mollis tellus volutpat eu. Aenean a nulla vel lectus rhoncus aliquam. Donec
+ vitae lacinia neque. Donec non lectus eget sem finibus ultrices vel nec felis. Proin fringilla mi a leo rhoncus aliquam sit amet quis augue. Duis congue ligula at est suscipit
+ fringilla. Proin aliquam erat ut consequat dapibus. Suspendisse non nisi orci. Donec ac erat vel libero egestas laoreet. Nullam felis odio, tincidunt eget eleifend a, porttitor
+ eu nisi. Suspendisse tristique eros at dolor scelerisque hendrerit. Etiam id dignissim lectus. Fusce lacinia metus eu risus placerat, et eleifend nunc ultrices. Ut gravida a dui
+ sed volutpat. Sed semper quis erat sed ornare. Pellentesque sapien sem, fermentum vel nunc at, auctor posuere nisl. Maecenas aliquet lobortis leo. Vivamus tellus urna, dignissim
+ consectetur sapien vitae, hendrerit varius sem. Nunc dictum tristique fermentum. Duis eu suscipit odio. Curabitur quis egestas neque. Fusce eu fringilla orci, vitae euismod
+ sapien. Donec sit amet iaculis urna. Phasellus maximus nisl in libero bibendum volutpat. Nulla at vehicula lorem. Phasellus varius, elit ac suscipit pretium, turpis ipsum
+ porttitor lectus, vitae ullamcorper orci velit ut ligula. Proin mollis, orci vel commodo auctor, sapien ipsum vulputate enim, sit amet aliquam nulla sapien ut sapien. Proin
+ tincidunt ex non massa aliquet, quis aliquam nulla egestas. Maecenas mollis turpis dapibus, dignissim lectus tincidunt, egestas ligula. Suspendisse in lobortis purus. Sed tellus
+ tellus, mollis eget tempor sed, interdum ut lectus. Nulla sed ex efficitur, porta dui cursus, tristique elit. Maecenas tincidunt tortor vitae massa laoreet ultricies. Mauris ac
+ elit vitae orci eleifend ornare non eu ligula. Curabitur venenatis nulla ut neque tristique, non tincidunt justo pretium. Suspendisse mattis semper dui, eget vestibulum risus
+ elementum sed. In consequat nisi sit amet nulla euismod, at convallis tortor tincidunt. Aliquam hendrerit venenatis risus in interdum. Duis ullamcorper imperdiet elit sit amet
+ blandit. Mauris placerat lacinia velit id pharetra. Nam nec iaculis dui. Etiam odio mi, fringilla in rutrum in, viverra quis tellus. Aliquam egestas mauris id nisi facilisis, in
+ laoreet nibh malesuada. Ut eu dui laoreet, venenatis tellus ac, feugiat mauris. Nunc in velit laoreet, venenatis tellus quis, blandit dolor. Nulla ultrices et neque id placerat.
+ Nulla eu interdum nulla. Aliquam molestie enim quis rutrum finibus. Nulla bibendum orci vel scelerisque posuere. Praesent quis magna molestie, luctus tortor tincidunt, gravida
+ neque. Quisque et ligula eget magna viverra interdum at a sapien. Mauris ornare efficitur nunc sed vulputate. Praesent laoreet mollis tincidunt. Vestibulum id arcu vulputate,
+ eleifend enim vel, accumsan turpis. Morbi faucibus convallis tellus, semper laoreet justo lacinia nec. Sed sodales ligula consectetur dui rhoncus, et convallis metus accumsan.
+ Sed ullamcorper non ex sit amet ultricies. Donec finibus nulla nec blandit porttitor. Etiam aliquam quis leo a imperdiet. Cras at lobortis est. In convallis semper enim, ac porta
+ ligula fringilla at. Donec augue est, facilisis et odio sit amet, viverra ullamcorper nisl. Ut porta velit nec sem lacinia, sit amet mollis magna auctor. Nulla lobortis lacinia
+ mauris nec sagittis. Suspendisse rutrum ex vel nisi interdum hendrerit et ut purus. Sed consectetur sodales nibh eget tempus. Aenean egestas luctus viverra. Integer fermentum
+ tincidunt tellus, nec rhoncus velit hendrerit vitae. Proin quis neque porttitor, scelerisque risus gravida, volutpat sem. Fusce nec ex rhoncus, tempor libero nec, pellentesque
+ ex. Integer quis iaculis purus. Nullam vitae imperdiet orci. Sed sit amet eros condimentum, scelerisque turpis facilisis, dignissim ante. Proin quis tristique lacus, sed sagittis
+ nisl. Cras pharetra ultrices purus, sed ullamcorper nisi fringilla eu. Praesent risus turpis, auctor in fringilla a, fringilla eu dolor. Phasellus auctor tristique enim, eleifend
+ molestie diam venenatis ut. Mauris dapibus, enim eget pharetra semper, nulla dui porttitor mi, auctor hendrerit augue nulla quis urna. Aliquam in cursus justo.
+</p>
diff --git a/examples/framework-lit/src/components/Test.js b/examples/framework-lit/src/components/Test.js
index 182755c79..7f565f777 100644
--- a/examples/framework-lit/src/components/Test.js
+++ b/examples/framework-lit/src/components/Test.js
@@ -3,17 +3,17 @@ import { LitElement, html } from 'lit';
export const tagName = 'calc-add';
class CalcAdd extends LitElement {
- static get properties() {
- return {
- num: {
- type: Number,
- },
- };
- }
+ static get properties() {
+ return {
+ num: {
+ type: Number,
+ },
+ };
+ }
- render() {
- return html` <div>Number: ${this.num}</div> `;
- }
+ render() {
+ return html` <div>Number: ${this.num}</div> `;
+ }
}
customElements.define(tagName, CalcAdd);
diff --git a/examples/framework-lit/src/pages/index.astro b/examples/framework-lit/src/pages/index.astro
index ec4a7f73d..6b825848e 100644
--- a/examples/framework-lit/src/pages/index.astro
+++ b/examples/framework-lit/src/pages/index.astro
@@ -4,16 +4,16 @@ import '../components/Test.js';
import '../components/Counter.js';
---
-<!doctype html>
+<!DOCTYPE html>
<html lang="en">
- <head>
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <title>Demo</title>
- </head>
- <body>
- <h1>Test app</h1>
- <my-counter client:load />
- <Lorem />
- <calc-add num={33} />
- </body>
+ <head>
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <title>Demo</title>
+ </head>
+ <body>
+ <h1>Test app</h1>
+ <my-counter client:load></my-counter>
+ <Lorem />
+ <calc-add num={33}></calc-add>
+ </body>
</html>
diff --git a/examples/framework-multiple/astro.config.mjs b/examples/framework-multiple/astro.config.mjs
index b5fe6a073..ce173ed92 100644
--- a/examples/framework-multiple/astro.config.mjs
+++ b/examples/framework-multiple/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable many renderers to support all different kinds of components.
- renderers: ['@astrojs/renderer-preact', '@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue', '@astrojs/renderer-solid'],
+ // Enable many renderers to support all different kinds of components.
+ renderers: ['@astrojs/renderer-preact', '@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue', '@astrojs/renderer-solid'],
});
diff --git a/examples/framework-multiple/src/components/A.astro b/examples/framework-multiple/src/components/A.astro
index 702a4be35..2b7bd482a 100644
--- a/examples/framework-multiple/src/components/A.astro
+++ b/examples/framework-multiple/src/components/A.astro
@@ -1,3 +1,3 @@
<div class="children">
- <h1>Hello Astro (A)</h1>
+ <h1>Hello Astro (A)</h1>
</div>
diff --git a/examples/framework-multiple/src/components/B.astro b/examples/framework-multiple/src/components/B.astro
index 9022cb372..3640fe831 100644
--- a/examples/framework-multiple/src/components/B.astro
+++ b/examples/framework-multiple/src/components/B.astro
@@ -1,3 +1,3 @@
<div class="children">
- <h1>Hello Astro (B)</h1>
+ <h1>Hello Astro (B)</h1>
</div>
diff --git a/examples/framework-multiple/src/components/PreactCounter.tsx b/examples/framework-multiple/src/components/PreactCounter.tsx
index 9c9bf73a1..261f275a1 100644
--- a/examples/framework-multiple/src/components/PreactCounter.tsx
+++ b/examples/framework-multiple/src/components/PreactCounter.tsx
@@ -2,18 +2,18 @@ import { useState } from 'preact/hooks';
/** a counter written in Preact */
export function PreactCounter({ children }) {
- const [count, setCount] = useState(0);
- const add = () => setCount((i) => i + 1);
- const subtract = () => setCount((i) => i - 1);
+ const [count, setCount] = useState(0);
+ const add = () => setCount((i) => i + 1);
+ const subtract = () => setCount((i) => i - 1);
- return (
- <>
- <div class="counter">
- <button onClick={subtract}>-</button>
- <pre>{count}</pre>
- <button onClick={add}>+</button>
- </div>
- <div class="counter-message">{children}</div>
- </>
- );
+ return (
+ <>
+ <div class="counter">
+ <button onClick={subtract}>-</button>
+ <pre>{count}</pre>
+ <button onClick={add}>+</button>
+ </div>
+ <div class="counter-message">{children}</div>
+ </>
+ );
}
diff --git a/examples/framework-multiple/src/components/PreactSFC.tsx b/examples/framework-multiple/src/components/PreactSFC.tsx
index 3638caebd..60d0fe836 100644
--- a/examples/framework-multiple/src/components/PreactSFC.tsx
+++ b/examples/framework-multiple/src/components/PreactSFC.tsx
@@ -2,9 +2,9 @@
/** a counter written in Preact */
export default function PreactSFC({ children }) {
- return (
- <>
- <div className="counter">Hello from Preact!</div>
- </>
- );
+ return (
+ <>
+ <div className="counter">Hello from Preact!</div>
+ </>
+ );
}
diff --git a/examples/framework-multiple/src/components/ReactCounter.jsx b/examples/framework-multiple/src/components/ReactCounter.jsx
index eca3cd2f7..cdd42bee2 100644
--- a/examples/framework-multiple/src/components/ReactCounter.jsx
+++ b/examples/framework-multiple/src/components/ReactCounter.jsx
@@ -2,18 +2,18 @@ import { useState } from 'react';
/** a counter written in React */
export function Counter({ children }) {
- const [count, setCount] = useState(0);
- const add = () => setCount((i) => i + 1);
- const subtract = () => setCount((i) => i - 1);
+ const [count, setCount] = useState(0);
+ const add = () => setCount((i) => i + 1);
+ const subtract = () => setCount((i) => i - 1);
- return (
- <>
- <div className="counter">
- <button onClick={subtract}>-</button>
- <pre>{count}</pre>
- <button onClick={add}>+</button>
- </div>
- <div className="counter-message">{children}</div>
- </>
- );
+ return (
+ <>
+ <div className="counter">
+ <button onClick={subtract}>-</button>
+ <pre>{count}</pre>
+ <button onClick={add}>+</button>
+ </div>
+ <div className="counter-message">{children}</div>
+ </>
+ );
}
diff --git a/examples/framework-multiple/src/components/SolidCounter.tsx b/examples/framework-multiple/src/components/SolidCounter.tsx
index 63fe5cb11..b16a463d5 100644
--- a/examples/framework-multiple/src/components/SolidCounter.tsx
+++ b/examples/framework-multiple/src/components/SolidCounter.tsx
@@ -2,18 +2,18 @@ import { createSignal } from 'solid-js';
/** a counter written with Solid */
export default function SolidCounter({ children }) {
- const [count, setCount] = createSignal(0);
- const add = () => setCount(count() + 1);
- const subtract = () => setCount(count() - 1);
+ const [count, setCount] = createSignal(0);
+ const add = () => setCount(count() + 1);
+ const subtract = () => setCount(count() - 1);
- return (
- <>
- <div id="solid" class="counter">
- <button onClick={subtract}>-</button>
- <pre>{count()}</pre>
- <button onClick={add}>+</button>
- </div>
- <div class="counter-message">{children}</div>
- </>
- );
+ return (
+ <>
+ <div id="solid" class="counter">
+ <button onClick={subtract}>-</button>
+ <pre>{count()}</pre>
+ <button onClick={add}>+</button>
+ </div>
+ <div class="counter-message">{children}</div>
+ </>
+ );
}
diff --git a/examples/framework-multiple/src/components/VueCounter.vue b/examples/framework-multiple/src/components/VueCounter.vue
index 6c04c401a..bd801ca81 100644
--- a/examples/framework-multiple/src/components/VueCounter.vue
+++ b/examples/framework-multiple/src/components/VueCounter.vue
@@ -1,27 +1,27 @@
<template>
- <div class="counter">
- <button @click="subtract()">-</button>
- <pre>{{ count }}</pre>
- <button @click="add()">+</button>
- </div>
- <div class="counter-message">
- <slot />
- </div>
+ <div class="counter">
+ <button @click="subtract()">-</button>
+ <pre>{{ count }}</pre>
+ <button @click="add()">+</button>
+ </div>
+ <div class="counter-message">
+ <slot />
+ </div>
</template>
<script>
import { ref } from 'vue';
export default {
- setup() {
- const count = ref(0);
- const add = () => (count.value = count.value + 1);
- const subtract = () => (count.value = count.value - 1);
+ setup() {
+ const count = ref(0);
+ const add = () => (count.value = count.value + 1);
+ const subtract = () => (count.value = count.value - 1);
- return {
- count,
- add,
- subtract,
- };
- },
+ return {
+ count,
+ add,
+ subtract,
+ };
+ },
};
</script>
diff --git a/examples/framework-multiple/src/pages/index.astro b/examples/framework-multiple/src/pages/index.astro
index e27a5466d..3864b6430 100644
--- a/examples/framework-multiple/src/pages/index.astro
+++ b/examples/framework-multiple/src/pages/index.astro
@@ -11,41 +11,40 @@ import SvelteCounter from '../components/SvelteCounter.svelte';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width">
- <link rel="icon" type="image/x-icon" href="/favicon.ico">
- <link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/global.css')}>
- </head>
- <body>
- <main>
-
- <react.Counter client:visible>
- <h1>Hello React!</h1>
- <p>What's up?</p>
- </react.Counter>
-
- <PreactCounter client:visible>
- <h1>Hello Preact!</h1>
- </PreactCounter>
-
- <SolidCounter client:visible>
- <h1>Hello Solid!</h1>
- </SolidCounter>
-
- <VueCounter client:visible>
- <h1>Hello Vue!</h1>
- </VueCounter>
-
- <SvelteCounter client:visible>
- <h1>Hello Svelte!</h1>
- </SvelteCounter>
-
- <A />
-
- <Renamed />
-
- </main>
- </body>
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/global.css')} />
+ </head>
+ <body>
+ <main>
+ <react.Counter client:visible>
+ <h1>Hello React!</h1>
+ <p>What's up?</p>
+ </react.Counter>
+
+ <PreactCounter client:visible>
+ <h1>Hello Preact!</h1>
+ </PreactCounter>
+
+ <SolidCounter client:visible>
+ <h1>Hello Solid!</h1>
+ </SolidCounter>
+
+ <VueCounter client:visible>
+ <h1>Hello Vue!</h1>
+ </VueCounter>
+
+ <SvelteCounter client:visible>
+ <h1>Hello Svelte!</h1>
+ </SvelteCounter>
+
+ <A />
+
+ <Renamed />
+ </main>
+ </body>
</html>
diff --git a/examples/framework-multiple/src/styles/global.css b/examples/framework-multiple/src/styles/global.css
index 5997a5afd..4912b4c39 100644
--- a/examples/framework-multiple/src/styles/global.css
+++ b/examples/framework-multiple/src/styles/global.css
@@ -1,21 +1,21 @@
html,
body {
- font-family: system-ui;
- margin: 0;
+ font-family: system-ui;
+ margin: 0;
}
body {
- padding: 2rem;
+ padding: 2rem;
}
.counter {
- display: grid;
- font-size: 2em;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- margin-top: 2em;
- place-items: center;
+ display: grid;
+ font-size: 2em;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ margin-top: 2em;
+ place-items: center;
}
.counter-message {
- text-align: center;
+ text-align: center;
}
diff --git a/examples/framework-preact/astro.config.mjs b/examples/framework-preact/astro.config.mjs
index 68499b3fa..a1516f292 100644
--- a/examples/framework-preact/astro.config.mjs
+++ b/examples/framework-preact/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Preact renderer to support Preact JSX components.
- renderers: ['@astrojs/renderer-preact'],
+ // Enable the Preact renderer to support Preact JSX components.
+ renderers: ['@astrojs/renderer-preact'],
});
diff --git a/examples/framework-preact/src/components/Counter.css b/examples/framework-preact/src/components/Counter.css
index 5d3de4803..fb21044d7 100644
--- a/examples/framework-preact/src/components/Counter.css
+++ b/examples/framework-preact/src/components/Counter.css
@@ -1,11 +1,11 @@
.counter {
- display: grid;
- font-size: 2em;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- margin-top: 2em;
- place-items: center;
+ display: grid;
+ font-size: 2em;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ margin-top: 2em;
+ place-items: center;
}
.counter-message {
- text-align: center;
+ text-align: center;
}
diff --git a/examples/framework-preact/src/components/Counter.tsx b/examples/framework-preact/src/components/Counter.tsx
index a2ed0277d..61a9f9d5a 100644
--- a/examples/framework-preact/src/components/Counter.tsx
+++ b/examples/framework-preact/src/components/Counter.tsx
@@ -3,18 +3,18 @@ import { useState } from 'preact/hooks';
import './Counter.css';
export default function Counter({ children }) {
- const [count, setCount] = useState(0);
- const add = () => setCount((i) => i + 1);
- const subtract = () => setCount((i) => i - 1);
+ const [count, setCount] = useState(0);
+ const add = () => setCount((i) => i + 1);
+ const subtract = () => setCount((i) => i - 1);
- return (
- <>
- <div class="counter">
- <button onClick={subtract}>-</button>
- <pre>{count}</pre>
- <button onClick={add}>+</button>
- </div>
- <div class="counter-message">{children}</div>
- </>
- );
+ return (
+ <>
+ <div class="counter">
+ <button onClick={subtract}>-</button>
+ <pre>{count}</pre>
+ <button onClick={add}>+</button>
+ </div>
+ <div class="counter-message">{children}</div>
+ </>
+ );
}
diff --git a/examples/framework-preact/src/pages/index.astro b/examples/framework-preact/src/pages/index.astro
index 4811f6e31..77ce1c2c7 100644
--- a/examples/framework-preact/src/pages/index.astro
+++ b/examples/framework-preact/src/pages/index.astro
@@ -1,31 +1,32 @@
---
// Component Imports
-import Counter from '../components/Counter.tsx'
+import Counter from '../components/Counter.tsx';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <style>
- html,
- body {
- font-family: system-ui;
- margin: 0;
- }
- body {
- padding: 2rem;
- }
- </style>
- </head>
- <body>
- <main>
- <Counter client:visible>
- <h1>Hello, Preact!</h1>
- </Counter>
- </main>
- </body>
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <style>
+ html,
+ body {
+ font-family: system-ui;
+ margin: 0;
+ }
+ body {
+ padding: 2rem;
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <Counter client:visible>
+ <h1>Hello, Preact!</h1>
+ </Counter>
+ </main>
+ </body>
</html>
diff --git a/examples/framework-react/astro.config.mjs b/examples/framework-react/astro.config.mjs
index 1d13cb140..443a69232 100644
--- a/examples/framework-react/astro.config.mjs
+++ b/examples/framework-react/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the React renderer to support React JSX components.
- renderers: ['@astrojs/renderer-react'],
+ // Enable the React renderer to support React JSX components.
+ renderers: ['@astrojs/renderer-react'],
});
diff --git a/examples/framework-react/src/components/Counter.css b/examples/framework-react/src/components/Counter.css
index 5d3de4803..fb21044d7 100644
--- a/examples/framework-react/src/components/Counter.css
+++ b/examples/framework-react/src/components/Counter.css
@@ -1,11 +1,11 @@
.counter {
- display: grid;
- font-size: 2em;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- margin-top: 2em;
- place-items: center;
+ display: grid;
+ font-size: 2em;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ margin-top: 2em;
+ place-items: center;
}
.counter-message {
- text-align: center;
+ text-align: center;
}
diff --git a/examples/framework-react/src/components/Counter.jsx b/examples/framework-react/src/components/Counter.jsx
index e222d680f..97ddf34c7 100644
--- a/examples/framework-react/src/components/Counter.jsx
+++ b/examples/framework-react/src/components/Counter.jsx
@@ -2,18 +2,18 @@ import React, { useState } from 'react';
import './Counter.css';
export default function Counter({ children, count: initialCount }) {
- const [count, setCount] = useState(initialCount);
- const add = () => setCount((i) => i + 1);
- const subtract = () => setCount((i) => i - 1);
+ const [count, setCount] = useState(initialCount);
+ const add = () => setCount((i) => i + 1);
+ const subtract = () => setCount((i) => i - 1);
- return (
- <>
- <div className="counter">
- <button onClick={subtract}>-</button>
- <pre>{count}</pre>
- <button onClick={add}>+</button>
- </div>
- <div className="counter-message">{children}</div>
- </>
- );
+ return (
+ <>
+ <div className="counter">
+ <button onClick={subtract}>-</button>
+ <pre>{count}</pre>
+ <button onClick={add}>+</button>
+ </div>
+ <div className="counter-message">{children}</div>
+ </>
+ );
}
diff --git a/examples/framework-react/src/pages/index.astro b/examples/framework-react/src/pages/index.astro
index dd2950782..39a15a797 100644
--- a/examples/framework-react/src/pages/index.astro
+++ b/examples/framework-react/src/pages/index.astro
@@ -1,34 +1,35 @@
---
// Component Imports
-import Counter from '../components/Counter.jsx'
+import Counter from '../components/Counter.jsx';
const someProps = {
- count: 0,
-}
+ count: 0,
+};
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <style>
- html,
- body {
- font-family: system-ui;
- margin: 0;
- }
- body {
- padding: 2rem;
- }
- </style>
- </head>
- <body>
- <main>
- <Counter {...someProps} client:visible>
- <h1>Hello, React!</h1>
- </Counter>
- </main>
- </body>
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <style>
+ html,
+ body {
+ font-family: system-ui;
+ margin: 0;
+ }
+ body {
+ padding: 2rem;
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <Counter {...someProps} client:visible>
+ <h1>Hello, React!</h1>
+ </Counter>
+ </main>
+ </body>
</html>
diff --git a/examples/framework-solid/astro.config.mjs b/examples/framework-solid/astro.config.mjs
index afb71e071..72c154cb3 100644
--- a/examples/framework-solid/astro.config.mjs
+++ b/examples/framework-solid/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Solid renderer to support Solid JSX components.
- renderers: ['@astrojs/renderer-solid'],
+ // Enable the Solid renderer to support Solid JSX components.
+ renderers: ['@astrojs/renderer-solid'],
});
diff --git a/examples/framework-solid/src/components/Counter.css b/examples/framework-solid/src/components/Counter.css
index 3ee017965..cffdbda7b 100644
--- a/examples/framework-solid/src/components/Counter.css
+++ b/examples/framework-solid/src/components/Counter.css
@@ -1,11 +1,11 @@
.counter {
- display: grid;
- font-size: 2em;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- margin-top: 3em;
- place-items: center;
+ display: grid;
+ font-size: 2em;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ margin-top: 3em;
+ place-items: center;
}
.counter-message {
- text-align: center;
+ text-align: center;
}
diff --git a/examples/framework-solid/src/components/Counter.jsx b/examples/framework-solid/src/components/Counter.jsx
index bbd435f09..1255fa399 100644
--- a/examples/framework-solid/src/components/Counter.jsx
+++ b/examples/framework-solid/src/components/Counter.jsx
@@ -2,18 +2,18 @@ import { createSignal } from 'solid-js';
import './Counter.css';
export default function Counter({ children }) {
- const [count, setCount] = createSignal(0);
- const add = () => setCount(count() + 1);
- const subtract = () => setCount(count() - 1);
+ const [count, setCount] = createSignal(0);
+ const add = () => setCount(count() + 1);
+ const subtract = () => setCount(count() - 1);
- return (
- <>
- <div class="counter">
- <button onClick={subtract}>-</button>
- <pre>{count()}</pre>
- <button onClick={add}>+</button>
- </div>
- <div class="counter-message">{children}</div>
- </>
- );
+ return (
+ <>
+ <div class="counter">
+ <button onClick={subtract}>-</button>
+ <pre>{count()}</pre>
+ <button onClick={add}>+</button>
+ </div>
+ <div class="counter-message">{children}</div>
+ </>
+ );
}
diff --git a/examples/framework-solid/src/pages/index.astro b/examples/framework-solid/src/pages/index.astro
index af4669816..feb30a401 100644
--- a/examples/framework-solid/src/pages/index.astro
+++ b/examples/framework-solid/src/pages/index.astro
@@ -1,27 +1,28 @@
---
import Counter from '../components/Counter.jsx';
---
+
<html>
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <style>
- html,
- body {
- font-family: system-ui;
- margin: 0;
- }
- body {
- padding: 2rem;
- }
- </style>
- </head>
- <body>
- <main>
- <Counter client:visible>
- <h1>Hello, Solid!</h1>
- </Counter>
- </main>
- </body>
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <style>
+ html,
+ body {
+ font-family: system-ui;
+ margin: 0;
+ }
+ body {
+ padding: 2rem;
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <Counter client:visible>
+ <h1>Hello, Solid!</h1>
+ </Counter>
+ </main>
+ </body>
</html>
diff --git a/examples/framework-svelte/astro.config.mjs b/examples/framework-svelte/astro.config.mjs
index 3e7d4acdb..a22ba9c95 100644
--- a/examples/framework-svelte/astro.config.mjs
+++ b/examples/framework-svelte/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Svelte renderer to support Svelte components.
- renderers: ['@astrojs/renderer-svelte'],
+ // Enable the Svelte renderer to support Svelte components.
+ renderers: ['@astrojs/renderer-svelte'],
});
diff --git a/examples/framework-svelte/src/pages/index.astro b/examples/framework-svelte/src/pages/index.astro
index e5e7883d0..2cc0fed69 100644
--- a/examples/framework-svelte/src/pages/index.astro
+++ b/examples/framework-svelte/src/pages/index.astro
@@ -1,32 +1,32 @@
---
// Component Imports
-import Counter from '../components/Counter.svelte'
-
+import Counter from '../components/Counter.svelte';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <style>
- html,
- body {
- font-family: system-ui;
- margin: 0;
- }
- body {
- padding: 2rem;
- }
- </style>
- </head>
- <body>
- <main>
- <Counter client:visible>
- <h1>Hello, Svelte!</h1>
- </Counter>
- </main>
- </body>
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <style>
+ html,
+ body {
+ font-family: system-ui;
+ margin: 0;
+ }
+ body {
+ padding: 2rem;
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <Counter client:visible>
+ <h1>Hello, Svelte!</h1>
+ </Counter>
+ </main>
+ </body>
</html>
diff --git a/examples/framework-vue/astro.config.mjs b/examples/framework-vue/astro.config.mjs
index c60760357..950355760 100644
--- a/examples/framework-vue/astro.config.mjs
+++ b/examples/framework-vue/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Vue renderer to support Vue components.
- renderers: ['@astrojs/renderer-vue'],
+ // Enable the Vue renderer to support Vue components.
+ renderers: ['@astrojs/renderer-vue'],
});
diff --git a/examples/framework-vue/src/components/Counter.vue b/examples/framework-vue/src/components/Counter.vue
index 155717ea4..d5d5215f7 100644
--- a/examples/framework-vue/src/components/Counter.vue
+++ b/examples/framework-vue/src/components/Counter.vue
@@ -1,40 +1,40 @@
<template>
- <div class="counter">
- <button @click="subtract()">-</button>
- <pre>{{ count }}</pre>
- <button @click="add()">+</button>
- </div>
- <div class="counter-message">
- <slot />
- </div>
+ <div class="counter">
+ <button @click="subtract()">-</button>
+ <pre>{{ count }}</pre>
+ <button @click="add()">+</button>
+ </div>
+ <div class="counter-message">
+ <slot />
+ </div>
</template>
<script>
import { ref } from 'vue';
export default {
- setup() {
- const count = ref(0);
- const add = () => (count.value = count.value + 1);
- const subtract = () => (count.value = count.value - 1);
+ setup() {
+ const count = ref(0);
+ const add = () => (count.value = count.value + 1);
+ const subtract = () => (count.value = count.value - 1);
- return {
- count,
- add,
- subtract,
- };
- },
+ return {
+ count,
+ add,
+ subtract,
+ };
+ },
};
</script>
<style>
.counter {
- display: grid;
- font-size: 2em;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- margin-top: 2em;
- place-items: center;
+ display: grid;
+ font-size: 2em;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ margin-top: 2em;
+ place-items: center;
}
.counter-message {
- text-align: center;
+ text-align: center;
}
</style>
diff --git a/examples/framework-vue/src/pages/index.astro b/examples/framework-vue/src/pages/index.astro
index 449c3a614..98a667f77 100644
--- a/examples/framework-vue/src/pages/index.astro
+++ b/examples/framework-vue/src/pages/index.astro
@@ -1,32 +1,32 @@
---
// Component Imports
-import Counter from '../components/Counter.vue'
-
+import Counter from '../components/Counter.vue';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <style>
- html,
- body {
- font-family: system-ui;
- margin: 0;
- }
- body {
- padding: 2rem;
- }
- </style>
- </head>
- <body>
- <main>
- <Counter client:visible>
- <h1>Hello, Vue!</h1>
- </Counter>
- </main>
- </body>
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <style>
+ html,
+ body {
+ font-family: system-ui;
+ margin: 0;
+ }
+ body {
+ padding: 2rem;
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <Counter client:visible>
+ <h1>Hello, Vue!</h1>
+ </Counter>
+ </main>
+ </body>
</html>
diff --git a/examples/minimal/astro.config.mjs b/examples/minimal/astro.config.mjs
index 94846abde..d68cea82c 100644
--- a/examples/minimal/astro.config.mjs
+++ b/examples/minimal/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Comment out "renderers: []" to enable Astro's default component support.
- renderers: [],
+ // Comment out "renderers: []" to enable Astro's default component support.
+ renderers: [],
});
diff --git a/examples/minimal/src/pages/index.astro b/examples/minimal/src/pages/index.astro
index 3ce40a78a..e1e7a987b 100644
--- a/examples/minimal/src/pages/index.astro
+++ b/examples/minimal/src/pages/index.astro
@@ -3,20 +3,18 @@
---
<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <meta name="viewport" content="width=device-width" />
+ <title>Welcome to Astro</title>
+ <style>
+ /* scoped CSS styles go here: */
+ /* h1 { ... } */
+ </style>
+ </head>
-<head>
- <meta charset="utf-8" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <meta name="viewport" content="width=device-width" />
- <title>Welcome to Astro</title>
- <style>
- /* scoped CSS styles go here: */
- /* h1 { ... } */
- </style>
-</head>
-
-<body>
- <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
-</body>
-
+ <body>
+ <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
+ </body>
</html>
diff --git a/examples/portfolio-svelte/astro.config.mjs b/examples/portfolio-svelte/astro.config.mjs
index 3e7d4acdb..a22ba9c95 100644
--- a/examples/portfolio-svelte/astro.config.mjs
+++ b/examples/portfolio-svelte/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Svelte renderer to support Svelte components.
- renderers: ['@astrojs/renderer-svelte'],
+ // Enable the Svelte renderer to support Svelte components.
+ renderers: ['@astrojs/renderer-svelte'],
});
diff --git a/examples/portfolio-svelte/src/components/MainHead.astro b/examples/portfolio-svelte/src/components/MainHead.astro
index ae2eb1d1f..0597b178f 100644
--- a/examples/portfolio-svelte/src/components/MainHead.astro
+++ b/examples/portfolio-svelte/src/components/MainHead.astro
@@ -2,12 +2,12 @@
const { title = 'Jeanine White: Personal Site', description = 'The personal site of Jeanine White' } = Astro.props;
---
-<meta charset="UTF-8">
-<meta name="description" property="og:description" content={description}>
-<meta name="viewport" content="width=device-width">
+<meta charset="UTF-8" />
+<meta name="description" property="og:description" content={description} />
+<meta name="viewport" content="width=device-width" />
<title>{title}</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
-<link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/global.scss')}>
-<link rel="preconnect" href="https://fonts.gstatic.com">
-<link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700;900&display=swap" rel="stylesheet">
+<link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/global.scss')} />
+<link rel="preconnect" href="https://fonts.gstatic.com" />
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700;900&display=swap" rel="stylesheet" />
diff --git a/examples/portfolio-svelte/src/layouts/project.astro b/examples/portfolio-svelte/src/layouts/project.astro
index da2a4741b..e60be99ae 100644
--- a/examples/portfolio-svelte/src/layouts/project.astro
+++ b/examples/portfolio-svelte/src/layouts/project.astro
@@ -6,87 +6,86 @@ import Nav from '../components/Nav.svelte';
const { content } = Astro.props;
---
-<html lang={ content.lang || 'en' }>
- <head>
- <MainHead title={content.title} description={content.description} />
- <style lang="scss">
- .hero {
- padding: 8rem;
- display: flex;
- background-color: var(--t-fg);
- background-repeat: no-repeat;
- background-size: cover;
- min-height: 25vw;
- color: white;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- }
- .tag {
- margin-left: 0.5em;
- margin-right: 0.5em;
- text-transform: uppercase;
+<html lang={content.lang || 'en'}>
+ <head>
+ <MainHead title={content.title} description={content.description} />
+ <style lang="scss">
+ .hero {
+ padding: 8rem;
+ display: flex;
+ background-color: var(--t-fg);
+ background-repeat: no-repeat;
+ background-size: cover;
+ min-height: 25vw;
+ color: white;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ }
- &:nth-of-type(1n) {
- color: var(--c-green);
- }
- &:nth-of-type(2n) {
- color: var(--c-orange);
- }
- &:nth-of-type(3n) {
- color: var(--c-blue);
- }
- &:nth-of-type(4n) {
- color: var(--c-pink);
- }
- }
+ .tag {
+ margin-left: 0.5em;
+ margin-right: 0.5em;
+ text-transform: uppercase;
- .title {
- font-size: var(--f-u10);
- font-weight: 900;
- text-transform: uppercase;
- letter-spacing: 0.0625em;
- }
+ &:nth-of-type(1n) {
+ color: var(--c-green);
+ }
+ &:nth-of-type(2n) {
+ color: var(--c-orange);
+ }
+ &:nth-of-type(3n) {
+ color: var(--c-blue);
+ }
+ &:nth-of-type(4n) {
+ color: var(--c-pink);
+ }
+ }
- .leadIn {
- color: var(--t-bg);
- background-color: var(--t-fg);
- }
+ .title {
+ font-size: var(--f-u10);
+ font-weight: 900;
+ text-transform: uppercase;
+ letter-spacing: 0.0625em;
+ }
- .tagline {
- font-weight: 300;
- font-size: var(--f-u3);
- line-height: 1.5;
- }
+ .leadIn {
+ color: var(--t-bg);
+ background-color: var(--t-fg);
+ }
- .content {
- font-size: var(--f-u1);
- line-height: 2.2;
- }
- </style>
- </head>
- <body>
- <Nav />
- <header style={`background-image:url(${content.img})`} class="hero">
- <h1 class="title">{content.title}</h1>
- </header>
- <div class="leadIn">
- <div class="wrapper pt8 pb8 mb8 tac">
- {content.tags.map((t) => (
- <span class="tag">{t}</span>
- ))}
- <h3 class="tagline">{content.description}</h3>
- </div>
- </div>
- <div class="wrapper wrapper__readable">
- <div class="content"><slot></slot></div>
- </div>
- <footer class="tac mt6">
- <a href="/projects">
- <Button>View More</Button>
- </a>
- </footer>
- <Footer />
- </body>
+ .tagline {
+ font-weight: 300;
+ font-size: var(--f-u3);
+ line-height: 1.5;
+ }
+
+ .content {
+ font-size: var(--f-u1);
+ line-height: 2.2;
+ }
+ </style>
+ </head>
+ <body>
+ <Nav />
+ <header style={`background-image:url(${content.img})`} class="hero">
+ <h1 class="title">{content.title}</h1>
+ </header>
+ <div class="leadIn">
+ <div class="wrapper pt8 pb8 mb8 tac">
+ {content.tags.map((t) => <span class="tag">{t}</span>)}
+ <h3 class="tagline">{content.description}</h3>
+ </div>
+ </div>
+ <div class="wrapper wrapper__readable">
+ <div class="content"><slot /></div>
+ </div>
+ <footer class="tac mt6">
+ <a href="/projects">
+ <Button>View More</Button>
+ </a>
+ </footer>
+ <Footer />
+ </body>
</html>
diff --git a/examples/portfolio-svelte/src/pages/404.astro b/examples/portfolio-svelte/src/pages/404.astro
index e886c2514..b379c181b 100644
--- a/examples/portfolio-svelte/src/pages/404.astro
+++ b/examples/portfolio-svelte/src/pages/404.astro
@@ -2,19 +2,18 @@
import MainHead from '../components/MainHead.astro';
import Footer from '../components/Footer.svelte';
import Nav from '../components/Nav.svelte';
-
---
<html lang="en">
- <head>
- <MainHead title="Not Found" />
- </head>
- <body>
- <Nav />
- <div class="wrapper mt4 mb4">
- <h1>Page Not Found</h1>
- <p>Not found</p>
- </div>
- <Footer />
- </body>
+ <head>
+ <MainHead title="Not Found" />
+ </head>
+ <body>
+ <nav />
+ <div class="wrapper mt4 mb4">
+ <h1>Page Not Found</h1>
+ <p>Not found</p>
+ </div>
+ <footer />
+ </body>
</html>
diff --git a/examples/portfolio-svelte/src/pages/about.astro b/examples/portfolio-svelte/src/pages/about.astro
index 0d1375040..6ecb645d9 100644
--- a/examples/portfolio-svelte/src/pages/about.astro
+++ b/examples/portfolio-svelte/src/pages/about.astro
@@ -2,59 +2,58 @@
import MainHead from '../components/MainHead.astro';
import Footer from '../components/Footer.svelte';
import Nav from '../components/Nav.svelte';
-
---
<html lang="en">
- <head>
- <MainHead title="About | Jeanine White" description="About Jeanine White Lorem Ipsum" />
- <style lang="scss">
- .heroImg {
- object-fit: cover;
+ <head>
+ <MainHead title="About | Jeanine White" description="About Jeanine White Lorem Ipsum" />
+ <style lang="scss">
+ .heroImg {
+ object-fit: cover;
- img {
- width: 100%;
- height: 100%;
- }
- }
+ img {
+ width: 100%;
+ height: 100%;
+ }
+ }
- .bio {
- font-size: var(--f-u1);
- line-height: 2;
- }
- </style>
- </head>
- <body>
- <Nav />
- <div class="wrapper">
- <h1>About Jeanine</h1>
- <div class="heroImg">
- <img width="1400" height="350" src="https://images.unsplash.com/photo-1581977012607-4091712d36f9?auto=format&fit=crop&w=1400&h=350&q=75" />
- </div>
- <div class="bio wrapper wrapper__readable mt8">
- <p>
- Cream cheese say cheese stinking bishop. Brie fondue hard cheese bocconcini feta camembert de normandie babybel airedale. Red leicester swiss manchego mascarpone pepper
- jack airedale fromage frais ricotta. Cheese and biscuits cauliflower cheese boursin.
- </p>
- <p>
- Pepper jack cheesy feet cheese slices. Halloumi port-salut queso caerphilly roquefort cheese slices cheesy feet rubber cheese. Cheese slices smelly cheese pecorino
- macaroni cheese feta blue castello roquefort edam. Babybel pepper jack airedale cheddar fromage frais manchego.
- </p>
- <p>
- Cauliflower cheese lancashire macaroni cheese. Cheeseburger babybel cheese on toast airedale cauliflower cheese who moved my cheese roquefort paneer. Stinking bishop
- cheddar taleggio port-salut port-salut stinking bishop cheesy grin babybel. Blue castello feta everyone loves brie.
- </p>
- <p>
- Goat squirty cheese cut the cheese. Cheese and wine cheddar fondue airedale cottage cheese camembert de normandie feta babybel. Rubber cheese melted cheese pecorino
- port-salut fondue gouda cheese on toast cheesy feet. Feta edam everyone loves cheese strings camembert de normandie.
- </p>
- <p>
- Caerphilly monterey jack goat. Squirty cheese cheesy grin hard cheese cheese strings cheese and biscuits croque monsieur smelly cheese danish fontina. Swiss cheese
- triangles everyone loves mascarpone cheese on toast who moved my cheese lancashire cheeseburger. Fromage frais fromage frais cheese and biscuits stinking bishop
- cauliflower cheese.
- </p>
- </div>
- </div>
- <Footer />
-</body>
+ .bio {
+ font-size: var(--f-u1);
+ line-height: 2;
+ }
+ </style>
+ </head>
+ <body>
+ <Nav />
+ <div class="wrapper">
+ <h1>About Jeanine</h1>
+ <div class="heroImg">
+ <img width="1400" height="350" src="https://images.unsplash.com/photo-1581977012607-4091712d36f9?auto=format&fit=crop&w=1400&h=350&q=75" />
+ </div>
+ <div class="bio wrapper wrapper__readable mt8">
+ <p>
+ Cream cheese say cheese stinking bishop. Brie fondue hard cheese bocconcini feta camembert de normandie babybel airedale. Red leicester swiss manchego mascarpone pepper
+ jack airedale fromage frais ricotta. Cheese and biscuits cauliflower cheese boursin.
+ </p>
+ <p>
+ Pepper jack cheesy feet cheese slices. Halloumi port-salut queso caerphilly roquefort cheese slices cheesy feet rubber cheese. Cheese slices smelly cheese pecorino
+ macaroni cheese feta blue castello roquefort edam. Babybel pepper jack airedale cheddar fromage frais manchego.
+ </p>
+ <p>
+ Cauliflower cheese lancashire macaroni cheese. Cheeseburger babybel cheese on toast airedale cauliflower cheese who moved my cheese roquefort paneer. Stinking bishop
+ cheddar taleggio port-salut port-salut stinking bishop cheesy grin babybel. Blue castello feta everyone loves brie.
+ </p>
+ <p>
+ Goat squirty cheese cut the cheese. Cheese and wine cheddar fondue airedale cottage cheese camembert de normandie feta babybel. Rubber cheese melted cheese pecorino
+ port-salut fondue gouda cheese on toast cheesy feet. Feta edam everyone loves cheese strings camembert de normandie.
+ </p>
+ <p>
+ Caerphilly monterey jack goat. Squirty cheese cheesy grin hard cheese cheese strings cheese and biscuits croque monsieur smelly cheese danish fontina. Swiss cheese
+ triangles everyone loves mascarpone cheese on toast who moved my cheese lancashire cheeseburger. Fromage frais fromage frais cheese and biscuits stinking bishop
+ cauliflower cheese.
+ </p>
+ </div>
+ </div>
+ <Footer />
+ </body>
</html>
diff --git a/examples/portfolio-svelte/src/pages/index.astro b/examples/portfolio-svelte/src/pages/index.astro
index 60e8dc411..f2b76224e 100644
--- a/examples/portfolio-svelte/src/pages/index.astro
+++ b/examples/portfolio-svelte/src/pages/index.astro
@@ -13,235 +13,234 @@ const featuredProject = projects[0];
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <MainHead title="Jeanine White: Personal Site" description="Jeanine White: Developer, Speaker, and Writer..." />
- <style lang="scss">
- $w-s: 750px;
-
- .hero {
- position: relative;
- overflow: hidden;
-
- @media (min-width: $w-s) {
- height: 45vw;
- }
- }
-
- .img {
- display: block;
- width: 100%;
- height: auto;
- }
-
- .gradient,
- .gradient2 {
- background-image: url('/assets/mesh-gradient.jpg');
- background-size: cover;
- pointer-events: none;
- mix-blend-mode: screen;
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
- z-index: 2;
- }
-
- .gradient2 {
- mix-blend-mode: multiply;
- background-size: cover;
- }
-
- .overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 10;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- justify-content: center;
- padding-left: 2rem;
-
- @media (min-width: $w-s) {
- padding-left: 4rem;
- }
- }
-
- .title {
- font-weight: 900;
- font-size: var(--f-u8);
- margin-bottom: 0.5rem;
- margin-top: 0;
-
- @media (min-width: $w-s) {
- font-size: var(--f-u12);
- }
- }
-
- .grid {
- display: grid;
- grid-gap: 2rem;
-
- @media (min-width: 1200px) {
- grid-template-columns: 2fr 1fr;
- }
- }
-
- .sectionTitle {
- font-weight: 700;
- font-size: var(--f-u8);
- margin-top: 4rem;
- margin-bottom: 2rem;
- }
-
- .role {
- position: relative;
- display: inline-block;
- font-weight: 900;
- color: var(--t-bg);
- background-color: var(--t-fg);
- padding: 0.25em 0.5em;
- z-index: 2;
-
- @media (min-width: $w-s) {
- font-size: var(--f-u3);
- }
-
- + .role {
- margin-left: 1em;
- }
-
- &:nth-of-type(1) {
- .invert {
- background-color: var(--c-pink);
- }
- }
-
- &:nth-of-type(2) {
- .invert {
- background-color: var(--c-blue);
- }
- }
-
- &:nth-of-type(3) {
- .invert {
- background-color: var(--c-green);
- }
- }
-
- &:hover {
- .invert {
- clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
- }
- }
- }
-
- .invert {
- position: absolute;
- color: var(--t-fg);
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- pointer-events: none;
- clip-path: polygon(0% 100%, 100% 100%, 100% 200%, 0% 200%);
- transition: clip-path cubic-bezier(0.4, 0, 0.5, 1) 150ms;
- }
-
- .desc {
- font-size: var(--f-u2);
- margin-top: 1.5rem;
- margin-bottom: 0;
- }
-
- .subtitle {
- display: block;
- font-weight: 400;
- font-size: var(--f-d6);
- letter-spacing: -0.0625em;
- }
-
- .bio {
- line-height: 2;
- margin-bottom: 2rem;
-
- > span:first-of-type {
- line-height: 1;
- margin-bottom: 0.5em;
- display: block;
- font-weight: 700;
- font-size: var(--f-u4);
- }
- }
- </style>
- </head>
- <body>
- <Nav />
- <header class="hero">
- <img
- width="1600"
- height="1131"
- class="img"
- src="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75"
- srcSet="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 800w,
+ <head>
+ <MainHead title="Jeanine White: Personal Site" description="Jeanine White: Developer, Speaker, and Writer..." />
+ <style lang="scss">
+ $w-s: 750px;
+
+ .hero {
+ position: relative;
+ overflow: hidden;
+
+ @media (min-width: $w-s) {
+ height: 45vw;
+ }
+ }
+
+ .img {
+ display: block;
+ width: 100%;
+ height: auto;
+ }
+
+ .gradient,
+ .gradient2 {
+ background-image: url('/assets/mesh-gradient.jpg');
+ background-size: cover;
+ pointer-events: none;
+ mix-blend-mode: screen;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 2;
+ }
+
+ .gradient2 {
+ mix-blend-mode: multiply;
+ background-size: cover;
+ }
+
+ .overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 10;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+ padding-left: 2rem;
+
+ @media (min-width: $w-s) {
+ padding-left: 4rem;
+ }
+ }
+
+ .title {
+ font-weight: 900;
+ font-size: var(--f-u8);
+ margin-bottom: 0.5rem;
+ margin-top: 0;
+
+ @media (min-width: $w-s) {
+ font-size: var(--f-u12);
+ }
+ }
+
+ .grid {
+ display: grid;
+ grid-gap: 2rem;
+
+ @media (min-width: 1200px) {
+ grid-template-columns: 2fr 1fr;
+ }
+ }
+
+ .sectionTitle {
+ font-weight: 700;
+ font-size: var(--f-u8);
+ margin-top: 4rem;
+ margin-bottom: 2rem;
+ }
+
+ .role {
+ position: relative;
+ display: inline-block;
+ font-weight: 900;
+ color: var(--t-bg);
+ background-color: var(--t-fg);
+ padding: 0.25em 0.5em;
+ z-index: 2;
+
+ @media (min-width: $w-s) {
+ font-size: var(--f-u3);
+ }
+
+ + .role {
+ margin-left: 1em;
+ }
+
+ &:nth-of-type(1) {
+ .invert {
+ background-color: var(--c-pink);
+ }
+ }
+
+ &:nth-of-type(2) {
+ .invert {
+ background-color: var(--c-blue);
+ }
+ }
+
+ &:nth-of-type(3) {
+ .invert {
+ background-color: var(--c-green);
+ }
+ }
+
+ &:hover {
+ .invert {
+ clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
+ }
+ }
+ }
+
+ .invert {
+ position: absolute;
+ color: var(--t-fg);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ pointer-events: none;
+ clip-path: polygon(0% 100%, 100% 100%, 100% 200%, 0% 200%);
+ transition: clip-path cubic-bezier(0.4, 0, 0.5, 1) 150ms;
+ }
+
+ .desc {
+ font-size: var(--f-u2);
+ margin-top: 1.5rem;
+ margin-bottom: 0;
+ }
+
+ .subtitle {
+ display: block;
+ font-weight: 400;
+ font-size: var(--f-d6);
+ letter-spacing: -0.0625em;
+ }
+
+ .bio {
+ line-height: 2;
+ margin-bottom: 2rem;
+
+ > span:first-of-type {
+ line-height: 1;
+ margin-bottom: 0.5em;
+ display: block;
+ font-weight: 700;
+ font-size: var(--f-u4);
+ }
+ }
+ </style>
+ </head>
+ <body>
+ <Nav />
+ <header class="hero">
+ <img
+ width="1600"
+ height="1131"
+ class="img"
+ src="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75"
+ srcSet="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 800w,
https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 1200w,
https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1600&q=75 1600w,
https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=2400&q=75 2400w,"
- sizes="(max-width: 800px) 800px, (max-width: 1200px) 1200px, (max-width: 1600px) 1600px, (max-width: 2400px) 2400px, 1200px"
- />
- <div class="gradient" />
- <div class="gradient2" />
- <div class="overlay">
- <h1 class="title">
- <small class="subtitle">The personal site of </small>Jeanine White
- </h1>
- <div>
- <span class="role">
- 👩‍💻 Developer <span class="invert">👩‍💻 Developer</span>
- </span>&nbsp;
- <span class="role">
- 🎤 Speaker <span class="invert">🎤 Speaker</span>
- </span>&nbsp;
- <span class="role">
- ✏️ Writer <span class="invert">✏️ Writer</span>
- </span>
- </div>
- <p class="desc">Lover of dogs, roadtrips, and poetry.</p>
- </div>
- </header>
- <main class="wrapper mt4 mb4">
- <div class="grid">
- <div class="section">
- <h3 class="sectionTitle">Selected Work</h3>
- <PortfolioPreview project={featuredProject} />
- <div class="tac mt4">
- <a href="/projects">
- <Button>View All</Button>
- </a>
- </div>
- </div>
- <div class="section">
- <h3 class="sectionTitle">About me</h3>
- <p class="bio">
- <span>Hello!</span> I’m Jeanine, and this is my website. It was made using{' '}
- <a href="https://github.com/withastro/astro" target="_blank" rel="nofollow">
- Astro
- </a>
- , a new way to build static sites. This is just an example template for you to modify.
- </p>
- <p>
- <a href="/about">Read more</a>
- </p>
- </div>
- </div>
- </main>
- <Footer />
- </body>
+ sizes="(max-width: 800px) 800px, (max-width: 1200px) 1200px, (max-width: 1600px) 1600px, (max-width: 2400px) 2400px, 1200px"
+ />
+ <div class="gradient"></div>
+ <div class="gradient2"></div>
+ <div class="overlay">
+ <h1 class="title">
+ <small class="subtitle">The personal site of </small>Jeanine White
+ </h1>
+ <div>
+ <span class="role">
+ 👩‍💻 Developer <span class="invert">👩‍💻 Developer</span>
+ </span>&nbsp;
+ <span class="role">
+ 🎤 Speaker <span class="invert">🎤 Speaker</span>
+ </span>&nbsp;
+ <span class="role">
+ ✏️ Writer <span class="invert">✏️ Writer</span>
+ </span>
+ </div>
+ <p class="desc">Lover of dogs, roadtrips, and poetry.</p>
+ </div>
+ </header>
+ <main class="wrapper mt4 mb4">
+ <div class="grid">
+ <div class="section">
+ <h3 class="sectionTitle">Selected Work</h3>
+ <PortfolioPreview project={featuredProject} />
+ <div class="tac mt4">
+ <a href="/projects">
+ <Button>View All</Button>
+ </a>
+ </div>
+ </div>
+ <div class="section">
+ <h3 class="sectionTitle">About me</h3>
+ <p class="bio">
+ <span>Hello!</span> I’m Jeanine, and this is my website. It was made using{' '}
+ <a href="https://github.com/withastro/astro" target="_blank" rel="nofollow"> Astro </a>
+ , a new way to build static sites. This is just an example template for you to modify.
+ </p>
+ <p>
+ <a href="/about">Read more</a>
+ </p>
+ </div>
+ </div>
+ </main>
+ <Footer />
+ </body>
</html>
diff --git a/examples/portfolio-svelte/src/pages/projects.astro b/examples/portfolio-svelte/src/pages/projects.astro
index 0c639a13d..dea004128 100644
--- a/examples/portfolio-svelte/src/pages/projects.astro
+++ b/examples/portfolio-svelte/src/pages/projects.astro
@@ -5,34 +5,32 @@ import Nav from '../components/Nav.svelte';
import PortfolioPreview from '../components/PortfolioPreview.svelte';
interface MarkdownFrontmatter {
- publishDate: number;
+ publishDate: number;
}
const projects = Astro.fetchContent<MarkdownFrontmatter>('./project/**/*.md')
- .filter(({ publishDate }) => !!publishDate)
- .sort((a, b) => new Date(b.publishDate).valueOf() - new Date(a.publishDate).valueOf());
+ .filter(({ publishDate }) => !!publishDate)
+ .sort((a, b) => new Date(b.publishDate).valueOf() - new Date(a.publishDate).valueOf());
---
<html lang="en">
- <head>
- <MainHead title="All Projects | Jeanine White" description="Learn about Jenine White's most recent projects" />
- <style lang="scss">
- .grid {
- display: grid;
- grid-gap: 3rem;
- }
- </style>
- </head>
- <body>
- <Nav />
- <div class="wrapper">
- <h1 class="title mt4 mb4">All Projects</h1>
- <div class="grid">
- {projects.map((project) => (
- <PortfolioPreview project={project} />
- ))}
- </div>
- </div>
- <Footer />
- </body>
+ <head>
+ <MainHead title="All Projects | Jeanine White" description="Learn about Jenine White's most recent projects" />
+ <style lang="scss">
+ .grid {
+ display: grid;
+ grid-gap: 3rem;
+ }
+ </style>
+ </head>
+ <body>
+ <Nav />
+ <div class="wrapper">
+ <h1 class="title mt4 mb4">All Projects</h1>
+ <div class="grid">
+ {projects.map((project) => <PortfolioPreview project={project} />)}
+ </div>
+ </div>
+ <Footer />
+ </body>
</html>
diff --git a/examples/portfolio-svelte/src/styles/global.scss b/examples/portfolio-svelte/src/styles/global.scss
index b5a95caf2..453adadbe 100644
--- a/examples/portfolio-svelte/src/styles/global.scss
+++ b/examples/portfolio-svelte/src/styles/global.scss
@@ -1,83 +1,83 @@
// Tokens
:root {
- // (c)olor
- --c-black: #05091e;
- --c-blue: #46b4ff;
- --c-gray: #90aab7;
- --c-green: #9ef2cb;
- --c-pink: #ffb8d9;
- --c-orange: #ffb7a3;
- --c-yellow: #ffdace;
- --c-white: #fff;
+ // (c)olor
+ --c-black: #05091e;
+ --c-blue: #46b4ff;
+ --c-gray: #90aab7;
+ --c-green: #9ef2cb;
+ --c-pink: #ffb8d9;
+ --c-orange: #ffb7a3;
+ --c-yellow: #ffdace;
+ --c-white: #fff;
- // (f)ont
- --f-u18: 11.390625em;
- --f-u17: 9.950627481136905em;
- --f-u16: 8.692673779389363em;
- --f-u15: 7.59375em;
- --f-u14: 6.63375165409127em;
- --f-u13: 5.795115852926242em;
- --f-u12: 5.0625em;
- --f-u11: 4.422501102727513em;
- --f-u10: 3.8634105686174953em;
- --f-u9: 3.375em;
- --f-u8: 2.9483340684850083em;
- --f-u7: 2.575607045744997em;
- --f-u6: 2.25em;
- --f-u5: 1.9655560456566725em;
- --f-u4: 1.7170713638299977em;
- --f-u3: 1.5em;
- --f-u2: 1.3103706971044482em;
- --f-u1: 1.1447142425533319em;
- --f-d1: 0.8735804647362989em;
- --f-d2: 0.7631428283688879em;
- --f-d3: 0.6666666666666666em;
- --f-d4: 0.5823869764908659em;
- --f-d5: 0.5087618855792586em;
- --f-d6: 0.4444444444444444em;
- --f-d7: 0.3882579843272439em;
- --f-d8: 0.3391745903861724em;
- --f-d9: 0.2962962962962963em;
- --f-d10: 0.2588386562181626em;
- --f-d11: 0.22611639359078162em;
- --f-d12: 0.19753086419753085em;
- --f-d13: 0.17255910414544176em;
- --f-d14: 0.15074426239385438em;
- --f-d15: 0.13168724279835392em;
- --f-d16: 0.11503940276362785em;
- --f-d17: 0.10049617492923625em;
- --f-d18: 0.0877914951989026em;
+ // (f)ont
+ --f-u18: 11.390625em;
+ --f-u17: 9.950627481136905em;
+ --f-u16: 8.692673779389363em;
+ --f-u15: 7.59375em;
+ --f-u14: 6.63375165409127em;
+ --f-u13: 5.795115852926242em;
+ --f-u12: 5.0625em;
+ --f-u11: 4.422501102727513em;
+ --f-u10: 3.8634105686174953em;
+ --f-u9: 3.375em;
+ --f-u8: 2.9483340684850083em;
+ --f-u7: 2.575607045744997em;
+ --f-u6: 2.25em;
+ --f-u5: 1.9655560456566725em;
+ --f-u4: 1.7170713638299977em;
+ --f-u3: 1.5em;
+ --f-u2: 1.3103706971044482em;
+ --f-u1: 1.1447142425533319em;
+ --f-d1: 0.8735804647362989em;
+ --f-d2: 0.7631428283688879em;
+ --f-d3: 0.6666666666666666em;
+ --f-d4: 0.5823869764908659em;
+ --f-d5: 0.5087618855792586em;
+ --f-d6: 0.4444444444444444em;
+ --f-d7: 0.3882579843272439em;
+ --f-d8: 0.3391745903861724em;
+ --f-d9: 0.2962962962962963em;
+ --f-d10: 0.2588386562181626em;
+ --f-d11: 0.22611639359078162em;
+ --f-d12: 0.19753086419753085em;
+ --f-d13: 0.17255910414544176em;
+ --f-d14: 0.15074426239385438em;
+ --f-d15: 0.13168724279835392em;
+ --f-d16: 0.11503940276362785em;
+ --f-d17: 0.10049617492923625em;
+ --f-d18: 0.0877914951989026em;
- // (t)heme
- --t-fg: var(--c-black);
- --t-bg: var(--c-white);
- --t-subdue: var(--c-gray);
- --t-active: var(--c-blue);
+ // (t)heme
+ --t-fg: var(--c-black);
+ --t-bg: var(--c-white);
+ --t-subdue: var(--c-gray);
+ --t-active: var(--c-blue);
}
// Base
body {
- margin: 0;
- color: var(--t-fg);
- font-family: 'Inter', 'system-ui', sans-serif;
+ margin: 0;
+ color: var(--t-fg);
+ font-family: 'Inter', 'system-ui', sans-serif;
}
* {
- box-sizing: content-box;
+ box-sizing: content-box;
}
img {
- max-width: 100%;
- height: auto;
+ max-width: 100%;
+ height: auto;
}
a {
- color: var(--t-active);
+ color: var(--t-active);
}
h1 {
- font-size: var(--f-u8);
+ font-size: var(--f-u8);
}
// Utils
@@ -85,79 +85,79 @@ h1 {
// color
$colors: 'black', 'blue', 'white';
@each $color in $colors {
- // text color
- .tc-#{$color} {
- color: var(--c-#{color});
- }
- // background color
- .bg-#{$color} {
- background-color: var(--c-#{color});
- }
+ // text color
+ .tc-#{$color} {
+ color: var(--c-#{color});
+ }
+ // background color
+ .bg-#{$color} {
+ background-color: var(--c-#{color});
+ }
}
// font size
@for $i from 0 through 18 {
- .f-u#{$i} {
- font-size: var(--f-u#{$i});
- }
- .f-d#{$i} {
- font-size: var(--f-d#{$i});
- }
+ .f-u#{$i} {
+ font-size: var(--f-u#{$i});
+ }
+ .f-d#{$i} {
+ font-size: var(--f-d#{$i});
+ }
}
// margin & padding
@for $i from 0 through 36 {
- .ma#{$i} {
- margin: #{0.5 * $i}rem;
- }
- .mt#{$i} {
- margin-top: #{0.5 * $i}rem;
- }
- .mr#{$i} {
- margin-right: #{0.5 * $i}rem;
- }
- .mb#{$i} {
- margin-bottom: #{0.5 * $i}rem;
- }
- .ml#{$i} {
- margin-left: #{0.5 * $i}rem;
- }
- .pa#{$i} {
- padding: #{0.5 * $i}rem;
- }
- .pt#{$i} {
- padding-top: #{0.5 * $i}rem;
- }
- .pr#{$i} {
- padding-right: #{0.5 * $i}rem;
- }
- .pb#{$i} {
- padding-bottom: #{0.5 * $i}rem;
- }
- .pl#{$i} {
- padding-left: #{0.5 * $i}rem;
- }
+ .ma#{$i} {
+ margin: #{0.5 * $i}rem;
+ }
+ .mt#{$i} {
+ margin-top: #{0.5 * $i}rem;
+ }
+ .mr#{$i} {
+ margin-right: #{0.5 * $i}rem;
+ }
+ .mb#{$i} {
+ margin-bottom: #{0.5 * $i}rem;
+ }
+ .ml#{$i} {
+ margin-left: #{0.5 * $i}rem;
+ }
+ .pa#{$i} {
+ padding: #{0.5 * $i}rem;
+ }
+ .pt#{$i} {
+ padding-top: #{0.5 * $i}rem;
+ }
+ .pr#{$i} {
+ padding-right: #{0.5 * $i}rem;
+ }
+ .pb#{$i} {
+ padding-bottom: #{0.5 * $i}rem;
+ }
+ .pl#{$i} {
+ padding-left: #{0.5 * $i}rem;
+ }
}
// text align
.tac {
- text-align: center;
+ text-align: center;
}
.tal {
- text-align: left;
+ text-align: left;
}
.tar {
- text-align: right;
+ text-align: right;
}
// wrapper
.wrapper {
- max-width: 80em;
- margin-left: auto;
- margin-right: auto;
- padding-left: 2rem;
- padding-right: 2rem;
+ max-width: 80em;
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: 2rem;
+ padding-right: 2rem;
}
.wrapper__readable {
- max-width: 50em;
+ max-width: 50em;
}
diff --git a/examples/portfolio/astro.config.mjs b/examples/portfolio/astro.config.mjs
index 68499b3fa..a1516f292 100644
--- a/examples/portfolio/astro.config.mjs
+++ b/examples/portfolio/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Preact renderer to support Preact JSX components.
- renderers: ['@astrojs/renderer-preact'],
+ // Enable the Preact renderer to support Preact JSX components.
+ renderers: ['@astrojs/renderer-preact'],
});
diff --git a/examples/portfolio/src/components/Button/index.jsx b/examples/portfolio/src/components/Button/index.jsx
index 0e1b6a4c8..d8e04aa71 100644
--- a/examples/portfolio/src/components/Button/index.jsx
+++ b/examples/portfolio/src/components/Button/index.jsx
@@ -2,7 +2,7 @@ import { h } from 'preact';
import Styles from './styles.module.scss';
function Button({ children }) {
- return <span className={Styles.button}>{children}</span>;
+ return <span className={Styles.button}>{children}</span>;
}
export default Button;
diff --git a/examples/portfolio/src/components/Button/styles.module.scss b/examples/portfolio/src/components/Button/styles.module.scss
index 67a343bdf..f0e39df3f 100644
--- a/examples/portfolio/src/components/Button/styles.module.scss
+++ b/examples/portfolio/src/components/Button/styles.module.scss
@@ -1,7 +1,7 @@
.button {
- display: inline-block;
- border: 3px solid currentColor;
- padding: 0.5em 1em;
- font-weight: 700;
- text-transform: uppercase;
+ display: inline-block;
+ border: 3px solid currentColor;
+ padding: 0.5em 1em;
+ font-weight: 700;
+ text-transform: uppercase;
}
diff --git a/examples/portfolio/src/components/Footer/index.jsx b/examples/portfolio/src/components/Footer/index.jsx
index 8e15a2d8e..42953c367 100644
--- a/examples/portfolio/src/components/Footer/index.jsx
+++ b/examples/portfolio/src/components/Footer/index.jsx
@@ -2,11 +2,11 @@ import { h } from 'preact';
import Styles from './styles.module.scss';
function Footer() {
- return (
- <footer className={Styles.footer}>
- &copy; {new Date().getFullYear()} Jeanine White
- <small className={Styles.byline}>🚀 Built by Astro</small>
- </footer>
- );
+ return (
+ <footer className={Styles.footer}>
+ &copy; {new Date().getFullYear()} Jeanine White
+ <small className={Styles.byline}>🚀 Built by Astro</small>
+ </footer>
+ );
}
export default Footer;
diff --git a/examples/portfolio/src/components/Footer/styles.module.scss b/examples/portfolio/src/components/Footer/styles.module.scss
index 0e77ee206..9e76a6a2f 100644
--- a/examples/portfolio/src/components/Footer/styles.module.scss
+++ b/examples/portfolio/src/components/Footer/styles.module.scss
@@ -1,15 +1,15 @@
.footer {
- text-align: center;
- padding-top: 8rem;
- padding-right: 2rem;
- padding-bottom: 4rem;
- padding-left: 2rem;
+ text-align: center;
+ padding-top: 8rem;
+ padding-right: 2rem;
+ padding-bottom: 4rem;
+ padding-left: 2rem;
}
.byline {
- display: block;
- margin-top: 1rem;
- color: var(--t-subdue);
- font-size: var(--f-d2);
- text-transform: uppercase;
+ display: block;
+ margin-top: 1rem;
+ color: var(--t-subdue);
+ font-size: var(--f-d2);
+ text-transform: uppercase;
}
diff --git a/examples/portfolio/src/components/MainHead.astro b/examples/portfolio/src/components/MainHead.astro
index ae2eb1d1f..0597b178f 100644
--- a/examples/portfolio/src/components/MainHead.astro
+++ b/examples/portfolio/src/components/MainHead.astro
@@ -2,12 +2,12 @@
const { title = 'Jeanine White: Personal Site', description = 'The personal site of Jeanine White' } = Astro.props;
---
-<meta charset="UTF-8">
-<meta name="description" property="og:description" content={description}>
-<meta name="viewport" content="width=device-width">
+<meta charset="UTF-8" />
+<meta name="description" property="og:description" content={description} />
+<meta name="viewport" content="width=device-width" />
<title>{title}</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
-<link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/global.scss')}>
-<link rel="preconnect" href="https://fonts.gstatic.com">
-<link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700;900&display=swap" rel="stylesheet">
+<link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/global.scss')} />
+<link rel="preconnect" href="https://fonts.gstatic.com" />
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700;900&display=swap" rel="stylesheet" />
diff --git a/examples/portfolio/src/components/Nav/index.jsx b/examples/portfolio/src/components/Nav/index.jsx
index da0914fd1..d74961d6d 100644
--- a/examples/portfolio/src/components/Nav/index.jsx
+++ b/examples/portfolio/src/components/Nav/index.jsx
@@ -2,34 +2,34 @@ import { h } from 'preact';
import Styles from './styles.module.scss';
function Nav() {
- return (
- <nav className={Styles.nav}>
- <a className={Styles.logolink} href="/">
- <div className={Styles.monogram}>JW</div>
- </a>
- <a className={Styles.link} href="/projects">
- Portfolio
- </a>
- <a className={Styles.link} href="/about">
- About
- </a>
- <a className={Styles.social} href="https://twitter.com/me">
- <svg className={Styles.socialicon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
- <path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z" />
- </svg>
- </a>
- <a className={Styles.social} href="https://github.com/me">
- <svg className={Styles.socialicon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
- <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z" />
- </svg>
- </a>
- <a className={Styles.social} href="https://dev.to/me">
- <svg className={Styles.socialicon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 40" style="enable-background:new 0 0 50 40" xmlSpace="preserve">
- <path d="M15.7 15.5c-.4-.3-.7-.4-1.1-.4h-1.7v10.1h1.7c.4 0 .8-.1 1.1-.4.4-.3.6-.7.6-1.3v-6.7c0-.6-.2-1-.6-1.3z" />
- <path d="M47 0H3C1.3 0 0 1.3 0 3v34c0 1.7 1.3 3 3 3h44c1.7 0 3-1.3 3-3V3c0-1.7-1.3-3-3-3zM19.1 23.5c0 1.3-.4 2.4-1.3 3.2-.8.9-1.9 1.3-3.3 1.3h-4.4V12.3h4.5c1.3 0 2.4.4 3.2 1.3.8.8 1.3 1.9 1.3 3.2v6.7zm9.1-8.4h-5.1v3.6h3.1v2.8h-3.1v3.7h5.1V28h-5.9c-.6 0-1-.2-1.4-.6-.4-.4-.6-.8-.6-1.4V14.2c0-.6.2-1 .6-1.4.4-.4.8-.6 1.4-.6h5.9v2.9zM37.5 26c-.6 1.3-1.3 2-2.2 2-.9 0-1.7-.7-2.2-2l-3.7-13.8h3.1L35.3 23l2.8-10.8h3.1L37.5 26z" />
- </svg>
- </a>
- </nav>
- );
+ return (
+ <nav className={Styles.nav}>
+ <a className={Styles.logolink} href="/">
+ <div className={Styles.monogram}>JW</div>
+ </a>
+ <a className={Styles.link} href="/projects">
+ Portfolio
+ </a>
+ <a className={Styles.link} href="/about">
+ About
+ </a>
+ <a className={Styles.social} href="https://twitter.com/me">
+ <svg className={Styles.socialicon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
+ <path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z" />
+ </svg>
+ </a>
+ <a className={Styles.social} href="https://github.com/me">
+ <svg className={Styles.socialicon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
+ <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z" />
+ </svg>
+ </a>
+ <a className={Styles.social} href="https://dev.to/me">
+ <svg className={Styles.socialicon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 40" style="enable-background:new 0 0 50 40" xmlSpace="preserve">
+ <path d="M15.7 15.5c-.4-.3-.7-.4-1.1-.4h-1.7v10.1h1.7c.4 0 .8-.1 1.1-.4.4-.3.6-.7.6-1.3v-6.7c0-.6-.2-1-.6-1.3z" />
+ <path d="M47 0H3C1.3 0 0 1.3 0 3v34c0 1.7 1.3 3 3 3h44c1.7 0 3-1.3 3-3V3c0-1.7-1.3-3-3-3zM19.1 23.5c0 1.3-.4 2.4-1.3 3.2-.8.9-1.9 1.3-3.3 1.3h-4.4V12.3h4.5c1.3 0 2.4.4 3.2 1.3.8.8 1.3 1.9 1.3 3.2v6.7zm9.1-8.4h-5.1v3.6h3.1v2.8h-3.1v3.7h5.1V28h-5.9c-.6 0-1-.2-1.4-.6-.4-.4-.6-.8-.6-1.4V14.2c0-.6.2-1 .6-1.4.4-.4.8-.6 1.4-.6h5.9v2.9zM37.5 26c-.6 1.3-1.3 2-2.2 2-.9 0-1.7-.7-2.2-2l-3.7-13.8h3.1L35.3 23l2.8-10.8h3.1L37.5 26z" />
+ </svg>
+ </a>
+ </nav>
+ );
}
export default Nav;
diff --git a/examples/portfolio/src/components/Nav/styles.module.scss b/examples/portfolio/src/components/Nav/styles.module.scss
index d39a65a5b..ecbe5585e 100644
--- a/examples/portfolio/src/components/Nav/styles.module.scss
+++ b/examples/portfolio/src/components/Nav/styles.module.scss
@@ -1,65 +1,65 @@
.nav {
- display: flex;
- align-items: center;
- padding-top: 1rem;
- padding-right: 2rem;
- padding-bottom: 1rem;
- padding-left: 2rem;
+ display: flex;
+ align-items: center;
+ padding-top: 1rem;
+ padding-right: 2rem;
+ padding-bottom: 1rem;
+ padding-left: 2rem;
}
.logolink {
- display: block;
- color: var(--t-fg);
- text-decoration: none;
+ display: block;
+ color: var(--t-fg);
+ text-decoration: none;
}
.link {
- color: var(--t-subdue);
- display: block;
- margin-left: 1rem;
- text-decoration: none;
- font-size: var(--f-d1);
- text-transform: uppercase;
- padding-top: 0.75em;
- padding-bottom: 0.75em;
+ color: var(--t-subdue);
+ display: block;
+ margin-left: 1rem;
+ text-decoration: none;
+ font-size: var(--f-d1);
+ text-transform: uppercase;
+ padding-top: 0.75em;
+ padding-bottom: 0.75em;
- &:focus,
- &:hover {
- color: var(--t-active);
- }
+ &:focus,
+ &:hover {
+ color: var(--t-active);
+ }
}
.monogram {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 2em;
- height: 2em;
- margin-right: 0.5rem;
- color: var(--c-black);
- font-weight: 900;
- letter-spacing: -0.125rem;
- border: 3px solid currentColor;
- border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 2em;
+ height: 2em;
+ margin-right: 0.5rem;
+ color: var(--c-black);
+ font-weight: 900;
+ letter-spacing: -0.125rem;
+ border: 3px solid currentColor;
+ border-radius: 50%;
}
.social {
- display: block;
- margin-left: auto;
+ display: block;
+ margin-left: auto;
- + .social {
- margin-left: 0.75rem;
- }
+ + .social {
+ margin-left: 0.75rem;
+ }
}
.socialicon {
- display: block;
- width: 1.25rem;
- height: 1.25rem;
- fill: var(--t-subdue);
- transition: fill linear 150ms;
+ display: block;
+ width: 1.25rem;
+ height: 1.25rem;
+ fill: var(--t-subdue);
+ transition: fill linear 150ms;
- &:hover {
- fill: var(--t-active);
- }
+ &:hover {
+ fill: var(--t-active);
+ }
}
diff --git a/examples/portfolio/src/components/PortfolioPreview/index.jsx b/examples/portfolio/src/components/PortfolioPreview/index.jsx
index 7918008c6..6957e5884 100644
--- a/examples/portfolio/src/components/PortfolioPreview/index.jsx
+++ b/examples/portfolio/src/components/PortfolioPreview/index.jsx
@@ -2,27 +2,27 @@ import { h } from 'preact';
import Styles from './styles.module.scss';
function PortfolioPreview({ project }) {
- return (
- <div className={Styles.card}>
- <div className={Styles.titleCard} style={`background-image:url(${project.img})`}>
- <h1 className={Styles.title}>{project.title}</h1>
- </div>
- <div className="pa3">
- <p className={`${Styles.desc} mt0 mb2`}>{project.description}</p>
- <div className={Styles.tags}>
- Tagged:
- {project.tags.map((t) => (
- <div className={Styles.tag} data-tag={t}>
- {t}
- </div>
- ))}
- </div>
- <a className={Styles.link} href={project.url}>
- <span className={Styles.linkInner}>View</span>
- </a>
- </div>
- </div>
- );
+ return (
+ <div className={Styles.card}>
+ <div className={Styles.titleCard} style={`background-image:url(${project.img})`}>
+ <h1 className={Styles.title}>{project.title}</h1>
+ </div>
+ <div className="pa3">
+ <p className={`${Styles.desc} mt0 mb2`}>{project.description}</p>
+ <div className={Styles.tags}>
+ Tagged:
+ {project.tags.map((t) => (
+ <div className={Styles.tag} data-tag={t}>
+ {t}
+ </div>
+ ))}
+ </div>
+ <a className={Styles.link} href={project.url}>
+ <span className={Styles.linkInner}>View</span>
+ </a>
+ </div>
+ </div>
+ );
}
export default PortfolioPreview;
diff --git a/examples/portfolio/src/components/PortfolioPreview/styles.module.scss b/examples/portfolio/src/components/PortfolioPreview/styles.module.scss
index ce169c2fc..bded61547 100644
--- a/examples/portfolio/src/components/PortfolioPreview/styles.module.scss
+++ b/examples/portfolio/src/components/PortfolioPreview/styles.module.scss
@@ -1,102 +1,102 @@
.card {
- position: relative;
- color: var(--t-bg);
- background: var(--t-fg);
- border: 1px solid #f0f0f0;
+ position: relative;
+ color: var(--t-bg);
+ background: var(--t-fg);
+ border: 1px solid #f0f0f0;
}
.title {
- position: absolute;
- top: 0;
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- margin: 0;
- color: white;
- flex-direction: column;
- font-size: var(--f-u4);
- font-weight: 900;
- text-transform: uppercase;
- letter-spacing: 0.0625em;
+ position: absolute;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin: 0;
+ color: white;
+ flex-direction: column;
+ font-size: var(--f-u4);
+ font-weight: 900;
+ text-transform: uppercase;
+ letter-spacing: 0.0625em;
}
.titleCard {
- position: relative;
- background-size: cover;
- background-position: 50% 100%;
- padding-top: 37.5%;
+ position: relative;
+ background-size: cover;
+ background-position: 50% 100%;
+ padding-top: 37.5%;
}
.desc {
- font-size: var(--f-u1);
- line-height: 1.4;
+ font-size: var(--f-u1);
+ line-height: 1.4;
}
.link {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- color: var(--t-bg);
- font-size: var(--f-u2);
- font-weight: 700;
- background: rgba(0, 0, 0, 0.25);
- opacity: 0;
- text-decoration: none;
- text-transform: uppercase;
- transition: opacity 150ms linear;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ color: var(--t-bg);
+ font-size: var(--f-u2);
+ font-weight: 700;
+ background: rgba(0, 0, 0, 0.25);
+ opacity: 0;
+ text-decoration: none;
+ text-transform: uppercase;
+ transition: opacity 150ms linear;
- &:focus,
- &:hover {
- opacity: 1;
+ &:focus,
+ &:hover {
+ opacity: 1;
- .linkInner {
- transform: translateY(0);
- border-color: rgba(255, 255, 255, 0.625);
- }
- }
+ .linkInner {
+ transform: translateY(0);
+ border-color: rgba(255, 255, 255, 0.625);
+ }
+ }
}
.linkInner {
- padding: 0.375em 1em;
- border: 2px solid rgba(255, 255, 255, 0);
- transition: transform 300ms cubic-bezier(0, 0.4, 0.6, 1), border-color 1s linear;
- transform: translateY(25%);
+ padding: 0.375em 1em;
+ border: 2px solid rgba(255, 255, 255, 0);
+ transition: transform 300ms cubic-bezier(0, 0.4, 0.6, 1), border-color 1s linear;
+ transform: translateY(25%);
}
.nav {
- display: flex;
- justify-content: flex-end;
+ display: flex;
+ justify-content: flex-end;
}
.tags {
- font-size: var(--f-d2);
- text-transform: uppercase;
+ font-size: var(--f-d2);
+ text-transform: uppercase;
}
.tag {
- display: inline-block;
- color: var(--c-yellow);
- text-transform: uppercase;
- margin-left: 0.5em;
+ display: inline-block;
+ color: var(--c-yellow);
+ text-transform: uppercase;
+ margin-left: 0.5em;
- &:nth-of-type(1n) {
- color: var(--c-green);
- }
- &:nth-of-type(2n) {
- color: var(--c-orange);
- }
- &:nth-of-type(3n) {
- color: var(--c-blue);
- }
- &:nth-of-type(4n) {
- color: var(--c-pink);
- }
+ &:nth-of-type(1n) {
+ color: var(--c-green);
+ }
+ &:nth-of-type(2n) {
+ color: var(--c-orange);
+ }
+ &:nth-of-type(3n) {
+ color: var(--c-blue);
+ }
+ &:nth-of-type(4n) {
+ color: var(--c-pink);
+ }
}
diff --git a/examples/portfolio/src/layouts/project.astro b/examples/portfolio/src/layouts/project.astro
index 6ad043a4e..aba14a5ac 100644
--- a/examples/portfolio/src/layouts/project.astro
+++ b/examples/portfolio/src/layouts/project.astro
@@ -6,87 +6,86 @@ import Nav from '../components/Nav/index.jsx';
const { content } = Astro.props;
---
-<html lang={ content.lang || 'en' }>
- <head>
- <MainHead title={content.title} description={content.description} />
- <style lang="scss">
- .hero {
- padding: 8rem;
- display: flex;
- background-color: var(--t-fg);
- background-repeat: no-repeat;
- background-size: cover;
- min-height: 25vw;
- color: white;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- }
- .tag {
- margin-left: 0.5em;
- margin-right: 0.5em;
- text-transform: uppercase;
+<html lang={content.lang || 'en'}>
+ <head>
+ <MainHead title={content.title} description={content.description} />
+ <style lang="scss">
+ .hero {
+ padding: 8rem;
+ display: flex;
+ background-color: var(--t-fg);
+ background-repeat: no-repeat;
+ background-size: cover;
+ min-height: 25vw;
+ color: white;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ }
- &:nth-of-type(1n) {
- color: var(--c-green);
- }
- &:nth-of-type(2n) {
- color: var(--c-orange);
- }
- &:nth-of-type(3n) {
- color: var(--c-blue);
- }
- &:nth-of-type(4n) {
- color: var(--c-pink);
- }
- }
+ .tag {
+ margin-left: 0.5em;
+ margin-right: 0.5em;
+ text-transform: uppercase;
- .title {
- font-size: var(--f-u10);
- font-weight: 900;
- text-transform: uppercase;
- letter-spacing: 0.0625em;
- }
+ &:nth-of-type(1n) {
+ color: var(--c-green);
+ }
+ &:nth-of-type(2n) {
+ color: var(--c-orange);
+ }
+ &:nth-of-type(3n) {
+ color: var(--c-blue);
+ }
+ &:nth-of-type(4n) {
+ color: var(--c-pink);
+ }
+ }
- .leadIn {
- color: var(--t-bg);
- background-color: var(--t-fg);
- }
+ .title {
+ font-size: var(--f-u10);
+ font-weight: 900;
+ text-transform: uppercase;
+ letter-spacing: 0.0625em;
+ }
- .tagline {
- font-weight: 300;
- font-size: var(--f-u3);
- line-height: 1.5;
- }
+ .leadIn {
+ color: var(--t-bg);
+ background-color: var(--t-fg);
+ }
- .content {
- font-size: var(--f-u1);
- line-height: 2.2;
- }
- </style>
- </head>
- <body>
- <Nav />
- <header style={`background-image:url(${content.img})`} class="hero">
- <h1 class="title">{content.title}</h1>
- </header>
- <div class="leadIn">
- <div class="wrapper pt8 pb8 mb8 tac">
- {content.tags.map((t) => (
- <span class="tag">{t}</span>
- ))}
- <h3 class="tagline">{content.description}</h3>
- </div>
- </div>
- <div class="wrapper wrapper__readable">
- <div class="content"><slot></slot></div>
- </div>
- <footer class="tac mt6">
- <a href="/projects">
- <Button>View More</Button>
- </a>
- </footer>
- <Footer />
- </body>
+ .tagline {
+ font-weight: 300;
+ font-size: var(--f-u3);
+ line-height: 1.5;
+ }
+
+ .content {
+ font-size: var(--f-u1);
+ line-height: 2.2;
+ }
+ </style>
+ </head>
+ <body>
+ <Nav />
+ <header style={`background-image:url(${content.img})`} class="hero">
+ <h1 class="title">{content.title}</h1>
+ </header>
+ <div class="leadIn">
+ <div class="wrapper pt8 pb8 mb8 tac">
+ {content.tags.map((t) => <span class="tag">{t}</span>)}
+ <h3 class="tagline">{content.description}</h3>
+ </div>
+ </div>
+ <div class="wrapper wrapper__readable">
+ <div class="content"><slot /></div>
+ </div>
+ <footer class="tac mt6">
+ <a href="/projects">
+ <Button>View More</Button>
+ </a>
+ </footer>
+ <Footer />
+ </body>
</html>
diff --git a/examples/portfolio/src/pages/404.astro b/examples/portfolio/src/pages/404.astro
index 48ec8d310..39f5994ae 100644
--- a/examples/portfolio/src/pages/404.astro
+++ b/examples/portfolio/src/pages/404.astro
@@ -2,19 +2,18 @@
import MainHead from '../components/MainHead.astro';
import Footer from '../components/Footer/index.jsx';
import Nav from '../components/Nav/index.jsx';
-
---
<html lang="en">
- <head>
- <MainHead title="Not Found" />
- </head>
- <body>
- <Nav />
- <div class="wrapper mt4 mb4">
- <h1>Page Not Found</h1>
- <p>Not found</p>
- </div>
- <Footer />
- </body>
+ <head>
+ <MainHead title="Not Found" />
+ </head>
+ <body>
+ <nav />
+ <div class="wrapper mt4 mb4">
+ <h1>Page Not Found</h1>
+ <p>Not found</p>
+ </div>
+ <footer />
+ </body>
</html>
diff --git a/examples/portfolio/src/pages/about.astro b/examples/portfolio/src/pages/about.astro
index 36f4fe942..fad3d6ad3 100644
--- a/examples/portfolio/src/pages/about.astro
+++ b/examples/portfolio/src/pages/about.astro
@@ -2,60 +2,59 @@
import MainHead from '../components/MainHead.astro';
import Footer from '../components/Footer/index.jsx';
import Nav from '../components/Nav/index.jsx';
-
---
<html lang="en">
- <head>
- <MainHead title="About | Jeanine White" description="About Jeanine White Lorem Ipsum" />
- <style lang="scss">
- .heroImg {
- max-height: 24rem;
- object-fit: cover;
- overflow: hidden;
+ <head>
+ <MainHead title="About | Jeanine White" description="About Jeanine White Lorem Ipsum" />
+ <style lang="scss">
+ .heroImg {
+ max-height: 24rem;
+ object-fit: cover;
+ overflow: hidden;
- img {
- width: 100%;
- }
- }
+ img {
+ width: 100%;
+ }
+ }
- .bio {
- font-size: var(--f-u1);
- line-height: 2;
- }
- </style>
- </head>
- <body>
- <Nav />
- <div class="wrapper">
- <h1>About Jeanine</h1>
- <div class="heroImg">
- <img width="1400" height="350" src="https://images.unsplash.com/photo-1581977012607-4091712d36f9?auto=format&fit=crop&w=1400&h=350&q=75" />
- </div>
- <div class="bio wrapper wrapper__readable mt8">
- <p>
- Cream cheese say cheese stinking bishop. Brie fondue hard cheese bocconcini feta camembert de normandie babybel airedale. Red leicester swiss manchego mascarpone pepper
- jack airedale fromage frais ricotta. Cheese and biscuits cauliflower cheese boursin.
- </p>
- <p>
- Pepper jack cheesy feet cheese slices. Halloumi port-salut queso caerphilly roquefort cheese slices cheesy feet rubber cheese. Cheese slices smelly cheese pecorino
- macaroni cheese feta blue castello roquefort edam. Babybel pepper jack airedale cheddar fromage frais manchego.
- </p>
- <p>
- Cauliflower cheese lancashire macaroni cheese. Cheeseburger babybel cheese on toast airedale cauliflower cheese who moved my cheese roquefort paneer. Stinking bishop
- cheddar taleggio port-salut port-salut stinking bishop cheesy grin babybel. Blue castello feta everyone loves brie.
- </p>
- <p>
- Goat squirty cheese cut the cheese. Cheese and wine cheddar fondue airedale cottage cheese camembert de normandie feta babybel. Rubber cheese melted cheese pecorino
- port-salut fondue gouda cheese on toast cheesy feet. Feta edam everyone loves cheese strings camembert de normandie.
- </p>
- <p>
- Caerphilly monterey jack goat. Squirty cheese cheesy grin hard cheese cheese strings cheese and biscuits croque monsieur smelly cheese danish fontina. Swiss cheese
- triangles everyone loves mascarpone cheese on toast who moved my cheese lancashire cheeseburger. Fromage frais fromage frais cheese and biscuits stinking bishop
- cauliflower cheese.
- </p>
- </div>
- </div>
- <Footer />
-</body>
+ .bio {
+ font-size: var(--f-u1);
+ line-height: 2;
+ }
+ </style>
+ </head>
+ <body>
+ <Nav />
+ <div class="wrapper">
+ <h1>About Jeanine</h1>
+ <div class="heroImg">
+ <img width="1400" height="350" src="https://images.unsplash.com/photo-1581977012607-4091712d36f9?auto=format&fit=crop&w=1400&h=350&q=75" />
+ </div>
+ <div class="bio wrapper wrapper__readable mt8">
+ <p>
+ Cream cheese say cheese stinking bishop. Brie fondue hard cheese bocconcini feta camembert de normandie babybel airedale. Red leicester swiss manchego mascarpone pepper
+ jack airedale fromage frais ricotta. Cheese and biscuits cauliflower cheese boursin.
+ </p>
+ <p>
+ Pepper jack cheesy feet cheese slices. Halloumi port-salut queso caerphilly roquefort cheese slices cheesy feet rubber cheese. Cheese slices smelly cheese pecorino
+ macaroni cheese feta blue castello roquefort edam. Babybel pepper jack airedale cheddar fromage frais manchego.
+ </p>
+ <p>
+ Cauliflower cheese lancashire macaroni cheese. Cheeseburger babybel cheese on toast airedale cauliflower cheese who moved my cheese roquefort paneer. Stinking bishop
+ cheddar taleggio port-salut port-salut stinking bishop cheesy grin babybel. Blue castello feta everyone loves brie.
+ </p>
+ <p>
+ Goat squirty cheese cut the cheese. Cheese and wine cheddar fondue airedale cottage cheese camembert de normandie feta babybel. Rubber cheese melted cheese pecorino
+ port-salut fondue gouda cheese on toast cheesy feet. Feta edam everyone loves cheese strings camembert de normandie.
+ </p>
+ <p>
+ Caerphilly monterey jack goat. Squirty cheese cheesy grin hard cheese cheese strings cheese and biscuits croque monsieur smelly cheese danish fontina. Swiss cheese
+ triangles everyone loves mascarpone cheese on toast who moved my cheese lancashire cheeseburger. Fromage frais fromage frais cheese and biscuits stinking bishop
+ cauliflower cheese.
+ </p>
+ </div>
+ </div>
+ <Footer />
+ </body>
</html>
diff --git a/examples/portfolio/src/pages/index.astro b/examples/portfolio/src/pages/index.astro
index 9388834ab..ce11119b5 100644
--- a/examples/portfolio/src/pages/index.astro
+++ b/examples/portfolio/src/pages/index.astro
@@ -7,231 +7,231 @@ import Footer from '../components/Footer/index.jsx';
import PortfolioPreview from '../components/PortfolioPreview/index.jsx';
// Data Fetching: List all Markdown posts in the repo.
-const projects = Astro.fetchContent("./project/**/*.md");
+const projects = Astro.fetchContent('./project/**/*.md');
const featuredProject = projects[0];
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <MainHead title="Jeanine White: Personal Site" description="Jeanine White: Developer, Speaker, and Writer..." />
- <style lang="scss">
- $w-s: 750px;
-
- .hero {
- position: relative;
- overflow: hidden;
-
- @media (min-width: $w-s) {
- height: 45vw;
- }
- }
-
- .img {
- display: block;
- width: 100%;
- height: auto;
- }
-
- .gradient,
- .gradient2 {
- background-image: url('/assets/mesh-gradient.jpg');
- background-size: cover;
- pointer-events: none;
- mix-blend-mode: screen;
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
- z-index: 2;
- }
-
- .gradient2 {
- mix-blend-mode: multiply;
- background-size: cover;
- }
-
- .overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 10;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- justify-content: center;
- padding-left: 2rem;
-
- @media (min-width: $w-s) {
- padding-left: 4rem;
- }
- }
-
- .title {
- font-weight: 900;
- font-size: var(--f-u8);
- margin-bottom: 0.5rem;
- margin-top: 0;
-
- @media (min-width: $w-s) {
- font-size: var(--f-u12);
- }
- }
-
- .grid {
- display: grid;
- grid-gap: 2rem;
-
- @media (min-width: 1200px) {
- grid-template-columns: 2fr 1fr;
- }
- }
-
- .sectionTitle {
- font-weight: 700;
- font-size: var(--f-u8);
- margin-top: 4rem;
- margin-bottom: 2rem;
- }
-
- .role {
- position: relative;
- display: inline-block;
- font-weight: 900;
- color: var(--t-bg);
- background-color: var(--t-fg);
- padding: 0.25em 0.5em;
- z-index: 2;
-
- @media (min-width: $w-s) {
- font-size: var(--f-u3);
- }
-
- + .role {
- margin-left: 1em;
- }
-
- &:nth-of-type(1) {
- .invert {
- background-color: var(--c-pink);
- }
- }
-
- &:nth-of-type(2) {
- .invert {
- background-color: var(--c-blue);
- }
- }
-
- &:nth-of-type(3) {
- .invert {
- background-color: var(--c-green);
- }
- }
-
- &:hover {
- .invert {
- clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
- }
- }
- }
-
- .invert {
- position: absolute;
- color: var(--t-fg);
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- pointer-events: none;
- clip-path: polygon(0% 100%, 100% 100%, 100% 200%, 0% 200%);
- transition: clip-path cubic-bezier(0.4, 0, 0.5, 1) 150ms;
- }
-
- .desc {
- font-size: var(--f-u2);
- margin-top: 1.5rem;
- margin-bottom: 0;
- }
-
- .subtitle {
- display: block;
- font-weight: 400;
- font-size: var(--f-d6);
- letter-spacing: -0.0625em;
- }
-
- .bio {
- line-height: 2;
- margin-bottom: 2rem;
-
- > span:first-of-type {
- line-height: 1;
- margin-bottom: 0.5em;
- display: block;
- font-weight: 700;
- font-size: var(--f-u4);
- }
- }
- </style>
- </head>
- <body>
- <Nav />
- <header class="hero">
- <img
- width="1600"
- height="1131"
- class="img"
- src="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75"
- srcSet="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 800w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 1200w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1600&q=75 1600w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=2400&q=75 2400w"
- sizes="(max-width: 800px) 800px, (max-width: 1200px) 1200px, (max-width: 1600px) 1600px, (max-width: 2400px) 2400px, 1200px"
- >
- <div class="gradient"></div>
- <div class="gradient2"></div>
- <div class="overlay">
- <h1 class="title">
- <small class="subtitle">The personal site of </small>Jeanine White
- </h1>
- <div>
- <span class="role">👩‍💻 Developer <span class="invert">👩‍💻 Developer</span></span>&nbsp;
- <span class="role">🎤 Speaker <span class="invert">🎤 Speaker</span></span>&nbsp;
- <span class="role">✏️ Writer <span class="invert">✏️ Writer</span></span>
- </div>
- <p class="desc">Lover of dogs, roadtrips, and poetry.</p>
- </div>
- </header>
- <main class="wrapper mt4 mb4">
- <div class="grid">
- <div class="section">
- <h3 class="sectionTitle">Selected Work</h3>
- <PortfolioPreview project={featuredProject} />
- <div class="tac mt4">
- <a href="/projects">
- <Button>View All</Button>
- </a>
- </div>
- </div>
- <div class="section">
- <h3 class="sectionTitle">About me</h3>
- <p class="bio">
- <span>Hello!</span> I’m Jeanine, and this is my website. It was made using{' '}
- <a href="https://github.com/withastro/astro" target="_blank" rel="nofollow">
- Astro
- </a>, a new way to build static sites. This is just an example template for you to modify.
- </p>
- <p>
- <a href="/about">Read more</a>
- </p>
- </div>
- </div>
- </main>
- <Footer />
- </body>
+ <head>
+ <MainHead title="Jeanine White: Personal Site" description="Jeanine White: Developer, Speaker, and Writer..." />
+ <style lang="scss">
+ $w-s: 750px;
+
+ .hero {
+ position: relative;
+ overflow: hidden;
+
+ @media (min-width: $w-s) {
+ height: 45vw;
+ }
+ }
+
+ .img {
+ display: block;
+ width: 100%;
+ height: auto;
+ }
+
+ .gradient,
+ .gradient2 {
+ background-image: url('/assets/mesh-gradient.jpg');
+ background-size: cover;
+ pointer-events: none;
+ mix-blend-mode: screen;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 2;
+ }
+
+ .gradient2 {
+ mix-blend-mode: multiply;
+ background-size: cover;
+ }
+
+ .overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 10;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+ padding-left: 2rem;
+
+ @media (min-width: $w-s) {
+ padding-left: 4rem;
+ }
+ }
+
+ .title {
+ font-weight: 900;
+ font-size: var(--f-u8);
+ margin-bottom: 0.5rem;
+ margin-top: 0;
+
+ @media (min-width: $w-s) {
+ font-size: var(--f-u12);
+ }
+ }
+
+ .grid {
+ display: grid;
+ grid-gap: 2rem;
+
+ @media (min-width: 1200px) {
+ grid-template-columns: 2fr 1fr;
+ }
+ }
+
+ .sectionTitle {
+ font-weight: 700;
+ font-size: var(--f-u8);
+ margin-top: 4rem;
+ margin-bottom: 2rem;
+ }
+
+ .role {
+ position: relative;
+ display: inline-block;
+ font-weight: 900;
+ color: var(--t-bg);
+ background-color: var(--t-fg);
+ padding: 0.25em 0.5em;
+ z-index: 2;
+
+ @media (min-width: $w-s) {
+ font-size: var(--f-u3);
+ }
+
+ + .role {
+ margin-left: 1em;
+ }
+
+ &:nth-of-type(1) {
+ .invert {
+ background-color: var(--c-pink);
+ }
+ }
+
+ &:nth-of-type(2) {
+ .invert {
+ background-color: var(--c-blue);
+ }
+ }
+
+ &:nth-of-type(3) {
+ .invert {
+ background-color: var(--c-green);
+ }
+ }
+
+ &:hover {
+ .invert {
+ clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
+ }
+ }
+ }
+
+ .invert {
+ position: absolute;
+ color: var(--t-fg);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ pointer-events: none;
+ clip-path: polygon(0% 100%, 100% 100%, 100% 200%, 0% 200%);
+ transition: clip-path cubic-bezier(0.4, 0, 0.5, 1) 150ms;
+ }
+
+ .desc {
+ font-size: var(--f-u2);
+ margin-top: 1.5rem;
+ margin-bottom: 0;
+ }
+
+ .subtitle {
+ display: block;
+ font-weight: 400;
+ font-size: var(--f-d6);
+ letter-spacing: -0.0625em;
+ }
+
+ .bio {
+ line-height: 2;
+ margin-bottom: 2rem;
+
+ > span:first-of-type {
+ line-height: 1;
+ margin-bottom: 0.5em;
+ display: block;
+ font-weight: 700;
+ font-size: var(--f-u4);
+ }
+ }
+ </style>
+ </head>
+ <body>
+ <Nav />
+ <header class="hero">
+ <img
+ width="1600"
+ height="1131"
+ class="img"
+ src="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75"
+ srcSet="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 800w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 1200w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1600&q=75 1600w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=2400&q=75 2400w"
+ sizes="(max-width: 800px) 800px, (max-width: 1200px) 1200px, (max-width: 1600px) 1600px, (max-width: 2400px) 2400px, 1200px"
+ />
+ <div class="gradient"></div>
+ <div class="gradient2"></div>
+ <div class="overlay">
+ <h1 class="title">
+ <small class="subtitle">The personal site of </small>Jeanine White
+ </h1>
+ <div>
+ <span class="role">👩‍💻 Developer <span class="invert">👩‍💻 Developer</span></span>&nbsp;
+ <span class="role">🎤 Speaker <span class="invert">🎤 Speaker</span></span>&nbsp;
+ <span class="role">✏️ Writer <span class="invert">✏️ Writer</span></span>
+ </div>
+ <p class="desc">Lover of dogs, roadtrips, and poetry.</p>
+ </div>
+ </header>
+ <main class="wrapper mt4 mb4">
+ <div class="grid">
+ <div class="section">
+ <h3 class="sectionTitle">Selected Work</h3>
+ <PortfolioPreview project={featuredProject} />
+ <div class="tac mt4">
+ <a href="/projects">
+ <Button>View All</Button>
+ </a>
+ </div>
+ </div>
+ <div class="section">
+ <h3 class="sectionTitle">About me</h3>
+ <p class="bio">
+ <span>Hello!</span> I’m Jeanine, and this is my website. It was made using{' '}
+ <a href="https://github.com/withastro/astro" target="_blank" rel="nofollow"> Astro </a>, a new way to build static sites. This is just an example template for you to
+ modify.
+ </p>
+ <p>
+ <a href="/about">Read more</a>
+ </p>
+ </div>
+ </div>
+ </main>
+ <Footer />
+ </body>
</html>
diff --git a/examples/portfolio/src/pages/projects.astro b/examples/portfolio/src/pages/projects.astro
index 44047117a..991c254bc 100644
--- a/examples/portfolio/src/pages/projects.astro
+++ b/examples/portfolio/src/pages/projects.astro
@@ -5,34 +5,32 @@ import Nav from '../components/Nav/index.jsx';
import PortfolioPreview from '../components/PortfolioPreview/index.jsx';
interface MarkdownFrontmatter {
- publishDate: number;
+ publishDate: number;
}
const projects = Astro.fetchContent<MarkdownFrontmatter>('./project/**/*.md')
- .filter(({ publishDate }) => !!publishDate)
- .sort((a, b) => new Date(b.publishDate).valueOf() - new Date(a.publishDate).valueOf());
+ .filter(({ publishDate }) => !!publishDate)
+ .sort((a, b) => new Date(b.publishDate).valueOf() - new Date(a.publishDate).valueOf());
---
<html lang="en">
- <head>
- <MainHead title="All Projects | Jeanine White" description="Learn about Jenine White's most recent projects" />
- <style lang="scss">
- .grid {
- display: grid;
- grid-gap: 3rem;
- }
- </style>
- </head>
- <body>
- <Nav />
- <div class="wrapper">
- <h1 class="title mt4 mb4">All Projects</h1>
- <div class="grid">
- {projects.map((project) => (
- <PortfolioPreview project={project} />
- ))}
- </div>
- </div>
- <Footer />
- </body>
+ <head>
+ <MainHead title="All Projects | Jeanine White" description="Learn about Jenine White's most recent projects" />
+ <style lang="scss">
+ .grid {
+ display: grid;
+ grid-gap: 3rem;
+ }
+ </style>
+ </head>
+ <body>
+ <Nav />
+ <div class="wrapper">
+ <h1 class="title mt4 mb4">All Projects</h1>
+ <div class="grid">
+ {projects.map((project) => <PortfolioPreview project={project} />)}
+ </div>
+ </div>
+ <Footer />
+ </body>
</html>
diff --git a/examples/portfolio/src/styles/global.scss b/examples/portfolio/src/styles/global.scss
index b5a95caf2..453adadbe 100644
--- a/examples/portfolio/src/styles/global.scss
+++ b/examples/portfolio/src/styles/global.scss
@@ -1,83 +1,83 @@
// Tokens
:root {
- // (c)olor
- --c-black: #05091e;
- --c-blue: #46b4ff;
- --c-gray: #90aab7;
- --c-green: #9ef2cb;
- --c-pink: #ffb8d9;
- --c-orange: #ffb7a3;
- --c-yellow: #ffdace;
- --c-white: #fff;
+ // (c)olor
+ --c-black: #05091e;
+ --c-blue: #46b4ff;
+ --c-gray: #90aab7;
+ --c-green: #9ef2cb;
+ --c-pink: #ffb8d9;
+ --c-orange: #ffb7a3;
+ --c-yellow: #ffdace;
+ --c-white: #fff;
- // (f)ont
- --f-u18: 11.390625em;
- --f-u17: 9.950627481136905em;
- --f-u16: 8.692673779389363em;
- --f-u15: 7.59375em;
- --f-u14: 6.63375165409127em;
- --f-u13: 5.795115852926242em;
- --f-u12: 5.0625em;
- --f-u11: 4.422501102727513em;
- --f-u10: 3.8634105686174953em;
- --f-u9: 3.375em;
- --f-u8: 2.9483340684850083em;
- --f-u7: 2.575607045744997em;
- --f-u6: 2.25em;
- --f-u5: 1.9655560456566725em;
- --f-u4: 1.7170713638299977em;
- --f-u3: 1.5em;
- --f-u2: 1.3103706971044482em;
- --f-u1: 1.1447142425533319em;
- --f-d1: 0.8735804647362989em;
- --f-d2: 0.7631428283688879em;
- --f-d3: 0.6666666666666666em;
- --f-d4: 0.5823869764908659em;
- --f-d5: 0.5087618855792586em;
- --f-d6: 0.4444444444444444em;
- --f-d7: 0.3882579843272439em;
- --f-d8: 0.3391745903861724em;
- --f-d9: 0.2962962962962963em;
- --f-d10: 0.2588386562181626em;
- --f-d11: 0.22611639359078162em;
- --f-d12: 0.19753086419753085em;
- --f-d13: 0.17255910414544176em;
- --f-d14: 0.15074426239385438em;
- --f-d15: 0.13168724279835392em;
- --f-d16: 0.11503940276362785em;
- --f-d17: 0.10049617492923625em;
- --f-d18: 0.0877914951989026em;
+ // (f)ont
+ --f-u18: 11.390625em;
+ --f-u17: 9.950627481136905em;
+ --f-u16: 8.692673779389363em;
+ --f-u15: 7.59375em;
+ --f-u14: 6.63375165409127em;
+ --f-u13: 5.795115852926242em;
+ --f-u12: 5.0625em;
+ --f-u11: 4.422501102727513em;
+ --f-u10: 3.8634105686174953em;
+ --f-u9: 3.375em;
+ --f-u8: 2.9483340684850083em;
+ --f-u7: 2.575607045744997em;
+ --f-u6: 2.25em;
+ --f-u5: 1.9655560456566725em;
+ --f-u4: 1.7170713638299977em;
+ --f-u3: 1.5em;
+ --f-u2: 1.3103706971044482em;
+ --f-u1: 1.1447142425533319em;
+ --f-d1: 0.8735804647362989em;
+ --f-d2: 0.7631428283688879em;
+ --f-d3: 0.6666666666666666em;
+ --f-d4: 0.5823869764908659em;
+ --f-d5: 0.5087618855792586em;
+ --f-d6: 0.4444444444444444em;
+ --f-d7: 0.3882579843272439em;
+ --f-d8: 0.3391745903861724em;
+ --f-d9: 0.2962962962962963em;
+ --f-d10: 0.2588386562181626em;
+ --f-d11: 0.22611639359078162em;
+ --f-d12: 0.19753086419753085em;
+ --f-d13: 0.17255910414544176em;
+ --f-d14: 0.15074426239385438em;
+ --f-d15: 0.13168724279835392em;
+ --f-d16: 0.11503940276362785em;
+ --f-d17: 0.10049617492923625em;
+ --f-d18: 0.0877914951989026em;
- // (t)heme
- --t-fg: var(--c-black);
- --t-bg: var(--c-white);
- --t-subdue: var(--c-gray);
- --t-active: var(--c-blue);
+ // (t)heme
+ --t-fg: var(--c-black);
+ --t-bg: var(--c-white);
+ --t-subdue: var(--c-gray);
+ --t-active: var(--c-blue);
}
// Base
body {
- margin: 0;
- color: var(--t-fg);
- font-family: 'Inter', 'system-ui', sans-serif;
+ margin: 0;
+ color: var(--t-fg);
+ font-family: 'Inter', 'system-ui', sans-serif;
}
* {
- box-sizing: content-box;
+ box-sizing: content-box;
}
img {
- max-width: 100%;
- height: auto;
+ max-width: 100%;
+ height: auto;
}
a {
- color: var(--t-active);
+ color: var(--t-active);
}
h1 {
- font-size: var(--f-u8);
+ font-size: var(--f-u8);
}
// Utils
@@ -85,79 +85,79 @@ h1 {
// color
$colors: 'black', 'blue', 'white';
@each $color in $colors {
- // text color
- .tc-#{$color} {
- color: var(--c-#{color});
- }
- // background color
- .bg-#{$color} {
- background-color: var(--c-#{color});
- }
+ // text color
+ .tc-#{$color} {
+ color: var(--c-#{color});
+ }
+ // background color
+ .bg-#{$color} {
+ background-color: var(--c-#{color});
+ }
}
// font size
@for $i from 0 through 18 {
- .f-u#{$i} {
- font-size: var(--f-u#{$i});
- }
- .f-d#{$i} {
- font-size: var(--f-d#{$i});
- }
+ .f-u#{$i} {
+ font-size: var(--f-u#{$i});
+ }
+ .f-d#{$i} {
+ font-size: var(--f-d#{$i});
+ }
}
// margin & padding
@for $i from 0 through 36 {
- .ma#{$i} {
- margin: #{0.5 * $i}rem;
- }
- .mt#{$i} {
- margin-top: #{0.5 * $i}rem;
- }
- .mr#{$i} {
- margin-right: #{0.5 * $i}rem;
- }
- .mb#{$i} {
- margin-bottom: #{0.5 * $i}rem;
- }
- .ml#{$i} {
- margin-left: #{0.5 * $i}rem;
- }
- .pa#{$i} {
- padding: #{0.5 * $i}rem;
- }
- .pt#{$i} {
- padding-top: #{0.5 * $i}rem;
- }
- .pr#{$i} {
- padding-right: #{0.5 * $i}rem;
- }
- .pb#{$i} {
- padding-bottom: #{0.5 * $i}rem;
- }
- .pl#{$i} {
- padding-left: #{0.5 * $i}rem;
- }
+ .ma#{$i} {
+ margin: #{0.5 * $i}rem;
+ }
+ .mt#{$i} {
+ margin-top: #{0.5 * $i}rem;
+ }
+ .mr#{$i} {
+ margin-right: #{0.5 * $i}rem;
+ }
+ .mb#{$i} {
+ margin-bottom: #{0.5 * $i}rem;
+ }
+ .ml#{$i} {
+ margin-left: #{0.5 * $i}rem;
+ }
+ .pa#{$i} {
+ padding: #{0.5 * $i}rem;
+ }
+ .pt#{$i} {
+ padding-top: #{0.5 * $i}rem;
+ }
+ .pr#{$i} {
+ padding-right: #{0.5 * $i}rem;
+ }
+ .pb#{$i} {
+ padding-bottom: #{0.5 * $i}rem;
+ }
+ .pl#{$i} {
+ padding-left: #{0.5 * $i}rem;
+ }
}
// text align
.tac {
- text-align: center;
+ text-align: center;
}
.tal {
- text-align: left;
+ text-align: left;
}
.tar {
- text-align: right;
+ text-align: right;
}
// wrapper
.wrapper {
- max-width: 80em;
- margin-left: auto;
- margin-right: auto;
- padding-left: 2rem;
- padding-right: 2rem;
+ max-width: 80em;
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: 2rem;
+ padding-right: 2rem;
}
.wrapper__readable {
- max-width: 50em;
+ max-width: 50em;
}
diff --git a/examples/starter/astro.config.mjs b/examples/starter/astro.config.mjs
index c92bf4e72..7c835f30a 100644
--- a/examples/starter/astro.config.mjs
+++ b/examples/starter/astro.config.mjs
@@ -8,8 +8,8 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ (
- {
- // Set "renderers" to "[]" to disable all default, builtin component support.
- // renderers: [],
- }
+ {
+ // Set "renderers" to "[]" to disable all default, builtin component support.
+ // renderers: [],
+ }
);
diff --git a/examples/starter/src/components/Tour.astro b/examples/starter/src/components/Tour.astro
index 16919a74a..0dd21ed75 100644
--- a/examples/starter/src/components/Tour.astro
+++ b/examples/starter/src/components/Tour.astro
@@ -1,85 +1,85 @@
---
import { Markdown } from 'astro/components';
---
-<article>
- <div class="banner">
- <p><strong>🧑‍🚀 Seasoned astronaut?</strong> Delete this file. Have fun!</p>
- </div>
- <section>
- <Markdown>
- ## 🚀 Project Structure
+<article>
+ <div class="banner">
+ <p><strong>🧑‍🚀 Seasoned astronaut?</strong> Delete this file. Have fun!</p>
+ </div>
- Inside of your Astro project, you'll see the following folders and files:
+ <section>
+ <Markdown>
+ ## 🚀 Project Structure
- ```
- /
- ├── public/
- │ ├── robots.txt
- │ └── favicon.ico
- ├── src/
- │ ├── components/
- │ │ └── Tour.astro
- │ └── pages/
- │ └── index.astro
- └── package.json
- ```
+ Inside of your Astro project, you'll see the following folders and files:
- Astro looks for `.astro` or `.md` files in the `src/pages/` directory.
- Each page is exposed as a route based on its file name.
+ ```
+ /
+ ├── public/
+ │ ├── robots.txt
+ │ └── favicon.ico
+ ├── src/
+ │ ├── components/
+ │ │ └── Tour.astro
+ │ └── pages/
+ │ └── index.astro
+ └── package.json
+ ```
- There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
+ Astro looks for `.astro` or `.md` files in the `src/pages/` directory.
+ Each page is exposed as a route based on its file name.
- Any static assets, like images, can be placed in the `public/` directory.
- </Markdown>
- </section>
+ There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
- <section>
- <h2>👀 Want to learn more?</h2>
- <p>Feel free to check <a href="https://github.com/withastro/astro">our documentation</a> or jump into our <a href="https://astro.build/chat">Discord server</a>.</p>
- </section>
+ Any static assets, like images, can be placed in the `public/` directory.
+ </Markdown>
+ </section>
+ <section>
+ <h2>👀 Want to learn more?</h2>
+ <p>Feel free to check <a href="https://github.com/withastro/astro">our documentation</a> or jump into our <a href="https://astro.build/chat">Discord server</a>.</p>
+ </section>
</article>
<style>
- article {
- padding-top: 2em;
- line-height: 1.5;
- }
- section {
- margin-top: 2em;
- display: flex;
- flex-direction: column;
- gap: 1em;
- max-width: 70ch;
- }
+ article {
+ padding-top: 2em;
+ line-height: 1.5;
+ }
+ section {
+ margin-top: 2em;
+ display: flex;
+ flex-direction: column;
+ gap: 1em;
+ max-width: 70ch;
+ }
- .banner {
- text-align: center;
- font-size: 1.2rem;
- background: var(--color-light);
- padding: 1em 1.5em;
- padding-left: 0.75em;
- border-radius: 4px;
- }
+ .banner {
+ text-align: center;
+ font-size: 1.2rem;
+ background: var(--color-light);
+ padding: 1em 1.5em;
+ padding-left: 0.75em;
+ border-radius: 4px;
+ }
- pre,
- code {
- font-family: var(--font-mono);
- background: var(--color-light);
- border-radius: 4px;
- }
+ pre,
+ code {
+ font-family: var(--font-mono);
+ background: var(--color-light);
+ border-radius: 4px;
+ }
- pre {
- padding: 1em 1.5em;
- }
+ pre {
+ padding: 1em 1.5em;
+ }
- .tree {
- line-height: 1.2;
- }
+ .tree {
+ line-height: 1.2;
+ }
- code:not(.tree) {
- padding: 0.125em;
- margin: 0 -0.125em;
- }
+ code:not(.tree) {
+ padding: 0.125em;
+ margin: 0 -0.125em;
+ }
</style>
diff --git a/examples/starter/src/pages/index.astro b/examples/starter/src/pages/index.astro
index fea66e595..85c0f6e0d 100644
--- a/examples/starter/src/pages/index.astro
+++ b/examples/starter/src/pages/index.astro
@@ -15,44 +15,46 @@ let title = 'My Astro Site';
---
<html lang="en">
<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width">
- <title>{title}</title>
-
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
-
- <link rel="stylesheet" href={Astro.resolve('../styles/global.css')}>
- <link rel="stylesheet" href={Astro.resolve('../styles/home.css')}>
-
- <style>
- header {
- display: flex;
- flex-direction: column;
- gap: 1em;
- max-width: min(100%, 68ch);
- }
- </style>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width">
+ <title>{title}</title>
+
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <link rel="stylesheet" href={Astro.resolve('../styles/global.css')}>
+ <link rel="stylesheet" href={Astro.resolve('../styles/home.css')}>
+
+ <style>
+ header {
+ display: flex;
+ flex-direction: column;
+ gap: 1em;
+ max-width: min(100%, 68ch);
+ }
+ </style>
</head>
<body>
- <main>
- <header>
- <div>
- <img width="60" height="80" src="/assets/logo.svg" alt="Astro logo">
- <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
- </div>
- </header>
-
- <Tour />
-
- <!--
- - You can also use imported framework components directly in your markup!
- -
- - Note: by default, these components are NOT interactive on the client.
- - The `:visible` directive tells Astro to make it interactive.
- -
- - See https://docs.astro.build/core-concepts/component-hydration/
- -->
- <!-- ASTRO:COMPONENT_MARKUP -->
- </main>
+ <main>
+ <header>
+ <div>
+ <img width="60" height="80" src="/assets/logo.svg" alt="Astro logo">
+ <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
+ </div>
+ </header>
+
+ <Tour />
+
+ <!--
+
+ You can also use imported framework components directly in your markup!
+
+ Note: by default, these components are NOT interactive on the client.
+ The `:visible` directive tells Astro to make it interactive.
+
+ See https://docs.astro.build/core-concepts/component-hydration/
+
+ -->
+
+ <!-- ASTRO:COMPONENT_MARKUP -->
+ </main>
</body>
</html>
diff --git a/examples/starter/src/styles/global.css b/examples/starter/src/styles/global.css
index e1a3a6bd1..a9f830eda 100644
--- a/examples/starter/src/styles/global.css
+++ b/examples/starter/src/styles/global.css
@@ -1,28 +1,28 @@
* {
- box-sizing: border-box;
- margin: 0;
+ box-sizing: border-box;
+ margin: 0;
}
:root {
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
- font-size: 1rem;
- --user-font-scale: 1rem - 16px;
- font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
+ font-size: 1rem;
+ --user-font-scale: 1rem - 16px;
+ font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
}
body {
- padding: 4rem 2rem;
- width: 100%;
- min-height: 100vh;
- display: grid;
- justify-content: center;
- background: #f9fafb;
- color: #111827;
+ padding: 4rem 2rem;
+ width: 100%;
+ min-height: 100vh;
+ display: grid;
+ justify-content: center;
+ background: #f9fafb;
+ color: #111827;
}
@media (prefers-color-scheme: dark) {
- body {
- background: #111827;
- color: #fff;
- }
+ body {
+ background: #111827;
+ color: #fff;
+ }
}
diff --git a/examples/starter/src/styles/home.css b/examples/starter/src/styles/home.css
index 39de76a44..b3cbd02d0 100644
--- a/examples/starter/src/styles/home.css
+++ b/examples/starter/src/styles/home.css
@@ -1,53 +1,53 @@
:root {
- --font-mono: Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono',
- 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
- --color-light: #f3f4f6;
+ --font-mono: Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono',
+ 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
+ --color-light: #f3f4f6;
}
@media (prefers-color-scheme: dark) {
- :root {
- --color-light: #1f2937;
- }
+ :root {
+ --color-light: #1f2937;
+ }
}
a {
- color: inherit;
+ color: inherit;
}
header > div {
- font-size: clamp(2rem, -0.4742rem + 6.1856vw, 2.75rem);
+ font-size: clamp(2rem, -0.4742rem + 6.1856vw, 2.75rem);
}
header > div {
- display: flex;
- flex-direction: column;
- align-items: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
}
header h1 {
- font-size: 1em;
- font-weight: 500;
+ font-size: 1em;
+ font-weight: 500;
}
header img {
- width: 2em;
- height: 2.667em;
+ width: 2em;
+ height: 2.667em;
}
h2 {
- font-weight: 500;
- font-size: clamp(1.5rem, 1rem + 1.25vw, 2rem);
+ font-weight: 500;
+ font-size: clamp(1.5rem, 1rem + 1.25vw, 2rem);
}
.counter {
- display: grid;
- grid-auto-flow: column;
- gap: 1em;
- font-size: 2rem;
- justify-content: center;
- padding: 2rem 1rem;
+ display: grid;
+ grid-auto-flow: column;
+ gap: 1em;
+ font-size: 2rem;
+ justify-content: center;
+ padding: 2rem 1rem;
}
.counter > pre {
- text-align: center;
- min-width: 3ch;
+ text-align: center;
+ min-width: 3ch;
}
diff --git a/examples/subpath/astro.config.mjs b/examples/subpath/astro.config.mjs
index 7a7a3ed2d..2b41e4ce7 100644
--- a/examples/subpath/astro.config.mjs
+++ b/examples/subpath/astro.config.mjs
@@ -8,9 +8,9 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Comment out "renderers: []" to enable Astro's default component support.
- buildOptions: {
- site: 'http://example.com/blog',
- },
- renderers: ['@astrojs/renderer-react'],
+ // Comment out "renderers: []" to enable Astro's default component support.
+ buildOptions: {
+ site: 'http://example.com/blog',
+ },
+ renderers: ['@astrojs/renderer-react'],
});
diff --git a/examples/subpath/src/components/Time.jsx b/examples/subpath/src/components/Time.jsx
index 8b5837c85..9a89669cb 100644
--- a/examples/subpath/src/components/Time.jsx
+++ b/examples/subpath/src/components/Time.jsx
@@ -1,7 +1,7 @@
import React from 'react';
export default function () {
- const date = new Date();
- const format = new Intl.DateTimeFormat('en-US');
- return <time>{format.format(date)}</time>;
+ const date = new Date();
+ const format = new Intl.DateTimeFormat('en-US');
+ return <time>{format.format(date)}</time>;
}
diff --git a/examples/subpath/src/pages/index.astro b/examples/subpath/src/pages/index.astro
index b08f2e06f..44cb83358 100644
--- a/examples/subpath/src/pages/index.astro
+++ b/examples/subpath/src/pages/index.astro
@@ -1,32 +1,30 @@
---
-import Time from '../components/Time.jsx'
+import Time from '../components/Time.jsx';
---
<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <link rel="stylesheet" href={Astro.resolve('../styles/main.scss')} />
+ <meta name="viewport" content="width=device-width" />
+ <title>Welcome to Astro</title>
+ </head>
-<head>
- <meta charset="utf-8" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <link rel="stylesheet" href={Astro.resolve('../styles/main.scss')}>
- <meta name="viewport" content="width=device-width" />
- <title>Welcome to Astro</title>
-</head>
+ <body>
+ <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
-<body>
- <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
+ <main id="app">
+ Today: <Time client:idle />
+ </main>
- <main id="app">
- Today: <Time client:idle />
- </main>
-
- <article>
- <h2>Animals</h2>
-
- <figure>
- <img src="/blog/images/penguin.png" />
- <figcaption>A penguin</figcaption>
- </figure>
- </article>
-</body>
+ <article>
+ <h2>Animals</h2>
+ <figure>
+ <img src="/blog/images/penguin.png" />
+ <figcaption>A penguin</figcaption>
+ </figure>
+ </article>
+ </body>
</html>
diff --git a/examples/subpath/src/styles/main.scss b/examples/subpath/src/styles/main.scss
index a54a15f01..2118ed2a0 100644
--- a/examples/subpath/src/styles/main.scss
+++ b/examples/subpath/src/styles/main.scss
@@ -1,5 +1,5 @@
body {
- #app {
- color: tomato;
- }
+ #app {
+ color: tomato;
+ }
}
diff --git a/examples/with-markdown-plugins/astro.config.mjs b/examples/with-markdown-plugins/astro.config.mjs
index d68232fae..eb7011933 100644
--- a/examples/with-markdown-plugins/astro.config.mjs
+++ b/examples/with-markdown-plugins/astro.config.mjs
@@ -9,19 +9,19 @@ import astroRemark from '@astrojs/markdown-remark';
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable Custom Markdown options, plugins, etc.
- markdownOptions: {
- render: [
- astroRemark,
- {
- remarkPlugins: ['remark-code-titles'],
- rehypePlugins: [
- ['rehype-autolink-headings', { behavior: 'prepend' }],
- ['rehype-toc', { headings: ['h2', 'h3'] }],
- ['rehype-add-classes', { 'h1,h2,h3': 'title' }],
- 'rehype-slug',
- ],
- },
- ],
- },
+ // Enable Custom Markdown options, plugins, etc.
+ markdownOptions: {
+ render: [
+ astroRemark,
+ {
+ remarkPlugins: ['remark-code-titles'],
+ rehypePlugins: [
+ ['rehype-autolink-headings', { behavior: 'prepend' }],
+ ['rehype-toc', { headings: ['h2', 'h3'] }],
+ ['rehype-add-classes', { 'h1,h2,h3': 'title' }],
+ 'rehype-slug',
+ ],
+ },
+ ],
+ },
});
diff --git a/examples/with-markdown-plugins/src/layouts/main.astro b/examples/with-markdown-plugins/src/layouts/main.astro
index 467f16951..745f70d86 100644
--- a/examples/with-markdown-plugins/src/layouts/main.astro
+++ b/examples/with-markdown-plugins/src/layouts/main.astro
@@ -2,34 +2,34 @@
const { content } = Astro.props;
---
-<html lang={ content.lang || 'en' }>
- <head>
- <meta charset="utf-8" />
+<html lang={content.lang || 'en'}>
+ <head>
+ <meta charset="utf-8" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <title>{content.title}</title>
- <link rel="stylesheet" href={Astro.resolve('../styles/global.css')}>
- <style>
- .nav {
- border-bottom: 1px solid #ccc;
- margin-bottom: 40px;
- padding-bottom: 20px;
- }
- .nav > * + * {
- margin-left: 10px;
- }
- </style>
- </head>
- <body>
- <main class="content">
- <header>
- <nav class="nav">
- <a href="/">Home</a>
- <a href="/about">About</a>
- </nav>
- </header>
- <slot />
- </main>
- </body>
+ <title>{content.title}</title>
+ <link rel="stylesheet" href={Astro.resolve('../styles/global.css')} />
+ <style>
+ .nav {
+ border-bottom: 1px solid #ccc;
+ margin-bottom: 40px;
+ padding-bottom: 20px;
+ }
+ .nav > * + * {
+ margin-left: 10px;
+ }
+ </style>
+ </head>
+ <body>
+ <main class="content">
+ <header>
+ <nav class="nav">
+ <a href="/">Home</a>
+ <a href="/about">About</a>
+ </nav>
+ </header>
+ <slot />
+ </main>
+ </body>
</html>
diff --git a/examples/with-markdown-plugins/src/pages/about.astro b/examples/with-markdown-plugins/src/pages/about.astro
index c313f98a9..1115fcb8c 100644
--- a/examples/with-markdown-plugins/src/pages/about.astro
+++ b/examples/with-markdown-plugins/src/pages/about.astro
@@ -1,20 +1,20 @@
---
import { Markdown } from 'astro/components';
-import MainLayout from '../layouts/main.astro'
+import MainLayout from '../layouts/main.astro';
---
-<MainLayout content={{ title: "About" }}>
- <Markdown>
- # About
+<MainLayout content={{ title: 'About' }}>
+ <Markdown>
+ # About
- Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
+ Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
- Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
+ Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
- ## My story
+ ## My story
- Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
-
- Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
- </Markdown>
+ Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
+
+ Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
+ </Markdown>
</MainLayout>
diff --git a/examples/with-markdown-plugins/src/styles/global.css b/examples/with-markdown-plugins/src/styles/global.css
index ea585d269..ced30f0a2 100644
--- a/examples/with-markdown-plugins/src/styles/global.css
+++ b/examples/with-markdown-plugins/src/styles/global.css
@@ -1,53 +1,53 @@
body {
- font-family: system-ui;
+ font-family: system-ui;
}
.content {
- max-width: 640px;
- margin: 40px auto;
- padding: 0 20px;
+ max-width: 640px;
+ margin: 40px auto;
+ padding: 0 20px;
}
.title {
- position: relative;
+ position: relative;
}
.title a {
- position: absolute;
- display: block;
- height: 100%;
- width: 100%;
- color: inherit;
+ position: absolute;
+ display: block;
+ height: 100%;
+ width: 100%;
+ color: inherit;
}
.title a:before {
- position: absolute;
- right: 100%;
- display: block;
- content: '#';
- margin-right: 0.2em;
- visibility: hidden;
- opacity: 0.5;
+ position: absolute;
+ right: 100%;
+ display: block;
+ content: '#';
+ margin-right: 0.2em;
+ visibility: hidden;
+ opacity: 0.5;
}
.title:hover a:before {
- visibility: visible;
+ visibility: visible;
}
.remark-code-title,
pre[class^='language-'] {
- padding: 10px;
- margin: 0;
+ padding: 10px;
+ margin: 0;
}
.remark-code-title {
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
- border-radius: 4px 4px 0 0;
- background: rgba(0, 0, 0, 0.08);
- font-family: monospace;
- font-weight: bold;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+ border-radius: 4px 4px 0 0;
+ background: rgba(0, 0, 0, 0.08);
+ font-family: monospace;
+ font-weight: bold;
}
pre[class^='language-'] {
- background: rgba(0, 0, 0, 0.05);
- border-radius: 0 0 4px 4px;
+ background: rgba(0, 0, 0, 0.05);
+ border-radius: 0 0 4px 4px;
}
diff --git a/examples/with-markdown/astro.config.mjs b/examples/with-markdown/astro.config.mjs
index c92bf4e72..7c835f30a 100644
--- a/examples/with-markdown/astro.config.mjs
+++ b/examples/with-markdown/astro.config.mjs
@@ -8,8 +8,8 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ (
- {
- // Set "renderers" to "[]" to disable all default, builtin component support.
- // renderers: [],
- }
+ {
+ // Set "renderers" to "[]" to disable all default, builtin component support.
+ // renderers: [],
+ }
);
diff --git a/examples/with-markdown/src/components/PreactCounter.tsx b/examples/with-markdown/src/components/PreactCounter.tsx
index e3761643f..e67afb4fe 100644
--- a/examples/with-markdown/src/components/PreactCounter.tsx
+++ b/examples/with-markdown/src/components/PreactCounter.tsx
@@ -3,18 +3,18 @@ import { useState } from 'preact/hooks';
/** a counter written in Preact */
export default function PreactCounter({ children }) {
- const [count, setCount] = useState(0);
- const add = () => setCount((i) => i + 1);
- const subtract = () => setCount((i) => i - 1);
+ const [count, setCount] = useState(0);
+ const add = () => setCount((i) => i + 1);
+ const subtract = () => setCount((i) => i - 1);
- return (
- <>
- <div className="counter">
- <button onClick={subtract}>-</button>
- <pre>{count}</pre>
- <button onClick={add}>+</button>
- </div>
- <div className="children">{children}</div>
- </>
- );
+ return (
+ <>
+ <div className="counter">
+ <button onClick={subtract}>-</button>
+ <pre>{count}</pre>
+ <button onClick={add}>+</button>
+ </div>
+ <div className="children">{children}</div>
+ </>
+ );
}
diff --git a/examples/with-markdown/src/components/ReactCounter.jsx b/examples/with-markdown/src/components/ReactCounter.jsx
index 92871a8d8..e322f7050 100644
--- a/examples/with-markdown/src/components/ReactCounter.jsx
+++ b/examples/with-markdown/src/components/ReactCounter.jsx
@@ -2,18 +2,18 @@ import React, { useState } from 'react';
/** a counter written in React */
export default function ReactCounter({ children }) {
- const [count, setCount] = useState(0);
- const add = () => setCount((i) => i + 1);
- const subtract = () => setCount((i) => i - 1);
+ const [count, setCount] = useState(0);
+ const add = () => setCount((i) => i + 1);
+ const subtract = () => setCount((i) => i - 1);
- return (
- <>
- <div className="counter">
- <button onClick={subtract}>-</button>
- <pre>{count}</pre>
- <button onClick={add}>+</button>
- </div>
- <div className="children">{children}</div>
- </>
- );
+ return (
+ <>
+ <div className="counter">
+ <button onClick={subtract}>-</button>
+ <pre>{count}</pre>
+ <button onClick={add}>+</button>
+ </div>
+ <div className="children">{children}</div>
+ </>
+ );
}
diff --git a/examples/with-markdown/src/components/VueCounter.vue b/examples/with-markdown/src/components/VueCounter.vue
index 2703b8b9b..2f25066dc 100644
--- a/examples/with-markdown/src/components/VueCounter.vue
+++ b/examples/with-markdown/src/components/VueCounter.vue
@@ -1,27 +1,27 @@
<template>
- <div class="counter">
- <button @click="subtract()">-</button>
- <pre>{{ count }}</pre>
- <button @click="add()">+</button>
- </div>
- <div class="children">
- <slot />
- </div>
+ <div class="counter">
+ <button @click="subtract()">-</button>
+ <pre>{{ count }}</pre>
+ <button @click="add()">+</button>
+ </div>
+ <div class="children">
+ <slot />
+ </div>
</template>
<script>
import { ref } from 'vue';
export default {
- setup() {
- const count = ref(0);
- const add = () => (count.value = count.value + 1);
- const subtract = () => (count.value = count.value - 1);
+ setup() {
+ const count = ref(0);
+ const add = () => (count.value = count.value + 1);
+ const subtract = () => (count.value = count.value - 1);
- return {
- count,
- add,
- subtract,
- };
- },
+ return {
+ count,
+ add,
+ subtract,
+ };
+ },
};
</script>
diff --git a/examples/with-markdown/src/layouts/main.astro b/examples/with-markdown/src/layouts/main.astro
index ae7f04565..b3fdb345e 100644
--- a/examples/with-markdown/src/layouts/main.astro
+++ b/examples/with-markdown/src/layouts/main.astro
@@ -2,16 +2,16 @@
const { content } = Astro.props;
---
-<html lang={ content.lang || 'en' }>
- <head>
- <meta charset="utf-8">
+<html lang={content.lang || 'en'}>
+ <head>
+ <meta charset="utf-8" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <title>{content.title}</title>
- <link rel="stylesheet" href={Astro.resolve('../styles/global.css')}>
- </head>
- <body>
- <slot/>
- </body>
+ <title>{content.title}</title>
+ <link rel="stylesheet" href={Astro.resolve('../styles/global.css')} />
+ </head>
+ <body>
+ <slot />
+ </body>
</html>
diff --git a/examples/with-markdown/src/pages/external.astro b/examples/with-markdown/src/pages/external.astro
index 1149666b2..82cac13d4 100644
--- a/examples/with-markdown/src/pages/external.astro
+++ b/examples/with-markdown/src/pages/external.astro
@@ -7,11 +7,13 @@ const content = `Markdown *content* to render`;
---
<Layout content={{ title }}>
- <main>
- <div>
- <Markdown {content} />
- <p>Some other stuff</p>
- </div>
- <p>Lastly...</p>
- </main>
+ <main>
+ <div>
+ <Markdown {content}>
+
+ </Markdown>
+ <p>Some other stuff</p>
+ </div>
+ <p>Lastly...</p>
+ </main>
</Layout>
diff --git a/examples/with-markdown/src/pages/index.astro b/examples/with-markdown/src/pages/index.astro
index 7249f07cd..e70e12f35 100644
--- a/examples/with-markdown/src/pages/index.astro
+++ b/examples/with-markdown/src/pages/index.astro
@@ -18,48 +18,48 @@ const items = ['A', 'B', 'C'];
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
-<Layout content={{ title }}>
- <Markdown>
- # Introducing {title}
- **Astro Markdown** brings native Markdown support to HTML!
+<Layout content={{ title }}>
+ <Markdown>
+ # Introducing {title}
- > It's inspired by [`MDX`](https://mdxjs.com/) and powered by [`remark`](https://github.com/remarkjs/remark).
+ **Astro Markdown** brings native Markdown support to HTML!
- The best part? It comes with all the Astro features you expect.
+ > It's inspired by [`MDX`](https://mdxjs.com/) and powered by [`remark`](https://github.com/remarkjs/remark).
- [Other example](./other)
+ The best part? It comes with all the Astro features you expect.
- ## Embed framework components
+ [Other example](./other)
- <ReactCounter client:visible />
- <PreactCounter client:visible />
- <VueCounter client:visible />
- <SvelteCounter client:visible />
+ ## Embed framework components
- ## Use Expressions
+ <ReactCounter client:visible />
+ <PreactCounter client:visible />
+ <VueCounter client:visible />
+ <SvelteCounter client:visible />
- You can use any {variable} in scope and use JavaScript for templating ({items.join(', ')})
+ ## Use Expressions
- ## Oh yeah...
+ You can use any {variable} in scope and use JavaScript for templating ({items.join(', ')})
- <ReactCounter client:visible>
+ ## Oh yeah...
- 🤯 It's also _recursive_!
+ <ReactCounter client:visible>
- ### Markdown can be embedded in any child component
+ 🤯 It's also _recursive_!
- </ReactCounter>
+ ### Markdown can be embedded in any child component
- ## Code
+ </ReactCounter>
- Should work!
+ ## Code
- ```js
- import Something from './another';
+ Should work!
- const thing = new Something();
- ```
+ ```js
+ import Something from './another';
- </Markdown>
+ const thing = new Something();
+ ```
+ </Markdown>
</Layout>
diff --git a/examples/with-markdown/src/styles/global.css b/examples/with-markdown/src/styles/global.css
index 16cd4577e..577e06182 100644
--- a/examples/with-markdown/src/styles/global.css
+++ b/examples/with-markdown/src/styles/global.css
@@ -1,69 +1,69 @@
pre,
code {
- color: #d4d4d4;
- font-size: 14px;
- font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
- line-height: 1.5;
- direction: ltr;
- white-space: pre;
- text-align: left;
- text-shadow: none;
- word-break: normal;
- word-spacing: normal;
- -moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
- -webkit-hyphens: none;
- -moz-hyphens: none;
- -ms-hyphens: none;
- hyphens: none;
+ color: #d4d4d4;
+ font-size: 14px;
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ line-height: 1.5;
+ direction: ltr;
+ white-space: pre;
+ text-align: left;
+ text-shadow: none;
+ word-break: normal;
+ word-spacing: normal;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
}
pre::selection,
code::selection {
- text-shadow: none;
- background: #b3d4fc;
+ text-shadow: none;
+ background: #b3d4fc;
}
@media print {
- pre,
- code {
- text-shadow: none;
- }
+ pre,
+ code {
+ text-shadow: none;
+ }
}
pre {
- margin: 0.5rem 0 16px;
- padding: 0.8rem 1rem 0.9rem;
- overflow: auto;
- background: #282a36;
- border-radius: 4px;
+ margin: 0.5rem 0 16px;
+ padding: 0.8rem 1rem 0.9rem;
+ overflow: auto;
+ background: #282a36;
+ border-radius: 4px;
}
:not(pre) > code {
- padding: 0.1em 0.3em;
- color: #db4c69;
- background: #f9f2f4;
- border-radius: 0.3em;
- white-space: pre-wrap;
+ padding: 0.1em 0.3em;
+ color: #db4c69;
+ background: #f9f2f4;
+ border-radius: 0.3em;
+ white-space: pre-wrap;
}
/*********************************************************
* Tokens
*/
.namespace {
- opacity: 0.7;
+ opacity: 0.7;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
- color: #6a9955;
+ color: #6a9955;
}
.token.punctuation {
- color: #d4d4d4;
+ color: #d4d4d4;
}
.token.property,
@@ -73,7 +73,7 @@ pre {
.token.constant,
.token.symbol,
.token.deleted {
- color: #b5cea8;
+ color: #b5cea8;
}
.token.selector,
@@ -82,7 +82,7 @@ pre {
.token.char,
.token.builtin,
.token.inserted {
- color: #ce9178;
+ color: #ce9178;
}
.token.operator,
@@ -90,86 +90,86 @@ pre {
.token.url,
.language-css .token.string,
.style .token.string {
- color: #d4d4d4;
- background: #2d3748;
+ color: #d4d4d4;
+ background: #2d3748;
}
.token.atrule,
.token.attr-value,
.token.keyword {
- color: #c586c0;
+ color: #c586c0;
}
.token.function {
- color: #dcdcaa;
+ color: #dcdcaa;
}
.token.regex,
.token.important,
.token.variable {
- color: #d16969;
+ color: #d16969;
}
.token.important,
.token.bold {
- font-weight: bold;
+ font-weight: bold;
}
.token.italic {
- font-style: italic;
+ font-style: italic;
}
.token.constant {
- color: #9cdcfe;
+ color: #9cdcfe;
}
.token.class-name {
- color: #4ec9b0;
+ color: #4ec9b0;
}
.token.parameter {
- color: #9cdcfe;
+ color: #9cdcfe;
}
.token.interpolation {
- color: #9cdcfe;
+ color: #9cdcfe;
}
.token.punctuation.interpolation-punctuation {
- color: #569cd6;
+ color: #569cd6;
}
.token.boolean {
- color: #569cd6;
+ color: #569cd6;
}
.token.property {
- color: #9cdcfe;
+ color: #9cdcfe;
}
.token.selector {
- color: #d7ba7d;
+ color: #d7ba7d;
}
.token.tag {
- color: #569cd6;
+ color: #569cd6;
}
.token.attr-name {
- color: #9cdcfe;
+ color: #9cdcfe;
}
.token.attr-value {
- color: #ce9178;
+ color: #ce9178;
}
.token.entity {
- color: #4ec9b0;
- cursor: unset;
+ color: #4ec9b0;
+ cursor: unset;
}
.token.namespace {
- color: #4ec9b0;
+ color: #4ec9b0;
}
/*********************************************************
@@ -177,58 +177,58 @@ pre {
*/
pre[class*='language-javascript'],
code[class*='language-javascript'] {
- color: #4ec9b0;
+ color: #4ec9b0;
}
pre[class*='language-css'],
code[class*='language-css'] {
- color: #ce9178;
+ color: #ce9178;
}
pre[class*='language-html'],
code[class*='language-html'] {
- color: #d4d4d4;
+ color: #d4d4d4;
}
.language-html .token.punctuation {
- color: #808080;
+ color: #808080;
}
/*********************************************************
* Line highlighting
*/
pre[data-line] {
- position: relative;
+ position: relative;
}
pre > code {
- position: relative;
- z-index: 1;
+ position: relative;
+ z-index: 1;
}
.line-highlight {
- position: absolute;
- right: 0;
- left: 0;
- z-index: 0;
- margin-top: 1em;
- padding: inherit 0;
- line-height: inherit;
- white-space: pre;
- background: #f7ebc6;
- box-shadow: inset 5px 0 0 #f7d87c;
- pointer-events: none;
+ position: absolute;
+ right: 0;
+ left: 0;
+ z-index: 0;
+ margin-top: 1em;
+ padding: inherit 0;
+ line-height: inherit;
+ white-space: pre;
+ background: #f7ebc6;
+ box-shadow: inset 5px 0 0 #f7d87c;
+ pointer-events: none;
}
pre[class*='language-bash'] .token.function {
- color: #d4d4d4;
+ color: #d4d4d4;
}
.token.comment {
- color: #fff7;
+ color: #fff7;
}
body {
- max-width: 900px;
- margin: auto;
+ max-width: 900px;
+ margin: auto;
}
diff --git a/examples/with-nanostores/astro.config.mjs b/examples/with-nanostores/astro.config.mjs
index b5fe6a073..ce173ed92 100644
--- a/examples/with-nanostores/astro.config.mjs
+++ b/examples/with-nanostores/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable many renderers to support all different kinds of components.
- renderers: ['@astrojs/renderer-preact', '@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue', '@astrojs/renderer-solid'],
+ // Enable many renderers to support all different kinds of components.
+ renderers: ['@astrojs/renderer-preact', '@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue', '@astrojs/renderer-solid'],
});
diff --git a/examples/with-nanostores/src/components/AdminsPreact.jsx b/examples/with-nanostores/src/components/AdminsPreact.jsx
index 327d82846..2f93ed437 100644
--- a/examples/with-nanostores/src/components/AdminsPreact.jsx
+++ b/examples/with-nanostores/src/components/AdminsPreact.jsx
@@ -5,26 +5,26 @@ import { admins } from '../store/admins.js';
import { counter, increaseCounter, decreaseCounter } from '../store/counter.js';
const AdminsPreact = () => {
- const list = useStore(admins);
- const count = useStore(counter);
+ const list = useStore(admins);
+ const count = useStore(counter);
- return (
- <>
- <h1>Preact</h1>
- <ul>
- {list.map((admin) => (
- <li key={admin.id}>{JSON.stringify(admin, null, 2)}</li>
- ))}
- </ul>
- <div>
- <h3>Counter</h3>
- <p>{count.value}</p>
- <button onClick={decreaseCounter}>-1</button>
- <button onClick={increaseCounter}>+1</button>
- </div>
- <br />
- </>
- );
+ return (
+ <>
+ <h1>Preact</h1>
+ <ul>
+ {list.map((admin) => (
+ <li key={admin.id}>{JSON.stringify(admin, null, 2)}</li>
+ ))}
+ </ul>
+ <div>
+ <h3>Counter</h3>
+ <p>{count.value}</p>
+ <button onClick={decreaseCounter}>-1</button>
+ <button onClick={increaseCounter}>+1</button>
+ </div>
+ <br />
+ </>
+ );
};
export default AdminsPreact;
diff --git a/examples/with-nanostores/src/components/AdminsReact.jsx b/examples/with-nanostores/src/components/AdminsReact.jsx
index dfddd3e83..f2b38a3cd 100644
--- a/examples/with-nanostores/src/components/AdminsReact.jsx
+++ b/examples/with-nanostores/src/components/AdminsReact.jsx
@@ -5,26 +5,26 @@ import { admins } from '../store/admins.js';
import { counter, increaseCounter, decreaseCounter } from '../store/counter.js';
const AdminsReact = () => {
- const list = useStore(admins);
- const count = useStore(counter);
+ const list = useStore(admins);
+ const count = useStore(counter);
- return (
- <>
- <h1>React</h1>
- <ul>
- {list.map((admin) => (
- <li key={admin.id}>{JSON.stringify(admin, null, 2)}</li>
- ))}
- </ul>
- <div>
- <h3>Counter</h3>
- <p>{count.value}</p>
- <button onClick={decreaseCounter}>-1</button>
- <button onClick={increaseCounter}>+1</button>
- </div>
- <br />
- </>
- );
+ return (
+ <>
+ <h1>React</h1>
+ <ul>
+ {list.map((admin) => (
+ <li key={admin.id}>{JSON.stringify(admin, null, 2)}</li>
+ ))}
+ </ul>
+ <div>
+ <h3>Counter</h3>
+ <p>{count.value}</p>
+ <button onClick={decreaseCounter}>-1</button>
+ <button onClick={increaseCounter}>+1</button>
+ </div>
+ <br />
+ </>
+ );
};
export default AdminsReact;
diff --git a/examples/with-nanostores/src/components/AdminsSolid.jsx b/examples/with-nanostores/src/components/AdminsSolid.jsx
index 360961a98..8ad2756a3 100644
--- a/examples/with-nanostores/src/components/AdminsSolid.jsx
+++ b/examples/with-nanostores/src/components/AdminsSolid.jsx
@@ -5,26 +5,26 @@ import { admins } from '../store/admins.js';
import { counter, increaseCounter, decreaseCounter } from '../store/counter.js';
const AdminsSolid = () => {
- const list = useStore(admins);
- const count = useStore(counter);
+ const list = useStore(admins);
+ const count = useStore(counter);
- return (
- <>
- <h1>Solid</h1>
- <ul>
- {list.map((admin) => (
- <li key={admin.id}>{JSON.stringify(admin, null, 2)}</li>
- ))}
- </ul>
- <div>
- <h3>Counter</h3>
- <p>{count.value}</p>
- <button onClick={decreaseCounter}>-1</button>
- <button onClick={increaseCounter}>+1</button>
- </div>
- <br />
- </>
- );
+ return (
+ <>
+ <h1>Solid</h1>
+ <ul>
+ {list.map((admin) => (
+ <li key={admin.id}>{JSON.stringify(admin, null, 2)}</li>
+ ))}
+ </ul>
+ <div>
+ <h3>Counter</h3>
+ <p>{count.value}</p>
+ <button onClick={decreaseCounter}>-1</button>
+ <button onClick={increaseCounter}>+1</button>
+ </div>
+ <br />
+ </>
+ );
};
export default AdminsSolid;
diff --git a/examples/with-nanostores/src/components/AdminsVue.vue b/examples/with-nanostores/src/components/AdminsVue.vue
index 2f83e9fc1..5eb73dd3d 100644
--- a/examples/with-nanostores/src/components/AdminsVue.vue
+++ b/examples/with-nanostores/src/components/AdminsVue.vue
@@ -1,19 +1,19 @@
<template>
- <div>
- <h1>Vue</h1>
- <ul>
- <li v-for="admin in list" :key="admin.id">
- {{ JSON.stringify(admin, null, 2) }}
- </li>
- </ul>
- <div>
- <h3>Counter</h3>
- <p>{{ count.value }}</p>
- <button @click="decreaseCounter">-1</button>
- <button @click="increaseCounter">+1</button>
- </div>
- <br />
- </div>
+ <div>
+ <h1>Vue</h1>
+ <ul>
+ <li v-for="admin in list" :key="admin.id">
+ {{ JSON.stringify(admin, null, 2) }}
+ </li>
+ </ul>
+ <div>
+ <h3>Counter</h3>
+ <p>{{ count.value }}</p>
+ <button @click="decreaseCounter">-1</button>
+ <button @click="increaseCounter">+1</button>
+ </div>
+ <br />
+ </div>
</template>
<script>
@@ -23,11 +23,11 @@ import { admins } from '../store/admins.js';
import { counter, increaseCounter, decreaseCounter } from '../store/counter.js';
export default {
- setup() {
- const list = useStore(admins);
- const count = useStore(counter);
+ setup() {
+ const list = useStore(admins);
+ const count = useStore(counter);
- return { list, count, increaseCounter, decreaseCounter };
- },
+ return { list, count, increaseCounter, decreaseCounter };
+ },
};
</script>
diff --git a/examples/with-nanostores/src/pages/index.astro b/examples/with-nanostores/src/pages/index.astro
index 1a579f0f8..e25769138 100644
--- a/examples/with-nanostores/src/pages/index.astro
+++ b/examples/with-nanostores/src/pages/index.astro
@@ -9,40 +9,43 @@ import AdminsSolid from '../components/AdminsSolid.jsx';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width">
- <title>Astro</title>
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width" />
+ <title>Astro</title>
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <link rel="stylesheet" href={Astro.resolve('../styles/global.css')}>
- <link rel="stylesheet" href={Astro.resolve('../styles/home.css')}>
+ <link rel="stylesheet" href={Astro.resolve('../styles/global.css')} />
+ <link rel="stylesheet" href={Astro.resolve('../styles/home.css')} />
- <style>
- header {
- display: flex;
- flex-direction: column;
- gap: 1em;
- max-width: min(100%, 68ch);
- }
- </style>
-</head>
-<body>
- <main>
- <header>
- <div>
- <img width="60" height="80" src="/assets/logo.svg" alt="Astro logo">
- <h1>Welcome to <a href="https://astro.build/">Astro</a> -
- <a href="https://github.com/nanostores/nanostores">nanostores</a></h1>
- </div>
- </header>
- <AdminsReact client:load />
- <AdminsSvelte client:load />
- <AdminsVue client:load />
- <AdminsPreact client:load />
- <AdminsSolid client:load />
- </main>
-</body>
+ <style>
+ header {
+ display: flex;
+ flex-direction: column;
+ gap: 1em;
+ max-width: min(100%, 68ch);
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <header>
+ <div>
+ <img width="60" height="80" src="/assets/logo.svg" alt="Astro logo" />
+ <h1>
+ Welcome to <a href="https://astro.build/">Astro</a> -
+ <a href="https://github.com/nanostores/nanostores">nanostores</a>
+ </h1>
+ </div>
+ </header>
+ <AdminsReact client:load />
+ <AdminsSvelte client:load />
+ <AdminsVue client:load />
+ <AdminsPreact client:load />
+ <AdminsSolid client:load />
+ </main>
+ </body>
</html>
diff --git a/examples/with-nanostores/src/store/counter.js b/examples/with-nanostores/src/store/counter.js
index 42e82fff2..d4c29ad62 100644
--- a/examples/with-nanostores/src/store/counter.js
+++ b/examples/with-nanostores/src/store/counter.js
@@ -5,11 +5,11 @@ const initialValue = { value: 0 };
const counter = atom(initialValue);
function increaseCounter() {
- counter.set({ value: counter.get().value + 1 });
+ counter.set({ value: counter.get().value + 1 });
}
function decreaseCounter() {
- counter.set({ value: counter.get().value - 1 });
+ counter.set({ value: counter.get().value - 1 });
}
export { counter, increaseCounter, decreaseCounter };
diff --git a/examples/with-nanostores/src/store/users.js b/examples/with-nanostores/src/store/users.js
index f00aa83bd..7a2e23e9d 100644
--- a/examples/with-nanostores/src/store/users.js
+++ b/examples/with-nanostores/src/store/users.js
@@ -1,30 +1,30 @@
import { atom } from 'nanostores';
const initialValue = [
- {
- id: 1,
- name: 'User Admin',
- age: 28,
- isAdmin: true,
- },
- {
- id: 2,
- name: 'NOT Admin',
- age: 35,
- isAdmin: false,
- },
- {
- id: 3,
- name: 'Another Admin',
- age: 46,
- isAdmin: true,
- },
+ {
+ id: 1,
+ name: 'User Admin',
+ age: 28,
+ isAdmin: true,
+ },
+ {
+ id: 2,
+ name: 'NOT Admin',
+ age: 35,
+ isAdmin: false,
+ },
+ {
+ id: 3,
+ name: 'Another Admin',
+ age: 46,
+ isAdmin: true,
+ },
];
const users = atom(initialValue);
const addUser = function addUser(user) {
- users.set([...users.get(), user]);
+ users.set([...users.get(), user]);
};
export { users, addUser };
diff --git a/examples/with-nanostores/src/styles/global.css b/examples/with-nanostores/src/styles/global.css
index e1a3a6bd1..a9f830eda 100644
--- a/examples/with-nanostores/src/styles/global.css
+++ b/examples/with-nanostores/src/styles/global.css
@@ -1,28 +1,28 @@
* {
- box-sizing: border-box;
- margin: 0;
+ box-sizing: border-box;
+ margin: 0;
}
:root {
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
- font-size: 1rem;
- --user-font-scale: 1rem - 16px;
- font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
+ font-size: 1rem;
+ --user-font-scale: 1rem - 16px;
+ font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
}
body {
- padding: 4rem 2rem;
- width: 100%;
- min-height: 100vh;
- display: grid;
- justify-content: center;
- background: #f9fafb;
- color: #111827;
+ padding: 4rem 2rem;
+ width: 100%;
+ min-height: 100vh;
+ display: grid;
+ justify-content: center;
+ background: #f9fafb;
+ color: #111827;
}
@media (prefers-color-scheme: dark) {
- body {
- background: #111827;
- color: #fff;
- }
+ body {
+ background: #111827;
+ color: #fff;
+ }
}
diff --git a/examples/with-nanostores/src/styles/home.css b/examples/with-nanostores/src/styles/home.css
index 280398c17..5770429d1 100644
--- a/examples/with-nanostores/src/styles/home.css
+++ b/examples/with-nanostores/src/styles/home.css
@@ -1,39 +1,39 @@
:root {
- --font-mono: Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono',
- 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
- --color-light: #f3f4f6;
+ --font-mono: Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono',
+ 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
+ --color-light: #f3f4f6;
}
@media (prefers-color-scheme: dark) {
- :root {
- --color-light: #1f2937;
- }
+ :root {
+ --color-light: #1f2937;
+ }
}
a {
- color: inherit;
+ color: inherit;
}
header > div {
- font-size: clamp(2rem, -0.4742rem + 6.1856vw, 2.75rem);
+ font-size: clamp(2rem, -0.4742rem + 6.1856vw, 2.75rem);
}
header > div {
- display: flex;
- flex-direction: column;
- align-items: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
}
header h1 {
- font-size: 1em;
- font-weight: 500;
+ font-size: 1em;
+ font-weight: 500;
}
header img {
- width: 2em;
- height: 2.667em;
+ width: 2em;
+ height: 2.667em;
}
h2 {
- font-weight: 500;
- font-size: clamp(1.5rem, 1rem + 1.25vw, 2rem);
+ font-weight: 500;
+ font-size: clamp(1.5rem, 1rem + 1.25vw, 2rem);
}
diff --git a/examples/with-tailwindcss/astro.config.mjs b/examples/with-tailwindcss/astro.config.mjs
index 68499b3fa..a1516f292 100644
--- a/examples/with-tailwindcss/astro.config.mjs
+++ b/examples/with-tailwindcss/astro.config.mjs
@@ -8,6 +8,6 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Preact renderer to support Preact JSX components.
- renderers: ['@astrojs/renderer-preact'],
+ // Enable the Preact renderer to support Preact JSX components.
+ renderers: ['@astrojs/renderer-preact'],
});
diff --git a/examples/with-tailwindcss/postcss.config.js b/examples/with-tailwindcss/postcss.config.js
index 7cffbeb55..c3a002bfe 100644
--- a/examples/with-tailwindcss/postcss.config.js
+++ b/examples/with-tailwindcss/postcss.config.js
@@ -1,10 +1,10 @@
const path = require('path');
module.exports = {
- plugins: {
- tailwindcss: {
- config: path.join(__dirname, 'tailwind.config.js'), // update this if your path differs!
- },
- autoprefixer: {},
- },
+ plugins: {
+ tailwindcss: {
+ config: path.join(__dirname, 'tailwind.config.js'), // update this if your path differs!
+ },
+ autoprefixer: {},
+ },
};
diff --git a/examples/with-tailwindcss/src/components/Button.astro b/examples/with-tailwindcss/src/components/Button.astro
index 966c613aa..5e241c44f 100644
--- a/examples/with-tailwindcss/src/components/Button.astro
+++ b/examples/with-tailwindcss/src/components/Button.astro
@@ -1,6 +1,10 @@
---
let { type = 'button' } = Astro.props;
---
-<button class="py-2 px-4 bg-purple-500 text-white font-semibold rounded-lg shadow-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:ring-opacity-75" type={type}>
- <slot />
+
+<button
+ class="py-2 px-4 bg-purple-500 text-white font-semibold rounded-lg shadow-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:ring-opacity-75"
+ {type}
+>
+ <slot />
</button>
diff --git a/examples/with-tailwindcss/src/pages/index.astro b/examples/with-tailwindcss/src/pages/index.astro
index 28d5f3dec..07262b870 100644
--- a/examples/with-tailwindcss/src/pages/index.astro
+++ b/examples/with-tailwindcss/src/pages/index.astro
@@ -5,15 +5,16 @@ import Button from '../components/Button.astro';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
+
<html lang="en">
- <head>
- <meta charset="UTF-8" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <title>Astro + TailwindCSS</title>
- <link rel="stylesheet" type="text/css" href={Astro.resolve("../styles/global.css")}>
- </head>
+ <head>
+ <meta charset="UTF-8" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <title>Astro + TailwindCSS</title>
+ <link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/global.css')} />
+ </head>
- <body>
- <Button>I’m a Tailwind Button!</Button>
- </body>
+ <body>
+ <Button>I’m a Tailwind Button!</Button>
+ </body>
</html>
diff --git a/examples/with-tailwindcss/tailwind.config.js b/examples/with-tailwindcss/tailwind.config.js
index 627a00ab2..30b9aff01 100644
--- a/examples/with-tailwindcss/tailwind.config.js
+++ b/examples/with-tailwindcss/tailwind.config.js
@@ -1,3 +1,3 @@
module.exports = {
- content: ['./src/**/*.{astro,html,js,jsx,svelte,ts,tsx,vue}'],
+ content: ['./src/**/*.{astro,html,js,jsx,svelte,ts,tsx,vue}'],
};
diff --git a/examples/with-vite-plugin-pwa/astro.config.mjs b/examples/with-vite-plugin-pwa/astro.config.mjs
index d8fc2ac26..05dac6602 100644
--- a/examples/with-vite-plugin-pwa/astro.config.mjs
+++ b/examples/with-vite-plugin-pwa/astro.config.mjs
@@ -10,7 +10,7 @@ import { VitePWA } from 'vite-plugin-pwa';
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- vite: {
- plugins: [VitePWA()],
- },
+ vite: {
+ plugins: [VitePWA()],
+ },
});
diff --git a/examples/with-vite-plugin-pwa/src/index.ts b/examples/with-vite-plugin-pwa/src/index.ts
index 187159528..2595fe3a8 100644
--- a/examples/with-vite-plugin-pwa/src/index.ts
+++ b/examples/with-vite-plugin-pwa/src/index.ts
@@ -1,10 +1,10 @@
import { registerSW } from 'virtual:pwa-register';
const updateSW = registerSW({
- onNeedRefresh() {},
- onOfflineReady() {
- console.log('Offline ready');
- },
+ onNeedRefresh() {},
+ onOfflineReady() {
+ console.log('Offline ready');
+ },
});
updateSW();
diff --git a/examples/with-vite-plugin-pwa/src/pages/index.astro b/examples/with-vite-plugin-pwa/src/pages/index.astro
index 008442824..7fdcc305e 100644
--- a/examples/with-vite-plugin-pwa/src/pages/index.astro
+++ b/examples/with-vite-plugin-pwa/src/pages/index.astro
@@ -2,17 +2,15 @@
---
<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <meta name="viewport" content="width=device-width" />
+ <title>Welcome to Astro</title>
+ </head>
-<head>
- <meta charset="utf-8" />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <meta name="viewport" content="width=device-width" />
- <title>Welcome to Astro</title>
-</head>
-
-<body>
- <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
- <script src={Astro.resolve('../index.ts')} type="module" hoist />
-</body>
-
+ <body>
+ <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
+ <script src={Astro.resolve('../index.ts')} type="module" hoist></script>
+ </body>
</html>
diff --git a/examples/with-vite-plugin-pwa/src/vite-env.d.ts b/examples/with-vite-plugin-pwa/src/vite-env.d.ts
index 6b53adfef..1a947fad0 100644
--- a/examples/with-vite-plugin-pwa/src/vite-env.d.ts
+++ b/examples/with-vite-plugin-pwa/src/vite-env.d.ts
@@ -1,11 +1,11 @@
declare module 'virtual:pwa-register' {
- export type RegisterSWOptions = {
- immediate?: boolean;
- onNeedRefresh?: () => void;
- onOfflineReady?: () => void;
- onRegistered?: (registration: ServiceWorkerRegistration | undefined) => void;
- onRegisterError?: (error: any) => void;
- };
+ export type RegisterSWOptions = {
+ immediate?: boolean;
+ onNeedRefresh?: () => void;
+ onOfflineReady?: () => void;
+ onRegistered?: (registration: ServiceWorkerRegistration | undefined) => void;
+ onRegisterError?: (error: any) => void;
+ };
- export function registerSW(options?: RegisterSWOptions): (reloadPage?: boolean) => Promise<void>;
+ export function registerSW(options?: RegisterSWOptions): (reloadPage?: boolean) => Promise<void>;
}
diff --git a/package.json b/package.json
index baca6f7ed..398a8523d 100644
--- a/package.json
+++ b/package.json
@@ -69,7 +69,6 @@
"npm": "7.11.2",
"yarn": "1.22.10"
},
- "dependencies": {},
"devDependencies": {
"@changesets/changelog-github": "^0.4.2",
"@changesets/cli": "^2.16.0",
diff --git a/packages/astro-parser/src/Stats.ts b/packages/astro-parser/src/Stats.ts
index 5f7f991f8..98f95c595 100644
--- a/packages/astro-parser/src/Stats.ts
+++ b/packages/astro-parser/src/Stats.ts
@@ -1,83 +1,83 @@
// @ts-nocheck
const now =
- typeof process !== 'undefined' && process.hrtime
- ? () => {
- const t = process.hrtime();
- return t[0] * 1e3 + t[1] / 1e6;
- }
- : () => self.performance.now();
+ typeof process !== 'undefined' && process.hrtime
+ ? () => {
+ const t = process.hrtime();
+ return t[0] * 1e3 + t[1] / 1e6;
+ }
+ : () => self.performance.now();
interface Timing {
- label: string;
- start: number;
- end: number;
- children: Timing[];
+ label: string;
+ start: number;
+ end: number;
+ children: Timing[];
}
/** Format benchmarks */
function collapse_timings(timings) {
- const result = {};
- timings.forEach((timing) => {
- result[timing.label] = Object.assign(
- {
- total: timing.end - timing.start,
- },
- timing.children && collapse_timings(timing.children)
- );
- });
- return result;
+ const result = {};
+ timings.forEach((timing) => {
+ result[timing.label] = Object.assign(
+ {
+ total: timing.end - timing.start,
+ },
+ timing.children && collapse_timings(timing.children)
+ );
+ });
+ return result;
}
export default class Stats {
- start_time: number;
- current_timing: Timing;
- current_children: Timing[];
- timings: Timing[];
- stack: Timing[];
+ start_time: number;
+ current_timing: Timing;
+ current_children: Timing[];
+ timings: Timing[];
+ stack: Timing[];
- constructor() {
- this.start_time = now();
- this.stack = [];
- this.current_children = this.timings = [];
- }
+ constructor() {
+ this.start_time = now();
+ this.stack = [];
+ this.current_children = this.timings = [];
+ }
- start(label) {
- const timing = {
- label,
- start: now(),
- end: null,
- children: [],
- };
+ start(label) {
+ const timing = {
+ label,
+ start: now(),
+ end: null,
+ children: [],
+ };
- this.current_children.push(timing);
- this.stack.push(timing);
+ this.current_children.push(timing);
+ this.stack.push(timing);
- this.current_timing = timing;
- this.current_children = timing.children;
- }
+ this.current_timing = timing;
+ this.current_children = timing.children;
+ }
- stop(label) {
- if (label !== this.current_timing.label) {
- throw new Error(`Mismatched timing labels (expected ${this.current_timing.label}, got ${label})`);
- }
+ stop(label) {
+ if (label !== this.current_timing.label) {
+ throw new Error(`Mismatched timing labels (expected ${this.current_timing.label}, got ${label})`);
+ }
- this.current_timing.end = now();
- this.stack.pop();
- this.current_timing = this.stack[this.stack.length - 1];
- this.current_children = this.current_timing ? this.current_timing.children : this.timings;
- }
+ this.current_timing.end = now();
+ this.stack.pop();
+ this.current_timing = this.stack[this.stack.length - 1];
+ this.current_children = this.current_timing ? this.current_timing.children : this.timings;
+ }
- render() {
- const timings = Object.assign(
- {
- total: now() - this.start_time,
- },
- collapse_timings(this.timings)
- );
+ render() {
+ const timings = Object.assign(
+ {
+ total: now() - this.start_time,
+ },
+ collapse_timings(this.timings)
+ );
- return {
- timings,
- };
- }
+ return {
+ timings,
+ };
+ }
}
diff --git a/packages/astro-parser/src/interfaces.ts b/packages/astro-parser/src/interfaces.ts
index 335643aa5..6ed19c058 100644
--- a/packages/astro-parser/src/interfaces.ts
+++ b/packages/astro-parser/src/interfaces.ts
@@ -2,62 +2,62 @@ import type { SourceMap } from 'magic-string';
export { CompileError } from './utils/error';
export interface BaseNode {
- start: number;
- end: number;
- type: string;
- children?: TemplateNode[];
- [prop_name: string]: any;
+ start: number;
+ end: number;
+ type: string;
+ children?: TemplateNode[];
+ [prop_name: string]: any;
}
export interface Fragment extends BaseNode {
- type: 'Fragment';
- children: TemplateNode[];
+ type: 'Fragment';
+ children: TemplateNode[];
}
export interface Text extends BaseNode {
- type: 'Text';
- data: string;
- raw: string;
+ type: 'Text';
+ data: string;
+ raw: string;
}
export interface CodeFence extends BaseNode {
- type: 'CodeFence';
- metadata: string;
- data: string;
- raw: string;
+ type: 'CodeFence';
+ metadata: string;
+ data: string;
+ raw: string;
}
export interface CodeSpan extends BaseNode {
- type: 'CodeFence';
- metadata: string;
- data: string;
- raw: string;
+ type: 'CodeFence';
+ metadata: string;
+ data: string;
+ raw: string;
}
export interface Attribute extends BaseNode {
- type: 'Attribute';
- name: string;
- value: Text[];
+ type: 'Attribute';
+ name: string;
+ value: Text[];
}
export interface MustacheTag extends BaseNode {
- type: 'MustacheTag';
- content: string;
+ type: 'MustacheTag';
+ content: string;
}
export type DirectiveType = 'Action' | 'Animation' | 'Binding' | 'Class' | 'EventHandler' | 'Let' | 'Ref' | 'Transition';
interface BaseDirective extends BaseNode {
- type: DirectiveType;
- expression: null | Node;
- name: string;
- modifiers: string[];
+ type: DirectiveType;
+ expression: null | Node;
+ name: string;
+ modifiers: string[];
}
export interface Transition extends BaseDirective {
- type: 'Transition';
- intro: boolean;
- outro: boolean;
+ type: 'Transition';
+ intro: boolean;
+ outro: boolean;
}
export type Directive = BaseDirective | Transition;
@@ -65,61 +65,61 @@ export type Directive = BaseDirective | Transition;
export type TemplateNode = Text | CodeSpan | CodeFence | MustacheTag | BaseNode | Directive | Transition;
export interface Expression {
- type: 'Expression';
- start: number;
- end: number;
- codeChunks: string[];
- children: BaseNode[];
+ type: 'Expression';
+ start: number;
+ end: number;
+ codeChunks: string[];
+ children: BaseNode[];
}
export interface Parser {
- readonly template: string;
- readonly filename?: string;
+ readonly template: string;
+ readonly filename?: string;
- index: number;
- stack: Node[];
+ index: number;
+ stack: Node[];
- html: Node;
- css: Node;
- js: Node;
- meta_tags: Map<string, string>;
+ html: Node;
+ css: Node;
+ js: Node;
+ meta_tags: Map<string, string>;
}
export interface Script extends BaseNode {
- type: 'Script';
- context: 'runtime' | 'setup';
- content: string;
+ type: 'Script';
+ context: 'runtime' | 'setup';
+ content: string;
}
export interface Style extends BaseNode {
- type: 'Style';
- attributes: any[]; // TODO
- content: {
- start: number;
- end: number;
- styles: string;
- };
+ type: 'Style';
+ attributes: any[]; // TODO
+ content: {
+ start: number;
+ end: number;
+ styles: string;
+ };
}
export interface Ast {
- html: TemplateNode;
- css: Style[];
- module: Script;
- // instance: Script;
- meta: {
- features: number;
- };
+ html: TemplateNode;
+ css: Style[];
+ module: Script;
+ // instance: Script;
+ meta: {
+ features: number;
+ };
}
export interface Warning {
- start?: { line: number; column: number; pos?: number };
- end?: { line: number; column: number };
- pos?: number;
- code: string;
- message: string;
- filename?: string;
- frame?: string;
- toString: () => string;
+ start?: { line: number; column: number; pos?: number };
+ end?: { line: number; column: number };
+ pos?: number;
+ code: string;
+ message: string;
+ filename?: string;
+ frame?: string;
+ toString: () => string;
}
export type ModuleFormat = 'esm' | 'cjs';
@@ -127,37 +127,37 @@ export type ModuleFormat = 'esm' | 'cjs';
export type CssHashGetter = (args: { name: string; filename: string | undefined; css: string; hash: (input: string) => string }) => string;
export interface Visitor {
- enter: (node: Node) => void;
- leave?: (node: Node) => void;
+ enter: (node: Node) => void;
+ leave?: (node: Node) => void;
}
export interface AppendTarget {
- slots: Record<string, string>;
- slot_stack: string[];
+ slots: Record<string, string>;
+ slot_stack: string[];
}
export interface Var {
- name: string;
- export_name?: string; // the `bar` in `export { foo as bar }`
- injected?: boolean;
- module?: boolean;
- mutated?: boolean;
- reassigned?: boolean;
- referenced?: boolean; // referenced from template scope
- referenced_from_script?: boolean; // referenced from script
- writable?: boolean;
-
- // used internally, but not exposed
- global?: boolean;
- internal?: boolean; // event handlers, bindings
- initialised?: boolean;
- hoistable?: boolean;
- subscribable?: boolean;
- is_reactive_dependency?: boolean;
- imported?: boolean;
+ name: string;
+ export_name?: string; // the `bar` in `export { foo as bar }`
+ injected?: boolean;
+ module?: boolean;
+ mutated?: boolean;
+ reassigned?: boolean;
+ referenced?: boolean; // referenced from template scope
+ referenced_from_script?: boolean; // referenced from script
+ writable?: boolean;
+
+ // used internally, but not exposed
+ global?: boolean;
+ internal?: boolean; // event handlers, bindings
+ initialised?: boolean;
+ hoistable?: boolean;
+ subscribable?: boolean;
+ is_reactive_dependency?: boolean;
+ imported?: boolean;
}
export interface CssResult {
- code: string;
- map: SourceMap;
+ code: string;
+ map: SourceMap;
}
diff --git a/packages/astro-parser/src/parse/index.ts b/packages/astro-parser/src/parse/index.ts
index e978fe0bc..0ce259dfb 100644
--- a/packages/astro-parser/src/parse/index.ts
+++ b/packages/astro-parser/src/parse/index.ts
@@ -11,211 +11,211 @@ import error from '../utils/error.js';
type ParserState = (parser: Parser) => ParserState | void;
interface LastAutoClosedTag {
- tag: string;
- reason: string;
- depth: number;
+ tag: string;
+ reason: string;
+ depth: number;
}
export class Parser {
- readonly template: string;
- readonly filename?: string;
- readonly customElement: boolean;
-
- index = 0;
- stack: TemplateNode[] = [];
-
- html: Fragment;
- css: Style[] = [];
- js: Script[] = [];
- meta_tags = {};
- last_auto_closed_tag?: LastAutoClosedTag;
- feature_flags = 0;
-
- constructor(template: string, options: ParserOptions) {
- if (typeof template !== 'string') {
- throw new TypeError('Template must be a string');
- }
-
- this.template = template.replace(/\s+$/, '');
- this.filename = options.filename;
- this.customElement = options.customElement;
-
- this.html = {
- start: null,
- end: null,
- type: 'Fragment',
- children: [],
- };
-
- this.stack.push(this.html);
-
- let state: ParserState = fragment;
-
- while (this.index < this.template.length) {
- state = state(this) || fragment;
- }
-
- if (this.stack.length > 1) {
- const current = this.current();
-
- const type = current.type === 'Element' ? `<${current.name}>` : 'Block';
- const slug = current.type === 'Element' ? 'element' : 'block';
-
- this.error(
- {
- code: `unclosed-${slug}`,
- message: `${type} was left open`,
- },
- current.start
- );
- }
-
- if (state !== fragment) {
- this.error({
- code: 'unexpected-eof',
- message: 'Unexpected end of input',
- });
- }
-
- if (this.html.children.length) {
- let start = this.html.children[0].start;
- while (whitespace.test(template[start])) start += 1;
-
- let end = this.html.children[this.html.children.length - 1].end;
- while (whitespace.test(template[end - 1])) end -= 1;
-
- this.html.start = start;
- this.html.end = end;
- } else {
- this.html.start = this.html.end = null;
- }
- }
-
- current() {
- return this.stack[this.stack.length - 1];
- }
-
- acorn_error(err: any) {
- this.error(
- {
- code: 'parse-error',
- message: err.message.replace(/ \(\d+:\d+\)$/, ''),
- },
- err.pos
- );
- }
-
- error({ code, message }: { code: string; message: string }, index = this.index) {
- error(this.template, message, {
- name: 'ParseError',
- code,
- source: this.template,
- start: index,
- filename: this.filename,
- });
- }
-
- eat(str: string, required?: boolean, message?: string) {
- if (this.match(str)) {
- this.index += str.length;
- return true;
- }
-
- if (required) {
- this.error({
- code: `unexpected-${this.index === this.template.length ? 'eof' : 'token'}`,
- message: message || `Expected ${str}`,
- });
- }
-
- return false;
- }
-
- match(str: string) {
- return this.template.slice(this.index, this.index + str.length) === str;
- }
-
- match_regex(pattern: RegExp) {
- const match = pattern.exec(this.template.slice(this.index));
- if (!match || match.index !== 0) return null;
-
- return match[0];
- }
-
- allow_whitespace() {
- while (this.index < this.template.length && whitespace.test(this.template[this.index])) {
- this.index++;
- }
- }
-
- read(pattern: RegExp) {
- const result = this.match_regex(pattern);
- if (result) this.index += result.length;
- return result;
- }
-
- read_identifier(allow_reserved = false) {
- const start = this.index;
-
- let i = this.index;
-
- const code = full_char_code_at(this.template, i);
- if (!isIdentifierStart(code, true)) return null;
-
- i += code <= 0xffff ? 1 : 2;
-
- while (i < this.template.length) {
- const code = full_char_code_at(this.template, i);
-
- if (!isIdentifierChar(code, true)) break;
- i += code <= 0xffff ? 1 : 2;
- }
-
- const identifier = this.template.slice(this.index, (this.index = i));
-
- if (!allow_reserved && reserved.has(identifier)) {
- this.error(
- {
- code: 'unexpected-reserved-word',
- message: `'${identifier}' is a reserved word in JavaScript and cannot be used here`,
- },
- start
- );
- }
-
- return identifier;
- }
-
- read_until(pattern: RegExp) {
- if (this.index >= this.template.length) {
- this.error({
- code: 'unexpected-eof',
- message: 'Unexpected end of input',
- });
- }
-
- const start = this.index;
- const match = pattern.exec(this.template.slice(start));
-
- if (match) {
- this.index = start + match.index;
- return this.template.slice(start, this.index);
- }
-
- this.index = this.template.length;
- return this.template.slice(start);
- }
-
- require_whitespace() {
- if (!whitespace.test(this.template[this.index])) {
- this.error({
- code: 'missing-whitespace',
- message: 'Expected whitespace',
- });
- }
-
- this.allow_whitespace();
- }
+ readonly template: string;
+ readonly filename?: string;
+ readonly customElement: boolean;
+
+ index = 0;
+ stack: TemplateNode[] = [];
+
+ html: Fragment;
+ css: Style[] = [];
+ js: Script[] = [];
+ meta_tags = {};
+ last_auto_closed_tag?: LastAutoClosedTag;
+ feature_flags = 0;
+
+ constructor(template: string, options: ParserOptions) {
+ if (typeof template !== 'string') {
+ throw new TypeError('Template must be a string');
+ }
+
+ this.template = template.replace(/\s+$/, '');
+ this.filename = options.filename;
+ this.customElement = options.customElement;
+
+ this.html = {
+ start: null,
+ end: null,
+ type: 'Fragment',
+ children: [],
+ };
+
+ this.stack.push(this.html);
+
+ let state: ParserState = fragment;
+
+ while (this.index < this.template.length) {
+ state = state(this) || fragment;
+ }
+
+ if (this.stack.length > 1) {
+ const current = this.current();
+
+ const type = current.type === 'Element' ? `<${current.name}>` : 'Block';
+ const slug = current.type === 'Element' ? 'element' : 'block';
+
+ this.error(
+ {
+ code: `unclosed-${slug}`,
+ message: `${type} was left open`,
+ },
+ current.start
+ );
+ }
+
+ if (state !== fragment) {
+ this.error({
+ code: 'unexpected-eof',
+ message: 'Unexpected end of input',
+ });
+ }
+
+ if (this.html.children.length) {
+ let start = this.html.children[0].start;
+ while (whitespace.test(template[start])) start += 1;
+
+ let end = this.html.children[this.html.children.length - 1].end;
+ while (whitespace.test(template[end - 1])) end -= 1;
+
+ this.html.start = start;
+ this.html.end = end;
+ } else {
+ this.html.start = this.html.end = null;
+ }
+ }
+
+ current() {
+ return this.stack[this.stack.length - 1];
+ }
+
+ acorn_error(err: any) {
+ this.error(
+ {
+ code: 'parse-error',
+ message: err.message.replace(/ \(\d+:\d+\)$/, ''),
+ },
+ err.pos
+ );
+ }
+
+ error({ code, message }: { code: string; message: string }, index = this.index) {
+ error(this.template, message, {
+ name: 'ParseError',
+ code,
+ source: this.template,
+ start: index,
+ filename: this.filename,
+ });
+ }
+
+ eat(str: string, required?: boolean, message?: string) {
+ if (this.match(str)) {
+ this.index += str.length;
+ return true;
+ }
+
+ if (required) {
+ this.error({
+ code: `unexpected-${this.index === this.template.length ? 'eof' : 'token'}`,
+ message: message || `Expected ${str}`,
+ });
+ }
+
+ return false;
+ }
+
+ match(str: string) {
+ return this.template.slice(this.index, this.index + str.length) === str;
+ }
+
+ match_regex(pattern: RegExp) {
+ const match = pattern.exec(this.template.slice(this.index));
+ if (!match || match.index !== 0) return null;
+
+ return match[0];
+ }
+
+ allow_whitespace() {
+ while (this.index < this.template.length && whitespace.test(this.template[this.index])) {
+ this.index++;
+ }
+ }
+
+ read(pattern: RegExp) {
+ const result = this.match_regex(pattern);
+ if (result) this.index += result.length;
+ return result;
+ }
+
+ read_identifier(allow_reserved = false) {
+ const start = this.index;
+
+ let i = this.index;
+
+ const code = full_char_code_at(this.template, i);
+ if (!isIdentifierStart(code, true)) return null;
+
+ i += code <= 0xffff ? 1 : 2;
+
+ while (i < this.template.length) {
+ const code = full_char_code_at(this.template, i);
+
+ if (!isIdentifierChar(code, true)) break;
+ i += code <= 0xffff ? 1 : 2;
+ }
+
+ const identifier = this.template.slice(this.index, (this.index = i));
+
+ if (!allow_reserved && reserved.has(identifier)) {
+ this.error(
+ {
+ code: 'unexpected-reserved-word',
+ message: `'${identifier}' is a reserved word in JavaScript and cannot be used here`,
+ },
+ start
+ );
+ }
+
+ return identifier;
+ }
+
+ read_until(pattern: RegExp) {
+ if (this.index >= this.template.length) {
+ this.error({
+ code: 'unexpected-eof',
+ message: 'Unexpected end of input',
+ });
+ }
+
+ const start = this.index;
+ const match = pattern.exec(this.template.slice(start));
+
+ if (match) {
+ this.index = start + match.index;
+ return this.template.slice(start, this.index);
+ }
+
+ this.index = this.template.length;
+ return this.template.slice(start);
+ }
+
+ require_whitespace() {
+ if (!whitespace.test(this.template[this.index])) {
+ this.error({
+ code: 'missing-whitespace',
+ message: 'Expected whitespace',
+ });
+ }
+
+ this.allow_whitespace();
+ }
}
/**
@@ -224,39 +224,39 @@ export class Parser {
* This is the first pass over .astro files and the step at which we convert a string to an AST for us to crawl.
*/
export default function parse(template: string, options: ParserOptions = {}): Ast {
- const parser = new Parser(template, options);
-
- // const instance_scripts = parser.js.filter((script) => script.context === 'default');
- // const module_scripts = parser.js.filter((script) => script.context === 'module');
- const astro_scripts = parser.js.filter((script) => script.context === 'setup');
-
- if (astro_scripts.length > 1) {
- parser.error(
- {
- code: 'invalid-script',
- message: 'A component can only have one frontmatter (---) script',
- },
- astro_scripts[1].start
- );
- }
-
- // if (module_scripts.length > 1) {
- // parser.error(
- // {
- // code: 'invalid-script',
- // message: 'A component can only have one <script context="module"> element',
- // },
- // module_scripts[1].start
- // );
- // }
-
- return {
- html: parser.html,
- css: parser.css,
- // instance: instance_scripts[0],
- module: astro_scripts[0],
- meta: {
- features: parser.feature_flags,
- },
- };
+ const parser = new Parser(template, options);
+
+ // const instance_scripts = parser.js.filter((script) => script.context === 'default');
+ // const module_scripts = parser.js.filter((script) => script.context === 'module');
+ const astro_scripts = parser.js.filter((script) => script.context === 'setup');
+
+ if (astro_scripts.length > 1) {
+ parser.error(
+ {
+ code: 'invalid-script',
+ message: 'A component can only have one frontmatter (---) script',
+ },
+ astro_scripts[1].start
+ );
+ }
+
+ // if (module_scripts.length > 1) {
+ // parser.error(
+ // {
+ // code: 'invalid-script',
+ // message: 'A component can only have one <script context="module"> element',
+ // },
+ // module_scripts[1].start
+ // );
+ // }
+
+ return {
+ html: parser.html,
+ css: parser.css,
+ // instance: instance_scripts[0],
+ module: astro_scripts[0],
+ meta: {
+ features: parser.feature_flags,
+ },
+ };
}
diff --git a/packages/astro-parser/src/parse/read/context.ts b/packages/astro-parser/src/parse/read/context.ts
index 565c66d18..4b69c693f 100644
--- a/packages/astro-parser/src/parse/read/context.ts
+++ b/packages/astro-parser/src/parse/read/context.ts
@@ -8,65 +8,65 @@ import { parse_expression_at } from './expression.js';
import { Pattern } from 'estree';
export default function read_context(parser: Parser): Pattern & { start: number; end: number } {
- const start = parser.index;
- let i = parser.index;
+ const start = parser.index;
+ let i = parser.index;
- const code = full_char_code_at(parser.template, i);
- if (isIdentifierStart(code, true)) {
- return {
- type: 'Identifier',
- name: parser.read_identifier(),
- start,
- end: parser.index,
- };
- }
+ const code = full_char_code_at(parser.template, i);
+ if (isIdentifierStart(code, true)) {
+ return {
+ type: 'Identifier',
+ name: parser.read_identifier(),
+ start,
+ end: parser.index,
+ };
+ }
- if (!is_bracket_open(code)) {
- parser.error({
- code: 'unexpected-token',
- message: 'Expected identifier or destructure pattern',
- });
- }
+ if (!is_bracket_open(code)) {
+ parser.error({
+ code: 'unexpected-token',
+ message: 'Expected identifier or destructure pattern',
+ });
+ }
- const bracket_stack = [code];
- i += code <= 0xffff ? 1 : 2;
+ const bracket_stack = [code];
+ i += code <= 0xffff ? 1 : 2;
- while (i < parser.template.length) {
- const code = full_char_code_at(parser.template, i);
- if (is_bracket_open(code)) {
- bracket_stack.push(code);
- } else if (is_bracket_close(code)) {
- if (!is_bracket_pair(bracket_stack[bracket_stack.length - 1], code)) {
- parser.error({
- code: 'unexpected-token',
- message: `Expected ${String.fromCharCode(get_bracket_close(bracket_stack[bracket_stack.length - 1]))}`,
- });
- }
- bracket_stack.pop();
- if (bracket_stack.length === 0) {
- i += code <= 0xffff ? 1 : 2;
- break;
- }
- }
- i += code <= 0xffff ? 1 : 2;
- }
+ while (i < parser.template.length) {
+ const code = full_char_code_at(parser.template, i);
+ if (is_bracket_open(code)) {
+ bracket_stack.push(code);
+ } else if (is_bracket_close(code)) {
+ if (!is_bracket_pair(bracket_stack[bracket_stack.length - 1], code)) {
+ parser.error({
+ code: 'unexpected-token',
+ message: `Expected ${String.fromCharCode(get_bracket_close(bracket_stack[bracket_stack.length - 1]))}`,
+ });
+ }
+ bracket_stack.pop();
+ if (bracket_stack.length === 0) {
+ i += code <= 0xffff ? 1 : 2;
+ break;
+ }
+ }
+ i += code <= 0xffff ? 1 : 2;
+ }
- parser.index = i;
+ parser.index = i;
- const pattern_string = parser.template.slice(start, i);
- try {
- // the length of the `space_with_newline` has to be start - 1
- // because we added a `(` in front of the pattern_string,
- // which shifted the entire string to right by 1
- // so we offset it by removing 1 character in the `space_with_newline`
- // to achieve that, we remove the 1st space encountered,
- // so it will not affect the `column` of the node
- let space_with_newline = parser.template.slice(0, start).replace(/[^\n]/g, ' ');
- const first_space = space_with_newline.indexOf(' ');
- space_with_newline = space_with_newline.slice(0, first_space) + space_with_newline.slice(first_space + 1);
+ const pattern_string = parser.template.slice(start, i);
+ try {
+ // the length of the `space_with_newline` has to be start - 1
+ // because we added a `(` in front of the pattern_string,
+ // which shifted the entire string to right by 1
+ // so we offset it by removing 1 character in the `space_with_newline`
+ // to achieve that, we remove the 1st space encountered,
+ // so it will not affect the `column` of the node
+ let space_with_newline = parser.template.slice(0, start).replace(/[^\n]/g, ' ');
+ const first_space = space_with_newline.indexOf(' ');
+ space_with_newline = space_with_newline.slice(0, first_space) + space_with_newline.slice(first_space + 1);
- return (parse_expression_at(`${space_with_newline}(${pattern_string} = 1)`, start - 1) as any).left;
- } catch (error) {
- parser.acorn_error(error);
- }
+ return (parse_expression_at(`${space_with_newline}(${pattern_string} = 1)`, start - 1) as any).left;
+ } catch (error) {
+ parser.acorn_error(error);
+ }
}
diff --git a/packages/astro-parser/src/parse/read/expression.ts b/packages/astro-parser/src/parse/read/expression.ts
index b4647ab06..5d4b05c99 100644
--- a/packages/astro-parser/src/parse/read/expression.ts
+++ b/packages/astro-parser/src/parse/read/expression.ts
@@ -3,252 +3,252 @@ import { Parser } from '../index.js';
import parseAstro from '../index.js';
interface ParseState {
- source: string;
- start: number;
- index: number;
- curlyCount: number;
- bracketCount: number;
- root: Expression;
- parser: Parser;
+ source: string;
+ start: number;
+ index: number;
+ curlyCount: number;
+ bracketCount: number;
+ root: Expression;
+ parser: Parser;
}
function peek_char(state: ParseState) {
- return state.source[state.index];
+ return state.source[state.index];
}
function peek_nonwhitespace(state: ParseState) {
- let index = state.index;
- do {
- let char = state.source[index];
- if (!/\s/.test(char)) {
- return char;
- }
- index++;
- } while (index < state.source.length);
+ let index = state.index;
+ do {
+ let char = state.source[index];
+ if (!/\s/.test(char)) {
+ return char;
+ }
+ index++;
+ } while (index < state.source.length);
}
function next_char(state: ParseState) {
- return state.source[state.index++];
+ return state.source[state.index++];
}
function in_bounds(state: ParseState) {
- return state.index < state.source.length;
+ return state.index < state.source.length;
}
function consume_string(state: ParseState, stringChar: string) {
- let inEscape;
- do {
- const char = next_char(state);
-
- if (inEscape) {
- inEscape = false;
- } else if (char === '\\') {
- inEscape = true;
- } else if (char === stringChar) {
- break;
- }
- } while (in_bounds(state));
+ let inEscape;
+ do {
+ const char = next_char(state);
+
+ if (inEscape) {
+ inEscape = false;
+ } else if (char === '\\') {
+ inEscape = true;
+ } else if (char === stringChar) {
+ break;
+ }
+ } while (in_bounds(state));
}
function consume_multiline_comment(state: ParseState) {
- do {
- const char = next_char(state);
+ do {
+ const char = next_char(state);
- if (char === '*' && peek_char(state) === '/') {
- break;
- }
- } while (in_bounds(state));
+ if (char === '*' && peek_char(state) === '/') {
+ break;
+ }
+ } while (in_bounds(state));
}
function consume_line_comment(state: ParseState) {
- do {
- const char = next_char(state);
- if (char === '\n') {
- break;
- }
- } while (in_bounds(state));
+ do {
+ const char = next_char(state);
+ if (char === '\n') {
+ break;
+ }
+ } while (in_bounds(state));
}
const voidElements = new Set(['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
function consume_tag(state: ParseState) {
- const start = state.index - 1;
- let tagName = '';
- let inTag = false;
- let inStart = true;
- let selfClosed = false;
- let inClose = false;
-
- let bracketIndex = 1;
- do {
- const char = next_char(state);
-
- switch (char) {
- case "'":
- case '"':
- case '`': {
- consume_string(state, char);
- break;
- }
- case '<': {
- inTag = false;
- tagName = '';
-
- if (peek_nonwhitespace(state) === '/') {
- inClose = true;
- bracketIndex--;
- } else {
- inStart = true;
- bracketIndex++;
- }
- break;
- }
- case '>': {
- // An arrow function, probably
- if (!inStart && !inClose) {
- break;
- }
-
- bracketIndex--;
-
- const addExpectedBrackets =
- // Void elements don't need a closing
- !voidElements.has(tagName.toLowerCase()) &&
- // Self-closing don't need a closing
- !selfClosed &&
- // If we're in a start tag, we expect to find 2 more brackets
- !inClose;
-
- if (addExpectedBrackets) {
- bracketIndex += 2;
- }
-
- inTag = false;
- selfClosed = false;
- inStart = false;
- inClose = false;
- break;
- }
- case ' ': {
- inTag = true;
- break;
- }
- case '/': {
- if (inStart) {
- selfClosed = true;
- }
- break;
- }
- default: {
- if (!inTag) {
- tagName += char;
- }
- break;
- }
- }
-
- // Unclosed tags
- if (state.curlyCount <= 0) {
- break;
- }
-
- if (bracketIndex === 0) {
- break;
- }
- } while (in_bounds(state));
-
- const source = state.source.substring(start, state.index);
-
- const ast = parseAstro(source);
- state.parser.feature_flags |= ast.meta.features;
- const fragment = ast.html;
-
- return fragment;
+ const start = state.index - 1;
+ let tagName = '';
+ let inTag = false;
+ let inStart = true;
+ let selfClosed = false;
+ let inClose = false;
+
+ let bracketIndex = 1;
+ do {
+ const char = next_char(state);
+
+ switch (char) {
+ case "'":
+ case '"':
+ case '`': {
+ consume_string(state, char);
+ break;
+ }
+ case '<': {
+ inTag = false;
+ tagName = '';
+
+ if (peek_nonwhitespace(state) === '/') {
+ inClose = true;
+ bracketIndex--;
+ } else {
+ inStart = true;
+ bracketIndex++;
+ }
+ break;
+ }
+ case '>': {
+ // An arrow function, probably
+ if (!inStart && !inClose) {
+ break;
+ }
+
+ bracketIndex--;
+
+ const addExpectedBrackets =
+ // Void elements don't need a closing
+ !voidElements.has(tagName.toLowerCase()) &&
+ // Self-closing don't need a closing
+ !selfClosed &&
+ // If we're in a start tag, we expect to find 2 more brackets
+ !inClose;
+
+ if (addExpectedBrackets) {
+ bracketIndex += 2;
+ }
+
+ inTag = false;
+ selfClosed = false;
+ inStart = false;
+ inClose = false;
+ break;
+ }
+ case ' ': {
+ inTag = true;
+ break;
+ }
+ case '/': {
+ if (inStart) {
+ selfClosed = true;
+ }
+ break;
+ }
+ default: {
+ if (!inTag) {
+ tagName += char;
+ }
+ break;
+ }
+ }
+
+ // Unclosed tags
+ if (state.curlyCount <= 0) {
+ break;
+ }
+
+ if (bracketIndex === 0) {
+ break;
+ }
+ } while (in_bounds(state));
+
+ const source = state.source.substring(start, state.index);
+
+ const ast = parseAstro(source);
+ state.parser.feature_flags |= ast.meta.features;
+ const fragment = ast.html;
+
+ return fragment;
}
function consume_expression(parser: Parser, source: string, start: number): Expression {
- const expr: Expression = {
- type: 'Expression',
- start,
- end: Number.NaN,
- codeChunks: [],
- children: [],
- };
-
- let codeStart: number = start;
-
- const state: ParseState = {
- source,
- start,
- index: start,
- curlyCount: 1,
- bracketCount: 0,
- root: expr,
- parser,
- };
-
- do {
- const char = next_char(state);
-
- switch (char) {
- case '{': {
- state.curlyCount++;
- break;
- }
- case '}': {
- state.curlyCount--;
- break;
- }
- case '<': {
- const chunk = source.substring(codeStart, state.index - 1);
- expr.codeChunks.push(chunk);
- const tag = consume_tag(state);
- expr.children.push(tag);
- codeStart = state.index;
- break;
- }
- case "'":
- case '"':
- case '`': {
- consume_string(state, char);
- break;
- }
- case '/': {
- switch (peek_char(state)) {
- case '/': {
- consume_line_comment(state);
- break;
- }
- case '*': {
- consume_multiline_comment(state);
- break;
- }
- }
- }
- }
- } while (in_bounds(state) && state.curlyCount > 0);
-
- expr.end = state.index - 1;
-
- if (expr.children.length || !expr.codeChunks.length) {
- expr.codeChunks.push(source.substring(codeStart, expr.end));
- }
-
- return expr;
+ const expr: Expression = {
+ type: 'Expression',
+ start,
+ end: Number.NaN,
+ codeChunks: [],
+ children: [],
+ };
+
+ let codeStart: number = start;
+
+ const state: ParseState = {
+ source,
+ start,
+ index: start,
+ curlyCount: 1,
+ bracketCount: 0,
+ root: expr,
+ parser,
+ };
+
+ do {
+ const char = next_char(state);
+
+ switch (char) {
+ case '{': {
+ state.curlyCount++;
+ break;
+ }
+ case '}': {
+ state.curlyCount--;
+ break;
+ }
+ case '<': {
+ const chunk = source.substring(codeStart, state.index - 1);
+ expr.codeChunks.push(chunk);
+ const tag = consume_tag(state);
+ expr.children.push(tag);
+ codeStart = state.index;
+ break;
+ }
+ case "'":
+ case '"':
+ case '`': {
+ consume_string(state, char);
+ break;
+ }
+ case '/': {
+ switch (peek_char(state)) {
+ case '/': {
+ consume_line_comment(state);
+ break;
+ }
+ case '*': {
+ consume_multiline_comment(state);
+ break;
+ }
+ }
+ }
+ }
+ } while (in_bounds(state) && state.curlyCount > 0);
+
+ expr.end = state.index - 1;
+
+ if (expr.children.length || !expr.codeChunks.length) {
+ expr.codeChunks.push(source.substring(codeStart, expr.end));
+ }
+
+ return expr;
}
export const parse_expression_at = (parser: Parser, source: string, index: number): Expression => {
- const expression = consume_expression(parser, source, index);
+ const expression = consume_expression(parser, source, index);
- return expression;
+ return expression;
};
export default function read_expression(parser: Parser) {
- try {
- const expression = parse_expression_at(parser, parser.template, parser.index);
- parser.index = expression.end;
- return expression;
- } catch (err) {
- parser.acorn_error(err);
- }
+ try {
+ const expression = parse_expression_at(parser, parser.template, parser.index);
+ parser.index = expression.end;
+ return expression;
+ } catch (err) {
+ parser.acorn_error(err);
+ }
}
diff --git a/packages/astro-parser/src/parse/read/script.ts b/packages/astro-parser/src/parse/read/script.ts
index 9b8d71110..8725f6a8e 100644
--- a/packages/astro-parser/src/parse/read/script.ts
+++ b/packages/astro-parser/src/parse/read/script.ts
@@ -7,54 +7,54 @@ import { Script } from '../../interfaces.js';
const script_closing_tag = '</script>';
function get_context(parser: Parser, attributes: any[], start: number): 'runtime' | 'setup' {
- const context = attributes.find((attribute) => attribute.name === 'astro');
- if (!context) return 'runtime';
- if (context.value === true) return 'setup';
-
- if (context.value.length !== 1 || context.value[0].type !== 'Text') {
- parser.error(
- {
- code: 'invalid-script',
- message: 'astro attribute must be static',
- },
- start
- );
- }
-
- const value = context.value[0].data;
-
- if (value !== 'setup') {
- parser.error(
- {
- code: 'invalid-script',
- message: 'If the "astro" attribute has a value, its value must be "setup"',
- },
- context.start
- );
- }
-
- return value;
+ const context = attributes.find((attribute) => attribute.name === 'astro');
+ if (!context) return 'runtime';
+ if (context.value === true) return 'setup';
+
+ if (context.value.length !== 1 || context.value[0].type !== 'Text') {
+ parser.error(
+ {
+ code: 'invalid-script',
+ message: 'astro attribute must be static',
+ },
+ start
+ );
+ }
+
+ const value = context.value[0].data;
+
+ if (value !== 'setup') {
+ parser.error(
+ {
+ code: 'invalid-script',
+ message: 'If the "astro" attribute has a value, its value must be "setup"',
+ },
+ context.start
+ );
+ }
+
+ return value;
}
export default function read_script(parser: Parser, start: number, attributes: Node[]): Script {
- const script_start = parser.index;
- const script_end = parser.template.indexOf(script_closing_tag, script_start);
-
- if (script_end === -1) {
- parser.error({
- code: 'unclosed-script',
- message: '<script> must have a closing tag',
- });
- }
-
- const source = parser.template.slice(0, script_start).replace(/[^\n]/g, ' ') + parser.template.slice(script_start, script_end);
- parser.index = script_end + script_closing_tag.length;
-
- return {
- type: 'Script',
- start,
- end: parser.index,
- context: get_context(parser, attributes, start),
- content: source,
- };
+ const script_start = parser.index;
+ const script_end = parser.template.indexOf(script_closing_tag, script_start);
+
+ if (script_end === -1) {
+ parser.error({
+ code: 'unclosed-script',
+ message: '<script> must have a closing tag',
+ });
+ }
+
+ const source = parser.template.slice(0, script_start).replace(/[^\n]/g, ' ') + parser.template.slice(script_start, script_end);
+ parser.index = script_end + script_closing_tag.length;
+
+ return {
+ type: 'Script',
+ start,
+ end: parser.index,
+ context: get_context(parser, attributes, start),
+ content: source,
+ };
}
diff --git a/packages/astro-parser/src/parse/read/style.ts b/packages/astro-parser/src/parse/read/style.ts
index f23d7b10e..05fb4141d 100644
--- a/packages/astro-parser/src/parse/read/style.ts
+++ b/packages/astro-parser/src/parse/read/style.ts
@@ -2,39 +2,39 @@ import { Parser } from '../index.js';
import { Style } from '../../interfaces.js';
interface Attribute {
- start: number;
- end: number;
- type: 'Attribute';
- name: string;
- value: {
- raw: string;
- data: string;
- }[];
+ start: number;
+ end: number;
+ type: 'Attribute';
+ name: string;
+ value: {
+ raw: string;
+ data: string;
+ }[];
}
export default function read_style(parser: Parser, start: number, attributes: Attribute[]): Style {
- const content_start = parser.index;
- const styles = parser.read_until(/<\/style>/);
- const content_end = parser.index;
- parser.eat('</style>', true);
- const end = parser.index;
+ const content_start = parser.index;
+ const styles = parser.read_until(/<\/style>/);
+ const content_end = parser.index;
+ parser.eat('</style>', true);
+ const end = parser.index;
- return {
- type: 'Style',
- start,
- end,
- attributes,
- content: {
- start: content_start,
- end: content_end,
- styles,
- },
- };
+ return {
+ type: 'Style',
+ start,
+ end,
+ attributes,
+ content: {
+ start: content_start,
+ end: content_end,
+ styles,
+ },
+ };
}
function is_ref_selector(a: any, b: any) {
- // TODO add CSS node types
- if (!b) return false;
+ // TODO add CSS node types
+ if (!b) return false;
- return a.type === 'TypeSelector' && a.name === 'ref' && b.type === 'PseudoClassSelector';
+ return a.type === 'TypeSelector' && a.name === 'ref' && b.type === 'PseudoClassSelector';
}
diff --git a/packages/astro-parser/src/parse/state/codefence.ts b/packages/astro-parser/src/parse/state/codefence.ts
index 72867867c..b8e96b262 100644
--- a/packages/astro-parser/src/parse/state/codefence.ts
+++ b/packages/astro-parser/src/parse/state/codefence.ts
@@ -2,37 +2,37 @@
import { Parser } from '../index.js';
export default function codefence(parser: Parser) {
- const start = parser.index;
- const open = parser.match_regex(/[`~]{3,}/);
- parser.index += open!.length;
+ const start = parser.index;
+ const open = parser.match_regex(/[`~]{3,}/);
+ parser.index += open!.length;
- let raw = open + '';
+ let raw = open + '';
- while (parser.index < parser.template.length && !parser.match(open)) {
- raw += parser.template[parser.index++];
- }
+ while (parser.index < parser.template.length && !parser.match(open)) {
+ raw += parser.template[parser.index++];
+ }
- parser.eat(open, true);
- raw += open;
- const trailingWhitespace = parser.read_until(/\S/);
- const { metadata, data } = extractCodeFence(raw);
+ parser.eat(open, true);
+ raw += open;
+ const trailingWhitespace = parser.read_until(/\S/);
+ const { metadata, data } = extractCodeFence(raw);
- const node = {
- start,
- end: parser.index,
- type: 'CodeFence',
- raw: `${raw}` + trailingWhitespace,
- metadata,
- data,
- };
+ const node = {
+ start,
+ end: parser.index,
+ type: 'CodeFence',
+ raw: `${raw}` + trailingWhitespace,
+ metadata,
+ data,
+ };
- parser.current().children.push(node);
+ parser.current().children.push(node);
}
/** Extract attributes on first line */
function extractCodeFence(str: string) {
- const [_, leadingLine] = str.match(/(^[^\n]*\r?\n)/m) ?? ['', ''];
- const metadata = leadingLine.trim();
- const data = str.slice(leadingLine.length);
- return { metadata, data };
+ const [_, leadingLine] = str.match(/(^[^\n]*\r?\n)/m) ?? ['', ''];
+ const metadata = leadingLine.trim();
+ const data = str.slice(leadingLine.length);
+ return { metadata, data };
}
diff --git a/packages/astro-parser/src/parse/state/codespan.ts b/packages/astro-parser/src/parse/state/codespan.ts
index 7f4b3354a..42d0ae932 100644
--- a/packages/astro-parser/src/parse/state/codespan.ts
+++ b/packages/astro-parser/src/parse/state/codespan.ts
@@ -2,27 +2,27 @@
import { Parser } from '../index.js';
export default function codespan(parser: Parser) {
- const start = parser.index;
- const open = parser.match_regex(/(?<!\\)`{1,2}/);
- parser.index += open!.length;
+ const start = parser.index;
+ const open = parser.match_regex(/(?<!\\)`{1,2}/);
+ parser.index += open!.length;
- let raw = open;
- while (parser.index < parser.template.length && !parser.match(open)) {
- raw += parser.template[parser.index++];
- }
- parser.eat(open, true);
- raw += open;
+ let raw = open;
+ while (parser.index < parser.template.length && !parser.match(open)) {
+ raw += parser.template[parser.index++];
+ }
+ parser.eat(open, true);
+ raw += open;
- const node = {
- start,
- end: parser.index,
- type: 'CodeSpan',
- raw,
- data: raw
- ?.slice(open?.length, open?.length * -1)
- .replace(/^ /, '')
- .replace(/ $/, ''),
- };
+ const node = {
+ start,
+ end: parser.index,
+ type: 'CodeSpan',
+ raw,
+ data: raw
+ ?.slice(open?.length, open?.length * -1)
+ .replace(/^ /, '')
+ .replace(/ $/, ''),
+ };
- parser.current().children.push(node);
+ parser.current().children.push(node);
}
diff --git a/packages/astro-parser/src/parse/state/fragment.ts b/packages/astro-parser/src/parse/state/fragment.ts
index d3b30f329..fc8874a19 100644
--- a/packages/astro-parser/src/parse/state/fragment.ts
+++ b/packages/astro-parser/src/parse/state/fragment.ts
@@ -7,26 +7,26 @@ import codespan from './codespan.js';
import { Parser } from '../index.js';
export default function fragment(parser: Parser) {
- if (parser.html.children.length === 0 && parser.match_regex(/^---/m)) {
- return setup;
- }
+ if (parser.html.children.length === 0 && parser.match_regex(/^---/m)) {
+ return setup;
+ }
- // Fenced code blocks are pretty complex in the GFM spec
- // https://github.github.com/gfm/#fenced-code-blocks
- if (parser.match_regex(/[`~]{3,}/)) {
- return codefence;
- }
- if (parser.match_regex(/(?<!\\)`{1,2}/)) {
- return codespan;
- }
+ // Fenced code blocks are pretty complex in the GFM spec
+ // https://github.github.com/gfm/#fenced-code-blocks
+ if (parser.match_regex(/[`~]{3,}/)) {
+ return codefence;
+ }
+ if (parser.match_regex(/(?<!\\)`{1,2}/)) {
+ return codespan;
+ }
- if (parser.match('<')) {
- return tag;
- }
+ if (parser.match('<')) {
+ return tag;
+ }
- if (parser.match('{')) {
- return mustache;
- }
+ if (parser.match('{')) {
+ return mustache;
+ }
- return text;
+ return text;
}
diff --git a/packages/astro-parser/src/parse/state/mustache.ts b/packages/astro-parser/src/parse/state/mustache.ts
index d07162049..9300627e1 100644
--- a/packages/astro-parser/src/parse/state/mustache.ts
+++ b/packages/astro-parser/src/parse/state/mustache.ts
@@ -9,404 +9,404 @@ import { TemplateNode } from '../../interfaces.js';
type TODO = any;
function trim_whitespace(block: TemplateNode, trim_before: boolean, trim_after: boolean) {
- if (!block.children || block.children.length === 0) return; // AwaitBlock
+ if (!block.children || block.children.length === 0) return; // AwaitBlock
- const first_child = block.children[0];
- const last_child = block.children[block.children.length - 1];
+ const first_child = block.children[0];
+ const last_child = block.children[block.children.length - 1];
- if (first_child.type === 'Text' && trim_before) {
- first_child.data = first_child.data.trimStart();
- if (!first_child.data) block.children.shift();
- }
+ if (first_child.type === 'Text' && trim_before) {
+ first_child.data = first_child.data.trimStart();
+ if (!first_child.data) block.children.shift();
+ }
- if (last_child.type === 'Text' && trim_after) {
- last_child.data = last_child.data.trimEnd();
- if (!last_child.data) block.children.pop();
- }
+ if (last_child.type === 'Text' && trim_after) {
+ last_child.data = last_child.data.trimEnd();
+ if (!last_child.data) block.children.pop();
+ }
- if (block.else) {
- trim_whitespace(block.else, trim_before, trim_after);
- }
+ if (block.else) {
+ trim_whitespace(block.else, trim_before, trim_after);
+ }
- if (first_child.elseif) {
- trim_whitespace(first_child, trim_before, trim_after);
- }
+ if (first_child.elseif) {
+ trim_whitespace(first_child, trim_before, trim_after);
+ }
}
export default function mustache(parser: Parser) {
- const start = parser.index;
- parser.index += 1;
-
- parser.allow_whitespace();
-
- // {/if}, {/each}, {/await} or {/key}
- if (parser.eat('/')) {
- let block = parser.current();
- let expected: TODO;
-
- if (closing_tag_omitted(block.name)) {
- block.end = start;
- parser.stack.pop();
- block = parser.current();
- }
-
- if (block.type === 'ElseBlock' || block.type === 'PendingBlock' || block.type === 'ThenBlock' || block.type === 'CatchBlock') {
- block.end = start;
- parser.stack.pop();
- block = parser.current();
-
- expected = 'await';
- }
-
- if (block.type === 'IfBlock') {
- expected = 'if';
- } else if (block.type === 'EachBlock') {
- expected = 'each';
- } else if (block.type === 'AwaitBlock') {
- expected = 'await';
- } else if (block.type === 'KeyBlock') {
- expected = 'key';
- } else {
- parser.error({
- code: 'unexpected-block-close',
- message: 'Unexpected block closing tag',
- });
- }
-
- parser.eat(expected, true);
- parser.allow_whitespace();
- parser.eat('}', true);
-
- while (block.elseif) {
- block.end = parser.index;
- parser.stack.pop();
- block = parser.current();
-
- if (block.else) {
- block.else.end = start;
- }
- }
-
- // strip leading/trailing whitespace as necessary
- const char_before = parser.template[block.start - 1];
- const char_after = parser.template[parser.index];
- const trim_before = !char_before || whitespace.test(char_before);
- const trim_after = !char_after || whitespace.test(char_after);
-
- trim_whitespace(block, trim_before, trim_after);
-
- block.end = parser.index;
- parser.stack.pop();
- } else if (parser.eat(':else')) {
- if (parser.eat('if')) {
- parser.error({
- code: 'invalid-elseif',
- message: "'elseif' should be 'else if'",
- });
- }
-
- parser.allow_whitespace();
-
- // :else if
- if (parser.eat('if')) {
- const block = parser.current();
- if (block.type !== 'IfBlock') {
- parser.error({
- code: 'invalid-elseif-placement',
- message: parser.stack.some((block) => block.type === 'IfBlock')
- ? `Expected to close ${to_string(block)} before seeing {:else if ...} block`
- : 'Cannot have an {:else if ...} block outside an {#if ...} block',
- });
- }
-
- parser.require_whitespace();
-
- const expression = read_expression(parser);
-
- parser.allow_whitespace();
- parser.eat('}', true);
-
- block.else = {
- start: parser.index,
- end: null,
- type: 'ElseBlock',
- children: [
- {
- start: parser.index,
- end: null,
- type: 'IfBlock',
- elseif: true,
- expression,
- children: [],
- },
- ],
- };
-
- parser.stack.push(block.else.children[0]);
- } else {
- // :else
- const block = parser.current();
- if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
- parser.error({
- code: 'invalid-else-placement',
- message: parser.stack.some((block) => block.type === 'IfBlock' || block.type === 'EachBlock')
- ? `Expected to close ${to_string(block)} before seeing {:else} block`
- : 'Cannot have an {:else} block outside an {#if ...} or {#each ...} block',
- });
- }
-
- parser.allow_whitespace();
- parser.eat('}', true);
-
- block.else = {
- start: parser.index,
- end: null,
- type: 'ElseBlock',
- children: [],
- };
-
- parser.stack.push(block.else);
- }
- } else if (parser.match(':then') || parser.match(':catch')) {
- const block = parser.current();
- const is_then = parser.eat(':then') || !parser.eat(':catch');
-
- if (is_then) {
- if (block.type !== 'PendingBlock') {
- parser.error({
- code: 'invalid-then-placement',
- message: parser.stack.some((block) => block.type === 'PendingBlock')
- ? `Expected to close ${to_string(block)} before seeing {:then} block`
- : 'Cannot have an {:then} block outside an {#await ...} block',
- });
- }
- } else {
- if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') {
- parser.error({
- code: 'invalid-catch-placement',
- message: parser.stack.some((block) => block.type === 'ThenBlock' || block.type === 'PendingBlock')
- ? `Expected to close ${to_string(block)} before seeing {:catch} block`
- : 'Cannot have an {:catch} block outside an {#await ...} block',
- });
- }
- }
-
- block.end = start;
- parser.stack.pop();
- const await_block = parser.current();
-
- if (!parser.eat('}')) {
- parser.require_whitespace();
- await_block[is_then ? 'value' : 'error'] = read_context(parser);
- parser.allow_whitespace();
- parser.eat('}', true);
- }
-
- const new_block: TemplateNode = {
- start,
- // @ts-ignore
- end: null,
- type: is_then ? 'ThenBlock' : 'CatchBlock',
- children: [],
- skip: false,
- };
-
- await_block[is_then ? 'then' : 'catch'] = new_block;
- parser.stack.push(new_block);
- } else if (parser.eat('#')) {
- // {#if foo}, {#each foo} or {#await foo}
- let type;
-
- if (parser.eat('if')) {
- type = 'IfBlock';
- } else if (parser.eat('each')) {
- type = 'EachBlock';
- } else if (parser.eat('await')) {
- type = 'AwaitBlock';
- } else if (parser.eat('key')) {
- type = 'KeyBlock';
- } else {
- parser.error({
- code: 'expected-block-type',
- message: 'Expected if, each, await or key',
- });
- }
-
- parser.require_whitespace();
-
- const expression = read_expression(parser);
-
- // @ts-ignore
- const block: TemplateNode =
- type === 'AwaitBlock'
- ? {
- start,
- end: null,
- type,
- expression,
- value: null,
- error: null,
- pending: {
- start: null,
- end: null,
- type: 'PendingBlock',
- children: [],
- skip: true,
- },
- then: {
- start: null,
- end: null,
- type: 'ThenBlock',
- children: [],
- skip: true,
- },
- catch: {
- start: null,
- end: null,
- type: 'CatchBlock',
- children: [],
- skip: true,
- },
- }
- : {
- start,
- end: null,
- type,
- expression,
- children: [],
- };
-
- parser.allow_whitespace();
-
- // {#each} blocks must declare a context – {#each list as item}
- if (type === 'EachBlock') {
- parser.eat('as', true);
- parser.require_whitespace();
-
- block.context = read_context(parser);
-
- parser.allow_whitespace();
-
- if (parser.eat(',')) {
- parser.allow_whitespace();
- block.index = parser.read_identifier();
- if (!block.index) {
- parser.error({
- code: 'expected-name',
- message: 'Expected name',
- });
- }
-
- parser.allow_whitespace();
- }
-
- if (parser.eat('(')) {
- parser.allow_whitespace();
-
- block.key = read_expression(parser);
- parser.allow_whitespace();
- parser.eat(')', true);
- parser.allow_whitespace();
- }
- }
-
- const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then');
- if (await_block_shorthand) {
- parser.require_whitespace();
- block.value = read_context(parser);
- parser.allow_whitespace();
- }
-
- const await_block_catch_shorthand = !await_block_shorthand && type === 'AwaitBlock' && parser.eat('catch');
- if (await_block_catch_shorthand) {
- parser.require_whitespace();
- block.error = read_context(parser);
- parser.allow_whitespace();
- }
-
- parser.eat('}', true);
-
- // @ts-ignore
- parser.current().children.push(block);
- parser.stack.push(block);
-
- if (type === 'AwaitBlock') {
- let child_block;
- if (await_block_shorthand) {
- block.then.skip = false;
- child_block = block.then;
- } else if (await_block_catch_shorthand) {
- block.catch.skip = false;
- child_block = block.catch;
- } else {
- block.pending.skip = false;
- child_block = block.pending;
- }
-
- child_block.start = parser.index;
- parser.stack.push(child_block);
- }
- } else if (parser.eat('@html')) {
- // {@html content} tag
- parser.require_whitespace();
-
- const expression = read_expression(parser);
-
- parser.allow_whitespace();
- parser.eat('}', true);
-
- // @ts-ignore
- parser.current().children.push({
- start,
- end: parser.index,
- type: 'RawMustacheTag',
- expression,
- });
- } else if (parser.eat('@debug')) {
- // let identifiers;
-
- // // Implies {@debug} which indicates "debug all"
- // if (parser.read(/\s*}/)) {
- // identifiers = [];
- // } else {
- // const expression = read_expression(parser);
-
- // identifiers = expression.type === 'SequenceExpression'
- // ? expression.expressions
- // : [expression];
-
- // identifiers.forEach(node => {
- // if (node.type !== 'Identifier') {
- // parser.error({
- // code: 'invalid-debug-args',
- // message: '{@debug ...} arguments must be identifiers, not arbitrary expressions'
- // }, node.start);
- // }
- // });
-
- // parser.allow_whitespace();
- // parser.eat('}', true);
- // }
-
- // parser.current().children.push({
- // start,
- // end: parser.index,
- // type: 'DebugTag',
- // identifiers
- // });
- throw new Error('@debug not yet supported');
- } else {
- const expression = read_expression(parser);
-
- parser.allow_whitespace();
- parser.eat('}', true);
-
- // @ts-ignore
- parser.current().children.push({
- start,
- end: parser.index,
- type: 'MustacheTag',
- expression,
- });
- }
+ const start = parser.index;
+ parser.index += 1;
+
+ parser.allow_whitespace();
+
+ // {/if}, {/each}, {/await} or {/key}
+ if (parser.eat('/')) {
+ let block = parser.current();
+ let expected: TODO;
+
+ if (closing_tag_omitted(block.name)) {
+ block.end = start;
+ parser.stack.pop();
+ block = parser.current();
+ }
+
+ if (block.type === 'ElseBlock' || block.type === 'PendingBlock' || block.type === 'ThenBlock' || block.type === 'CatchBlock') {
+ block.end = start;
+ parser.stack.pop();
+ block = parser.current();
+
+ expected = 'await';
+ }
+
+ if (block.type === 'IfBlock') {
+ expected = 'if';
+ } else if (block.type === 'EachBlock') {
+ expected = 'each';
+ } else if (block.type === 'AwaitBlock') {
+ expected = 'await';
+ } else if (block.type === 'KeyBlock') {
+ expected = 'key';
+ } else {
+ parser.error({
+ code: 'unexpected-block-close',
+ message: 'Unexpected block closing tag',
+ });
+ }
+
+ parser.eat(expected, true);
+ parser.allow_whitespace();
+ parser.eat('}', true);
+
+ while (block.elseif) {
+ block.end = parser.index;
+ parser.stack.pop();
+ block = parser.current();
+
+ if (block.else) {
+ block.else.end = start;
+ }
+ }
+
+ // strip leading/trailing whitespace as necessary
+ const char_before = parser.template[block.start - 1];
+ const char_after = parser.template[parser.index];
+ const trim_before = !char_before || whitespace.test(char_before);
+ const trim_after = !char_after || whitespace.test(char_after);
+
+ trim_whitespace(block, trim_before, trim_after);
+
+ block.end = parser.index;
+ parser.stack.pop();
+ } else if (parser.eat(':else')) {
+ if (parser.eat('if')) {
+ parser.error({
+ code: 'invalid-elseif',
+ message: "'elseif' should be 'else if'",
+ });
+ }
+
+ parser.allow_whitespace();
+
+ // :else if
+ if (parser.eat('if')) {
+ const block = parser.current();
+ if (block.type !== 'IfBlock') {
+ parser.error({
+ code: 'invalid-elseif-placement',
+ message: parser.stack.some((block) => block.type === 'IfBlock')
+ ? `Expected to close ${to_string(block)} before seeing {:else if ...} block`
+ : 'Cannot have an {:else if ...} block outside an {#if ...} block',
+ });
+ }
+
+ parser.require_whitespace();
+
+ const expression = read_expression(parser);
+
+ parser.allow_whitespace();
+ parser.eat('}', true);
+
+ block.else = {
+ start: parser.index,
+ end: null,
+ type: 'ElseBlock',
+ children: [
+ {
+ start: parser.index,
+ end: null,
+ type: 'IfBlock',
+ elseif: true,
+ expression,
+ children: [],
+ },
+ ],
+ };
+
+ parser.stack.push(block.else.children[0]);
+ } else {
+ // :else
+ const block = parser.current();
+ if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
+ parser.error({
+ code: 'invalid-else-placement',
+ message: parser.stack.some((block) => block.type === 'IfBlock' || block.type === 'EachBlock')
+ ? `Expected to close ${to_string(block)} before seeing {:else} block`
+ : 'Cannot have an {:else} block outside an {#if ...} or {#each ...} block',
+ });
+ }
+
+ parser.allow_whitespace();
+ parser.eat('}', true);
+
+ block.else = {
+ start: parser.index,
+ end: null,
+ type: 'ElseBlock',
+ children: [],
+ };
+
+ parser.stack.push(block.else);
+ }
+ } else if (parser.match(':then') || parser.match(':catch')) {
+ const block = parser.current();
+ const is_then = parser.eat(':then') || !parser.eat(':catch');
+
+ if (is_then) {
+ if (block.type !== 'PendingBlock') {
+ parser.error({
+ code: 'invalid-then-placement',
+ message: parser.stack.some((block) => block.type === 'PendingBlock')
+ ? `Expected to close ${to_string(block)} before seeing {:then} block`
+ : 'Cannot have an {:then} block outside an {#await ...} block',
+ });
+ }
+ } else {
+ if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') {
+ parser.error({
+ code: 'invalid-catch-placement',
+ message: parser.stack.some((block) => block.type === 'ThenBlock' || block.type === 'PendingBlock')
+ ? `Expected to close ${to_string(block)} before seeing {:catch} block`
+ : 'Cannot have an {:catch} block outside an {#await ...} block',
+ });
+ }
+ }
+
+ block.end = start;
+ parser.stack.pop();
+ const await_block = parser.current();
+
+ if (!parser.eat('}')) {
+ parser.require_whitespace();
+ await_block[is_then ? 'value' : 'error'] = read_context(parser);
+ parser.allow_whitespace();
+ parser.eat('}', true);
+ }
+
+ const new_block: TemplateNode = {
+ start,
+ // @ts-ignore
+ end: null,
+ type: is_then ? 'ThenBlock' : 'CatchBlock',
+ children: [],
+ skip: false,
+ };
+
+ await_block[is_then ? 'then' : 'catch'] = new_block;
+ parser.stack.push(new_block);
+ } else if (parser.eat('#')) {
+ // {#if foo}, {#each foo} or {#await foo}
+ let type;
+
+ if (parser.eat('if')) {
+ type = 'IfBlock';
+ } else if (parser.eat('each')) {
+ type = 'EachBlock';
+ } else if (parser.eat('await')) {
+ type = 'AwaitBlock';
+ } else if (parser.eat('key')) {
+ type = 'KeyBlock';
+ } else {
+ parser.error({
+ code: 'expected-block-type',
+ message: 'Expected if, each, await or key',
+ });
+ }
+
+ parser.require_whitespace();
+
+ const expression = read_expression(parser);
+
+ // @ts-ignore
+ const block: TemplateNode =
+ type === 'AwaitBlock'
+ ? {
+ start,
+ end: null,
+ type,
+ expression,
+ value: null,
+ error: null,
+ pending: {
+ start: null,
+ end: null,
+ type: 'PendingBlock',
+ children: [],
+ skip: true,
+ },
+ then: {
+ start: null,
+ end: null,
+ type: 'ThenBlock',
+ children: [],
+ skip: true,
+ },
+ catch: {
+ start: null,
+ end: null,
+ type: 'CatchBlock',
+ children: [],
+ skip: true,
+ },
+ }
+ : {
+ start,
+ end: null,
+ type,
+ expression,
+ children: [],
+ };
+
+ parser.allow_whitespace();
+
+ // {#each} blocks must declare a context – {#each list as item}
+ if (type === 'EachBlock') {
+ parser.eat('as', true);
+ parser.require_whitespace();
+
+ block.context = read_context(parser);
+
+ parser.allow_whitespace();
+
+ if (parser.eat(',')) {
+ parser.allow_whitespace();
+ block.index = parser.read_identifier();
+ if (!block.index) {
+ parser.error({
+ code: 'expected-name',
+ message: 'Expected name',
+ });
+ }
+
+ parser.allow_whitespace();
+ }
+
+ if (parser.eat('(')) {
+ parser.allow_whitespace();
+
+ block.key = read_expression(parser);
+ parser.allow_whitespace();
+ parser.eat(')', true);
+ parser.allow_whitespace();
+ }
+ }
+
+ const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then');
+ if (await_block_shorthand) {
+ parser.require_whitespace();
+ block.value = read_context(parser);
+ parser.allow_whitespace();
+ }
+
+ const await_block_catch_shorthand = !await_block_shorthand && type === 'AwaitBlock' && parser.eat('catch');
+ if (await_block_catch_shorthand) {
+ parser.require_whitespace();
+ block.error = read_context(parser);
+ parser.allow_whitespace();
+ }
+
+ parser.eat('}', true);
+
+ // @ts-ignore
+ parser.current().children.push(block);
+ parser.stack.push(block);
+
+ if (type === 'AwaitBlock') {
+ let child_block;
+ if (await_block_shorthand) {
+ block.then.skip = false;
+ child_block = block.then;
+ } else if (await_block_catch_shorthand) {
+ block.catch.skip = false;
+ child_block = block.catch;
+ } else {
+ block.pending.skip = false;
+ child_block = block.pending;
+ }
+
+ child_block.start = parser.index;
+ parser.stack.push(child_block);
+ }
+ } else if (parser.eat('@html')) {
+ // {@html content} tag
+ parser.require_whitespace();
+
+ const expression = read_expression(parser);
+
+ parser.allow_whitespace();
+ parser.eat('}', true);
+
+ // @ts-ignore
+ parser.current().children.push({
+ start,
+ end: parser.index,
+ type: 'RawMustacheTag',
+ expression,
+ });
+ } else if (parser.eat('@debug')) {
+ // let identifiers;
+
+ // // Implies {@debug} which indicates "debug all"
+ // if (parser.read(/\s*}/)) {
+ // identifiers = [];
+ // } else {
+ // const expression = read_expression(parser);
+
+ // identifiers = expression.type === 'SequenceExpression'
+ // ? expression.expressions
+ // : [expression];
+
+ // identifiers.forEach(node => {
+ // if (node.type !== 'Identifier') {
+ // parser.error({
+ // code: 'invalid-debug-args',
+ // message: '{@debug ...} arguments must be identifiers, not arbitrary expressions'
+ // }, node.start);
+ // }
+ // });
+
+ // parser.allow_whitespace();
+ // parser.eat('}', true);
+ // }
+
+ // parser.current().children.push({
+ // start,
+ // end: parser.index,
+ // type: 'DebugTag',
+ // identifiers
+ // });
+ throw new Error('@debug not yet supported');
+ } else {
+ const expression = read_expression(parser);
+
+ parser.allow_whitespace();
+ parser.eat('}', true);
+
+ // @ts-ignore
+ parser.current().children.push({
+ start,
+ end: parser.index,
+ type: 'MustacheTag',
+ expression,
+ });
+ }
}
diff --git a/packages/astro-parser/src/parse/state/setup.ts b/packages/astro-parser/src/parse/state/setup.ts
index f64d8c52b..cb23860ad 100644
--- a/packages/astro-parser/src/parse/state/setup.ts
+++ b/packages/astro-parser/src/parse/state/setup.ts
@@ -3,33 +3,33 @@
import { Parser } from '../index.js';
export default function setup(parser: Parser): void {
- // TODO: Error if not at top of file? currently, we ignore / just treat as text.
- // if (parser.html.children.length > 0) {
- // parser.error({
- // code: 'unexpected-token',
- // message: 'Frontmatter scripts only supported at the top of file.',
- // });
- // }
+ // TODO: Error if not at top of file? currently, we ignore / just treat as text.
+ // if (parser.html.children.length > 0) {
+ // parser.error({
+ // code: 'unexpected-token',
+ // message: 'Frontmatter scripts only supported at the top of file.',
+ // });
+ // }
- const start = parser.index;
- parser.index += 3;
- const content_start = parser.index;
- const setupScriptContent = parser.read_until(/^---/m);
- const content_end = parser.index;
- parser.eat('---', true);
- const end = parser.index;
- parser.js.push({
- type: 'Script',
- context: 'setup',
- start,
- end,
- content: setupScriptContent,
- // attributes,
- // content: {
- // start: content_start,
- // end: content_end,
- // styles,
- // },
- });
- return;
+ const start = parser.index;
+ parser.index += 3;
+ const content_start = parser.index;
+ const setupScriptContent = parser.read_until(/^---/m);
+ const content_end = parser.index;
+ parser.eat('---', true);
+ const end = parser.index;
+ parser.js.push({
+ type: 'Script',
+ context: 'setup',
+ start,
+ end,
+ content: setupScriptContent,
+ // attributes,
+ // content: {
+ // start: content_start,
+ // end: content_end,
+ // styles,
+ // },
+ });
+ return;
}
diff --git a/packages/astro-parser/src/parse/state/tag.ts b/packages/astro-parser/src/parse/state/tag.ts
index 8f0b29a71..8f940cbc3 100644
--- a/packages/astro-parser/src/parse/state/tag.ts
+++ b/packages/astro-parser/src/parse/state/tag.ts
@@ -13,31 +13,31 @@ import { FEATURE_CUSTOM_ELEMENT } from '../utils/features.js';
const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const meta_tags = new Map([
- ['astro:head', 'Head'],
- ['', 'SlotTemplate'],
- // ['astro:options', 'Options'],
- // ['astro:window', 'Window'],
- // ['astro:body', 'Body'],
+ ['astro:head', 'Head'],
+ ['', 'SlotTemplate'],
+ // ['astro:options', 'Options'],
+ // ['astro:window', 'Window'],
+ // ['astro:body', 'Body'],
]);
const valid_meta_tags = Array.from(meta_tags.keys()); //.concat('astro:self', 'astro:component', 'astro:fragment');
const specials = new Map([
- // Now handled as "setup" in setup.ts
- // [
- // 'script',
- // {
- // read: read_script,
- // property: 'js',
- // },
- // ],
- [
- 'style',
- {
- read: read_style,
- property: 'css',
- },
- ],
+ // Now handled as "setup" in setup.ts
+ // [
+ // 'script',
+ // {
+ // read: read_script,
+ // property: 'js',
+ // },
+ // ],
+ [
+ 'style',
+ {
+ read: read_style,
+ property: 'css',
+ },
+ ],
]);
const SELF = /^astro:self(?=[\s/>])/;
@@ -47,453 +47,453 @@ const HEAD = /^head(?=[\s/>])/;
const CUSTOM_ELEMENT = /-/;
function parent_is_head(stack) {
- let i = stack.length;
- while (i--) {
- const { type } = stack[i];
- if (type === 'Head') return true;
- if (type === 'Element' || type === 'InlineComponent') return false;
- }
- return false;
+ let i = stack.length;
+ while (i--) {
+ const { type } = stack[i];
+ if (type === 'Head') return true;
+ if (type === 'Element' || type === 'InlineComponent') return false;
+ }
+ return false;
}
export default function tag(parser: Parser) {
- const start = parser.index++;
-
- let parent = parser.current();
-
- if (parser.eat('!--')) {
- const data = parser.read_until(/-->/);
- parser.eat('-->', true, 'comment was left open, expected -->');
-
- parser.current().children.push({
- start,
- end: parser.index,
- type: 'Comment',
- data,
- });
-
- return;
- }
-
- const is_closing_tag = parser.eat('/');
-
- const name = read_tag_name(parser);
-
- if (CUSTOM_ELEMENT.test(name)) {
- parser.feature_flags |= FEATURE_CUSTOM_ELEMENT;
- }
-
- if (meta_tags.has(name)) {
- const slug = meta_tags.get(name).toLowerCase();
- if (is_closing_tag) {
- if ((name === 'astro:window' || name === 'astro:body') && parser.current().children.length) {
- parser.error(
- {
- code: `invalid-${slug}-content`,
- message: `<${name}> cannot have children`,
- },
- parser.current().children[0].start
- );
- }
- } else {
- if (name in parser.meta_tags) {
- parser.error(
- {
- code: `duplicate-${slug}`,
- message: `A component can only have one <${name}> tag`,
- },
- start
- );
- }
-
- if (parser.stack.length > 1) {
- parser.error(
- {
- code: `invalid-${slug}-placement`,
- message: `<${name}> tags cannot be inside elements or blocks`,
- },
- start
- );
- }
-
- parser.meta_tags[name] = true;
- }
- }
-
- const type = meta_tags.has(name)
- ? meta_tags.get(name)
- : /[A-Z]/.test(name[0]) || name === 'astro:self' || name === 'astro:component'
- ? 'InlineComponent'
- : name === ''
- ? 'SlotTemplate'
- : name === 'title' && parent_is_head(parser.stack)
- ? 'Title'
- : name === 'slot' && !parser.customElement
- ? 'Slot'
- : 'Element';
-
- const element: TemplateNode = {
- start,
- end: null, // filled in later
- type,
- name,
- attributes: [],
- children: [],
- };
-
- parser.allow_whitespace();
-
- if (is_closing_tag) {
- if (is_void(name)) {
- parser.error(
- {
- code: 'invalid-void-content',
- message: `<${name}> is a void element and cannot have children, or a closing tag`,
- },
- start
- );
- }
-
- parser.eat('>', true);
-
- // close any elements that don't have their own closing tags, e.g. <div><p></div>
- while (parent.name !== name) {
- if (parent.type !== 'Element') {
- const message =
- parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name
- ? `</${name}> attempted to close <${name}> that was already automatically closed by <${parser.last_auto_closed_tag.reason}>`
- : `</${name}> attempted to close an element that was not open`;
- parser.error(
- {
- code: 'invalid-closing-tag',
- message,
- },
- start
- );
- }
-
- parent.end = start;
- parser.stack.pop();
-
- parent = parser.current();
- }
-
- parent.end = parser.index;
- parser.stack.pop();
-
- if (parser.last_auto_closed_tag && parser.stack.length < parser.last_auto_closed_tag.depth) {
- parser.last_auto_closed_tag = null;
- }
-
- return;
- } else if (closing_tag_omitted(parent.name, name)) {
- parent.end = start;
- parser.stack.pop();
- parser.last_auto_closed_tag = {
- tag: parent.name,
- reason: name,
- depth: parser.stack.length,
- };
- }
-
- const unique_names: Set<string> = new Set();
-
- let attribute;
- while ((attribute = read_attribute(parser, unique_names))) {
- element.attributes.push(attribute);
- parser.allow_whitespace();
- }
-
- if (name === 'astro:component') {
- const index = element.attributes.findIndex((attr) => attr.type === 'Attribute' && attr.name === 'this');
- if (!~index) {
- parser.error(
- {
- code: 'missing-component-definition',
- message: "<astro:component> must have a 'this' attribute",
- },
- start
- );
- }
-
- const definition = element.attributes.splice(index, 1)[0];
- if (definition.value === true || definition.value.length !== 1 || definition.value[0].type === 'Text') {
- parser.error(
- {
- code: 'invalid-component-definition',
- message: 'invalid component definition',
- },
- definition.start
- );
- }
-
- element.expression = definition.value[0].expression;
- }
-
- // special cases – top-level <script> and <style>
- if (specials.has(name) && parser.stack.length === 1) {
- const special = specials.get(name);
-
- parser.eat('>', true);
- const content = special.read(parser, start, element.attributes);
- if (content) parser[special.property].push(content);
- return;
- }
-
- parser.current().children.push(element);
-
- const self_closing = parser.eat('/') || is_void(name);
-
- parser.eat('>', true);
-
- if (self_closing) {
- // don't push self-closing elements onto the stack
- element.end = parser.index;
- } else if (name === 'textarea') {
- // special case
- element.children = read_sequence(parser, () => parser.template.slice(parser.index, parser.index + 11) === '</textarea>');
- parser.read(/<\/textarea>/);
- element.end = parser.index;
- } else if (name === 'script' || name === 'style') {
- // special case
- const start = parser.index;
- const data = parser.read_until(new RegExp(`</${name}>`));
- const end = parser.index;
- element.children.push({ start, end, type: 'Text', data });
- parser.eat(`</${name}>`, true);
- element.end = parser.index;
- } else {
- parser.stack.push(element);
- }
+ const start = parser.index++;
+
+ let parent = parser.current();
+
+ if (parser.eat('!--')) {
+ const data = parser.read_until(/-->/);
+ parser.eat('-->', true, 'comment was left open, expected -->');
+
+ parser.current().children.push({
+ start,
+ end: parser.index,
+ type: 'Comment',
+ data,
+ });
+
+ return;
+ }
+
+ const is_closing_tag = parser.eat('/');
+
+ const name = read_tag_name(parser);
+
+ if (CUSTOM_ELEMENT.test(name)) {
+ parser.feature_flags |= FEATURE_CUSTOM_ELEMENT;
+ }
+
+ if (meta_tags.has(name)) {
+ const slug = meta_tags.get(name).toLowerCase();
+ if (is_closing_tag) {
+ if ((name === 'astro:window' || name === 'astro:body') && parser.current().children.length) {
+ parser.error(
+ {
+ code: `invalid-${slug}-content`,
+ message: `<${name}> cannot have children`,
+ },
+ parser.current().children[0].start
+ );
+ }
+ } else {
+ if (name in parser.meta_tags) {
+ parser.error(
+ {
+ code: `duplicate-${slug}`,
+ message: `A component can only have one <${name}> tag`,
+ },
+ start
+ );
+ }
+
+ if (parser.stack.length > 1) {
+ parser.error(
+ {
+ code: `invalid-${slug}-placement`,
+ message: `<${name}> tags cannot be inside elements or blocks`,
+ },
+ start
+ );
+ }
+
+ parser.meta_tags[name] = true;
+ }
+ }
+
+ const type = meta_tags.has(name)
+ ? meta_tags.get(name)
+ : /[A-Z]/.test(name[0]) || name === 'astro:self' || name === 'astro:component'
+ ? 'InlineComponent'
+ : name === ''
+ ? 'SlotTemplate'
+ : name === 'title' && parent_is_head(parser.stack)
+ ? 'Title'
+ : name === 'slot' && !parser.customElement
+ ? 'Slot'
+ : 'Element';
+
+ const element: TemplateNode = {
+ start,
+ end: null, // filled in later
+ type,
+ name,
+ attributes: [],
+ children: [],
+ };
+
+ parser.allow_whitespace();
+
+ if (is_closing_tag) {
+ if (is_void(name)) {
+ parser.error(
+ {
+ code: 'invalid-void-content',
+ message: `<${name}> is a void element and cannot have children, or a closing tag`,
+ },
+ start
+ );
+ }
+
+ parser.eat('>', true);
+
+ // close any elements that don't have their own closing tags, e.g. <div><p></div>
+ while (parent.name !== name) {
+ if (parent.type !== 'Element') {
+ const message =
+ parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name
+ ? `</${name}> attempted to close <${name}> that was already automatically closed by <${parser.last_auto_closed_tag.reason}>`
+ : `</${name}> attempted to close an element that was not open`;
+ parser.error(
+ {
+ code: 'invalid-closing-tag',
+ message,
+ },
+ start
+ );
+ }
+
+ parent.end = start;
+ parser.stack.pop();
+
+ parent = parser.current();
+ }
+
+ parent.end = parser.index;
+ parser.stack.pop();
+
+ if (parser.last_auto_closed_tag && parser.stack.length < parser.last_auto_closed_tag.depth) {
+ parser.last_auto_closed_tag = null;
+ }
+
+ return;
+ } else if (closing_tag_omitted(parent.name, name)) {
+ parent.end = start;
+ parser.stack.pop();
+ parser.last_auto_closed_tag = {
+ tag: parent.name,
+ reason: name,
+ depth: parser.stack.length,
+ };
+ }
+
+ const unique_names: Set<string> = new Set();
+
+ let attribute;
+ while ((attribute = read_attribute(parser, unique_names))) {
+ element.attributes.push(attribute);
+ parser.allow_whitespace();
+ }
+
+ if (name === 'astro:component') {
+ const index = element.attributes.findIndex((attr) => attr.type === 'Attribute' && attr.name === 'this');
+ if (!~index) {
+ parser.error(
+ {
+ code: 'missing-component-definition',
+ message: "<astro:component> must have a 'this' attribute",
+ },
+ start
+ );
+ }
+
+ const definition = element.attributes.splice(index, 1)[0];
+ if (definition.value === true || definition.value.length !== 1 || definition.value[0].type === 'Text') {
+ parser.error(
+ {
+ code: 'invalid-component-definition',
+ message: 'invalid component definition',
+ },
+ definition.start
+ );
+ }
+
+ element.expression = definition.value[0].expression;
+ }
+
+ // special cases – top-level <script> and <style>
+ if (specials.has(name) && parser.stack.length === 1) {
+ const special = specials.get(name);
+
+ parser.eat('>', true);
+ const content = special.read(parser, start, element.attributes);
+ if (content) parser[special.property].push(content);
+ return;
+ }
+
+ parser.current().children.push(element);
+
+ const self_closing = parser.eat('/') || is_void(name);
+
+ parser.eat('>', true);
+
+ if (self_closing) {
+ // don't push self-closing elements onto the stack
+ element.end = parser.index;
+ } else if (name === 'textarea') {
+ // special case
+ element.children = read_sequence(parser, () => parser.template.slice(parser.index, parser.index + 11) === '</textarea>');
+ parser.read(/<\/textarea>/);
+ element.end = parser.index;
+ } else if (name === 'script' || name === 'style') {
+ // special case
+ const start = parser.index;
+ const data = parser.read_until(new RegExp(`</${name}>`));
+ const end = parser.index;
+ element.children.push({ start, end, type: 'Text', data });
+ parser.eat(`</${name}>`, true);
+ element.end = parser.index;
+ } else {
+ parser.stack.push(element);
+ }
}
function read_tag_name(parser: Parser) {
- const start = parser.index;
-
- if (parser.read(SELF)) {
- // check we're inside a block, otherwise this
- // will cause infinite recursion
- let i = parser.stack.length;
- let legal = false;
-
- while (i--) {
- const fragment = parser.stack[i];
- if (fragment.type === 'IfBlock' || fragment.type === 'EachBlock' || fragment.type === 'InlineComponent') {
- legal = true;
- break;
- }
- }
-
- if (!legal) {
- parser.error(
- {
- code: 'invalid-self-placement',
- message: '<astro:self> components can only exist inside {#if} blocks, {#each} blocks, or slots passed to components',
- },
- start
- );
- }
-
- return 'astro:self';
- }
-
- if (parser.read(COMPONENT)) return 'astro:component';
-
- if (parser.read(SLOT)) return 'astro:fragment';
-
- if (parser.read(HEAD)) return 'head';
-
- const name = parser.read_until(/(\s|\/|>)/);
-
- if (meta_tags.has(name)) return name;
-
- if (name.startsWith('astro:')) {
- const match = fuzzymatch(name.slice(7), valid_meta_tags);
-
- let message = `Valid <astro:...> tag names are ${list(valid_meta_tags)}`;
- if (match) message += ` (did you mean '${match}'?)`;
-
- parser.error(
- {
- code: 'invalid-tag-name',
- message,
- },
- start
- );
- }
-
- if (!valid_tag_name.test(name)) {
- parser.error(
- {
- code: 'invalid-tag-name',
- message: 'Expected valid tag name',
- },
- start
- );
- }
-
- return name;
+ const start = parser.index;
+
+ if (parser.read(SELF)) {
+ // check we're inside a block, otherwise this
+ // will cause infinite recursion
+ let i = parser.stack.length;
+ let legal = false;
+
+ while (i--) {
+ const fragment = parser.stack[i];
+ if (fragment.type === 'IfBlock' || fragment.type === 'EachBlock' || fragment.type === 'InlineComponent') {
+ legal = true;
+ break;
+ }
+ }
+
+ if (!legal) {
+ parser.error(
+ {
+ code: 'invalid-self-placement',
+ message: '<astro:self> components can only exist inside {#if} blocks, {#each} blocks, or slots passed to components',
+ },
+ start
+ );
+ }
+
+ return 'astro:self';
+ }
+
+ if (parser.read(COMPONENT)) return 'astro:component';
+
+ if (parser.read(SLOT)) return 'astro:fragment';
+
+ if (parser.read(HEAD)) return 'head';
+
+ const name = parser.read_until(/(\s|\/|>)/);
+
+ if (meta_tags.has(name)) return name;
+
+ if (name.startsWith('astro:')) {
+ const match = fuzzymatch(name.slice(7), valid_meta_tags);
+
+ let message = `Valid <astro:...> tag names are ${list(valid_meta_tags)}`;
+ if (match) message += ` (did you mean '${match}'?)`;
+
+ parser.error(
+ {
+ code: 'invalid-tag-name',
+ message,
+ },
+ start
+ );
+ }
+
+ if (!valid_tag_name.test(name)) {
+ parser.error(
+ {
+ code: 'invalid-tag-name',
+ message: 'Expected valid tag name',
+ },
+ start
+ );
+ }
+
+ return name;
}
function read_attribute(parser: Parser, unique_names: Set<string>) {
- const start = parser.index;
-
- function check_unique(name: string) {
- if (unique_names.has(name)) {
- parser.error(
- {
- code: 'duplicate-attribute',
- message: 'Attributes need to be unique',
- },
- start
- );
- }
- unique_names.add(name);
- }
-
- if (parser.eat('{')) {
- parser.allow_whitespace();
-
- if (parser.eat('...')) {
- const expression = read_expression(parser);
- parser.allow_whitespace();
- parser.eat('}', true);
-
- return {
- start,
- end: parser.index,
- type: 'Spread',
- expression,
- };
- } else {
- const value_start = parser.index;
-
- const name = parser.read_identifier();
- parser.allow_whitespace();
- parser.eat('}', true);
-
- check_unique(name);
-
- return {
- start,
- end: parser.index,
- type: 'Attribute',
- name,
- value: [
- {
- start: value_start,
- end: value_start + name.length,
- type: 'AttributeShorthand',
- expression: {
- start: value_start,
- end: value_start + name.length,
- type: 'Identifier',
- name,
- },
- },
- ],
- };
- }
- }
-
- const name = parser.read_until(/[\s=\/>"']/);
- if (!name) return null;
-
- let end = parser.index;
-
- parser.allow_whitespace();
-
- let value: any[] | true = true;
- if (parser.eat('=')) {
- parser.allow_whitespace();
- value = read_attribute_value(parser);
- end = parser.index;
- } else if (parser.match_regex(/["']/)) {
- parser.error(
- {
- code: 'unexpected-token',
- message: 'Expected =',
- },
- parser.index
- );
- }
-
- check_unique(name);
-
- return {
- start,
- end,
- type: 'Attribute',
- name,
- value,
- };
+ const start = parser.index;
+
+ function check_unique(name: string) {
+ if (unique_names.has(name)) {
+ parser.error(
+ {
+ code: 'duplicate-attribute',
+ message: 'Attributes need to be unique',
+ },
+ start
+ );
+ }
+ unique_names.add(name);
+ }
+
+ if (parser.eat('{')) {
+ parser.allow_whitespace();
+
+ if (parser.eat('...')) {
+ const expression = read_expression(parser);
+ parser.allow_whitespace();
+ parser.eat('}', true);
+
+ return {
+ start,
+ end: parser.index,
+ type: 'Spread',
+ expression,
+ };
+ } else {
+ const value_start = parser.index;
+
+ const name = parser.read_identifier();
+ parser.allow_whitespace();
+ parser.eat('}', true);
+
+ check_unique(name);
+
+ return {
+ start,
+ end: parser.index,
+ type: 'Attribute',
+ name,
+ value: [
+ {
+ start: value_start,
+ end: value_start + name.length,
+ type: 'AttributeShorthand',
+ expression: {
+ start: value_start,
+ end: value_start + name.length,
+ type: 'Identifier',
+ name,
+ },
+ },
+ ],
+ };
+ }
+ }
+
+ const name = parser.read_until(/[\s=\/>"']/);
+ if (!name) return null;
+
+ let end = parser.index;
+
+ parser.allow_whitespace();
+
+ let value: any[] | true = true;
+ if (parser.eat('=')) {
+ parser.allow_whitespace();
+ value = read_attribute_value(parser);
+ end = parser.index;
+ } else if (parser.match_regex(/["']/)) {
+ parser.error(
+ {
+ code: 'unexpected-token',
+ message: 'Expected =',
+ },
+ parser.index
+ );
+ }
+
+ check_unique(name);
+
+ return {
+ start,
+ end,
+ type: 'Attribute',
+ name,
+ value,
+ };
}
function read_attribute_value(parser: Parser) {
- const quote_mark = parser.eat("'") ? "'" : parser.eat('"') ? '"' : null;
+ const quote_mark = parser.eat("'") ? "'" : parser.eat('"') ? '"' : null;
- const regex = quote_mark === "'" ? /'/ : quote_mark === '"' ? /"/ : /(\/>|[\s"'=<>`])/;
+ const regex = quote_mark === "'" ? /'/ : quote_mark === '"' ? /"/ : /(\/>|[\s"'=<>`])/;
- const value = read_sequence(parser, () => !!parser.match_regex(regex));
+ const value = read_sequence(parser, () => !!parser.match_regex(regex));
- if (quote_mark) parser.index += 1;
- return value;
+ if (quote_mark) parser.index += 1;
+ return value;
}
export function read_sequence(parser: Parser, done: () => boolean): TemplateNode[] {
- let current_chunk: Text = {
- start: parser.index,
- end: null,
- type: 'Text',
- raw: '',
- data: null,
- };
-
- function flush() {
- if (current_chunk.raw) {
- current_chunk.data = decode_character_references(current_chunk.raw);
- current_chunk.end = parser.index;
- chunks.push(current_chunk);
- }
- }
-
- const chunks: TemplateNode[] = [];
-
- while (parser.index < parser.template.length) {
- const index = parser.index;
-
- if (done()) {
- flush();
- return chunks;
- } else if (parser.eat('{')) {
- flush();
-
- parser.allow_whitespace();
- const expression = read_expression(parser);
- parser.allow_whitespace();
- parser.eat('}', true);
-
- chunks.push({
- start: index,
- end: parser.index,
- type: 'MustacheTag',
- expression,
- });
-
- current_chunk = {
- start: parser.index,
- end: null,
- type: 'Text',
- raw: '',
- data: null,
- };
- } else {
- current_chunk.raw += parser.template[parser.index++];
- }
- }
-
- parser.error({
- code: 'unexpected-eof',
- message: 'Unexpected end of input',
- });
+ let current_chunk: Text = {
+ start: parser.index,
+ end: null,
+ type: 'Text',
+ raw: '',
+ data: null,
+ };
+
+ function flush() {
+ if (current_chunk.raw) {
+ current_chunk.data = decode_character_references(current_chunk.raw);
+ current_chunk.end = parser.index;
+ chunks.push(current_chunk);
+ }
+ }
+
+ const chunks: TemplateNode[] = [];
+
+ while (parser.index < parser.template.length) {
+ const index = parser.index;
+
+ if (done()) {
+ flush();
+ return chunks;
+ } else if (parser.eat('{')) {
+ flush();
+
+ parser.allow_whitespace();
+ const expression = read_expression(parser);
+ parser.allow_whitespace();
+ parser.eat('}', true);
+
+ chunks.push({
+ start: index,
+ end: parser.index,
+ type: 'MustacheTag',
+ expression,
+ });
+
+ current_chunk = {
+ start: parser.index,
+ end: null,
+ type: 'Text',
+ raw: '',
+ data: null,
+ };
+ } else {
+ current_chunk.raw += parser.template[parser.index++];
+ }
+ }
+
+ parser.error({
+ code: 'unexpected-eof',
+ message: 'Unexpected end of input',
+ });
}
diff --git a/packages/astro-parser/src/parse/state/text.ts b/packages/astro-parser/src/parse/state/text.ts
index b6509caf9..2bd87e37c 100644
--- a/packages/astro-parser/src/parse/state/text.ts
+++ b/packages/astro-parser/src/parse/state/text.ts
@@ -4,29 +4,29 @@ import { decode_character_references } from '../utils/html.js';
import { Parser } from '../index.js';
export default function text(parser: Parser) {
- const start = parser.index;
+ const start = parser.index;
- let data = '';
+ let data = '';
- const shouldContinue = () => {
- // Special case 'code' content to avoid tripping up on user code
- if (parser.current().name === 'code') {
- return !parser.match('<') && !parser.match('{');
- }
- return !parser.match('<') && !parser.match('{') && !parser.match('`');
- };
+ const shouldContinue = () => {
+ // Special case 'code' content to avoid tripping up on user code
+ if (parser.current().name === 'code') {
+ return !parser.match('<') && !parser.match('{');
+ }
+ return !parser.match('<') && !parser.match('{') && !parser.match('`');
+ };
- while (parser.index < parser.template.length && shouldContinue()) {
- data += parser.template[parser.index++];
- }
+ while (parser.index < parser.template.length && shouldContinue()) {
+ data += parser.template[parser.index++];
+ }
- const node = {
- start,
- end: parser.index,
- type: 'Text',
- raw: data,
- data: decode_character_references(data),
- };
+ const node = {
+ start,
+ end: parser.index,
+ type: 'Text',
+ raw: data,
+ data: decode_character_references(data),
+ };
- parser.current().children.push(node);
+ parser.current().children.push(node);
}
diff --git a/packages/astro-parser/src/parse/utils/bracket.ts b/packages/astro-parser/src/parse/utils/bracket.ts
index 7e885ad78..a265e9f3d 100644
--- a/packages/astro-parser/src/parse/utils/bracket.ts
+++ b/packages/astro-parser/src/parse/utils/bracket.ts
@@ -6,22 +6,22 @@ const CURLY_BRACKET_OPEN = '{'.charCodeAt(0);
const CURLY_BRACKET_CLOSE = '}'.charCodeAt(0);
export function is_bracket_open(code) {
- return code === SQUARE_BRACKET_OPEN || code === CURLY_BRACKET_OPEN;
+ return code === SQUARE_BRACKET_OPEN || code === CURLY_BRACKET_OPEN;
}
export function is_bracket_close(code) {
- return code === SQUARE_BRACKET_CLOSE || code === CURLY_BRACKET_CLOSE;
+ return code === SQUARE_BRACKET_CLOSE || code === CURLY_BRACKET_CLOSE;
}
export function is_bracket_pair(open, close) {
- return (open === SQUARE_BRACKET_OPEN && close === SQUARE_BRACKET_CLOSE) || (open === CURLY_BRACKET_OPEN && close === CURLY_BRACKET_CLOSE);
+ return (open === SQUARE_BRACKET_OPEN && close === SQUARE_BRACKET_CLOSE) || (open === CURLY_BRACKET_OPEN && close === CURLY_BRACKET_CLOSE);
}
export function get_bracket_close(open) {
- if (open === SQUARE_BRACKET_OPEN) {
- return SQUARE_BRACKET_CLOSE;
- }
- if (open === CURLY_BRACKET_OPEN) {
- return CURLY_BRACKET_CLOSE;
- }
+ if (open === SQUARE_BRACKET_OPEN) {
+ return SQUARE_BRACKET_CLOSE;
+ }
+ if (open === CURLY_BRACKET_OPEN) {
+ return CURLY_BRACKET_CLOSE;
+ }
}
diff --git a/packages/astro-parser/src/parse/utils/entities.ts b/packages/astro-parser/src/parse/utils/entities.ts
index e554664eb..91689c5f3 100644
--- a/packages/astro-parser/src/parse/utils/entities.ts
+++ b/packages/astro-parser/src/parse/utils/entities.ts
@@ -1,2034 +1,2034 @@
// https://dev.w3.org/html5/html-author/charref
export default {
- CounterClockwiseContourIntegral: 8755,
- ClockwiseContourIntegral: 8754,
- DoubleLongLeftRightArrow: 10234,
- DiacriticalDoubleAcute: 733,
- NotSquareSupersetEqual: 8931,
- CloseCurlyDoubleQuote: 8221,
- DoubleContourIntegral: 8751,
- FilledVerySmallSquare: 9642,
- NegativeVeryThinSpace: 8203,
- NotPrecedesSlantEqual: 8928,
- NotRightTriangleEqual: 8941,
- NotSucceedsSlantEqual: 8929,
- CapitalDifferentialD: 8517,
- DoubleLeftRightArrow: 8660,
- DoubleLongRightArrow: 10233,
- EmptyVerySmallSquare: 9643,
- NestedGreaterGreater: 8811,
- NotDoubleVerticalBar: 8742,
- NotLeftTriangleEqual: 8940,
- NotSquareSubsetEqual: 8930,
- OpenCurlyDoubleQuote: 8220,
- ReverseUpEquilibrium: 10607,
- DoubleLongLeftArrow: 10232,
- DownLeftRightVector: 10576,
- LeftArrowRightArrow: 8646,
- NegativeMediumSpace: 8203,
- RightArrowLeftArrow: 8644,
- SquareSupersetEqual: 8850,
- leftrightsquigarrow: 8621,
- DownRightTeeVector: 10591,
- DownRightVectorBar: 10583,
- LongLeftRightArrow: 10231,
- Longleftrightarrow: 10234,
- NegativeThickSpace: 8203,
- PrecedesSlantEqual: 8828,
- ReverseEquilibrium: 8651,
- RightDoubleBracket: 10215,
- RightDownTeeVector: 10589,
- RightDownVectorBar: 10581,
- RightTriangleEqual: 8885,
- SquareIntersection: 8851,
- SucceedsSlantEqual: 8829,
- blacktriangleright: 9656,
- longleftrightarrow: 10231,
- DoubleUpDownArrow: 8661,
- DoubleVerticalBar: 8741,
- DownLeftTeeVector: 10590,
- DownLeftVectorBar: 10582,
- FilledSmallSquare: 9724,
- GreaterSlantEqual: 10878,
- LeftDoubleBracket: 10214,
- LeftDownTeeVector: 10593,
- LeftDownVectorBar: 10585,
- LeftTriangleEqual: 8884,
- NegativeThinSpace: 8203,
- NotReverseElement: 8716,
- NotTildeFullEqual: 8775,
- RightAngleBracket: 10217,
- RightUpDownVector: 10575,
- SquareSubsetEqual: 8849,
- VerticalSeparator: 10072,
- blacktriangledown: 9662,
- blacktriangleleft: 9666,
- leftrightharpoons: 8651,
- rightleftharpoons: 8652,
- twoheadrightarrow: 8608,
- DiacriticalAcute: 180,
- DiacriticalGrave: 96,
- DiacriticalTilde: 732,
- DoubleRightArrow: 8658,
- DownArrowUpArrow: 8693,
- EmptySmallSquare: 9723,
- GreaterEqualLess: 8923,
- GreaterFullEqual: 8807,
- LeftAngleBracket: 10216,
- LeftUpDownVector: 10577,
- LessEqualGreater: 8922,
- NonBreakingSpace: 160,
- NotRightTriangle: 8939,
- NotSupersetEqual: 8841,
- RightTriangleBar: 10704,
- RightUpTeeVector: 10588,
- RightUpVectorBar: 10580,
- UnderParenthesis: 9181,
- UpArrowDownArrow: 8645,
- circlearrowright: 8635,
- downharpoonright: 8642,
- ntrianglerighteq: 8941,
- rightharpoondown: 8641,
- rightrightarrows: 8649,
- twoheadleftarrow: 8606,
- vartriangleright: 8883,
- CloseCurlyQuote: 8217,
- ContourIntegral: 8750,
- DoubleDownArrow: 8659,
- DoubleLeftArrow: 8656,
- DownRightVector: 8641,
- LeftRightVector: 10574,
- LeftTriangleBar: 10703,
- LeftUpTeeVector: 10592,
- LeftUpVectorBar: 10584,
- LowerRightArrow: 8600,
- NotGreaterEqual: 8817,
- NotGreaterTilde: 8821,
- NotLeftTriangle: 8938,
- OverParenthesis: 9180,
- RightDownVector: 8642,
- ShortRightArrow: 8594,
- UpperRightArrow: 8599,
- bigtriangledown: 9661,
- circlearrowleft: 8634,
- curvearrowright: 8631,
- downharpoonleft: 8643,
- leftharpoondown: 8637,
- leftrightarrows: 8646,
- nLeftrightarrow: 8654,
- nleftrightarrow: 8622,
- ntrianglelefteq: 8940,
- rightleftarrows: 8644,
- rightsquigarrow: 8605,
- rightthreetimes: 8908,
- straightepsilon: 1013,
- trianglerighteq: 8885,
- vartriangleleft: 8882,
- DiacriticalDot: 729,
- DoubleRightTee: 8872,
- DownLeftVector: 8637,
- GreaterGreater: 10914,
- HorizontalLine: 9472,
- InvisibleComma: 8291,
- InvisibleTimes: 8290,
- LeftDownVector: 8643,
- LeftRightArrow: 8596,
- Leftrightarrow: 8660,
- LessSlantEqual: 10877,
- LongRightArrow: 10230,
- Longrightarrow: 10233,
- LowerLeftArrow: 8601,
- NestedLessLess: 8810,
- NotGreaterLess: 8825,
- NotLessGreater: 8824,
- NotSubsetEqual: 8840,
- NotVerticalBar: 8740,
- OpenCurlyQuote: 8216,
- ReverseElement: 8715,
- RightTeeVector: 10587,
- RightVectorBar: 10579,
- ShortDownArrow: 8595,
- ShortLeftArrow: 8592,
- SquareSuperset: 8848,
- TildeFullEqual: 8773,
- UpperLeftArrow: 8598,
- ZeroWidthSpace: 8203,
- curvearrowleft: 8630,
- doublebarwedge: 8966,
- downdownarrows: 8650,
- hookrightarrow: 8618,
- leftleftarrows: 8647,
- leftrightarrow: 8596,
- leftthreetimes: 8907,
- longrightarrow: 10230,
- looparrowright: 8620,
- nshortparallel: 8742,
- ntriangleright: 8939,
- rightarrowtail: 8611,
- rightharpoonup: 8640,
- trianglelefteq: 8884,
- upharpoonright: 8638,
- ApplyFunction: 8289,
- DifferentialD: 8518,
- DoubleLeftTee: 10980,
- DoubleUpArrow: 8657,
- LeftTeeVector: 10586,
- LeftVectorBar: 10578,
- LessFullEqual: 8806,
- LongLeftArrow: 10229,
- Longleftarrow: 10232,
- NotTildeEqual: 8772,
- NotTildeTilde: 8777,
- Poincareplane: 8460,
- PrecedesEqual: 10927,
- PrecedesTilde: 8830,
- RightArrowBar: 8677,
- RightTeeArrow: 8614,
- RightTriangle: 8883,
- RightUpVector: 8638,
- SucceedsEqual: 10928,
- SucceedsTilde: 8831,
- SupersetEqual: 8839,
- UpEquilibrium: 10606,
- VerticalTilde: 8768,
- VeryThinSpace: 8202,
- bigtriangleup: 9651,
- blacktriangle: 9652,
- divideontimes: 8903,
- fallingdotseq: 8786,
- hookleftarrow: 8617,
- leftarrowtail: 8610,
- leftharpoonup: 8636,
- longleftarrow: 10229,
- looparrowleft: 8619,
- measuredangle: 8737,
- ntriangleleft: 8938,
- shortparallel: 8741,
- smallsetminus: 8726,
- triangleright: 9657,
- upharpoonleft: 8639,
- DownArrowBar: 10515,
- DownTeeArrow: 8615,
- ExponentialE: 8519,
- GreaterEqual: 8805,
- GreaterTilde: 8819,
- HilbertSpace: 8459,
- HumpDownHump: 8782,
- Intersection: 8898,
- LeftArrowBar: 8676,
- LeftTeeArrow: 8612,
- LeftTriangle: 8882,
- LeftUpVector: 8639,
- NotCongruent: 8802,
- NotLessEqual: 8816,
- NotLessTilde: 8820,
- Proportional: 8733,
- RightCeiling: 8969,
- RoundImplies: 10608,
- ShortUpArrow: 8593,
- SquareSubset: 8847,
- UnderBracket: 9141,
- VerticalLine: 124,
- blacklozenge: 10731,
- exponentiale: 8519,
- risingdotseq: 8787,
- triangledown: 9663,
- triangleleft: 9667,
- CircleMinus: 8854,
- CircleTimes: 8855,
- Equilibrium: 8652,
- GreaterLess: 8823,
- LeftCeiling: 8968,
- LessGreater: 8822,
- MediumSpace: 8287,
- NotPrecedes: 8832,
- NotSucceeds: 8833,
- OverBracket: 9140,
- RightVector: 8640,
- Rrightarrow: 8667,
- RuleDelayed: 10740,
- SmallCircle: 8728,
- SquareUnion: 8852,
- SubsetEqual: 8838,
- UpDownArrow: 8597,
- Updownarrow: 8661,
- VerticalBar: 8739,
- backepsilon: 1014,
- blacksquare: 9642,
- circledcirc: 8858,
- circleddash: 8861,
- curlyeqprec: 8926,
- curlyeqsucc: 8927,
- diamondsuit: 9830,
- eqslantless: 10901,
- expectation: 8496,
- nRightarrow: 8655,
- nrightarrow: 8603,
- preccurlyeq: 8828,
- precnapprox: 10937,
- quaternions: 8461,
- straightphi: 981,
- succcurlyeq: 8829,
- succnapprox: 10938,
- thickapprox: 8776,
- updownarrow: 8597,
- Bernoullis: 8492,
- CirclePlus: 8853,
- EqualTilde: 8770,
- Fouriertrf: 8497,
- ImaginaryI: 8520,
- Laplacetrf: 8466,
- LeftVector: 8636,
- Lleftarrow: 8666,
- NotElement: 8713,
- NotGreater: 8815,
- Proportion: 8759,
- RightArrow: 8594,
- RightFloor: 8971,
- Rightarrow: 8658,
- TildeEqual: 8771,
- TildeTilde: 8776,
- UnderBrace: 9183,
- UpArrowBar: 10514,
- UpTeeArrow: 8613,
- circledast: 8859,
- complement: 8705,
- curlywedge: 8911,
- eqslantgtr: 10902,
- gtreqqless: 10892,
- lessapprox: 10885,
- lesseqqgtr: 10891,
- lmoustache: 9136,
- longmapsto: 10236,
- mapstodown: 8615,
- mapstoleft: 8612,
- nLeftarrow: 8653,
- nleftarrow: 8602,
- precapprox: 10935,
- rightarrow: 8594,
- rmoustache: 9137,
- sqsubseteq: 8849,
- sqsupseteq: 8850,
- subsetneqq: 10955,
- succapprox: 10936,
- supsetneqq: 10956,
- upuparrows: 8648,
- varepsilon: 949,
- varnothing: 8709,
- Backslash: 8726,
- CenterDot: 183,
- CircleDot: 8857,
- Congruent: 8801,
- Coproduct: 8720,
- DoubleDot: 168,
- DownArrow: 8595,
- DownBreve: 785,
- Downarrow: 8659,
- HumpEqual: 8783,
- LeftArrow: 8592,
- LeftFloor: 8970,
- Leftarrow: 8656,
- LessTilde: 8818,
- Mellintrf: 8499,
- MinusPlus: 8723,
- NotCupCap: 8813,
- NotExists: 8708,
- OverBrace: 9182,
- PlusMinus: 177,
- Therefore: 8756,
- ThinSpace: 8201,
- TripleDot: 8411,
- UnionPlus: 8846,
- backprime: 8245,
- backsimeq: 8909,
- bigotimes: 10754,
- centerdot: 183,
- checkmark: 10003,
- complexes: 8450,
- dotsquare: 8865,
- downarrow: 8595,
- gtrapprox: 10886,
- gtreqless: 8923,
- heartsuit: 9829,
- leftarrow: 8592,
- lesseqgtr: 8922,
- nparallel: 8742,
- nshortmid: 8740,
- nsubseteq: 8840,
- nsupseteq: 8841,
- pitchfork: 8916,
- rationals: 8474,
- spadesuit: 9824,
- subseteqq: 10949,
- subsetneq: 8842,
- supseteqq: 10950,
- supsetneq: 8843,
- therefore: 8756,
- triangleq: 8796,
- varpropto: 8733,
- DDotrahd: 10513,
- DotEqual: 8784,
- Integral: 8747,
- LessLess: 10913,
- NotEqual: 8800,
- NotTilde: 8769,
- PartialD: 8706,
- Precedes: 8826,
- RightTee: 8866,
- Succeeds: 8827,
- SuchThat: 8715,
- Superset: 8835,
- Uarrocir: 10569,
- UnderBar: 818,
- andslope: 10840,
- angmsdaa: 10664,
- angmsdab: 10665,
- angmsdac: 10666,
- angmsdad: 10667,
- angmsdae: 10668,
- angmsdaf: 10669,
- angmsdag: 10670,
- angmsdah: 10671,
- angrtvbd: 10653,
- approxeq: 8778,
- awconint: 8755,
- backcong: 8780,
- barwedge: 8965,
- bbrktbrk: 9142,
- bigoplus: 10753,
- bigsqcup: 10758,
- biguplus: 10756,
- bigwedge: 8896,
- boxminus: 8863,
- boxtimes: 8864,
- capbrcup: 10825,
- circledR: 174,
- circledS: 9416,
- cirfnint: 10768,
- clubsuit: 9827,
- cupbrcap: 10824,
- curlyvee: 8910,
- cwconint: 8754,
- doteqdot: 8785,
- dotminus: 8760,
- drbkarow: 10512,
- dzigrarr: 10239,
- elinters: 9191,
- emptyset: 8709,
- eqvparsl: 10725,
- fpartint: 10765,
- geqslant: 10878,
- gesdotol: 10884,
- gnapprox: 10890,
- hksearow: 10533,
- hkswarow: 10534,
- imagline: 8464,
- imagpart: 8465,
- infintie: 10717,
- integers: 8484,
- intercal: 8890,
- intlarhk: 10775,
- laemptyv: 10676,
- ldrushar: 10571,
- leqslant: 10877,
- lesdotor: 10883,
- llcorner: 8990,
- lnapprox: 10889,
- lrcorner: 8991,
- lurdshar: 10570,
- mapstoup: 8613,
- multimap: 8888,
- naturals: 8469,
- otimesas: 10806,
- parallel: 8741,
- plusacir: 10787,
- pointint: 10773,
- precneqq: 10933,
- precnsim: 8936,
- profalar: 9006,
- profline: 8978,
- profsurf: 8979,
- raemptyv: 10675,
- realpart: 8476,
- rppolint: 10770,
- rtriltri: 10702,
- scpolint: 10771,
- setminus: 8726,
- shortmid: 8739,
- smeparsl: 10724,
- sqsubset: 8847,
- sqsupset: 8848,
- subseteq: 8838,
- succneqq: 10934,
- succnsim: 8937,
- supseteq: 8839,
- thetasym: 977,
- thicksim: 8764,
- timesbar: 10801,
- triangle: 9653,
- triminus: 10810,
- trpezium: 9186,
- ulcorner: 8988,
- urcorner: 8989,
- varkappa: 1008,
- varsigma: 962,
- vartheta: 977,
- Because: 8757,
- Cayleys: 8493,
- Cconint: 8752,
- Cedilla: 184,
- Diamond: 8900,
- DownTee: 8868,
- Element: 8712,
- Epsilon: 917,
- Implies: 8658,
- LeftTee: 8867,
- NewLine: 10,
- NoBreak: 8288,
- NotLess: 8814,
- Omicron: 927,
- OverBar: 175,
- Product: 8719,
- UpArrow: 8593,
- Uparrow: 8657,
- Upsilon: 933,
- alefsym: 8501,
- angrtvb: 8894,
- angzarr: 9084,
- asympeq: 8781,
- backsim: 8765,
- because: 8757,
- bemptyv: 10672,
- between: 8812,
- bigcirc: 9711,
- bigodot: 10752,
- bigstar: 9733,
- boxplus: 8862,
- ccupssm: 10832,
- cemptyv: 10674,
- cirscir: 10690,
- coloneq: 8788,
- congdot: 10861,
- cudarrl: 10552,
- cudarrr: 10549,
- cularrp: 10557,
- curarrm: 10556,
- dbkarow: 10511,
- ddagger: 8225,
- ddotseq: 10871,
- demptyv: 10673,
- diamond: 8900,
- digamma: 989,
- dotplus: 8724,
- dwangle: 10662,
- epsilon: 949,
- eqcolon: 8789,
- equivDD: 10872,
- gesdoto: 10882,
- gtquest: 10876,
- gtrless: 8823,
- harrcir: 10568,
- intprod: 10812,
- isindot: 8949,
- larrbfs: 10527,
- larrsim: 10611,
- lbrksld: 10639,
- lbrkslu: 10637,
- ldrdhar: 10599,
- lesdoto: 10881,
- lessdot: 8918,
- lessgtr: 8822,
- lesssim: 8818,
- lotimes: 10804,
- lozenge: 9674,
- ltquest: 10875,
- luruhar: 10598,
- maltese: 10016,
- minusdu: 10794,
- napprox: 8777,
- natural: 9838,
- nearrow: 8599,
- nexists: 8708,
- notinva: 8713,
- notinvb: 8951,
- notinvc: 8950,
- notniva: 8716,
- notnivb: 8958,
- notnivc: 8957,
- npolint: 10772,
- nsqsube: 8930,
- nsqsupe: 8931,
- nvinfin: 10718,
- nwarrow: 8598,
- olcross: 10683,
- omicron: 959,
- orderof: 8500,
- orslope: 10839,
- pertenk: 8241,
- planckh: 8462,
- pluscir: 10786,
- plussim: 10790,
- plustwo: 10791,
- precsim: 8830,
- quatint: 10774,
- questeq: 8799,
- rarrbfs: 10528,
- rarrsim: 10612,
- rbrksld: 10638,
- rbrkslu: 10640,
- rdldhar: 10601,
- realine: 8475,
- rotimes: 10805,
- ruluhar: 10600,
- searrow: 8600,
- simplus: 10788,
- simrarr: 10610,
- subedot: 10947,
- submult: 10945,
- subplus: 10943,
- subrarr: 10617,
- succsim: 8831,
- supdsub: 10968,
- supedot: 10948,
- suphsub: 10967,
- suplarr: 10619,
- supmult: 10946,
- supplus: 10944,
- swarrow: 8601,
- topfork: 10970,
- triplus: 10809,
- tritime: 10811,
- uparrow: 8593,
- upsilon: 965,
- uwangle: 10663,
- vzigzag: 10650,
- zigrarr: 8669,
- Aacute: 193,
- Abreve: 258,
- Agrave: 192,
- Assign: 8788,
- Atilde: 195,
- Barwed: 8966,
- Bumpeq: 8782,
- Cacute: 262,
- Ccaron: 268,
- Ccedil: 199,
- Colone: 10868,
- Conint: 8751,
- CupCap: 8781,
- Dagger: 8225,
- Dcaron: 270,
- DotDot: 8412,
- Dstrok: 272,
- Eacute: 201,
- Ecaron: 282,
- Egrave: 200,
- Exists: 8707,
- ForAll: 8704,
- Gammad: 988,
- Gbreve: 286,
- Gcedil: 290,
- HARDcy: 1066,
- Hstrok: 294,
- Iacute: 205,
- Igrave: 204,
- Itilde: 296,
- Jsercy: 1032,
- Kcedil: 310,
- Lacute: 313,
- Lambda: 923,
- Lcaron: 317,
- Lcedil: 315,
- Lmidot: 319,
- Lstrok: 321,
- Nacute: 323,
- Ncaron: 327,
- Ncedil: 325,
- Ntilde: 209,
- Oacute: 211,
- Odblac: 336,
- Ograve: 210,
- Oslash: 216,
- Otilde: 213,
- Otimes: 10807,
- Racute: 340,
- Rarrtl: 10518,
- Rcaron: 344,
- Rcedil: 342,
- SHCHcy: 1065,
- SOFTcy: 1068,
- Sacute: 346,
- Scaron: 352,
- Scedil: 350,
- Square: 9633,
- Subset: 8912,
- Supset: 8913,
- Tcaron: 356,
- Tcedil: 354,
- Tstrok: 358,
- Uacute: 218,
- Ubreve: 364,
- Udblac: 368,
- Ugrave: 217,
- Utilde: 360,
- Vdashl: 10982,
- Verbar: 8214,
- Vvdash: 8874,
- Yacute: 221,
- Zacute: 377,
- Zcaron: 381,
- aacute: 225,
- abreve: 259,
- agrave: 224,
- andand: 10837,
- angmsd: 8737,
- angsph: 8738,
- apacir: 10863,
- approx: 8776,
- atilde: 227,
- barvee: 8893,
- barwed: 8965,
- becaus: 8757,
- bernou: 8492,
- bigcap: 8898,
- bigcup: 8899,
- bigvee: 8897,
- bkarow: 10509,
- bottom: 8869,
- bowtie: 8904,
- boxbox: 10697,
- bprime: 8245,
- brvbar: 166,
- bullet: 8226,
- bumpeq: 8783,
- cacute: 263,
- capand: 10820,
- capcap: 10827,
- capcup: 10823,
- capdot: 10816,
- ccaron: 269,
- ccedil: 231,
- circeq: 8791,
- cirmid: 10991,
- colone: 8788,
- commat: 64,
- compfn: 8728,
- conint: 8750,
- coprod: 8720,
- copysr: 8471,
- cularr: 8630,
- cupcap: 10822,
- cupcup: 10826,
- cupdot: 8845,
- curarr: 8631,
- curren: 164,
- cylcty: 9005,
- dagger: 8224,
- daleth: 8504,
- dcaron: 271,
- dfisht: 10623,
- divide: 247,
- divonx: 8903,
- dlcorn: 8990,
- dlcrop: 8973,
- dollar: 36,
- drcorn: 8991,
- drcrop: 8972,
- dstrok: 273,
- eacute: 233,
- easter: 10862,
- ecaron: 283,
- ecolon: 8789,
- egrave: 232,
- egsdot: 10904,
- elsdot: 10903,
- emptyv: 8709,
- emsp13: 8196,
- emsp14: 8197,
- eparsl: 10723,
- eqcirc: 8790,
- equals: 61,
- equest: 8799,
- female: 9792,
- ffilig: 64259,
- ffllig: 64260,
- forall: 8704,
- frac12: 189,
- frac13: 8531,
- frac14: 188,
- frac15: 8533,
- frac16: 8537,
- frac18: 8539,
- frac23: 8532,
- frac25: 8534,
- frac34: 190,
- frac35: 8535,
- frac38: 8540,
- frac45: 8536,
- frac56: 8538,
- frac58: 8541,
- frac78: 8542,
- gacute: 501,
- gammad: 989,
- gbreve: 287,
- gesdot: 10880,
- gesles: 10900,
- gtlPar: 10645,
- gtrarr: 10616,
- gtrdot: 8919,
- gtrsim: 8819,
- hairsp: 8202,
- hamilt: 8459,
- hardcy: 1098,
- hearts: 9829,
- hellip: 8230,
- hercon: 8889,
- homtht: 8763,
- horbar: 8213,
- hslash: 8463,
- hstrok: 295,
- hybull: 8259,
- hyphen: 8208,
- iacute: 237,
- igrave: 236,
- iiiint: 10764,
- iinfin: 10716,
- incare: 8453,
- inodot: 305,
- intcal: 8890,
- iquest: 191,
- isinsv: 8947,
- itilde: 297,
- jsercy: 1112,
- kappav: 1008,
- kcedil: 311,
- kgreen: 312,
- lAtail: 10523,
- lacute: 314,
- lagran: 8466,
- lambda: 955,
- langle: 10216,
- larrfs: 10525,
- larrhk: 8617,
- larrlp: 8619,
- larrpl: 10553,
- larrtl: 8610,
- latail: 10521,
- lbrace: 123,
- lbrack: 91,
- lcaron: 318,
- lcedil: 316,
- ldquor: 8222,
- lesdot: 10879,
- lesges: 10899,
- lfisht: 10620,
- lfloor: 8970,
- lharul: 10602,
- llhard: 10603,
- lmidot: 320,
- lmoust: 9136,
- loplus: 10797,
- lowast: 8727,
- lowbar: 95,
- lparlt: 10643,
- lrhard: 10605,
- lsaquo: 8249,
- lsquor: 8218,
- lstrok: 322,
- lthree: 8907,
- ltimes: 8905,
- ltlarr: 10614,
- ltrPar: 10646,
- mapsto: 8614,
- marker: 9646,
- mcomma: 10793,
- midast: 42,
- midcir: 10992,
- middot: 183,
- minusb: 8863,
- minusd: 8760,
- mnplus: 8723,
- models: 8871,
- mstpos: 8766,
- nVDash: 8879,
- nVdash: 8878,
- nacute: 324,
- ncaron: 328,
- ncedil: 326,
- nearhk: 10532,
- nequiv: 8802,
- nesear: 10536,
- nexist: 8708,
- nltrie: 8940,
- nprcue: 8928,
- nrtrie: 8941,
- nsccue: 8929,
- nsimeq: 8772,
- ntilde: 241,
- numero: 8470,
- nvDash: 8877,
- nvHarr: 10500,
- nvdash: 8876,
- nvlArr: 10498,
- nvrArr: 10499,
- nwarhk: 10531,
- nwnear: 10535,
- oacute: 243,
- odblac: 337,
- odsold: 10684,
- ograve: 242,
- ominus: 8854,
- origof: 8886,
- oslash: 248,
- otilde: 245,
- otimes: 8855,
- parsim: 10995,
- percnt: 37,
- period: 46,
- permil: 8240,
- phmmat: 8499,
- planck: 8463,
- plankv: 8463,
- plusdo: 8724,
- plusdu: 10789,
- plusmn: 177,
- preceq: 10927,
- primes: 8473,
- prnsim: 8936,
- propto: 8733,
- prurel: 8880,
- puncsp: 8200,
- qprime: 8279,
- rAtail: 10524,
- racute: 341,
- rangle: 10217,
- rarrap: 10613,
- rarrfs: 10526,
- rarrhk: 8618,
- rarrlp: 8620,
- rarrpl: 10565,
- rarrtl: 8611,
- ratail: 10522,
- rbrace: 125,
- rbrack: 93,
- rcaron: 345,
- rcedil: 343,
- rdquor: 8221,
- rfisht: 10621,
- rfloor: 8971,
- rharul: 10604,
- rmoust: 9137,
- roplus: 10798,
- rpargt: 10644,
- rsaquo: 8250,
- rsquor: 8217,
- rthree: 8908,
- rtimes: 8906,
- sacute: 347,
- scaron: 353,
- scedil: 351,
- scnsim: 8937,
- searhk: 10533,
- seswar: 10537,
- sfrown: 8994,
- shchcy: 1097,
- sigmaf: 962,
- sigmav: 962,
- simdot: 10858,
- smashp: 10803,
- softcy: 1100,
- solbar: 9023,
- spades: 9824,
- sqsube: 8849,
- sqsupe: 8850,
- square: 9633,
- squarf: 9642,
- ssetmn: 8726,
- ssmile: 8995,
- sstarf: 8902,
- subdot: 10941,
- subset: 8834,
- subsim: 10951,
- subsub: 10965,
- subsup: 10963,
- succeq: 10928,
- supdot: 10942,
- supset: 8835,
- supsim: 10952,
- supsub: 10964,
- supsup: 10966,
- swarhk: 10534,
- swnwar: 10538,
- target: 8982,
- tcaron: 357,
- tcedil: 355,
- telrec: 8981,
- there4: 8756,
- thetav: 977,
- thinsp: 8201,
- thksim: 8764,
- timesb: 8864,
- timesd: 10800,
- topbot: 9014,
- topcir: 10993,
- tprime: 8244,
- tridot: 9708,
- tstrok: 359,
- uacute: 250,
- ubreve: 365,
- udblac: 369,
- ufisht: 10622,
- ugrave: 249,
- ulcorn: 8988,
- ulcrop: 8975,
- urcorn: 8989,
- urcrop: 8974,
- utilde: 361,
- vangrt: 10652,
- varphi: 966,
- varrho: 1009,
- veebar: 8891,
- vellip: 8942,
- verbar: 124,
- wedbar: 10847,
- wedgeq: 8793,
- weierp: 8472,
- wreath: 8768,
- xoplus: 10753,
- xotime: 10754,
- xsqcup: 10758,
- xuplus: 10756,
- xwedge: 8896,
- yacute: 253,
- zacute: 378,
- zcaron: 382,
- zeetrf: 8488,
- AElig: 198,
- Acirc: 194,
- Alpha: 913,
- Amacr: 256,
- Aogon: 260,
- Aring: 197,
- Breve: 728,
- Ccirc: 264,
- Colon: 8759,
- Cross: 10799,
- Dashv: 10980,
- Delta: 916,
- Ecirc: 202,
- Emacr: 274,
- Eogon: 280,
- Equal: 10869,
- Gamma: 915,
- Gcirc: 284,
- Hacek: 711,
- Hcirc: 292,
- IJlig: 306,
- Icirc: 206,
- Imacr: 298,
- Iogon: 302,
- Iukcy: 1030,
- Jcirc: 308,
- Jukcy: 1028,
- Kappa: 922,
- OElig: 338,
- Ocirc: 212,
- Omacr: 332,
- Omega: 937,
- Prime: 8243,
- RBarr: 10512,
- Scirc: 348,
- Sigma: 931,
- THORN: 222,
- TRADE: 8482,
- TSHcy: 1035,
- Theta: 920,
- Tilde: 8764,
- Ubrcy: 1038,
- Ucirc: 219,
- Umacr: 362,
- Union: 8899,
- Uogon: 370,
- UpTee: 8869,
- Uring: 366,
- VDash: 8875,
- Vdash: 8873,
- Wcirc: 372,
- Wedge: 8896,
- Ycirc: 374,
- acirc: 226,
- acute: 180,
- aelig: 230,
- aleph: 8501,
- alpha: 945,
- amacr: 257,
- amalg: 10815,
- angle: 8736,
- angrt: 8735,
- angst: 8491,
- aogon: 261,
- aring: 229,
- asymp: 8776,
- awint: 10769,
- bcong: 8780,
- bdquo: 8222,
- bepsi: 1014,
- blank: 9251,
- blk12: 9618,
- blk14: 9617,
- blk34: 9619,
- block: 9608,
- boxDL: 9559,
- boxDR: 9556,
- boxDl: 9558,
- boxDr: 9555,
- boxHD: 9574,
- boxHU: 9577,
- boxHd: 9572,
- boxHu: 9575,
- boxUL: 9565,
- boxUR: 9562,
- boxUl: 9564,
- boxUr: 9561,
- boxVH: 9580,
- boxVL: 9571,
- boxVR: 9568,
- boxVh: 9579,
- boxVl: 9570,
- boxVr: 9567,
- boxdL: 9557,
- boxdR: 9554,
- boxdl: 9488,
- boxdr: 9484,
- boxhD: 9573,
- boxhU: 9576,
- boxhd: 9516,
- boxhu: 9524,
- boxuL: 9563,
- boxuR: 9560,
- boxul: 9496,
- boxur: 9492,
- boxvH: 9578,
- boxvL: 9569,
- boxvR: 9566,
- boxvh: 9532,
- boxvl: 9508,
- boxvr: 9500,
- breve: 728,
- bsemi: 8271,
- bsime: 8909,
- bsolb: 10693,
- bumpE: 10926,
- bumpe: 8783,
- caret: 8257,
- caron: 711,
- ccaps: 10829,
- ccirc: 265,
- ccups: 10828,
- cedil: 184,
- check: 10003,
- clubs: 9827,
- colon: 58,
- comma: 44,
- crarr: 8629,
- cross: 10007,
- csube: 10961,
- csupe: 10962,
- ctdot: 8943,
- cuepr: 8926,
- cuesc: 8927,
- cupor: 10821,
- cuvee: 8910,
- cuwed: 8911,
- cwint: 8753,
- dashv: 8867,
- dblac: 733,
- ddarr: 8650,
- delta: 948,
- dharl: 8643,
- dharr: 8642,
- diams: 9830,
- disin: 8946,
- doteq: 8784,
- dtdot: 8945,
- dtrif: 9662,
- duarr: 8693,
- duhar: 10607,
- eDDot: 10871,
- ecirc: 234,
- efDot: 8786,
- emacr: 275,
- empty: 8709,
- eogon: 281,
- eplus: 10865,
- epsiv: 949,
- eqsim: 8770,
- equiv: 8801,
- erDot: 8787,
- erarr: 10609,
- esdot: 8784,
- exist: 8707,
- fflig: 64256,
- filig: 64257,
- fllig: 64258,
- fltns: 9649,
- forkv: 10969,
- frasl: 8260,
- frown: 8994,
- gamma: 947,
- gcirc: 285,
- gescc: 10921,
- gimel: 8503,
- gneqq: 8809,
- gnsim: 8935,
- grave: 96,
- gsime: 10894,
- gsiml: 10896,
- gtcir: 10874,
- gtdot: 8919,
- harrw: 8621,
- hcirc: 293,
- hoarr: 8703,
- icirc: 238,
- iexcl: 161,
- iiint: 8749,
- iiota: 8489,
- ijlig: 307,
- imacr: 299,
- image: 8465,
- imath: 305,
- imped: 437,
- infin: 8734,
- iogon: 303,
- iprod: 10812,
- isinE: 8953,
- isins: 8948,
- isinv: 8712,
- iukcy: 1110,
- jcirc: 309,
- jmath: 567,
- jukcy: 1108,
- kappa: 954,
- lAarr: 8666,
- lBarr: 10510,
- langd: 10641,
- laquo: 171,
- larrb: 8676,
- lbarr: 10508,
- lbbrk: 10098,
- lbrke: 10635,
- lceil: 8968,
- ldquo: 8220,
- lescc: 10920,
- lhard: 8637,
- lharu: 8636,
- lhblk: 9604,
- llarr: 8647,
- lltri: 9722,
- lneqq: 8808,
- lnsim: 8934,
- loang: 10220,
- loarr: 8701,
- lobrk: 10214,
- lopar: 10629,
- lrarr: 8646,
- lrhar: 8651,
- lrtri: 8895,
- lsime: 10893,
- lsimg: 10895,
- lsquo: 8216,
- ltcir: 10873,
- ltdot: 8918,
- ltrie: 8884,
- ltrif: 9666,
- mDDot: 8762,
- mdash: 8212,
- micro: 181,
- minus: 8722,
- mumap: 8888,
- nabla: 8711,
- napos: 329,
- natur: 9838,
- ncong: 8775,
- ndash: 8211,
- neArr: 8663,
- nearr: 8599,
- ngsim: 8821,
- nhArr: 8654,
- nharr: 8622,
- nhpar: 10994,
- nlArr: 8653,
- nlarr: 8602,
- nless: 8814,
- nlsim: 8820,
- nltri: 8938,
- notin: 8713,
- notni: 8716,
- nprec: 8832,
- nrArr: 8655,
- nrarr: 8603,
- nrtri: 8939,
- nsime: 8772,
- nsmid: 8740,
- nspar: 8742,
- nsube: 8840,
- nsucc: 8833,
- nsupe: 8841,
- numsp: 8199,
- nwArr: 8662,
- nwarr: 8598,
- ocirc: 244,
- odash: 8861,
- oelig: 339,
- ofcir: 10687,
- ohbar: 10677,
- olarr: 8634,
- olcir: 10686,
- oline: 8254,
- omacr: 333,
- omega: 969,
- operp: 10681,
- oplus: 8853,
- orarr: 8635,
- order: 8500,
- ovbar: 9021,
- parsl: 11005,
- phone: 9742,
- plusb: 8862,
- pluse: 10866,
- pound: 163,
- prcue: 8828,
- prime: 8242,
- prnap: 10937,
- prsim: 8830,
- quest: 63,
- rAarr: 8667,
- rBarr: 10511,
- radic: 8730,
- rangd: 10642,
- range: 10661,
- raquo: 187,
- rarrb: 8677,
- rarrc: 10547,
- rarrw: 8605,
- ratio: 8758,
- rbarr: 10509,
- rbbrk: 10099,
- rbrke: 10636,
- rceil: 8969,
- rdquo: 8221,
- reals: 8477,
- rhard: 8641,
- rharu: 8640,
- rlarr: 8644,
- rlhar: 8652,
- rnmid: 10990,
- roang: 10221,
- roarr: 8702,
- robrk: 10215,
- ropar: 10630,
- rrarr: 8649,
- rsquo: 8217,
- rtrie: 8885,
- rtrif: 9656,
- sbquo: 8218,
- sccue: 8829,
- scirc: 349,
- scnap: 10938,
- scsim: 8831,
- sdotb: 8865,
- sdote: 10854,
- seArr: 8664,
- searr: 8600,
- setmn: 8726,
- sharp: 9839,
- sigma: 963,
- simeq: 8771,
- simgE: 10912,
- simlE: 10911,
- simne: 8774,
- slarr: 8592,
- smile: 8995,
- sqcap: 8851,
- sqcup: 8852,
- sqsub: 8847,
- sqsup: 8848,
- srarr: 8594,
- starf: 9733,
- strns: 175,
- subnE: 10955,
- subne: 8842,
- supnE: 10956,
- supne: 8843,
- swArr: 8665,
- swarr: 8601,
- szlig: 223,
- theta: 952,
- thkap: 8776,
- thorn: 254,
- tilde: 732,
- times: 215,
- trade: 8482,
- trisb: 10701,
- tshcy: 1115,
- twixt: 8812,
- ubrcy: 1118,
- ucirc: 251,
- udarr: 8645,
- udhar: 10606,
- uharl: 8639,
- uharr: 8638,
- uhblk: 9600,
- ultri: 9720,
- umacr: 363,
- uogon: 371,
- uplus: 8846,
- upsih: 978,
- uring: 367,
- urtri: 9721,
- utdot: 8944,
- utrif: 9652,
- uuarr: 8648,
- vBarv: 10985,
- vDash: 8872,
- varpi: 982,
- vdash: 8866,
- veeeq: 8794,
- vltri: 8882,
- vprop: 8733,
- vrtri: 8883,
- wcirc: 373,
- wedge: 8743,
- xcirc: 9711,
- xdtri: 9661,
- xhArr: 10234,
- xharr: 10231,
- xlArr: 10232,
- xlarr: 10229,
- xodot: 10752,
- xrArr: 10233,
- xrarr: 10230,
- xutri: 9651,
- ycirc: 375,
- Aopf: 120120,
- Ascr: 119964,
- Auml: 196,
- Barv: 10983,
- Beta: 914,
- Bopf: 120121,
- Bscr: 8492,
- CHcy: 1063,
- COPY: 169,
- Cdot: 266,
- Copf: 8450,
- Cscr: 119966,
- DJcy: 1026,
- DScy: 1029,
- DZcy: 1039,
- Darr: 8609,
- Dopf: 120123,
- Dscr: 119967,
- Edot: 278,
- Eopf: 120124,
- Escr: 8496,
- Esim: 10867,
- Euml: 203,
- Fopf: 120125,
- Fscr: 8497,
- GJcy: 1027,
- Gdot: 288,
- Gopf: 120126,
- Gscr: 119970,
- Hopf: 8461,
- Hscr: 8459,
- IEcy: 1045,
- IOcy: 1025,
- Idot: 304,
- Iopf: 120128,
- Iota: 921,
- Iscr: 8464,
- Iuml: 207,
- Jopf: 120129,
- Jscr: 119973,
- KHcy: 1061,
- KJcy: 1036,
- Kopf: 120130,
- Kscr: 119974,
- LJcy: 1033,
- Lang: 10218,
- Larr: 8606,
- Lopf: 120131,
- Lscr: 8466,
- Mopf: 120132,
- Mscr: 8499,
- NJcy: 1034,
- Nopf: 8469,
- Nscr: 119977,
- Oopf: 120134,
- Oscr: 119978,
- Ouml: 214,
- Popf: 8473,
- Pscr: 119979,
- QUOT: 34,
- Qopf: 8474,
- Qscr: 119980,
- Rang: 10219,
- Rarr: 8608,
- Ropf: 8477,
- Rscr: 8475,
- SHcy: 1064,
- Sopf: 120138,
- Sqrt: 8730,
- Sscr: 119982,
- Star: 8902,
- TScy: 1062,
- Topf: 120139,
- Tscr: 119983,
- Uarr: 8607,
- Uopf: 120140,
- Upsi: 978,
- Uscr: 119984,
- Uuml: 220,
- Vbar: 10987,
- Vert: 8214,
- Vopf: 120141,
- Vscr: 119985,
- Wopf: 120142,
- Wscr: 119986,
- Xopf: 120143,
- Xscr: 119987,
- YAcy: 1071,
- YIcy: 1031,
- YUcy: 1070,
- Yopf: 120144,
- Yscr: 119988,
- Yuml: 376,
- ZHcy: 1046,
- Zdot: 379,
- Zeta: 918,
- Zopf: 8484,
- Zscr: 119989,
- andd: 10844,
- andv: 10842,
- ange: 10660,
- aopf: 120146,
- apid: 8779,
- apos: 39,
- ascr: 119990,
- auml: 228,
- bNot: 10989,
- bbrk: 9141,
- beta: 946,
- beth: 8502,
- bnot: 8976,
- bopf: 120147,
- boxH: 9552,
- boxV: 9553,
- boxh: 9472,
- boxv: 9474,
- bscr: 119991,
- bsim: 8765,
- bsol: 92,
- bull: 8226,
- bump: 8782,
- cdot: 267,
- cent: 162,
- chcy: 1095,
- cirE: 10691,
- circ: 710,
- cire: 8791,
- comp: 8705,
- cong: 8773,
- copf: 120148,
- copy: 169,
- cscr: 119992,
- csub: 10959,
- csup: 10960,
- dArr: 8659,
- dHar: 10597,
- darr: 8595,
- dash: 8208,
- diam: 8900,
- djcy: 1106,
- dopf: 120149,
- dscr: 119993,
- dscy: 1109,
- dsol: 10742,
- dtri: 9663,
- dzcy: 1119,
- eDot: 8785,
- ecir: 8790,
- edot: 279,
- emsp: 8195,
- ensp: 8194,
- eopf: 120150,
- epar: 8917,
- epsi: 1013,
- escr: 8495,
- esim: 8770,
- euml: 235,
- euro: 8364,
- excl: 33,
- flat: 9837,
- fnof: 402,
- fopf: 120151,
- fork: 8916,
- fscr: 119995,
- gdot: 289,
- geqq: 8807,
- gjcy: 1107,
- gnap: 10890,
- gneq: 10888,
- gopf: 120152,
- gscr: 8458,
- gsim: 8819,
- gtcc: 10919,
- hArr: 8660,
- half: 189,
- harr: 8596,
- hbar: 8463,
- hopf: 120153,
- hscr: 119997,
- iecy: 1077,
- imof: 8887,
- iocy: 1105,
- iopf: 120154,
- iota: 953,
- iscr: 119998,
- isin: 8712,
- iuml: 239,
- jopf: 120155,
- jscr: 119999,
- khcy: 1093,
- kjcy: 1116,
- kopf: 120156,
- kscr: 120000,
- lArr: 8656,
- lHar: 10594,
- lang: 10216,
- larr: 8592,
- late: 10925,
- lcub: 123,
- ldca: 10550,
- ldsh: 8626,
- leqq: 8806,
- ljcy: 1113,
- lnap: 10889,
- lneq: 10887,
- lopf: 120157,
- lozf: 10731,
- lpar: 40,
- lscr: 120001,
- lsim: 8818,
- lsqb: 91,
- ltcc: 10918,
- ltri: 9667,
- macr: 175,
- male: 9794,
- malt: 10016,
- mlcp: 10971,
- mldr: 8230,
- mopf: 120158,
- mscr: 120002,
- nbsp: 160,
- ncap: 10819,
- ncup: 10818,
- ngeq: 8817,
- ngtr: 8815,
- nisd: 8954,
- njcy: 1114,
- nldr: 8229,
- nleq: 8816,
- nmid: 8740,
- nopf: 120159,
- npar: 8742,
- nscr: 120003,
- nsim: 8769,
- nsub: 8836,
- nsup: 8837,
- ntgl: 8825,
- ntlg: 8824,
- oast: 8859,
- ocir: 8858,
- odiv: 10808,
- odot: 8857,
- ogon: 731,
- oint: 8750,
- omid: 10678,
- oopf: 120160,
- opar: 10679,
- ordf: 170,
- ordm: 186,
- oror: 10838,
- oscr: 8500,
- osol: 8856,
- ouml: 246,
- para: 182,
- part: 8706,
- perp: 8869,
- phiv: 966,
- plus: 43,
- popf: 120161,
- prap: 10935,
- prec: 8826,
- prnE: 10933,
- prod: 8719,
- prop: 8733,
- pscr: 120005,
- qint: 10764,
- qopf: 120162,
- qscr: 120006,
- quot: 34,
- rArr: 8658,
- rHar: 10596,
- race: 10714,
- rang: 10217,
- rarr: 8594,
- rcub: 125,
- rdca: 10551,
- rdsh: 8627,
- real: 8476,
- rect: 9645,
- rhov: 1009,
- ring: 730,
- ropf: 120163,
- rpar: 41,
- rscr: 120007,
- rsqb: 93,
- rtri: 9657,
- scap: 10936,
- scnE: 10934,
- sdot: 8901,
- sect: 167,
- semi: 59,
- sext: 10038,
- shcy: 1096,
- sime: 8771,
- simg: 10910,
- siml: 10909,
- smid: 8739,
- smte: 10924,
- solb: 10692,
- sopf: 120164,
- spar: 8741,
- squf: 9642,
- sscr: 120008,
- star: 9734,
- subE: 10949,
- sube: 8838,
- succ: 8827,
- sung: 9834,
- sup1: 185,
- sup2: 178,
- sup3: 179,
- supE: 10950,
- supe: 8839,
- tbrk: 9140,
- tdot: 8411,
- tint: 8749,
- toea: 10536,
- topf: 120165,
- tosa: 10537,
- trie: 8796,
- tscr: 120009,
- tscy: 1094,
- uArr: 8657,
- uHar: 10595,
- uarr: 8593,
- uopf: 120166,
- upsi: 965,
- uscr: 120010,
- utri: 9653,
- uuml: 252,
- vArr: 8661,
- vBar: 10984,
- varr: 8597,
- vert: 124,
- vopf: 120167,
- vscr: 120011,
- wopf: 120168,
- wscr: 120012,
- xcap: 8898,
- xcup: 8899,
- xmap: 10236,
- xnis: 8955,
- xopf: 120169,
- xscr: 120013,
- xvee: 8897,
- yacy: 1103,
- yicy: 1111,
- yopf: 120170,
- yscr: 120014,
- yucy: 1102,
- yuml: 255,
- zdot: 380,
- zeta: 950,
- zhcy: 1078,
- zopf: 120171,
- zscr: 120015,
- zwnj: 8204,
- AMP: 38,
- Acy: 1040,
- Afr: 120068,
- And: 10835,
- Bcy: 1041,
- Bfr: 120069,
- Cap: 8914,
- Cfr: 8493,
- Chi: 935,
- Cup: 8915,
- Dcy: 1044,
- Del: 8711,
- Dfr: 120071,
- Dot: 168,
- ENG: 330,
- ETH: 208,
- Ecy: 1069,
- Efr: 120072,
- Eta: 919,
- Fcy: 1060,
- Ffr: 120073,
- Gcy: 1043,
- Gfr: 120074,
- Hat: 94,
- Hfr: 8460,
- Icy: 1048,
- Ifr: 8465,
- Int: 8748,
- Jcy: 1049,
- Jfr: 120077,
- Kcy: 1050,
- Kfr: 120078,
- Lcy: 1051,
- Lfr: 120079,
- Lsh: 8624,
- Map: 10501,
- Mcy: 1052,
- Mfr: 120080,
- Ncy: 1053,
- Nfr: 120081,
- Not: 10988,
- Ocy: 1054,
- Ofr: 120082,
- Pcy: 1055,
- Pfr: 120083,
- Phi: 934,
- Psi: 936,
- Qfr: 120084,
- REG: 174,
- Rcy: 1056,
- Rfr: 8476,
- Rho: 929,
- Rsh: 8625,
- Scy: 1057,
- Sfr: 120086,
- Sub: 8912,
- Sum: 8721,
- Sup: 8913,
- Tab: 9,
- Tau: 932,
- Tcy: 1058,
- Tfr: 120087,
- Ucy: 1059,
- Ufr: 120088,
- Vcy: 1042,
- Vee: 8897,
- Vfr: 120089,
- Wfr: 120090,
- Xfr: 120091,
- Ycy: 1067,
- Yfr: 120092,
- Zcy: 1047,
- Zfr: 8488,
- acd: 8767,
- acy: 1072,
- afr: 120094,
- amp: 38,
- and: 8743,
- ang: 8736,
- apE: 10864,
- ape: 8778,
- ast: 42,
- bcy: 1073,
- bfr: 120095,
- bot: 8869,
- cap: 8745,
- cfr: 120096,
- chi: 967,
- cir: 9675,
- cup: 8746,
- dcy: 1076,
- deg: 176,
- dfr: 120097,
- die: 168,
- div: 247,
- dot: 729,
- ecy: 1101,
- efr: 120098,
- egs: 10902,
- ell: 8467,
- els: 10901,
- eng: 331,
- eta: 951,
- eth: 240,
- fcy: 1092,
- ffr: 120099,
- gEl: 10892,
- gap: 10886,
- gcy: 1075,
- gel: 8923,
- geq: 8805,
- ges: 10878,
- gfr: 120100,
- ggg: 8921,
- glE: 10898,
- gla: 10917,
- glj: 10916,
- gnE: 8809,
- gne: 10888,
- hfr: 120101,
- icy: 1080,
- iff: 8660,
- ifr: 120102,
- int: 8747,
- jcy: 1081,
- jfr: 120103,
- kcy: 1082,
- kfr: 120104,
- lEg: 10891,
- lap: 10885,
- lat: 10923,
- lcy: 1083,
- leg: 8922,
- leq: 8804,
- les: 10877,
- lfr: 120105,
- lgE: 10897,
- lnE: 8808,
- lne: 10887,
- loz: 9674,
- lrm: 8206,
- lsh: 8624,
- map: 8614,
- mcy: 1084,
- mfr: 120106,
- mho: 8487,
- mid: 8739,
- nap: 8777,
- ncy: 1085,
- nfr: 120107,
- nge: 8817,
- ngt: 8815,
- nis: 8956,
- niv: 8715,
- nle: 8816,
- nlt: 8814,
- not: 172,
- npr: 8832,
- nsc: 8833,
- num: 35,
- ocy: 1086,
- ofr: 120108,
- ogt: 10689,
- ohm: 8486,
- olt: 10688,
- ord: 10845,
- orv: 10843,
- par: 8741,
- pcy: 1087,
- pfr: 120109,
- phi: 966,
- piv: 982,
- prE: 10931,
- pre: 10927,
- psi: 968,
- qfr: 120110,
- rcy: 1088,
- reg: 174,
- rfr: 120111,
- rho: 961,
- rlm: 8207,
- rsh: 8625,
- scE: 10932,
- sce: 10928,
- scy: 1089,
- sfr: 120112,
- shy: 173,
- sim: 8764,
- smt: 10922,
- sol: 47,
- squ: 9633,
- sub: 8834,
- sum: 8721,
- sup: 8835,
- tau: 964,
- tcy: 1090,
- tfr: 120113,
- top: 8868,
- ucy: 1091,
- ufr: 120114,
- uml: 168,
- vcy: 1074,
- vee: 8744,
- vfr: 120115,
- wfr: 120116,
- xfr: 120117,
- ycy: 1099,
- yen: 165,
- yfr: 120118,
- zcy: 1079,
- zfr: 120119,
- zwj: 8205,
- DD: 8517,
- GT: 62,
- Gg: 8921,
- Gt: 8811,
- Im: 8465,
- LT: 60,
- Ll: 8920,
- Lt: 8810,
- Mu: 924,
- Nu: 925,
- Or: 10836,
- Pi: 928,
- Pr: 10939,
- Re: 8476,
- Sc: 10940,
- Xi: 926,
- ac: 8766,
- af: 8289,
- ap: 8776,
- dd: 8518,
- ee: 8519,
- eg: 10906,
- el: 10905,
- gE: 8807,
- ge: 8805,
- gg: 8811,
- gl: 8823,
- gt: 62,
- ic: 8291,
- ii: 8520,
- in: 8712,
- it: 8290,
- lE: 8806,
- le: 8804,
- lg: 8822,
- ll: 8810,
- lt: 60,
- mp: 8723,
- mu: 956,
- ne: 8800,
- ni: 8715,
- nu: 957,
- oS: 9416,
- or: 8744,
- pi: 960,
- pm: 177,
- pr: 8826,
- rx: 8478,
- sc: 8827,
- wp: 8472,
- wr: 8768,
- xi: 958,
+ CounterClockwiseContourIntegral: 8755,
+ ClockwiseContourIntegral: 8754,
+ DoubleLongLeftRightArrow: 10234,
+ DiacriticalDoubleAcute: 733,
+ NotSquareSupersetEqual: 8931,
+ CloseCurlyDoubleQuote: 8221,
+ DoubleContourIntegral: 8751,
+ FilledVerySmallSquare: 9642,
+ NegativeVeryThinSpace: 8203,
+ NotPrecedesSlantEqual: 8928,
+ NotRightTriangleEqual: 8941,
+ NotSucceedsSlantEqual: 8929,
+ CapitalDifferentialD: 8517,
+ DoubleLeftRightArrow: 8660,
+ DoubleLongRightArrow: 10233,
+ EmptyVerySmallSquare: 9643,
+ NestedGreaterGreater: 8811,
+ NotDoubleVerticalBar: 8742,
+ NotLeftTriangleEqual: 8940,
+ NotSquareSubsetEqual: 8930,
+ OpenCurlyDoubleQuote: 8220,
+ ReverseUpEquilibrium: 10607,
+ DoubleLongLeftArrow: 10232,
+ DownLeftRightVector: 10576,
+ LeftArrowRightArrow: 8646,
+ NegativeMediumSpace: 8203,
+ RightArrowLeftArrow: 8644,
+ SquareSupersetEqual: 8850,
+ leftrightsquigarrow: 8621,
+ DownRightTeeVector: 10591,
+ DownRightVectorBar: 10583,
+ LongLeftRightArrow: 10231,
+ Longleftrightarrow: 10234,
+ NegativeThickSpace: 8203,
+ PrecedesSlantEqual: 8828,
+ ReverseEquilibrium: 8651,
+ RightDoubleBracket: 10215,
+ RightDownTeeVector: 10589,
+ RightDownVectorBar: 10581,
+ RightTriangleEqual: 8885,
+ SquareIntersection: 8851,
+ SucceedsSlantEqual: 8829,
+ blacktriangleright: 9656,
+ longleftrightarrow: 10231,
+ DoubleUpDownArrow: 8661,
+ DoubleVerticalBar: 8741,
+ DownLeftTeeVector: 10590,
+ DownLeftVectorBar: 10582,
+ FilledSmallSquare: 9724,
+ GreaterSlantEqual: 10878,
+ LeftDoubleBracket: 10214,
+ LeftDownTeeVector: 10593,
+ LeftDownVectorBar: 10585,
+ LeftTriangleEqual: 8884,
+ NegativeThinSpace: 8203,
+ NotReverseElement: 8716,
+ NotTildeFullEqual: 8775,
+ RightAngleBracket: 10217,
+ RightUpDownVector: 10575,
+ SquareSubsetEqual: 8849,
+ VerticalSeparator: 10072,
+ blacktriangledown: 9662,
+ blacktriangleleft: 9666,
+ leftrightharpoons: 8651,
+ rightleftharpoons: 8652,
+ twoheadrightarrow: 8608,
+ DiacriticalAcute: 180,
+ DiacriticalGrave: 96,
+ DiacriticalTilde: 732,
+ DoubleRightArrow: 8658,
+ DownArrowUpArrow: 8693,
+ EmptySmallSquare: 9723,
+ GreaterEqualLess: 8923,
+ GreaterFullEqual: 8807,
+ LeftAngleBracket: 10216,
+ LeftUpDownVector: 10577,
+ LessEqualGreater: 8922,
+ NonBreakingSpace: 160,
+ NotRightTriangle: 8939,
+ NotSupersetEqual: 8841,
+ RightTriangleBar: 10704,
+ RightUpTeeVector: 10588,
+ RightUpVectorBar: 10580,
+ UnderParenthesis: 9181,
+ UpArrowDownArrow: 8645,
+ circlearrowright: 8635,
+ downharpoonright: 8642,
+ ntrianglerighteq: 8941,
+ rightharpoondown: 8641,
+ rightrightarrows: 8649,
+ twoheadleftarrow: 8606,
+ vartriangleright: 8883,
+ CloseCurlyQuote: 8217,
+ ContourIntegral: 8750,
+ DoubleDownArrow: 8659,
+ DoubleLeftArrow: 8656,
+ DownRightVector: 8641,
+ LeftRightVector: 10574,
+ LeftTriangleBar: 10703,
+ LeftUpTeeVector: 10592,
+ LeftUpVectorBar: 10584,
+ LowerRightArrow: 8600,
+ NotGreaterEqual: 8817,
+ NotGreaterTilde: 8821,
+ NotLeftTriangle: 8938,
+ OverParenthesis: 9180,
+ RightDownVector: 8642,
+ ShortRightArrow: 8594,
+ UpperRightArrow: 8599,
+ bigtriangledown: 9661,
+ circlearrowleft: 8634,
+ curvearrowright: 8631,
+ downharpoonleft: 8643,
+ leftharpoondown: 8637,
+ leftrightarrows: 8646,
+ nLeftrightarrow: 8654,
+ nleftrightarrow: 8622,
+ ntrianglelefteq: 8940,
+ rightleftarrows: 8644,
+ rightsquigarrow: 8605,
+ rightthreetimes: 8908,
+ straightepsilon: 1013,
+ trianglerighteq: 8885,
+ vartriangleleft: 8882,
+ DiacriticalDot: 729,
+ DoubleRightTee: 8872,
+ DownLeftVector: 8637,
+ GreaterGreater: 10914,
+ HorizontalLine: 9472,
+ InvisibleComma: 8291,
+ InvisibleTimes: 8290,
+ LeftDownVector: 8643,
+ LeftRightArrow: 8596,
+ Leftrightarrow: 8660,
+ LessSlantEqual: 10877,
+ LongRightArrow: 10230,
+ Longrightarrow: 10233,
+ LowerLeftArrow: 8601,
+ NestedLessLess: 8810,
+ NotGreaterLess: 8825,
+ NotLessGreater: 8824,
+ NotSubsetEqual: 8840,
+ NotVerticalBar: 8740,
+ OpenCurlyQuote: 8216,
+ ReverseElement: 8715,
+ RightTeeVector: 10587,
+ RightVectorBar: 10579,
+ ShortDownArrow: 8595,
+ ShortLeftArrow: 8592,
+ SquareSuperset: 8848,
+ TildeFullEqual: 8773,
+ UpperLeftArrow: 8598,
+ ZeroWidthSpace: 8203,
+ curvearrowleft: 8630,
+ doublebarwedge: 8966,
+ downdownarrows: 8650,
+ hookrightarrow: 8618,
+ leftleftarrows: 8647,
+ leftrightarrow: 8596,
+ leftthreetimes: 8907,
+ longrightarrow: 10230,
+ looparrowright: 8620,
+ nshortparallel: 8742,
+ ntriangleright: 8939,
+ rightarrowtail: 8611,
+ rightharpoonup: 8640,
+ trianglelefteq: 8884,
+ upharpoonright: 8638,
+ ApplyFunction: 8289,
+ DifferentialD: 8518,
+ DoubleLeftTee: 10980,
+ DoubleUpArrow: 8657,
+ LeftTeeVector: 10586,
+ LeftVectorBar: 10578,
+ LessFullEqual: 8806,
+ LongLeftArrow: 10229,
+ Longleftarrow: 10232,
+ NotTildeEqual: 8772,
+ NotTildeTilde: 8777,
+ Poincareplane: 8460,
+ PrecedesEqual: 10927,
+ PrecedesTilde: 8830,
+ RightArrowBar: 8677,
+ RightTeeArrow: 8614,
+ RightTriangle: 8883,
+ RightUpVector: 8638,
+ SucceedsEqual: 10928,
+ SucceedsTilde: 8831,
+ SupersetEqual: 8839,
+ UpEquilibrium: 10606,
+ VerticalTilde: 8768,
+ VeryThinSpace: 8202,
+ bigtriangleup: 9651,
+ blacktriangle: 9652,
+ divideontimes: 8903,
+ fallingdotseq: 8786,
+ hookleftarrow: 8617,
+ leftarrowtail: 8610,
+ leftharpoonup: 8636,
+ longleftarrow: 10229,
+ looparrowleft: 8619,
+ measuredangle: 8737,
+ ntriangleleft: 8938,
+ shortparallel: 8741,
+ smallsetminus: 8726,
+ triangleright: 9657,
+ upharpoonleft: 8639,
+ DownArrowBar: 10515,
+ DownTeeArrow: 8615,
+ ExponentialE: 8519,
+ GreaterEqual: 8805,
+ GreaterTilde: 8819,
+ HilbertSpace: 8459,
+ HumpDownHump: 8782,
+ Intersection: 8898,
+ LeftArrowBar: 8676,
+ LeftTeeArrow: 8612,
+ LeftTriangle: 8882,
+ LeftUpVector: 8639,
+ NotCongruent: 8802,
+ NotLessEqual: 8816,
+ NotLessTilde: 8820,
+ Proportional: 8733,
+ RightCeiling: 8969,
+ RoundImplies: 10608,
+ ShortUpArrow: 8593,
+ SquareSubset: 8847,
+ UnderBracket: 9141,
+ VerticalLine: 124,
+ blacklozenge: 10731,
+ exponentiale: 8519,
+ risingdotseq: 8787,
+ triangledown: 9663,
+ triangleleft: 9667,
+ CircleMinus: 8854,
+ CircleTimes: 8855,
+ Equilibrium: 8652,
+ GreaterLess: 8823,
+ LeftCeiling: 8968,
+ LessGreater: 8822,
+ MediumSpace: 8287,
+ NotPrecedes: 8832,
+ NotSucceeds: 8833,
+ OverBracket: 9140,
+ RightVector: 8640,
+ Rrightarrow: 8667,
+ RuleDelayed: 10740,
+ SmallCircle: 8728,
+ SquareUnion: 8852,
+ SubsetEqual: 8838,
+ UpDownArrow: 8597,
+ Updownarrow: 8661,
+ VerticalBar: 8739,
+ backepsilon: 1014,
+ blacksquare: 9642,
+ circledcirc: 8858,
+ circleddash: 8861,
+ curlyeqprec: 8926,
+ curlyeqsucc: 8927,
+ diamondsuit: 9830,
+ eqslantless: 10901,
+ expectation: 8496,
+ nRightarrow: 8655,
+ nrightarrow: 8603,
+ preccurlyeq: 8828,
+ precnapprox: 10937,
+ quaternions: 8461,
+ straightphi: 981,
+ succcurlyeq: 8829,
+ succnapprox: 10938,
+ thickapprox: 8776,
+ updownarrow: 8597,
+ Bernoullis: 8492,
+ CirclePlus: 8853,
+ EqualTilde: 8770,
+ Fouriertrf: 8497,
+ ImaginaryI: 8520,
+ Laplacetrf: 8466,
+ LeftVector: 8636,
+ Lleftarrow: 8666,
+ NotElement: 8713,
+ NotGreater: 8815,
+ Proportion: 8759,
+ RightArrow: 8594,
+ RightFloor: 8971,
+ Rightarrow: 8658,
+ TildeEqual: 8771,
+ TildeTilde: 8776,
+ UnderBrace: 9183,
+ UpArrowBar: 10514,
+ UpTeeArrow: 8613,
+ circledast: 8859,
+ complement: 8705,
+ curlywedge: 8911,
+ eqslantgtr: 10902,
+ gtreqqless: 10892,
+ lessapprox: 10885,
+ lesseqqgtr: 10891,
+ lmoustache: 9136,
+ longmapsto: 10236,
+ mapstodown: 8615,
+ mapstoleft: 8612,
+ nLeftarrow: 8653,
+ nleftarrow: 8602,
+ precapprox: 10935,
+ rightarrow: 8594,
+ rmoustache: 9137,
+ sqsubseteq: 8849,
+ sqsupseteq: 8850,
+ subsetneqq: 10955,
+ succapprox: 10936,
+ supsetneqq: 10956,
+ upuparrows: 8648,
+ varepsilon: 949,
+ varnothing: 8709,
+ Backslash: 8726,
+ CenterDot: 183,
+ CircleDot: 8857,
+ Congruent: 8801,
+ Coproduct: 8720,
+ DoubleDot: 168,
+ DownArrow: 8595,
+ DownBreve: 785,
+ Downarrow: 8659,
+ HumpEqual: 8783,
+ LeftArrow: 8592,
+ LeftFloor: 8970,
+ Leftarrow: 8656,
+ LessTilde: 8818,
+ Mellintrf: 8499,
+ MinusPlus: 8723,
+ NotCupCap: 8813,
+ NotExists: 8708,
+ OverBrace: 9182,
+ PlusMinus: 177,
+ Therefore: 8756,
+ ThinSpace: 8201,
+ TripleDot: 8411,
+ UnionPlus: 8846,
+ backprime: 8245,
+ backsimeq: 8909,
+ bigotimes: 10754,
+ centerdot: 183,
+ checkmark: 10003,
+ complexes: 8450,
+ dotsquare: 8865,
+ downarrow: 8595,
+ gtrapprox: 10886,
+ gtreqless: 8923,
+ heartsuit: 9829,
+ leftarrow: 8592,
+ lesseqgtr: 8922,
+ nparallel: 8742,
+ nshortmid: 8740,
+ nsubseteq: 8840,
+ nsupseteq: 8841,
+ pitchfork: 8916,
+ rationals: 8474,
+ spadesuit: 9824,
+ subseteqq: 10949,
+ subsetneq: 8842,
+ supseteqq: 10950,
+ supsetneq: 8843,
+ therefore: 8756,
+ triangleq: 8796,
+ varpropto: 8733,
+ DDotrahd: 10513,
+ DotEqual: 8784,
+ Integral: 8747,
+ LessLess: 10913,
+ NotEqual: 8800,
+ NotTilde: 8769,
+ PartialD: 8706,
+ Precedes: 8826,
+ RightTee: 8866,
+ Succeeds: 8827,
+ SuchThat: 8715,
+ Superset: 8835,
+ Uarrocir: 10569,
+ UnderBar: 818,
+ andslope: 10840,
+ angmsdaa: 10664,
+ angmsdab: 10665,
+ angmsdac: 10666,
+ angmsdad: 10667,
+ angmsdae: 10668,
+ angmsdaf: 10669,
+ angmsdag: 10670,
+ angmsdah: 10671,
+ angrtvbd: 10653,
+ approxeq: 8778,
+ awconint: 8755,
+ backcong: 8780,
+ barwedge: 8965,
+ bbrktbrk: 9142,
+ bigoplus: 10753,
+ bigsqcup: 10758,
+ biguplus: 10756,
+ bigwedge: 8896,
+ boxminus: 8863,
+ boxtimes: 8864,
+ capbrcup: 10825,
+ circledR: 174,
+ circledS: 9416,
+ cirfnint: 10768,
+ clubsuit: 9827,
+ cupbrcap: 10824,
+ curlyvee: 8910,
+ cwconint: 8754,
+ doteqdot: 8785,
+ dotminus: 8760,
+ drbkarow: 10512,
+ dzigrarr: 10239,
+ elinters: 9191,
+ emptyset: 8709,
+ eqvparsl: 10725,
+ fpartint: 10765,
+ geqslant: 10878,
+ gesdotol: 10884,
+ gnapprox: 10890,
+ hksearow: 10533,
+ hkswarow: 10534,
+ imagline: 8464,
+ imagpart: 8465,
+ infintie: 10717,
+ integers: 8484,
+ intercal: 8890,
+ intlarhk: 10775,
+ laemptyv: 10676,
+ ldrushar: 10571,
+ leqslant: 10877,
+ lesdotor: 10883,
+ llcorner: 8990,
+ lnapprox: 10889,
+ lrcorner: 8991,
+ lurdshar: 10570,
+ mapstoup: 8613,
+ multimap: 8888,
+ naturals: 8469,
+ otimesas: 10806,
+ parallel: 8741,
+ plusacir: 10787,
+ pointint: 10773,
+ precneqq: 10933,
+ precnsim: 8936,
+ profalar: 9006,
+ profline: 8978,
+ profsurf: 8979,
+ raemptyv: 10675,
+ realpart: 8476,
+ rppolint: 10770,
+ rtriltri: 10702,
+ scpolint: 10771,
+ setminus: 8726,
+ shortmid: 8739,
+ smeparsl: 10724,
+ sqsubset: 8847,
+ sqsupset: 8848,
+ subseteq: 8838,
+ succneqq: 10934,
+ succnsim: 8937,
+ supseteq: 8839,
+ thetasym: 977,
+ thicksim: 8764,
+ timesbar: 10801,
+ triangle: 9653,
+ triminus: 10810,
+ trpezium: 9186,
+ ulcorner: 8988,
+ urcorner: 8989,
+ varkappa: 1008,
+ varsigma: 962,
+ vartheta: 977,
+ Because: 8757,
+ Cayleys: 8493,
+ Cconint: 8752,
+ Cedilla: 184,
+ Diamond: 8900,
+ DownTee: 8868,
+ Element: 8712,
+ Epsilon: 917,
+ Implies: 8658,
+ LeftTee: 8867,
+ NewLine: 10,
+ NoBreak: 8288,
+ NotLess: 8814,
+ Omicron: 927,
+ OverBar: 175,
+ Product: 8719,
+ UpArrow: 8593,
+ Uparrow: 8657,
+ Upsilon: 933,
+ alefsym: 8501,
+ angrtvb: 8894,
+ angzarr: 9084,
+ asympeq: 8781,
+ backsim: 8765,
+ because: 8757,
+ bemptyv: 10672,
+ between: 8812,
+ bigcirc: 9711,
+ bigodot: 10752,
+ bigstar: 9733,
+ boxplus: 8862,
+ ccupssm: 10832,
+ cemptyv: 10674,
+ cirscir: 10690,
+ coloneq: 8788,
+ congdot: 10861,
+ cudarrl: 10552,
+ cudarrr: 10549,
+ cularrp: 10557,
+ curarrm: 10556,
+ dbkarow: 10511,
+ ddagger: 8225,
+ ddotseq: 10871,
+ demptyv: 10673,
+ diamond: 8900,
+ digamma: 989,
+ dotplus: 8724,
+ dwangle: 10662,
+ epsilon: 949,
+ eqcolon: 8789,
+ equivDD: 10872,
+ gesdoto: 10882,
+ gtquest: 10876,
+ gtrless: 8823,
+ harrcir: 10568,
+ intprod: 10812,
+ isindot: 8949,
+ larrbfs: 10527,
+ larrsim: 10611,
+ lbrksld: 10639,
+ lbrkslu: 10637,
+ ldrdhar: 10599,
+ lesdoto: 10881,
+ lessdot: 8918,
+ lessgtr: 8822,
+ lesssim: 8818,
+ lotimes: 10804,
+ lozenge: 9674,
+ ltquest: 10875,
+ luruhar: 10598,
+ maltese: 10016,
+ minusdu: 10794,
+ napprox: 8777,
+ natural: 9838,
+ nearrow: 8599,
+ nexists: 8708,
+ notinva: 8713,
+ notinvb: 8951,
+ notinvc: 8950,
+ notniva: 8716,
+ notnivb: 8958,
+ notnivc: 8957,
+ npolint: 10772,
+ nsqsube: 8930,
+ nsqsupe: 8931,
+ nvinfin: 10718,
+ nwarrow: 8598,
+ olcross: 10683,
+ omicron: 959,
+ orderof: 8500,
+ orslope: 10839,
+ pertenk: 8241,
+ planckh: 8462,
+ pluscir: 10786,
+ plussim: 10790,
+ plustwo: 10791,
+ precsim: 8830,
+ quatint: 10774,
+ questeq: 8799,
+ rarrbfs: 10528,
+ rarrsim: 10612,
+ rbrksld: 10638,
+ rbrkslu: 10640,
+ rdldhar: 10601,
+ realine: 8475,
+ rotimes: 10805,
+ ruluhar: 10600,
+ searrow: 8600,
+ simplus: 10788,
+ simrarr: 10610,
+ subedot: 10947,
+ submult: 10945,
+ subplus: 10943,
+ subrarr: 10617,
+ succsim: 8831,
+ supdsub: 10968,
+ supedot: 10948,
+ suphsub: 10967,
+ suplarr: 10619,
+ supmult: 10946,
+ supplus: 10944,
+ swarrow: 8601,
+ topfork: 10970,
+ triplus: 10809,
+ tritime: 10811,
+ uparrow: 8593,
+ upsilon: 965,
+ uwangle: 10663,
+ vzigzag: 10650,
+ zigrarr: 8669,
+ Aacute: 193,
+ Abreve: 258,
+ Agrave: 192,
+ Assign: 8788,
+ Atilde: 195,
+ Barwed: 8966,
+ Bumpeq: 8782,
+ Cacute: 262,
+ Ccaron: 268,
+ Ccedil: 199,
+ Colone: 10868,
+ Conint: 8751,
+ CupCap: 8781,
+ Dagger: 8225,
+ Dcaron: 270,
+ DotDot: 8412,
+ Dstrok: 272,
+ Eacute: 201,
+ Ecaron: 282,
+ Egrave: 200,
+ Exists: 8707,
+ ForAll: 8704,
+ Gammad: 988,
+ Gbreve: 286,
+ Gcedil: 290,
+ HARDcy: 1066,
+ Hstrok: 294,
+ Iacute: 205,
+ Igrave: 204,
+ Itilde: 296,
+ Jsercy: 1032,
+ Kcedil: 310,
+ Lacute: 313,
+ Lambda: 923,
+ Lcaron: 317,
+ Lcedil: 315,
+ Lmidot: 319,
+ Lstrok: 321,
+ Nacute: 323,
+ Ncaron: 327,
+ Ncedil: 325,
+ Ntilde: 209,
+ Oacute: 211,
+ Odblac: 336,
+ Ograve: 210,
+ Oslash: 216,
+ Otilde: 213,
+ Otimes: 10807,
+ Racute: 340,
+ Rarrtl: 10518,
+ Rcaron: 344,
+ Rcedil: 342,
+ SHCHcy: 1065,
+ SOFTcy: 1068,
+ Sacute: 346,
+ Scaron: 352,
+ Scedil: 350,
+ Square: 9633,
+ Subset: 8912,
+ Supset: 8913,
+ Tcaron: 356,
+ Tcedil: 354,
+ Tstrok: 358,
+ Uacute: 218,
+ Ubreve: 364,
+ Udblac: 368,
+ Ugrave: 217,
+ Utilde: 360,
+ Vdashl: 10982,
+ Verbar: 8214,
+ Vvdash: 8874,
+ Yacute: 221,
+ Zacute: 377,
+ Zcaron: 381,
+ aacute: 225,
+ abreve: 259,
+ agrave: 224,
+ andand: 10837,
+ angmsd: 8737,
+ angsph: 8738,
+ apacir: 10863,
+ approx: 8776,
+ atilde: 227,
+ barvee: 8893,
+ barwed: 8965,
+ becaus: 8757,
+ bernou: 8492,
+ bigcap: 8898,
+ bigcup: 8899,
+ bigvee: 8897,
+ bkarow: 10509,
+ bottom: 8869,
+ bowtie: 8904,
+ boxbox: 10697,
+ bprime: 8245,
+ brvbar: 166,
+ bullet: 8226,
+ bumpeq: 8783,
+ cacute: 263,
+ capand: 10820,
+ capcap: 10827,
+ capcup: 10823,
+ capdot: 10816,
+ ccaron: 269,
+ ccedil: 231,
+ circeq: 8791,
+ cirmid: 10991,
+ colone: 8788,
+ commat: 64,
+ compfn: 8728,
+ conint: 8750,
+ coprod: 8720,
+ copysr: 8471,
+ cularr: 8630,
+ cupcap: 10822,
+ cupcup: 10826,
+ cupdot: 8845,
+ curarr: 8631,
+ curren: 164,
+ cylcty: 9005,
+ dagger: 8224,
+ daleth: 8504,
+ dcaron: 271,
+ dfisht: 10623,
+ divide: 247,
+ divonx: 8903,
+ dlcorn: 8990,
+ dlcrop: 8973,
+ dollar: 36,
+ drcorn: 8991,
+ drcrop: 8972,
+ dstrok: 273,
+ eacute: 233,
+ easter: 10862,
+ ecaron: 283,
+ ecolon: 8789,
+ egrave: 232,
+ egsdot: 10904,
+ elsdot: 10903,
+ emptyv: 8709,
+ emsp13: 8196,
+ emsp14: 8197,
+ eparsl: 10723,
+ eqcirc: 8790,
+ equals: 61,
+ equest: 8799,
+ female: 9792,
+ ffilig: 64259,
+ ffllig: 64260,
+ forall: 8704,
+ frac12: 189,
+ frac13: 8531,
+ frac14: 188,
+ frac15: 8533,
+ frac16: 8537,
+ frac18: 8539,
+ frac23: 8532,
+ frac25: 8534,
+ frac34: 190,
+ frac35: 8535,
+ frac38: 8540,
+ frac45: 8536,
+ frac56: 8538,
+ frac58: 8541,
+ frac78: 8542,
+ gacute: 501,
+ gammad: 989,
+ gbreve: 287,
+ gesdot: 10880,
+ gesles: 10900,
+ gtlPar: 10645,
+ gtrarr: 10616,
+ gtrdot: 8919,
+ gtrsim: 8819,
+ hairsp: 8202,
+ hamilt: 8459,
+ hardcy: 1098,
+ hearts: 9829,
+ hellip: 8230,
+ hercon: 8889,
+ homtht: 8763,
+ horbar: 8213,
+ hslash: 8463,
+ hstrok: 295,
+ hybull: 8259,
+ hyphen: 8208,
+ iacute: 237,
+ igrave: 236,
+ iiiint: 10764,
+ iinfin: 10716,
+ incare: 8453,
+ inodot: 305,
+ intcal: 8890,
+ iquest: 191,
+ isinsv: 8947,
+ itilde: 297,
+ jsercy: 1112,
+ kappav: 1008,
+ kcedil: 311,
+ kgreen: 312,
+ lAtail: 10523,
+ lacute: 314,
+ lagran: 8466,
+ lambda: 955,
+ langle: 10216,
+ larrfs: 10525,
+ larrhk: 8617,
+ larrlp: 8619,
+ larrpl: 10553,
+ larrtl: 8610,
+ latail: 10521,
+ lbrace: 123,
+ lbrack: 91,
+ lcaron: 318,
+ lcedil: 316,
+ ldquor: 8222,
+ lesdot: 10879,
+ lesges: 10899,
+ lfisht: 10620,
+ lfloor: 8970,
+ lharul: 10602,
+ llhard: 10603,
+ lmidot: 320,
+ lmoust: 9136,
+ loplus: 10797,
+ lowast: 8727,
+ lowbar: 95,
+ lparlt: 10643,
+ lrhard: 10605,
+ lsaquo: 8249,
+ lsquor: 8218,
+ lstrok: 322,
+ lthree: 8907,
+ ltimes: 8905,
+ ltlarr: 10614,
+ ltrPar: 10646,
+ mapsto: 8614,
+ marker: 9646,
+ mcomma: 10793,
+ midast: 42,
+ midcir: 10992,
+ middot: 183,
+ minusb: 8863,
+ minusd: 8760,
+ mnplus: 8723,
+ models: 8871,
+ mstpos: 8766,
+ nVDash: 8879,
+ nVdash: 8878,
+ nacute: 324,
+ ncaron: 328,
+ ncedil: 326,
+ nearhk: 10532,
+ nequiv: 8802,
+ nesear: 10536,
+ nexist: 8708,
+ nltrie: 8940,
+ nprcue: 8928,
+ nrtrie: 8941,
+ nsccue: 8929,
+ nsimeq: 8772,
+ ntilde: 241,
+ numero: 8470,
+ nvDash: 8877,
+ nvHarr: 10500,
+ nvdash: 8876,
+ nvlArr: 10498,
+ nvrArr: 10499,
+ nwarhk: 10531,
+ nwnear: 10535,
+ oacute: 243,
+ odblac: 337,
+ odsold: 10684,
+ ograve: 242,
+ ominus: 8854,
+ origof: 8886,
+ oslash: 248,
+ otilde: 245,
+ otimes: 8855,
+ parsim: 10995,
+ percnt: 37,
+ period: 46,
+ permil: 8240,
+ phmmat: 8499,
+ planck: 8463,
+ plankv: 8463,
+ plusdo: 8724,
+ plusdu: 10789,
+ plusmn: 177,
+ preceq: 10927,
+ primes: 8473,
+ prnsim: 8936,
+ propto: 8733,
+ prurel: 8880,
+ puncsp: 8200,
+ qprime: 8279,
+ rAtail: 10524,
+ racute: 341,
+ rangle: 10217,
+ rarrap: 10613,
+ rarrfs: 10526,
+ rarrhk: 8618,
+ rarrlp: 8620,
+ rarrpl: 10565,
+ rarrtl: 8611,
+ ratail: 10522,
+ rbrace: 125,
+ rbrack: 93,
+ rcaron: 345,
+ rcedil: 343,
+ rdquor: 8221,
+ rfisht: 10621,
+ rfloor: 8971,
+ rharul: 10604,
+ rmoust: 9137,
+ roplus: 10798,
+ rpargt: 10644,
+ rsaquo: 8250,
+ rsquor: 8217,
+ rthree: 8908,
+ rtimes: 8906,
+ sacute: 347,
+ scaron: 353,
+ scedil: 351,
+ scnsim: 8937,
+ searhk: 10533,
+ seswar: 10537,
+ sfrown: 8994,
+ shchcy: 1097,
+ sigmaf: 962,
+ sigmav: 962,
+ simdot: 10858,
+ smashp: 10803,
+ softcy: 1100,
+ solbar: 9023,
+ spades: 9824,
+ sqsube: 8849,
+ sqsupe: 8850,
+ square: 9633,
+ squarf: 9642,
+ ssetmn: 8726,
+ ssmile: 8995,
+ sstarf: 8902,
+ subdot: 10941,
+ subset: 8834,
+ subsim: 10951,
+ subsub: 10965,
+ subsup: 10963,
+ succeq: 10928,
+ supdot: 10942,
+ supset: 8835,
+ supsim: 10952,
+ supsub: 10964,
+ supsup: 10966,
+ swarhk: 10534,
+ swnwar: 10538,
+ target: 8982,
+ tcaron: 357,
+ tcedil: 355,
+ telrec: 8981,
+ there4: 8756,
+ thetav: 977,
+ thinsp: 8201,
+ thksim: 8764,
+ timesb: 8864,
+ timesd: 10800,
+ topbot: 9014,
+ topcir: 10993,
+ tprime: 8244,
+ tridot: 9708,
+ tstrok: 359,
+ uacute: 250,
+ ubreve: 365,
+ udblac: 369,
+ ufisht: 10622,
+ ugrave: 249,
+ ulcorn: 8988,
+ ulcrop: 8975,
+ urcorn: 8989,
+ urcrop: 8974,
+ utilde: 361,
+ vangrt: 10652,
+ varphi: 966,
+ varrho: 1009,
+ veebar: 8891,
+ vellip: 8942,
+ verbar: 124,
+ wedbar: 10847,
+ wedgeq: 8793,
+ weierp: 8472,
+ wreath: 8768,
+ xoplus: 10753,
+ xotime: 10754,
+ xsqcup: 10758,
+ xuplus: 10756,
+ xwedge: 8896,
+ yacute: 253,
+ zacute: 378,
+ zcaron: 382,
+ zeetrf: 8488,
+ AElig: 198,
+ Acirc: 194,
+ Alpha: 913,
+ Amacr: 256,
+ Aogon: 260,
+ Aring: 197,
+ Breve: 728,
+ Ccirc: 264,
+ Colon: 8759,
+ Cross: 10799,
+ Dashv: 10980,
+ Delta: 916,
+ Ecirc: 202,
+ Emacr: 274,
+ Eogon: 280,
+ Equal: 10869,
+ Gamma: 915,
+ Gcirc: 284,
+ Hacek: 711,
+ Hcirc: 292,
+ IJlig: 306,
+ Icirc: 206,
+ Imacr: 298,
+ Iogon: 302,
+ Iukcy: 1030,
+ Jcirc: 308,
+ Jukcy: 1028,
+ Kappa: 922,
+ OElig: 338,
+ Ocirc: 212,
+ Omacr: 332,
+ Omega: 937,
+ Prime: 8243,
+ RBarr: 10512,
+ Scirc: 348,
+ Sigma: 931,
+ THORN: 222,
+ TRADE: 8482,
+ TSHcy: 1035,
+ Theta: 920,
+ Tilde: 8764,
+ Ubrcy: 1038,
+ Ucirc: 219,
+ Umacr: 362,
+ Union: 8899,
+ Uogon: 370,
+ UpTee: 8869,
+ Uring: 366,
+ VDash: 8875,
+ Vdash: 8873,
+ Wcirc: 372,
+ Wedge: 8896,
+ Ycirc: 374,
+ acirc: 226,
+ acute: 180,
+ aelig: 230,
+ aleph: 8501,
+ alpha: 945,
+ amacr: 257,
+ amalg: 10815,
+ angle: 8736,
+ angrt: 8735,
+ angst: 8491,
+ aogon: 261,
+ aring: 229,
+ asymp: 8776,
+ awint: 10769,
+ bcong: 8780,
+ bdquo: 8222,
+ bepsi: 1014,
+ blank: 9251,
+ blk12: 9618,
+ blk14: 9617,
+ blk34: 9619,
+ block: 9608,
+ boxDL: 9559,
+ boxDR: 9556,
+ boxDl: 9558,
+ boxDr: 9555,
+ boxHD: 9574,
+ boxHU: 9577,
+ boxHd: 9572,
+ boxHu: 9575,
+ boxUL: 9565,
+ boxUR: 9562,
+ boxUl: 9564,
+ boxUr: 9561,
+ boxVH: 9580,
+ boxVL: 9571,
+ boxVR: 9568,
+ boxVh: 9579,
+ boxVl: 9570,
+ boxVr: 9567,
+ boxdL: 9557,
+ boxdR: 9554,
+ boxdl: 9488,
+ boxdr: 9484,
+ boxhD: 9573,
+ boxhU: 9576,
+ boxhd: 9516,
+ boxhu: 9524,
+ boxuL: 9563,
+ boxuR: 9560,
+ boxul: 9496,
+ boxur: 9492,
+ boxvH: 9578,
+ boxvL: 9569,
+ boxvR: 9566,
+ boxvh: 9532,
+ boxvl: 9508,
+ boxvr: 9500,
+ breve: 728,
+ bsemi: 8271,
+ bsime: 8909,
+ bsolb: 10693,
+ bumpE: 10926,
+ bumpe: 8783,
+ caret: 8257,
+ caron: 711,
+ ccaps: 10829,
+ ccirc: 265,
+ ccups: 10828,
+ cedil: 184,
+ check: 10003,
+ clubs: 9827,
+ colon: 58,
+ comma: 44,
+ crarr: 8629,
+ cross: 10007,
+ csube: 10961,
+ csupe: 10962,
+ ctdot: 8943,
+ cuepr: 8926,
+ cuesc: 8927,
+ cupor: 10821,
+ cuvee: 8910,
+ cuwed: 8911,
+ cwint: 8753,
+ dashv: 8867,
+ dblac: 733,
+ ddarr: 8650,
+ delta: 948,
+ dharl: 8643,
+ dharr: 8642,
+ diams: 9830,
+ disin: 8946,
+ doteq: 8784,
+ dtdot: 8945,
+ dtrif: 9662,
+ duarr: 8693,
+ duhar: 10607,
+ eDDot: 10871,
+ ecirc: 234,
+ efDot: 8786,
+ emacr: 275,
+ empty: 8709,
+ eogon: 281,
+ eplus: 10865,
+ epsiv: 949,
+ eqsim: 8770,
+ equiv: 8801,
+ erDot: 8787,
+ erarr: 10609,
+ esdot: 8784,
+ exist: 8707,
+ fflig: 64256,
+ filig: 64257,
+ fllig: 64258,
+ fltns: 9649,
+ forkv: 10969,
+ frasl: 8260,
+ frown: 8994,
+ gamma: 947,
+ gcirc: 285,
+ gescc: 10921,
+ gimel: 8503,
+ gneqq: 8809,
+ gnsim: 8935,
+ grave: 96,
+ gsime: 10894,
+ gsiml: 10896,
+ gtcir: 10874,
+ gtdot: 8919,
+ harrw: 8621,
+ hcirc: 293,
+ hoarr: 8703,
+ icirc: 238,
+ iexcl: 161,
+ iiint: 8749,
+ iiota: 8489,
+ ijlig: 307,
+ imacr: 299,
+ image: 8465,
+ imath: 305,
+ imped: 437,
+ infin: 8734,
+ iogon: 303,
+ iprod: 10812,
+ isinE: 8953,
+ isins: 8948,
+ isinv: 8712,
+ iukcy: 1110,
+ jcirc: 309,
+ jmath: 567,
+ jukcy: 1108,
+ kappa: 954,
+ lAarr: 8666,
+ lBarr: 10510,
+ langd: 10641,
+ laquo: 171,
+ larrb: 8676,
+ lbarr: 10508,
+ lbbrk: 10098,
+ lbrke: 10635,
+ lceil: 8968,
+ ldquo: 8220,
+ lescc: 10920,
+ lhard: 8637,
+ lharu: 8636,
+ lhblk: 9604,
+ llarr: 8647,
+ lltri: 9722,
+ lneqq: 8808,
+ lnsim: 8934,
+ loang: 10220,
+ loarr: 8701,
+ lobrk: 10214,
+ lopar: 10629,
+ lrarr: 8646,
+ lrhar: 8651,
+ lrtri: 8895,
+ lsime: 10893,
+ lsimg: 10895,
+ lsquo: 8216,
+ ltcir: 10873,
+ ltdot: 8918,
+ ltrie: 8884,
+ ltrif: 9666,
+ mDDot: 8762,
+ mdash: 8212,
+ micro: 181,
+ minus: 8722,
+ mumap: 8888,
+ nabla: 8711,
+ napos: 329,
+ natur: 9838,
+ ncong: 8775,
+ ndash: 8211,
+ neArr: 8663,
+ nearr: 8599,
+ ngsim: 8821,
+ nhArr: 8654,
+ nharr: 8622,
+ nhpar: 10994,
+ nlArr: 8653,
+ nlarr: 8602,
+ nless: 8814,
+ nlsim: 8820,
+ nltri: 8938,
+ notin: 8713,
+ notni: 8716,
+ nprec: 8832,
+ nrArr: 8655,
+ nrarr: 8603,
+ nrtri: 8939,
+ nsime: 8772,
+ nsmid: 8740,
+ nspar: 8742,
+ nsube: 8840,
+ nsucc: 8833,
+ nsupe: 8841,
+ numsp: 8199,
+ nwArr: 8662,
+ nwarr: 8598,
+ ocirc: 244,
+ odash: 8861,
+ oelig: 339,
+ ofcir: 10687,
+ ohbar: 10677,
+ olarr: 8634,
+ olcir: 10686,
+ oline: 8254,
+ omacr: 333,
+ omega: 969,
+ operp: 10681,
+ oplus: 8853,
+ orarr: 8635,
+ order: 8500,
+ ovbar: 9021,
+ parsl: 11005,
+ phone: 9742,
+ plusb: 8862,
+ pluse: 10866,
+ pound: 163,
+ prcue: 8828,
+ prime: 8242,
+ prnap: 10937,
+ prsim: 8830,
+ quest: 63,
+ rAarr: 8667,
+ rBarr: 10511,
+ radic: 8730,
+ rangd: 10642,
+ range: 10661,
+ raquo: 187,
+ rarrb: 8677,
+ rarrc: 10547,
+ rarrw: 8605,
+ ratio: 8758,
+ rbarr: 10509,
+ rbbrk: 10099,
+ rbrke: 10636,
+ rceil: 8969,
+ rdquo: 8221,
+ reals: 8477,
+ rhard: 8641,
+ rharu: 8640,
+ rlarr: 8644,
+ rlhar: 8652,
+ rnmid: 10990,
+ roang: 10221,
+ roarr: 8702,
+ robrk: 10215,
+ ropar: 10630,
+ rrarr: 8649,
+ rsquo: 8217,
+ rtrie: 8885,
+ rtrif: 9656,
+ sbquo: 8218,
+ sccue: 8829,
+ scirc: 349,
+ scnap: 10938,
+ scsim: 8831,
+ sdotb: 8865,
+ sdote: 10854,
+ seArr: 8664,
+ searr: 8600,
+ setmn: 8726,
+ sharp: 9839,
+ sigma: 963,
+ simeq: 8771,
+ simgE: 10912,
+ simlE: 10911,
+ simne: 8774,
+ slarr: 8592,
+ smile: 8995,
+ sqcap: 8851,
+ sqcup: 8852,
+ sqsub: 8847,
+ sqsup: 8848,
+ srarr: 8594,
+ starf: 9733,
+ strns: 175,
+ subnE: 10955,
+ subne: 8842,
+ supnE: 10956,
+ supne: 8843,
+ swArr: 8665,
+ swarr: 8601,
+ szlig: 223,
+ theta: 952,
+ thkap: 8776,
+ thorn: 254,
+ tilde: 732,
+ times: 215,
+ trade: 8482,
+ trisb: 10701,
+ tshcy: 1115,
+ twixt: 8812,
+ ubrcy: 1118,
+ ucirc: 251,
+ udarr: 8645,
+ udhar: 10606,
+ uharl: 8639,
+ uharr: 8638,
+ uhblk: 9600,
+ ultri: 9720,
+ umacr: 363,
+ uogon: 371,
+ uplus: 8846,
+ upsih: 978,
+ uring: 367,
+ urtri: 9721,
+ utdot: 8944,
+ utrif: 9652,
+ uuarr: 8648,
+ vBarv: 10985,
+ vDash: 8872,
+ varpi: 982,
+ vdash: 8866,
+ veeeq: 8794,
+ vltri: 8882,
+ vprop: 8733,
+ vrtri: 8883,
+ wcirc: 373,
+ wedge: 8743,
+ xcirc: 9711,
+ xdtri: 9661,
+ xhArr: 10234,
+ xharr: 10231,
+ xlArr: 10232,
+ xlarr: 10229,
+ xodot: 10752,
+ xrArr: 10233,
+ xrarr: 10230,
+ xutri: 9651,
+ ycirc: 375,
+ Aopf: 120120,
+ Ascr: 119964,
+ Auml: 196,
+ Barv: 10983,
+ Beta: 914,
+ Bopf: 120121,
+ Bscr: 8492,
+ CHcy: 1063,
+ COPY: 169,
+ Cdot: 266,
+ Copf: 8450,
+ Cscr: 119966,
+ DJcy: 1026,
+ DScy: 1029,
+ DZcy: 1039,
+ Darr: 8609,
+ Dopf: 120123,
+ Dscr: 119967,
+ Edot: 278,
+ Eopf: 120124,
+ Escr: 8496,
+ Esim: 10867,
+ Euml: 203,
+ Fopf: 120125,
+ Fscr: 8497,
+ GJcy: 1027,
+ Gdot: 288,
+ Gopf: 120126,
+ Gscr: 119970,
+ Hopf: 8461,
+ Hscr: 8459,
+ IEcy: 1045,
+ IOcy: 1025,
+ Idot: 304,
+ Iopf: 120128,
+ Iota: 921,
+ Iscr: 8464,
+ Iuml: 207,
+ Jopf: 120129,
+ Jscr: 119973,
+ KHcy: 1061,
+ KJcy: 1036,
+ Kopf: 120130,
+ Kscr: 119974,
+ LJcy: 1033,
+ Lang: 10218,
+ Larr: 8606,
+ Lopf: 120131,
+ Lscr: 8466,
+ Mopf: 120132,
+ Mscr: 8499,
+ NJcy: 1034,
+ Nopf: 8469,
+ Nscr: 119977,
+ Oopf: 120134,
+ Oscr: 119978,
+ Ouml: 214,
+ Popf: 8473,
+ Pscr: 119979,
+ QUOT: 34,
+ Qopf: 8474,
+ Qscr: 119980,
+ Rang: 10219,
+ Rarr: 8608,
+ Ropf: 8477,
+ Rscr: 8475,
+ SHcy: 1064,
+ Sopf: 120138,
+ Sqrt: 8730,
+ Sscr: 119982,
+ Star: 8902,
+ TScy: 1062,
+ Topf: 120139,
+ Tscr: 119983,
+ Uarr: 8607,
+ Uopf: 120140,
+ Upsi: 978,
+ Uscr: 119984,
+ Uuml: 220,
+ Vbar: 10987,
+ Vert: 8214,
+ Vopf: 120141,
+ Vscr: 119985,
+ Wopf: 120142,
+ Wscr: 119986,
+ Xopf: 120143,
+ Xscr: 119987,
+ YAcy: 1071,
+ YIcy: 1031,
+ YUcy: 1070,
+ Yopf: 120144,
+ Yscr: 119988,
+ Yuml: 376,
+ ZHcy: 1046,
+ Zdot: 379,
+ Zeta: 918,
+ Zopf: 8484,
+ Zscr: 119989,
+ andd: 10844,
+ andv: 10842,
+ ange: 10660,
+ aopf: 120146,
+ apid: 8779,
+ apos: 39,
+ ascr: 119990,
+ auml: 228,
+ bNot: 10989,
+ bbrk: 9141,
+ beta: 946,
+ beth: 8502,
+ bnot: 8976,
+ bopf: 120147,
+ boxH: 9552,
+ boxV: 9553,
+ boxh: 9472,
+ boxv: 9474,
+ bscr: 119991,
+ bsim: 8765,
+ bsol: 92,
+ bull: 8226,
+ bump: 8782,
+ cdot: 267,
+ cent: 162,
+ chcy: 1095,
+ cirE: 10691,
+ circ: 710,
+ cire: 8791,
+ comp: 8705,
+ cong: 8773,
+ copf: 120148,
+ copy: 169,
+ cscr: 119992,
+ csub: 10959,
+ csup: 10960,
+ dArr: 8659,
+ dHar: 10597,
+ darr: 8595,
+ dash: 8208,
+ diam: 8900,
+ djcy: 1106,
+ dopf: 120149,
+ dscr: 119993,
+ dscy: 1109,
+ dsol: 10742,
+ dtri: 9663,
+ dzcy: 1119,
+ eDot: 8785,
+ ecir: 8790,
+ edot: 279,
+ emsp: 8195,
+ ensp: 8194,
+ eopf: 120150,
+ epar: 8917,
+ epsi: 1013,
+ escr: 8495,
+ esim: 8770,
+ euml: 235,
+ euro: 8364,
+ excl: 33,
+ flat: 9837,
+ fnof: 402,
+ fopf: 120151,
+ fork: 8916,
+ fscr: 119995,
+ gdot: 289,
+ geqq: 8807,
+ gjcy: 1107,
+ gnap: 10890,
+ gneq: 10888,
+ gopf: 120152,
+ gscr: 8458,
+ gsim: 8819,
+ gtcc: 10919,
+ hArr: 8660,
+ half: 189,
+ harr: 8596,
+ hbar: 8463,
+ hopf: 120153,
+ hscr: 119997,
+ iecy: 1077,
+ imof: 8887,
+ iocy: 1105,
+ iopf: 120154,
+ iota: 953,
+ iscr: 119998,
+ isin: 8712,
+ iuml: 239,
+ jopf: 120155,
+ jscr: 119999,
+ khcy: 1093,
+ kjcy: 1116,
+ kopf: 120156,
+ kscr: 120000,
+ lArr: 8656,
+ lHar: 10594,
+ lang: 10216,
+ larr: 8592,
+ late: 10925,
+ lcub: 123,
+ ldca: 10550,
+ ldsh: 8626,
+ leqq: 8806,
+ ljcy: 1113,
+ lnap: 10889,
+ lneq: 10887,
+ lopf: 120157,
+ lozf: 10731,
+ lpar: 40,
+ lscr: 120001,
+ lsim: 8818,
+ lsqb: 91,
+ ltcc: 10918,
+ ltri: 9667,
+ macr: 175,
+ male: 9794,
+ malt: 10016,
+ mlcp: 10971,
+ mldr: 8230,
+ mopf: 120158,
+ mscr: 120002,
+ nbsp: 160,
+ ncap: 10819,
+ ncup: 10818,
+ ngeq: 8817,
+ ngtr: 8815,
+ nisd: 8954,
+ njcy: 1114,
+ nldr: 8229,
+ nleq: 8816,
+ nmid: 8740,
+ nopf: 120159,
+ npar: 8742,
+ nscr: 120003,
+ nsim: 8769,
+ nsub: 8836,
+ nsup: 8837,
+ ntgl: 8825,
+ ntlg: 8824,
+ oast: 8859,
+ ocir: 8858,
+ odiv: 10808,
+ odot: 8857,
+ ogon: 731,
+ oint: 8750,
+ omid: 10678,
+ oopf: 120160,
+ opar: 10679,
+ ordf: 170,
+ ordm: 186,
+ oror: 10838,
+ oscr: 8500,
+ osol: 8856,
+ ouml: 246,
+ para: 182,
+ part: 8706,
+ perp: 8869,
+ phiv: 966,
+ plus: 43,
+ popf: 120161,
+ prap: 10935,
+ prec: 8826,
+ prnE: 10933,
+ prod: 8719,
+ prop: 8733,
+ pscr: 120005,
+ qint: 10764,
+ qopf: 120162,
+ qscr: 120006,
+ quot: 34,
+ rArr: 8658,
+ rHar: 10596,
+ race: 10714,
+ rang: 10217,
+ rarr: 8594,
+ rcub: 125,
+ rdca: 10551,
+ rdsh: 8627,
+ real: 8476,
+ rect: 9645,
+ rhov: 1009,
+ ring: 730,
+ ropf: 120163,
+ rpar: 41,
+ rscr: 120007,
+ rsqb: 93,
+ rtri: 9657,
+ scap: 10936,
+ scnE: 10934,
+ sdot: 8901,
+ sect: 167,
+ semi: 59,
+ sext: 10038,
+ shcy: 1096,
+ sime: 8771,
+ simg: 10910,
+ siml: 10909,
+ smid: 8739,
+ smte: 10924,
+ solb: 10692,
+ sopf: 120164,
+ spar: 8741,
+ squf: 9642,
+ sscr: 120008,
+ star: 9734,
+ subE: 10949,
+ sube: 8838,
+ succ: 8827,
+ sung: 9834,
+ sup1: 185,
+ sup2: 178,
+ sup3: 179,
+ supE: 10950,
+ supe: 8839,
+ tbrk: 9140,
+ tdot: 8411,
+ tint: 8749,
+ toea: 10536,
+ topf: 120165,
+ tosa: 10537,
+ trie: 8796,
+ tscr: 120009,
+ tscy: 1094,
+ uArr: 8657,
+ uHar: 10595,
+ uarr: 8593,
+ uopf: 120166,
+ upsi: 965,
+ uscr: 120010,
+ utri: 9653,
+ uuml: 252,
+ vArr: 8661,
+ vBar: 10984,
+ varr: 8597,
+ vert: 124,
+ vopf: 120167,
+ vscr: 120011,
+ wopf: 120168,
+ wscr: 120012,
+ xcap: 8898,
+ xcup: 8899,
+ xmap: 10236,
+ xnis: 8955,
+ xopf: 120169,
+ xscr: 120013,
+ xvee: 8897,
+ yacy: 1103,
+ yicy: 1111,
+ yopf: 120170,
+ yscr: 120014,
+ yucy: 1102,
+ yuml: 255,
+ zdot: 380,
+ zeta: 950,
+ zhcy: 1078,
+ zopf: 120171,
+ zscr: 120015,
+ zwnj: 8204,
+ AMP: 38,
+ Acy: 1040,
+ Afr: 120068,
+ And: 10835,
+ Bcy: 1041,
+ Bfr: 120069,
+ Cap: 8914,
+ Cfr: 8493,
+ Chi: 935,
+ Cup: 8915,
+ Dcy: 1044,
+ Del: 8711,
+ Dfr: 120071,
+ Dot: 168,
+ ENG: 330,
+ ETH: 208,
+ Ecy: 1069,
+ Efr: 120072,
+ Eta: 919,
+ Fcy: 1060,
+ Ffr: 120073,
+ Gcy: 1043,
+ Gfr: 120074,
+ Hat: 94,
+ Hfr: 8460,
+ Icy: 1048,
+ Ifr: 8465,
+ Int: 8748,
+ Jcy: 1049,
+ Jfr: 120077,
+ Kcy: 1050,
+ Kfr: 120078,
+ Lcy: 1051,
+ Lfr: 120079,
+ Lsh: 8624,
+ Map: 10501,
+ Mcy: 1052,
+ Mfr: 120080,
+ Ncy: 1053,
+ Nfr: 120081,
+ Not: 10988,
+ Ocy: 1054,
+ Ofr: 120082,
+ Pcy: 1055,
+ Pfr: 120083,
+ Phi: 934,
+ Psi: 936,
+ Qfr: 120084,
+ REG: 174,
+ Rcy: 1056,
+ Rfr: 8476,
+ Rho: 929,
+ Rsh: 8625,
+ Scy: 1057,
+ Sfr: 120086,
+ Sub: 8912,
+ Sum: 8721,
+ Sup: 8913,
+ Tab: 9,
+ Tau: 932,
+ Tcy: 1058,
+ Tfr: 120087,
+ Ucy: 1059,
+ Ufr: 120088,
+ Vcy: 1042,
+ Vee: 8897,
+ Vfr: 120089,
+ Wfr: 120090,
+ Xfr: 120091,
+ Ycy: 1067,
+ Yfr: 120092,
+ Zcy: 1047,
+ Zfr: 8488,
+ acd: 8767,
+ acy: 1072,
+ afr: 120094,
+ amp: 38,
+ and: 8743,
+ ang: 8736,
+ apE: 10864,
+ ape: 8778,
+ ast: 42,
+ bcy: 1073,
+ bfr: 120095,
+ bot: 8869,
+ cap: 8745,
+ cfr: 120096,
+ chi: 967,
+ cir: 9675,
+ cup: 8746,
+ dcy: 1076,
+ deg: 176,
+ dfr: 120097,
+ die: 168,
+ div: 247,
+ dot: 729,
+ ecy: 1101,
+ efr: 120098,
+ egs: 10902,
+ ell: 8467,
+ els: 10901,
+ eng: 331,
+ eta: 951,
+ eth: 240,
+ fcy: 1092,
+ ffr: 120099,
+ gEl: 10892,
+ gap: 10886,
+ gcy: 1075,
+ gel: 8923,
+ geq: 8805,
+ ges: 10878,
+ gfr: 120100,
+ ggg: 8921,
+ glE: 10898,
+ gla: 10917,
+ glj: 10916,
+ gnE: 8809,
+ gne: 10888,
+ hfr: 120101,
+ icy: 1080,
+ iff: 8660,
+ ifr: 120102,
+ int: 8747,
+ jcy: 1081,
+ jfr: 120103,
+ kcy: 1082,
+ kfr: 120104,
+ lEg: 10891,
+ lap: 10885,
+ lat: 10923,
+ lcy: 1083,
+ leg: 8922,
+ leq: 8804,
+ les: 10877,
+ lfr: 120105,
+ lgE: 10897,
+ lnE: 8808,
+ lne: 10887,
+ loz: 9674,
+ lrm: 8206,
+ lsh: 8624,
+ map: 8614,
+ mcy: 1084,
+ mfr: 120106,
+ mho: 8487,
+ mid: 8739,
+ nap: 8777,
+ ncy: 1085,
+ nfr: 120107,
+ nge: 8817,
+ ngt: 8815,
+ nis: 8956,
+ niv: 8715,
+ nle: 8816,
+ nlt: 8814,
+ not: 172,
+ npr: 8832,
+ nsc: 8833,
+ num: 35,
+ ocy: 1086,
+ ofr: 120108,
+ ogt: 10689,
+ ohm: 8486,
+ olt: 10688,
+ ord: 10845,
+ orv: 10843,
+ par: 8741,
+ pcy: 1087,
+ pfr: 120109,
+ phi: 966,
+ piv: 982,
+ prE: 10931,
+ pre: 10927,
+ psi: 968,
+ qfr: 120110,
+ rcy: 1088,
+ reg: 174,
+ rfr: 120111,
+ rho: 961,
+ rlm: 8207,
+ rsh: 8625,
+ scE: 10932,
+ sce: 10928,
+ scy: 1089,
+ sfr: 120112,
+ shy: 173,
+ sim: 8764,
+ smt: 10922,
+ sol: 47,
+ squ: 9633,
+ sub: 8834,
+ sum: 8721,
+ sup: 8835,
+ tau: 964,
+ tcy: 1090,
+ tfr: 120113,
+ top: 8868,
+ ucy: 1091,
+ ufr: 120114,
+ uml: 168,
+ vcy: 1074,
+ vee: 8744,
+ vfr: 120115,
+ wfr: 120116,
+ xfr: 120117,
+ ycy: 1099,
+ yen: 165,
+ yfr: 120118,
+ zcy: 1079,
+ zfr: 120119,
+ zwj: 8205,
+ DD: 8517,
+ GT: 62,
+ Gg: 8921,
+ Gt: 8811,
+ Im: 8465,
+ LT: 60,
+ Ll: 8920,
+ Lt: 8810,
+ Mu: 924,
+ Nu: 925,
+ Or: 10836,
+ Pi: 928,
+ Pr: 10939,
+ Re: 8476,
+ Sc: 10940,
+ Xi: 926,
+ ac: 8766,
+ af: 8289,
+ ap: 8776,
+ dd: 8518,
+ ee: 8519,
+ eg: 10906,
+ el: 10905,
+ gE: 8807,
+ ge: 8805,
+ gg: 8811,
+ gl: 8823,
+ gt: 62,
+ ic: 8291,
+ ii: 8520,
+ in: 8712,
+ it: 8290,
+ lE: 8806,
+ le: 8804,
+ lg: 8822,
+ ll: 8810,
+ lt: 60,
+ mp: 8723,
+ mu: 956,
+ ne: 8800,
+ ni: 8715,
+ nu: 957,
+ oS: 9416,
+ or: 8744,
+ pi: 960,
+ pm: 177,
+ pr: 8826,
+ rx: 8478,
+ sc: 8827,
+ wp: 8472,
+ wr: 8768,
+ xi: 958,
};
diff --git a/packages/astro-parser/src/parse/utils/html.ts b/packages/astro-parser/src/parse/utils/html.ts
index e4669a2db..7b9e35bc6 100644
--- a/packages/astro-parser/src/parse/utils/html.ts
+++ b/packages/astro-parser/src/parse/utils/html.ts
@@ -3,30 +3,30 @@
import entities from './entities.js';
const windows_1252 = [
- 8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 141, 381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250, 339, 157, 382, 376,
+ 8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 141, 381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250, 339, 157, 382, 376,
];
const entity_pattern = new RegExp(`&(#?(?:x[\\w\\d]+|\\d+|${Object.keys(entities).join('|')}))(?:;|\\b)`, 'g');
export function decode_character_references(html: string) {
- return html.replace(entity_pattern, (match, entity) => {
- let code;
-
- // Handle named entities
- if (entity[0] !== '#') {
- code = entities[entity];
- } else if (entity[1] === 'x') {
- code = parseInt(entity.substring(2), 16);
- } else {
- code = parseInt(entity.substring(1), 10);
- }
-
- if (!code) {
- return match;
- }
-
- return String.fromCodePoint(validate_code(code));
- });
+ return html.replace(entity_pattern, (match, entity) => {
+ let code;
+
+ // Handle named entities
+ if (entity[0] !== '#') {
+ code = entities[entity];
+ } else if (entity[1] === 'x') {
+ code = parseInt(entity.substring(2), 16);
+ } else {
+ code = parseInt(entity.substring(1), 10);
+ }
+
+ if (!code) {
+ return match;
+ }
+
+ return String.fromCodePoint(validate_code(code));
+ });
}
const NUL = 0;
@@ -37,76 +37,76 @@ const NUL = 0;
//
// Source: http://en.wikipedia.org/wiki/Character_encodings_in_HTML#Illegal_characters
function validate_code(code: number) {
- // line feed becomes generic whitespace
- if (code === 10) {
- return 32;
- }
-
- // ASCII range. (Why someone would use HTML entities for ASCII characters I don't know, but...)
- if (code < 128) {
- return code;
- }
-
- // code points 128-159 are dealt with leniently by browsers, but they're incorrect. We need
- // to correct the mistake or we'll end up with missing € signs and so on
- if (code <= 159) {
- return windows_1252[code - 128];
- }
-
- // basic multilingual plane
- if (code < 55296) {
- return code;
- }
-
- // UTF-16 surrogate halves
- if (code <= 57343) {
- return NUL;
- }
-
- // rest of the basic multilingual plane
- if (code <= 65535) {
- return code;
- }
-
- // supplementary multilingual plane 0x10000 - 0x1ffff
- if (code >= 65536 && code <= 131071) {
- return code;
- }
-
- // supplementary ideographic plane 0x20000 - 0x2ffff
- if (code >= 131072 && code <= 196607) {
- return code;
- }
-
- return NUL;
+ // line feed becomes generic whitespace
+ if (code === 10) {
+ return 32;
+ }
+
+ // ASCII range. (Why someone would use HTML entities for ASCII characters I don't know, but...)
+ if (code < 128) {
+ return code;
+ }
+
+ // code points 128-159 are dealt with leniently by browsers, but they're incorrect. We need
+ // to correct the mistake or we'll end up with missing € signs and so on
+ if (code <= 159) {
+ return windows_1252[code - 128];
+ }
+
+ // basic multilingual plane
+ if (code < 55296) {
+ return code;
+ }
+
+ // UTF-16 surrogate halves
+ if (code <= 57343) {
+ return NUL;
+ }
+
+ // rest of the basic multilingual plane
+ if (code <= 65535) {
+ return code;
+ }
+
+ // supplementary multilingual plane 0x10000 - 0x1ffff
+ if (code >= 65536 && code <= 131071) {
+ return code;
+ }
+
+ // supplementary ideographic plane 0x20000 - 0x2ffff
+ if (code >= 131072 && code <= 196607) {
+ return code;
+ }
+
+ return NUL;
}
// based on http://developers.whatwg.org/syntax.html#syntax-tag-omission
const disallowed_contents = new Map([
- ['li', new Set(['li'])],
- ['dt', new Set(['dt', 'dd'])],
- ['dd', new Set(['dt', 'dd'])],
- ['p', new Set('address article aside blockquote div dl fieldset footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav ol p pre section table ul'.split(' '))],
- ['rt', new Set(['rt', 'rp'])],
- ['rp', new Set(['rt', 'rp'])],
- ['optgroup', new Set(['optgroup'])],
- ['option', new Set(['option', 'optgroup'])],
- ['thead', new Set(['tbody', 'tfoot'])],
- ['tbody', new Set(['tbody', 'tfoot'])],
- ['tfoot', new Set(['tbody'])],
- ['tr', new Set(['tr', 'tbody'])],
- ['td', new Set(['td', 'th', 'tr'])],
- ['th', new Set(['td', 'th', 'tr'])],
+ ['li', new Set(['li'])],
+ ['dt', new Set(['dt', 'dd'])],
+ ['dd', new Set(['dt', 'dd'])],
+ ['p', new Set('address article aside blockquote div dl fieldset footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav ol p pre section table ul'.split(' '))],
+ ['rt', new Set(['rt', 'rp'])],
+ ['rp', new Set(['rt', 'rp'])],
+ ['optgroup', new Set(['optgroup'])],
+ ['option', new Set(['option', 'optgroup'])],
+ ['thead', new Set(['tbody', 'tfoot'])],
+ ['tbody', new Set(['tbody', 'tfoot'])],
+ ['tfoot', new Set(['tbody'])],
+ ['tr', new Set(['tr', 'tbody'])],
+ ['td', new Set(['td', 'th', 'tr'])],
+ ['th', new Set(['td', 'th', 'tr'])],
]);
// can this be a child of the parent element, or does it implicitly
// close it, like `<li>one<li>two`?
export function closing_tag_omitted(current: string, next?: string) {
- if (disallowed_contents.has(current)) {
- if (!next || disallowed_contents.get(current).has(next)) {
- return true;
- }
- }
+ if (disallowed_contents.has(current)) {
+ if (!next || disallowed_contents.get(current).has(next)) {
+ return true;
+ }
+ }
- return false;
+ return false;
}
diff --git a/packages/astro-parser/src/parse/utils/node.ts b/packages/astro-parser/src/parse/utils/node.ts
index 45769f96e..27891d6f3 100644
--- a/packages/astro-parser/src/parse/utils/node.ts
+++ b/packages/astro-parser/src/parse/utils/node.ts
@@ -1,30 +1,30 @@
import { TemplateNode } from '../../interfaces.js';
export function to_string(node: TemplateNode) {
- switch (node.type) {
- case 'IfBlock':
- return '{#if} block';
- case 'ThenBlock':
- return '{:then} block';
- case 'ElseBlock':
- return '{:else} block';
- case 'PendingBlock':
- case 'AwaitBlock':
- return '{#await} block';
- case 'CatchBlock':
- return '{:catch} block';
- case 'EachBlock':
- return '{#each} block';
- case 'RawMustacheTag':
- return '{@html} block';
- case 'DebugTag':
- return '{@debug} block';
- case 'Element':
- case 'InlineComponent':
- case 'Slot':
- case 'Title':
- return `<${node.name}> tag`;
- default:
- return node.type;
- }
+ switch (node.type) {
+ case 'IfBlock':
+ return '{#if} block';
+ case 'ThenBlock':
+ return '{:then} block';
+ case 'ElseBlock':
+ return '{:else} block';
+ case 'PendingBlock':
+ case 'AwaitBlock':
+ return '{#await} block';
+ case 'CatchBlock':
+ return '{:catch} block';
+ case 'EachBlock':
+ return '{#each} block';
+ case 'RawMustacheTag':
+ return '{@html} block';
+ case 'DebugTag':
+ return '{@debug} block';
+ case 'Element':
+ case 'InlineComponent':
+ case 'Slot':
+ case 'Title':
+ return `<${node.name}> tag`;
+ default:
+ return node.type;
+ }
}
diff --git a/packages/astro-parser/src/utils/error.ts b/packages/astro-parser/src/utils/error.ts
index a532f07dd..bb71db1c7 100644
--- a/packages/astro-parser/src/utils/error.ts
+++ b/packages/astro-parser/src/utils/error.ts
@@ -4,41 +4,41 @@ import { locate } from 'locate-character';
import get_code_frame from './get_code_frame.js';
export class CompileError extends Error {
- code: string;
- end: { line: number; column: number };
- filename: string;
- frame: string;
- start: { line: number; column: number };
+ code: string;
+ end: { line: number; column: number };
+ filename: string;
+ frame: string;
+ start: { line: number; column: number };
- constructor({ code, filename, start, end, message }: { code: string; filename: string; start: number; message: string; end?: number }) {
- super(message);
+ constructor({ code, filename, start, end, message }: { code: string; filename: string; start: number; message: string; end?: number }) {
+ super(message);
- this.start = locate(code, start, { offsetLine: 1 });
- this.end = locate(code, end || start, { offsetLine: 1 });
- this.filename = filename;
- this.message = message;
- this.frame = get_code_frame(code, this.start.line - 1, this.start.column);
- }
+ this.start = locate(code, start, { offsetLine: 1 });
+ this.end = locate(code, end || start, { offsetLine: 1 });
+ this.filename = filename;
+ this.message = message;
+ this.frame = get_code_frame(code, this.start.line - 1, this.start.column);
+ }
- toString() {
- return `${this.filename}:${this.start.line}:${this.start.column}\n\t${this.message}\n${this.frame}`;
- }
+ toString() {
+ return `${this.filename}:${this.start.line}:${this.start.column}\n\t${this.message}\n${this.frame}`;
+ }
}
/** Throw CompileError */
export default function error(
- code: string,
- message: string,
- props: {
- name: string;
- source: string;
- filename: string;
- start: number;
- end?: number;
- }
+ code: string,
+ message: string,
+ props: {
+ name: string;
+ source: string;
+ filename: string;
+ start: number;
+ end?: number;
+ }
): never {
- const err = new CompileError({ code, message, start: props.start, end: props.end, filename: props.filename });
- err.name = props.name;
+ const err = new CompileError({ code, message, start: props.start, end: props.end, filename: props.filename });
+ err.name = props.name;
- throw err;
+ throw err;
}
diff --git a/packages/astro-parser/src/utils/full_char_code_at.ts b/packages/astro-parser/src/utils/full_char_code_at.ts
index b62b2c77a..701070201 100644
--- a/packages/astro-parser/src/utils/full_char_code_at.ts
+++ b/packages/astro-parser/src/utils/full_char_code_at.ts
@@ -3,9 +3,9 @@
/** @url https://github.com/acornjs/acorn/blob/6584815dca7440e00de841d1dad152302fdd7ca5/src/tokenize.js */
export default function full_char_code_at(str: string, i: number): number {
- const code = str.charCodeAt(i);
- if (code <= 0xd7ff || code >= 0xe000) return code;
+ const code = str.charCodeAt(i);
+ if (code <= 0xd7ff || code >= 0xe000) return code;
- const next = str.charCodeAt(i + 1);
- return (code << 10) + next - 0x35fdc00;
+ const next = str.charCodeAt(i + 1);
+ return (code << 10) + next - 0x35fdc00;
}
diff --git a/packages/astro-parser/src/utils/fuzzymatch.ts b/packages/astro-parser/src/utils/fuzzymatch.ts
index 4d17aafdf..278c57014 100644
--- a/packages/astro-parser/src/utils/fuzzymatch.ts
+++ b/packages/astro-parser/src/utils/fuzzymatch.ts
@@ -2,10 +2,10 @@
/** Utility for accessing FuzzySet */
export default function fuzzymatch(name: string, names: string[]) {
- const set = new FuzzySet(names);
- const matches = set.get(name);
+ const set = new FuzzySet(names);
+ const matches = set.get(name);
- return matches && matches[0] && matches[0][0] > 0.7 ? matches[0][1] : null;
+ return matches && matches[0] && matches[0][0] > 0.7 ? matches[0][1] : null;
}
// adapted from https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js
@@ -16,218 +16,218 @@ const GRAM_SIZE_UPPER = 3;
/** Return an edit distance from 0 to 1 */
function _distance(str1: string, str2: string) {
- if (str1 === null && str2 === null) {
- throw 'Trying to compare two null values';
- }
- if (str1 === null || str2 === null) return 0;
- str1 = String(str1);
- str2 = String(str2);
-
- const distance = levenshtein(str1, str2);
- if (str1.length > str2.length) {
- return 1 - distance / str1.length;
- } else {
- return 1 - distance / str2.length;
- }
+ if (str1 === null && str2 === null) {
+ throw 'Trying to compare two null values';
+ }
+ if (str1 === null || str2 === null) return 0;
+ str1 = String(str1);
+ str2 = String(str2);
+
+ const distance = levenshtein(str1, str2);
+ if (str1.length > str2.length) {
+ return 1 - distance / str1.length;
+ } else {
+ return 1 - distance / str2.length;
+ }
}
/** @url https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js#L18 */
function levenshtein(str1: string, str2: string) {
- const current: number[] = [];
- let prev;
- let value;
-
- for (let i = 0; i <= str2.length; i++) {
- for (let j = 0; j <= str1.length; j++) {
- if (i && j) {
- if (str1.charAt(j - 1) === str2.charAt(i - 1)) {
- value = prev;
- } else {
- value = Math.min(current[j], current[j - 1], prev) + 1;
- }
- } else {
- value = i + j;
- }
-
- prev = current[j];
- current[j] = value;
- }
- }
-
- return current.pop();
+ const current: number[] = [];
+ let prev;
+ let value;
+
+ for (let i = 0; i <= str2.length; i++) {
+ for (let j = 0; j <= str1.length; j++) {
+ if (i && j) {
+ if (str1.charAt(j - 1) === str2.charAt(i - 1)) {
+ value = prev;
+ } else {
+ value = Math.min(current[j], current[j - 1], prev) + 1;
+ }
+ } else {
+ value = i + j;
+ }
+
+ prev = current[j];
+ current[j] = value;
+ }
+ }
+
+ return current.pop();
}
const non_word_regex = /[^\w, ]+/;
/** @url https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js#L53 */
function iterate_grams(value: string, gram_size = 2) {
- const simplified = '-' + value.toLowerCase().replace(non_word_regex, '') + '-';
- const len_diff = gram_size - simplified.length;
- const results = [];
-
- if (len_diff > 0) {
- for (let i = 0; i < len_diff; ++i) {
- value += '-';
- }
- }
- for (let i = 0; i < simplified.length - gram_size + 1; ++i) {
- results.push(simplified.slice(i, i + gram_size));
- }
- return results;
+ const simplified = '-' + value.toLowerCase().replace(non_word_regex, '') + '-';
+ const len_diff = gram_size - simplified.length;
+ const results = [];
+
+ if (len_diff > 0) {
+ for (let i = 0; i < len_diff; ++i) {
+ value += '-';
+ }
+ }
+ for (let i = 0; i < simplified.length - gram_size + 1; ++i) {
+ results.push(simplified.slice(i, i + gram_size));
+ }
+ return results;
}
/** @url https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js#L69 */
function gram_counter(value: string, gram_size = 2) {
- // return an object where key=gram, value=number of occurrences
- const result = {};
- const grams = iterate_grams(value, gram_size);
- let i = 0;
-
- for (i; i < grams.length; ++i) {
- if (grams[i] in result) {
- result[grams[i]] += 1;
- } else {
- result[grams[i]] = 1;
- }
- }
- return result;
+ // return an object where key=gram, value=number of occurrences
+ const result = {};
+ const grams = iterate_grams(value, gram_size);
+ let i = 0;
+
+ for (i; i < grams.length; ++i) {
+ if (grams[i] in result) {
+ result[grams[i]] += 1;
+ } else {
+ result[grams[i]] = 1;
+ }
+ }
+ return result;
}
/** @url https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js#L158 */
function sort_descending(a, b) {
- return b[0] - a[0];
+ return b[0] - a[0];
}
class FuzzySet {
- exact_set = {};
- match_dict = {};
- items = {};
-
- constructor(arr: string[]) {
- // initialization
- for (let i = GRAM_SIZE_LOWER; i < GRAM_SIZE_UPPER + 1; ++i) {
- this.items[i] = [];
- }
-
- // add all the items to the set
- for (let i = 0; i < arr.length; ++i) {
- this.add(arr[i]);
- }
- }
-
- add(value: string) {
- const normalized_value = value.toLowerCase();
- if (normalized_value in this.exact_set) {
- return false;
- }
-
- let i = GRAM_SIZE_LOWER;
- for (i; i < GRAM_SIZE_UPPER + 1; ++i) {
- this._add(value, i);
- }
- }
-
- _add(value: string, gram_size: number) {
- const normalized_value = value.toLowerCase();
- const items = this.items[gram_size] || [];
- const index = items.length;
-
- items.push(0);
- const gram_counts = gram_counter(normalized_value, gram_size);
- let sum_of_square_gram_counts = 0;
- let gram;
- let gram_count;
-
- for (gram in gram_counts) {
- gram_count = gram_counts[gram];
- sum_of_square_gram_counts += Math.pow(gram_count, 2);
- if (gram in this.match_dict) {
- this.match_dict[gram].push([index, gram_count]);
- } else {
- this.match_dict[gram] = [[index, gram_count]];
- }
- }
- const vector_normal = Math.sqrt(sum_of_square_gram_counts);
- items[index] = [vector_normal, normalized_value];
- this.items[gram_size] = items;
- this.exact_set[normalized_value] = value;
- }
-
- get(value: string) {
- const normalized_value = value.toLowerCase();
- const result = this.exact_set[normalized_value];
-
- if (result) {
- return [[1, result]];
- }
-
- let results = [];
- // start with high gram size and if there are no results, go to lower gram sizes
- for (let gram_size = GRAM_SIZE_UPPER; gram_size >= GRAM_SIZE_LOWER; --gram_size) {
- results = this.__get(value, gram_size);
- if (results) {
- return results;
- }
- }
- return null;
- }
-
- __get(value: string, gram_size: number) {
- const normalized_value = value.toLowerCase();
- const matches = {};
- const gram_counts = gram_counter(normalized_value, gram_size);
- const items = this.items[gram_size];
- let sum_of_square_gram_counts = 0;
- let gram;
- let gram_count;
- let i;
- let index;
- let other_gram_count;
-
- for (gram in gram_counts) {
- gram_count = gram_counts[gram];
- sum_of_square_gram_counts += Math.pow(gram_count, 2);
- if (gram in this.match_dict) {
- for (i = 0; i < this.match_dict[gram].length; ++i) {
- index = this.match_dict[gram][i][0];
- other_gram_count = this.match_dict[gram][i][1];
- if (index in matches) {
- matches[index] += gram_count * other_gram_count;
- } else {
- matches[index] = gram_count * other_gram_count;
- }
- }
- }
- }
-
- const vector_normal = Math.sqrt(sum_of_square_gram_counts);
- let results = [];
- let match_score;
-
- // build a results list of [score, str]
- for (const match_index in matches) {
- match_score = matches[match_index];
- results.push([match_score / (vector_normal * items[match_index][0]), items[match_index][1]]);
- }
-
- results.sort(sort_descending);
-
- let new_results = [];
- const end_index = Math.min(50, results.length);
- // truncate somewhat arbitrarily to 50
- for (let j = 0; j < end_index; ++j) {
- new_results.push([_distance(results[j][1], normalized_value), results[j][1]]);
- }
- results = new_results;
- results.sort(sort_descending);
-
- new_results = [];
- for (let j = 0; j < results.length; ++j) {
- if (results[j][0] == results[0][0]) {
- new_results.push([results[j][0], this.exact_set[results[j][1]]]);
- }
- }
-
- return new_results;
- }
+ exact_set = {};
+ match_dict = {};
+ items = {};
+
+ constructor(arr: string[]) {
+ // initialization
+ for (let i = GRAM_SIZE_LOWER; i < GRAM_SIZE_UPPER + 1; ++i) {
+ this.items[i] = [];
+ }
+
+ // add all the items to the set
+ for (let i = 0; i < arr.length; ++i) {
+ this.add(arr[i]);
+ }
+ }
+
+ add(value: string) {
+ const normalized_value = value.toLowerCase();
+ if (normalized_value in this.exact_set) {
+ return false;
+ }
+
+ let i = GRAM_SIZE_LOWER;
+ for (i; i < GRAM_SIZE_UPPER + 1; ++i) {
+ this._add(value, i);
+ }
+ }
+
+ _add(value: string, gram_size: number) {
+ const normalized_value = value.toLowerCase();
+ const items = this.items[gram_size] || [];
+ const index = items.length;
+
+ items.push(0);
+ const gram_counts = gram_counter(normalized_value, gram_size);
+ let sum_of_square_gram_counts = 0;
+ let gram;
+ let gram_count;
+
+ for (gram in gram_counts) {
+ gram_count = gram_counts[gram];
+ sum_of_square_gram_counts += Math.pow(gram_count, 2);
+ if (gram in this.match_dict) {
+ this.match_dict[gram].push([index, gram_count]);
+ } else {
+ this.match_dict[gram] = [[index, gram_count]];
+ }
+ }
+ const vector_normal = Math.sqrt(sum_of_square_gram_counts);
+ items[index] = [vector_normal, normalized_value];
+ this.items[gram_size] = items;
+ this.exact_set[normalized_value] = value;
+ }
+
+ get(value: string) {
+ const normalized_value = value.toLowerCase();
+ const result = this.exact_set[normalized_value];
+
+ if (result) {
+ return [[1, result]];
+ }
+
+ let results = [];
+ // start with high gram size and if there are no results, go to lower gram sizes
+ for (let gram_size = GRAM_SIZE_UPPER; gram_size >= GRAM_SIZE_LOWER; --gram_size) {
+ results = this.__get(value, gram_size);
+ if (results) {
+ return results;
+ }
+ }
+ return null;
+ }
+
+ __get(value: string, gram_size: number) {
+ const normalized_value = value.toLowerCase();
+ const matches = {};
+ const gram_counts = gram_counter(normalized_value, gram_size);
+ const items = this.items[gram_size];
+ let sum_of_square_gram_counts = 0;
+ let gram;
+ let gram_count;
+ let i;
+ let index;
+ let other_gram_count;
+
+ for (gram in gram_counts) {
+ gram_count = gram_counts[gram];
+ sum_of_square_gram_counts += Math.pow(gram_count, 2);
+ if (gram in this.match_dict) {
+ for (i = 0; i < this.match_dict[gram].length; ++i) {
+ index = this.match_dict[gram][i][0];
+ other_gram_count = this.match_dict[gram][i][1];
+ if (index in matches) {
+ matches[index] += gram_count * other_gram_count;
+ } else {
+ matches[index] = gram_count * other_gram_count;
+ }
+ }
+ }
+ }
+
+ const vector_normal = Math.sqrt(sum_of_square_gram_counts);
+ let results = [];
+ let match_score;
+
+ // build a results list of [score, str]
+ for (const match_index in matches) {
+ match_score = matches[match_index];
+ results.push([match_score / (vector_normal * items[match_index][0]), items[match_index][1]]);
+ }
+
+ results.sort(sort_descending);
+
+ let new_results = [];
+ const end_index = Math.min(50, results.length);
+ // truncate somewhat arbitrarily to 50
+ for (let j = 0; j < end_index; ++j) {
+ new_results.push([_distance(results[j][1], normalized_value), results[j][1]]);
+ }
+ results = new_results;
+ results.sort(sort_descending);
+
+ new_results = [];
+ for (let j = 0; j < results.length; ++j) {
+ if (results[j][0] == results[0][0]) {
+ new_results.push([results[j][0], this.exact_set[results[j][1]]]);
+ }
+ }
+
+ return new_results;
+ }
}
diff --git a/packages/astro-parser/src/utils/get_code_frame.ts b/packages/astro-parser/src/utils/get_code_frame.ts
index e4f1834fd..098d5ad77 100644
--- a/packages/astro-parser/src/utils/get_code_frame.ts
+++ b/packages/astro-parser/src/utils/get_code_frame.ts
@@ -1,29 +1,29 @@
/** Die you stupid tabs */
function tabs_to_spaces(str: string) {
- return str.replace(/^\t+/, (match) => match.split('\t').join(' '));
+ return str.replace(/^\t+/, (match) => match.split('\t').join(' '));
}
/** Display syntax error in pretty format in logs */
export default function get_code_frame(source: string, line: number, column: number) {
- const lines = source.split('\n');
+ const lines = source.split('\n');
- const frame_start = Math.max(0, line - 2);
- const frame_end = Math.min(line + 3, lines.length);
+ const frame_start = Math.max(0, line - 2);
+ const frame_end = Math.min(line + 3, lines.length);
- const digits = String(frame_end + 1).length;
+ const digits = String(frame_end + 1).length;
- return lines
- .slice(frame_start, frame_end)
- .map((str, i) => {
- const isErrorLine = frame_start + i === line;
- const line_num = String(i + frame_start + 1).padStart(digits, ' ');
+ return lines
+ .slice(frame_start, frame_end)
+ .map((str, i) => {
+ const isErrorLine = frame_start + i === line;
+ const line_num = String(i + frame_start + 1).padStart(digits, ' ');
- if (isErrorLine) {
- const indicator = ' '.repeat(digits + 2 + tabs_to_spaces(str.slice(0, column)).length) + '^';
- return `${line_num}: ${tabs_to_spaces(str)}\n${indicator}`;
- }
+ if (isErrorLine) {
+ const indicator = ' '.repeat(digits + 2 + tabs_to_spaces(str.slice(0, column)).length) + '^';
+ return `${line_num}: ${tabs_to_spaces(str)}\n${indicator}`;
+ }
- return `${line_num}: ${tabs_to_spaces(str)}`;
- })
- .join('\n');
+ return `${line_num}: ${tabs_to_spaces(str)}`;
+ })
+ .join('\n');
}
diff --git a/packages/astro-parser/src/utils/link.ts b/packages/astro-parser/src/utils/link.ts
index 4e2ed662f..855152514 100644
--- a/packages/astro-parser/src/utils/link.ts
+++ b/packages/astro-parser/src/utils/link.ts
@@ -1,5 +1,5 @@
/** Linked list */
export function link<T extends { next?: T; prev?: T }>(next: T, prev: T) {
- prev.next = next;
- if (next) next.prev = prev;
+ prev.next = next;
+ if (next) next.prev = prev;
}
diff --git a/packages/astro-parser/src/utils/list.ts b/packages/astro-parser/src/utils/list.ts
index 9388adb14..671311a23 100644
--- a/packages/astro-parser/src/utils/list.ts
+++ b/packages/astro-parser/src/utils/list.ts
@@ -1,5 +1,5 @@
/** Display an array of strings in a human-readable format */
export default function list(items: string[], conjunction = 'or') {
- if (items.length === 1) return items[0];
- return `${items.slice(0, -1).join(', ')} ${conjunction} ${items[items.length - 1]}`;
+ if (items.length === 1) return items[0];
+ return `${items.slice(0, -1).join(', ')} ${conjunction} ${items[items.length - 1]}`;
}
diff --git a/packages/astro-parser/src/utils/names.ts b/packages/astro-parser/src/utils/names.ts
index f041d20ce..38e1ec2e8 100644
--- a/packages/astro-parser/src/utils/names.ts
+++ b/packages/astro-parser/src/utils/names.ts
@@ -2,141 +2,141 @@ import { isIdentifierStart, isIdentifierChar } from 'acorn';
import full_char_code_at from './full_char_code_at.js';
export const globals = new Set([
- 'alert',
- 'Array',
- 'Boolean',
- 'clearInterval',
- 'clearTimeout',
- 'confirm',
- 'console',
- 'Date',
- 'decodeURI',
- 'decodeURIComponent',
- 'document',
- 'Element',
- 'encodeURI',
- 'encodeURIComponent',
- 'Error',
- 'EvalError',
- 'Event',
- 'EventSource',
- 'fetch',
- 'global',
- 'globalThis',
- 'history',
- 'Infinity',
- 'InternalError',
- 'Intl',
- 'isFinite',
- 'isNaN',
- 'JSON',
- 'localStorage',
- 'location',
- 'Map',
- 'Math',
- 'NaN',
- 'navigator',
- 'Number',
- 'Node',
- 'Object',
- 'parseFloat',
- 'parseInt',
- 'process',
- 'Promise',
- 'prompt',
- 'RangeError',
- 'ReferenceError',
- 'RegExp',
- 'sessionStorage',
- 'Set',
- 'setInterval',
- 'setTimeout',
- 'String',
- 'SyntaxError',
- 'TypeError',
- 'undefined',
- 'URIError',
- 'URL',
- 'window',
+ 'alert',
+ 'Array',
+ 'Boolean',
+ 'clearInterval',
+ 'clearTimeout',
+ 'confirm',
+ 'console',
+ 'Date',
+ 'decodeURI',
+ 'decodeURIComponent',
+ 'document',
+ 'Element',
+ 'encodeURI',
+ 'encodeURIComponent',
+ 'Error',
+ 'EvalError',
+ 'Event',
+ 'EventSource',
+ 'fetch',
+ 'global',
+ 'globalThis',
+ 'history',
+ 'Infinity',
+ 'InternalError',
+ 'Intl',
+ 'isFinite',
+ 'isNaN',
+ 'JSON',
+ 'localStorage',
+ 'location',
+ 'Map',
+ 'Math',
+ 'NaN',
+ 'navigator',
+ 'Number',
+ 'Node',
+ 'Object',
+ 'parseFloat',
+ 'parseInt',
+ 'process',
+ 'Promise',
+ 'prompt',
+ 'RangeError',
+ 'ReferenceError',
+ 'RegExp',
+ 'sessionStorage',
+ 'Set',
+ 'setInterval',
+ 'setTimeout',
+ 'String',
+ 'SyntaxError',
+ 'TypeError',
+ 'undefined',
+ 'URIError',
+ 'URL',
+ 'window',
]);
export const reserved = new Set([
- 'arguments',
- 'await',
- 'break',
- 'case',
- 'catch',
- 'class',
- 'const',
- 'continue',
- 'debugger',
- 'default',
- 'delete',
- 'do',
- 'else',
- 'enum',
- 'eval',
- 'export',
- 'extends',
- 'false',
- 'finally',
- 'for',
- 'function',
- 'if',
- 'implements',
- 'import',
- 'in',
- 'instanceof',
- 'interface',
- 'let',
- 'new',
- 'null',
- 'package',
- 'private',
- 'protected',
- 'public',
- 'return',
- 'static',
- 'super',
- 'switch',
- 'this',
- 'throw',
- 'true',
- 'try',
- 'typeof',
- 'var',
- 'void',
- 'while',
- 'with',
- 'yield',
+ 'arguments',
+ 'await',
+ 'break',
+ 'case',
+ 'catch',
+ 'class',
+ 'const',
+ 'continue',
+ 'debugger',
+ 'default',
+ 'delete',
+ 'do',
+ 'else',
+ 'enum',
+ 'eval',
+ 'export',
+ 'extends',
+ 'false',
+ 'finally',
+ 'for',
+ 'function',
+ 'if',
+ 'implements',
+ 'import',
+ 'in',
+ 'instanceof',
+ 'interface',
+ 'let',
+ 'new',
+ 'null',
+ 'package',
+ 'private',
+ 'protected',
+ 'public',
+ 'return',
+ 'static',
+ 'super',
+ 'switch',
+ 'this',
+ 'throw',
+ 'true',
+ 'try',
+ 'typeof',
+ 'var',
+ 'void',
+ 'while',
+ 'with',
+ 'yield',
]);
const void_element_names = /^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/;
/** Is this a void HTML element? */
export function is_void(name: string) {
- return void_element_names.test(name) || name.toLowerCase() === '!doctype';
+ return void_element_names.test(name) || name.toLowerCase() === '!doctype';
}
/** Is this a valid HTML element? */
export function is_valid(str: string): boolean {
- let i = 0;
+ let i = 0;
- while (i < str.length) {
- const code = full_char_code_at(str, i);
- if (!(i === 0 ? isIdentifierStart : isIdentifierChar)(code, true)) return false;
+ while (i < str.length) {
+ const code = full_char_code_at(str, i);
+ if (!(i === 0 ? isIdentifierStart : isIdentifierChar)(code, true)) return false;
- i += code <= 0xffff ? 1 : 2;
- }
+ i += code <= 0xffff ? 1 : 2;
+ }
- return true;
+ return true;
}
/** Utility to normalize HTML */
export function sanitize(name: string) {
- return name
- .replace(/[^a-zA-Z0-9_]+/g, '_')
- .replace(/^_/, '')
- .replace(/_$/, '')
- .replace(/^[0-9]/, '_$&');
+ return name
+ .replace(/[^a-zA-Z0-9_]+/g, '_')
+ .replace(/^_/, '')
+ .replace(/_$/, '')
+ .replace(/^[0-9]/, '_$&');
}
diff --git a/packages/astro-parser/src/utils/nodes_match.ts b/packages/astro-parser/src/utils/nodes_match.ts
index 7e4093994..941025f52 100644
--- a/packages/astro-parser/src/utils/nodes_match.ts
+++ b/packages/astro-parser/src/utils/nodes_match.ts
@@ -2,34 +2,34 @@
/** Compare two TemplateNodes to determine if they are equivalent */
export function nodes_match(a, b) {
- if (!!a !== !!b) return false;
- if (Array.isArray(a) !== Array.isArray(b)) return false;
+ if (!!a !== !!b) return false;
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
- if (a && typeof a === 'object') {
- if (Array.isArray(a)) {
- if (a.length !== b.length) return false;
- return a.every((child, i) => nodes_match(child, b[i]));
- }
+ if (a && typeof a === 'object') {
+ if (Array.isArray(a)) {
+ if (a.length !== b.length) return false;
+ return a.every((child, i) => nodes_match(child, b[i]));
+ }
- const a_keys = Object.keys(a).sort();
- const b_keys = Object.keys(b).sort();
+ const a_keys = Object.keys(a).sort();
+ const b_keys = Object.keys(b).sort();
- if (a_keys.length !== b_keys.length) return false;
+ if (a_keys.length !== b_keys.length) return false;
- let i = a_keys.length;
- while (i--) {
- const key = a_keys[i];
- if (b_keys[i] !== key) return false;
+ let i = a_keys.length;
+ while (i--) {
+ const key = a_keys[i];
+ if (b_keys[i] !== key) return false;
- if (key === 'start' || key === 'end') continue;
+ if (key === 'start' || key === 'end') continue;
- if (!nodes_match(a[key], b[key])) {
- return false;
- }
- }
+ if (!nodes_match(a[key], b[key])) {
+ return false;
+ }
+ }
- return true;
- }
+ return true;
+ }
- return a === b;
+ return a === b;
}
diff --git a/packages/astro-prism/index.mjs b/packages/astro-prism/index.mjs
index 05f1ef2db..0a118ba1b 100644
--- a/packages/astro-prism/index.mjs
+++ b/packages/astro-prism/index.mjs
@@ -1,170 +1,170 @@
export function addAstro(Prism) {
- if (Prism.languages.astro) {
- return;
- }
-
- let scriptLang;
- if (Prism.languages.typescript) {
- scriptLang = 'typescript';
- } else {
- scriptLang = 'javascript';
- console.warn('Prism TypeScript language not loaded, Astro scripts will be treated as JavaScript.');
- }
-
- let script = Prism.util.clone(Prism.languages[scriptLang]);
-
- let space = /(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source;
- let braces = /(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source;
- let spread = /(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;
-
- /**
- * @param {string} source
- * @param {string} [flags]
- */
- function re(source, flags) {
- source = source
- .replace(/<S>/g, function () {
- return space;
- })
- .replace(/<BRACES>/g, function () {
- return braces;
- })
- .replace(/<SPREAD>/g, function () {
- return spread;
- });
- return RegExp(source, flags);
- }
-
- spread = re(spread).source;
-
- Prism.languages.astro = Prism.languages.extend('markup', script);
- Prism.languages.astro.tag.pattern = re(/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source);
-
- Prism.languages.astro.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/i;
- Prism.languages.astro.tag.inside['attr-value'].pattern = /=(?!\{)(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s'">]+)/i;
- Prism.languages.astro.tag.inside['tag'].inside['class-name'] = /^[A-Z]\w*(?:\.[A-Z]\w*)*$/;
- Prism.languages.astro.tag.inside['comment'] = script['comment'];
-
- Prism.languages.insertBefore(
- 'inside',
- 'attr-name',
- {
- spread: {
- pattern: re(/<SPREAD>/.source),
- inside: Prism.languages.astro,
- },
- },
- Prism.languages.astro.tag
- );
-
- Prism.languages.insertBefore(
- 'inside',
- 'special-attr',
- {
- script: {
- // Allow for two levels of nesting
- pattern: re(/=<BRACES>/.source),
- inside: {
- 'script-punctuation': {
- pattern: /^=(?={)/,
- alias: 'punctuation',
- },
- rest: Prism.languages.astro,
- },
- alias: `language-${scriptLang}`,
- },
- },
- Prism.languages.astro.tag
- );
-
- // The following will handle plain text inside tags
- let stringifyToken = function (token) {
- if (!token) {
- return '';
- }
- if (typeof token === 'string') {
- return token;
- }
- if (typeof token.content === 'string') {
- return token.content;
- }
- return token.content.map(stringifyToken).join('');
- };
-
- let walkTokens = function (tokens) {
- let openedTags = [];
- for (let i = 0; i < tokens.length; i++) {
- let token = tokens[i];
-
- // This breaks styles, not sure why
- if (token.type === 'style') {
- return;
- }
-
- let notTagNorBrace = false;
-
- if (typeof token !== 'string') {
- if (token.type === 'tag' && token.content[0] && token.content[0].type === 'tag') {
- // We found a tag, now find its kind
-
- if (token.content[0].content[0].content === '</') {
- // Closing tag
- if (openedTags.length > 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1])) {
- // Pop matching opening tag
- openedTags.pop();
- }
- } else {
- if (token.content[token.content.length - 1].content === '/>') {
- // Autoclosed tag, ignore
- } else {
- // Opening tag
- openedTags.push({
- tagName: stringifyToken(token.content[0].content[1]),
- openedBraces: 0,
- });
- }
- }
- } else if (openedTags.length > 0 && token.type === 'punctuation' && token.content === '{') {
- // Here we might have entered a Astro context inside a tag
- openedTags[openedTags.length - 1].openedBraces++;
- } else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') {
- // Here we might have left a Astro context inside a tag
- openedTags[openedTags.length - 1].openedBraces--;
- } else {
- notTagNorBrace = true;
- }
- }
- if (notTagNorBrace || typeof token === 'string') {
- if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0) {
- // Here we are inside a tag, and not inside a Astro context.
- // That's plain text: drop any tokens matched.
- let plainText = stringifyToken(token);
-
- // And merge text with adjacent text
- if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')) {
- plainText += stringifyToken(tokens[i + 1]);
- tokens.splice(i + 1, 1);
- }
- if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 'plain-text')) {
- plainText = stringifyToken(tokens[i - 1]) + plainText;
- tokens.splice(i - 1, 1);
- i--;
- }
-
- tokens[i] = new Prism.Token('plain-text', plainText, null, plainText);
- }
- }
-
- if (token.content && typeof token.content !== 'string') {
- walkTokens(token.content);
- }
- }
- };
-
- Prism.hooks.add('after-tokenize', function (env) {
- if (env.language !== 'astro') {
- return;
- }
- walkTokens(env.tokens);
- });
+ if (Prism.languages.astro) {
+ return;
+ }
+
+ let scriptLang;
+ if (Prism.languages.typescript) {
+ scriptLang = 'typescript';
+ } else {
+ scriptLang = 'javascript';
+ console.warn('Prism TypeScript language not loaded, Astro scripts will be treated as JavaScript.');
+ }
+
+ let script = Prism.util.clone(Prism.languages[scriptLang]);
+
+ let space = /(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source;
+ let braces = /(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source;
+ let spread = /(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;
+
+ /**
+ * @param {string} source
+ * @param {string} [flags]
+ */
+ function re(source, flags) {
+ source = source
+ .replace(/<S>/g, function () {
+ return space;
+ })
+ .replace(/<BRACES>/g, function () {
+ return braces;
+ })
+ .replace(/<SPREAD>/g, function () {
+ return spread;
+ });
+ return RegExp(source, flags);
+ }
+
+ spread = re(spread).source;
+
+ Prism.languages.astro = Prism.languages.extend('markup', script);
+ Prism.languages.astro.tag.pattern = re(/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source);
+
+ Prism.languages.astro.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/i;
+ Prism.languages.astro.tag.inside['attr-value'].pattern = /=(?!\{)(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s'">]+)/i;
+ Prism.languages.astro.tag.inside['tag'].inside['class-name'] = /^[A-Z]\w*(?:\.[A-Z]\w*)*$/;
+ Prism.languages.astro.tag.inside['comment'] = script['comment'];
+
+ Prism.languages.insertBefore(
+ 'inside',
+ 'attr-name',
+ {
+ spread: {
+ pattern: re(/<SPREAD>/.source),
+ inside: Prism.languages.astro,
+ },
+ },
+ Prism.languages.astro.tag
+ );
+
+ Prism.languages.insertBefore(
+ 'inside',
+ 'special-attr',
+ {
+ script: {
+ // Allow for two levels of nesting
+ pattern: re(/=<BRACES>/.source),
+ inside: {
+ 'script-punctuation': {
+ pattern: /^=(?={)/,
+ alias: 'punctuation',
+ },
+ rest: Prism.languages.astro,
+ },
+ alias: `language-${scriptLang}`,
+ },
+ },
+ Prism.languages.astro.tag
+ );
+
+ // The following will handle plain text inside tags
+ let stringifyToken = function (token) {
+ if (!token) {
+ return '';
+ }
+ if (typeof token === 'string') {
+ return token;
+ }
+ if (typeof token.content === 'string') {
+ return token.content;
+ }
+ return token.content.map(stringifyToken).join('');
+ };
+
+ let walkTokens = function (tokens) {
+ let openedTags = [];
+ for (let i = 0; i < tokens.length; i++) {
+ let token = tokens[i];
+
+ // This breaks styles, not sure why
+ if (token.type === 'style') {
+ return;
+ }
+
+ let notTagNorBrace = false;
+
+ if (typeof token !== 'string') {
+ if (token.type === 'tag' && token.content[0] && token.content[0].type === 'tag') {
+ // We found a tag, now find its kind
+
+ if (token.content[0].content[0].content === '</') {
+ // Closing tag
+ if (openedTags.length > 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1])) {
+ // Pop matching opening tag
+ openedTags.pop();
+ }
+ } else {
+ if (token.content[token.content.length - 1].content === '/>') {
+ // Autoclosed tag, ignore
+ } else {
+ // Opening tag
+ openedTags.push({
+ tagName: stringifyToken(token.content[0].content[1]),
+ openedBraces: 0,
+ });
+ }
+ }
+ } else if (openedTags.length > 0 && token.type === 'punctuation' && token.content === '{') {
+ // Here we might have entered a Astro context inside a tag
+ openedTags[openedTags.length - 1].openedBraces++;
+ } else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') {
+ // Here we might have left a Astro context inside a tag
+ openedTags[openedTags.length - 1].openedBraces--;
+ } else {
+ notTagNorBrace = true;
+ }
+ }
+ if (notTagNorBrace || typeof token === 'string') {
+ if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0) {
+ // Here we are inside a tag, and not inside a Astro context.
+ // That's plain text: drop any tokens matched.
+ let plainText = stringifyToken(token);
+
+ // And merge text with adjacent text
+ if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')) {
+ plainText += stringifyToken(tokens[i + 1]);
+ tokens.splice(i + 1, 1);
+ }
+ if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 'plain-text')) {
+ plainText = stringifyToken(tokens[i - 1]) + plainText;
+ tokens.splice(i - 1, 1);
+ i--;
+ }
+
+ tokens[i] = new Prism.Token('plain-text', plainText, null, plainText);
+ }
+ }
+
+ if (token.content && typeof token.content !== 'string') {
+ walkTokens(token.content);
+ }
+ }
+ };
+
+ Prism.hooks.add('after-tokenize', function (env) {
+ if (env.language !== 'astro') {
+ return;
+ }
+ walkTokens(env.tokens);
+ });
}
diff --git a/packages/astro/astro.js b/packages/astro/astro.js
index f0c71d974..0f458bb6d 100755
--- a/packages/astro/astro.js
+++ b/packages/astro/astro.js
@@ -8,86 +8,86 @@
// Needed for Stackblitz: https://github.com/stackblitz/webcontainer-core/issues/281
const CI_INSTRUCTIONS = {
- NETLIFY: 'https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript',
- GITHUB_ACTIONS: 'https://docs.github.com/en/actions/guides/building-and-testing-nodejs#specifying-the-nodejs-version',
- VERCEL: 'https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version',
+ NETLIFY: 'https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript',
+ GITHUB_ACTIONS: 'https://docs.github.com/en/actions/guides/building-and-testing-nodejs#specifying-the-nodejs-version',
+ VERCEL: 'https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version',
};
/** `astro *` */
async function main() {
- // Check for ESM support.
- // Load the "supports-esm" package in an way that works in both ESM & CJS.
- let supportsESM = typeof require !== 'undefined' ? require('supports-esm') : (await import('supports-esm')).default;
+ // Check for ESM support.
+ // Load the "supports-esm" package in an way that works in both ESM & CJS.
+ let supportsESM = typeof require !== 'undefined' ? require('supports-esm') : (await import('supports-esm')).default;
- // Check for CJS->ESM named export support.
- // "path-to-regexp" is a real-world package that we depend on, that only
- // works in later versions of Node with advanced CJS->ESM support.
- // If `import {compile} from 'path-to-regexp'` will fail, we need to know.
- if (supportsESM) {
- const testNamedExportsModule = await import('path-to-regexp');
- supportsESM = !!testNamedExportsModule.compile;
- }
+ // Check for CJS->ESM named export support.
+ // "path-to-regexp" is a real-world package that we depend on, that only
+ // works in later versions of Node with advanced CJS->ESM support.
+ // If `import {compile} from 'path-to-regexp'` will fail, we need to know.
+ if (supportsESM) {
+ const testNamedExportsModule = await import('path-to-regexp');
+ supportsESM = !!testNamedExportsModule.compile;
+ }
- // Preflight check complete. Enjoy! ✨
- if (supportsESM) {
- return import('./dist/cli/index.js')
- .then(({ cli }) => cli(process.argv))
- .catch((error) => {
- console.error(error);
- process.exit(1);
- });
- }
+ // Preflight check complete. Enjoy! ✨
+ if (supportsESM) {
+ return import('./dist/cli/index.js')
+ .then(({ cli }) => cli(process.argv))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+ }
- const version = process.versions.node;
+ const version = process.versions.node;
- // Not supported (incomplete ESM): It's very difficult (impossible?) to load the
- // dependencies below in an unknown module type. If `require` is undefined, then this file
- // actually was run as ESM but one of the ESM preflight checks above failed. In that case,
- // it's okay to hard-code the valid Node versions here since they will not change over time.
- if (typeof require === 'undefined') {
- console.error(`\nNode.js v${version} is not supported by Astro!
+ // Not supported (incomplete ESM): It's very difficult (impossible?) to load the
+ // dependencies below in an unknown module type. If `require` is undefined, then this file
+ // actually was run as ESM but one of the ESM preflight checks above failed. In that case,
+ // it's okay to hard-code the valid Node versions here since they will not change over time.
+ if (typeof require === 'undefined') {
+ console.error(`\nNode.js v${version} is not supported by Astro!
Please upgrade to a version of Node.js with complete ESM support: "^14.15.0 || >=16.0.0"\n`);
- }
+ }
- // Not supported: Report the most helpful error message possible.
- const pkg = require('./package.json');
- const ci = require('ci-info');
- const semver = require('semver');
- const engines = pkg.engines.node;
+ // Not supported: Report the most helpful error message possible.
+ const pkg = require('./package.json');
+ const ci = require('ci-info');
+ const semver = require('semver');
+ const engines = pkg.engines.node;
- // TODO: Remove "semver" in Astro v1.0: This is mainly just to check our work. Once run in
- // the wild for a bit without error, we can assume our engine range is correct and won't
- // change over time.
- const isSupported = semver.satisfies(version, engines);
- if (isSupported) {
- console.error(`\nNode.js v${version} is not supported by Astro!
+ // TODO: Remove "semver" in Astro v1.0: This is mainly just to check our work. Once run in
+ // the wild for a bit without error, we can assume our engine range is correct and won't
+ // change over time.
+ const isSupported = semver.satisfies(version, engines);
+ if (isSupported) {
+ console.error(`\nNode.js v${version} is not supported by Astro!
Supported versions: ${engines}\n
Issue Detected! This Node.js version was expected to work, but failed a system check.
Please file an issue so that we can take a look: https://github.com/withastro/astro/issues/new\n`);
- } else {
- console.error(`\nNode.js v${version} is not supported by Astro!
+ } else {
+ console.error(`\nNode.js v${version} is not supported by Astro!
Please upgrade Node.js to a supported version: "${engines}"\n`);
- }
+ }
- // Special instructions for CI environments, which may have special steps needed.
- // This is a common issue that we can help users with proactively.
- if (ci.isCI) {
- let platform;
- for (const [key, value] of Object.entries(ci)) {
- if (value === true) {
- platform = key;
- break;
- }
- }
- console.log(`${ci.name} CI Environment Detected!\nAdditional steps may be needed to set your Node.js version:`);
- console.log(`Documentation: https://docs.astro.build/guides/deploy`);
- if (CI_INSTRUCTIONS[platform]) {
- console.log(`${ci.name} Documentation: ${CI_INSTRUCTIONS[platform]}`);
- }
- console.log(``);
- }
+ // Special instructions for CI environments, which may have special steps needed.
+ // This is a common issue that we can help users with proactively.
+ if (ci.isCI) {
+ let platform;
+ for (const [key, value] of Object.entries(ci)) {
+ if (value === true) {
+ platform = key;
+ break;
+ }
+ }
+ console.log(`${ci.name} CI Environment Detected!\nAdditional steps may be needed to set your Node.js version:`);
+ console.log(`Documentation: https://docs.astro.build/guides/deploy`);
+ if (CI_INSTRUCTIONS[platform]) {
+ console.log(`${ci.name} Documentation: ${CI_INSTRUCTIONS[platform]}`);
+ }
+ console.log(``);
+ }
- process.exit(1);
+ process.exit(1);
}
main();
diff --git a/packages/astro/components/Code.astro b/packages/astro/components/Code.astro
index 0a642c3b6..1fc319d08 100644
--- a/packages/astro/components/Code.astro
+++ b/packages/astro/components/Code.astro
@@ -2,49 +2,49 @@
import shiki from 'shiki';
export interface Props {
- /** The code to highlight. Required. */
- code: string;
- /**
- * The language of your code. Defaults to "plaintext".
- * Supports all languages listed here: https://github.com/shikijs/shiki/blob/main/docs/themes.md
- */
- lang?: string;
- /**
- * The styling theme. Defaults to "github-dark".
- * Supports all themes listed here: https://github.com/shikijs/shiki/blob/main/docs/themes.md
- * Instructions for loading a custom theme: https://github.com/shikijs/shiki/blob/main/docs/themes.md#loading-theme
- */
- theme?: string;
- /**
- * Enable word wrapping. Defaults to "false".
- * - true: enabled.
- * - false: enabled.
- * - null: All overflow styling removed. Code will overflow the element by default.
- */
- wrap?: boolean | null;
+ /** The code to highlight. Required. */
+ code: string;
+ /**
+ * The language of your code. Defaults to "plaintext".
+ * Supports all languages listed here: https://github.com/shikijs/shiki/blob/main/docs/themes.md
+ */
+ lang?: string;
+ /**
+ * The styling theme. Defaults to "github-dark".
+ * Supports all themes listed here: https://github.com/shikijs/shiki/blob/main/docs/themes.md
+ * Instructions for loading a custom theme: https://github.com/shikijs/shiki/blob/main/docs/themes.md#loading-theme
+ */
+ theme?: string;
+ /**
+ * Enable word wrapping. Defaults to "false".
+ * - true: enabled.
+ * - false: enabled.
+ * - null: All overflow styling removed. Code will overflow the element by default.
+ */
+ wrap?: boolean | null;
}
const { code, lang = 'plaintext', theme = 'github-dark', wrap = false } = Astro.props;
/** Replace the shiki class name with a custom astro class name. */
function repairShikiTheme(html: string): string {
- // Replace "shiki" class naming with "astro".
- html = html.replace('<pre class="shiki"', '<pre class="astro-code"');
- // Replace "shiki" css variable naming with "astro".
- html = html.replace(/style="(background-)?color: var\(--shiki-/g, 'style="$1color: var(--astro-code-');
- // Handle code wrapping
- // if wrap=null, do nothing.
- if (wrap === false) {
- html = html.replace(/style="(.*?)"/, 'style="$1; overflow-x: auto;"');
- } else if (wrap === true) {
- html = html.replace(/style="(.*?)"/, 'style="$1; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"');
- }
- return html;
+ // Replace "shiki" class naming with "astro".
+ html = html.replace('<pre class="shiki"', '<pre class="astro-code"');
+ // Replace "shiki" css variable naming with "astro".
+ html = html.replace(/style="(background-)?color: var\(--shiki-/g, 'style="$1color: var(--astro-code-');
+ // Handle code wrapping
+ // if wrap=null, do nothing.
+ if (wrap === false) {
+ html = html.replace(/style="(.*?)"/, 'style="$1; overflow-x: auto;"');
+ } else if (wrap === true) {
+ html = html.replace(/style="(.*?)"/, 'style="$1; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"');
+ }
+ return html;
}
-
-const highlighter = await shiki.getHighlighter({theme});
+const highlighter = await shiki.getHighlighter({ theme });
const _html = highlighter.codeToHtml(code, lang);
const html = repairShikiTheme(_html);
---
+
{html}
diff --git a/packages/astro/components/Debug.astro b/packages/astro/components/Debug.astro
index 85e90bbb7..4256041cd 100644
--- a/packages/astro/components/Debug.astro
+++ b/packages/astro/components/Debug.astro
@@ -6,51 +6,52 @@ const value = Astro.props[key];
---
<div class="debug">
- <div class="debug-header">
- <h2 class="debug-title"><span class="debug-label">Debug</span> <span class="debug-name">"{key}"</span></h2>
- </div>
+ <div class="debug-header">
+ <h2 class="debug-title"><span class="debug-label">Debug</span> <span class="debug-name">"{key}"</span></h2>
+ </div>
- <Code code={JSON.stringify(value, null, 2)} />
+ <Code code={JSON.stringify(value, null, 2)} />
</div>
<style>
-.debug {
- font-size: 14px;
- padding: 1rem 1.5rem;
- background: white;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-}
-
-.debug-header, pre {
- margin: -1rem -1.5rem 1rem;
- padding: 0.25rem 0.75rem;
-}
-
-.debug-header {
- background: #FF1639;
- border-radius: 4px;
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
-}
-
-.debug-title {
- font-size: 1em;
- color: white;
- margin: 0.5em 0;
-}
-
-.debug-label {
- font-weight: bold;
- text-transform: uppercase;
- margin-right: 0.75em;
-}
-
-pre {
- border: 1px solid #eee;
- padding: 1rem 0.75rem;
- border-radius: 4px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- font-size: 14px;
-}
+ .debug {
+ font-size: 14px;
+ padding: 1rem 1.5rem;
+ background: white;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ }
+
+ .debug-header,
+ pre {
+ margin: -1rem -1.5rem 1rem;
+ padding: 0.25rem 0.75rem;
+ }
+
+ .debug-header {
+ background: #ff1639;
+ border-radius: 4px;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ .debug-title {
+ font-size: 1em;
+ color: white;
+ margin: 0.5em 0;
+ }
+
+ .debug-label {
+ font-weight: bold;
+ text-transform: uppercase;
+ margin-right: 0.75em;
+ }
+
+ pre {
+ border: 1px solid #eee;
+ padding: 1rem 0.75rem;
+ border-radius: 4px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ font-size: 14px;
+ }
</style>
diff --git a/packages/astro/components/Markdown.astro b/packages/astro/components/Markdown.astro
index 350b9c317..cb04ef065 100644
--- a/packages/astro/components/Markdown.astro
+++ b/packages/astro/components/Markdown.astro
@@ -1,39 +1,44 @@
---
export interface Props {
- content?: string;
+ content?: string;
}
-const dedent = (str: string) => str.split('\n').map(ln => ln.trimStart()).join('\n');
+const dedent = (str: string) =>
+ str
+ .split('\n')
+ .map((ln) => ln.trimStart())
+ .join('\n');
// Internal props that should not be part of the external interface.
interface InternalProps extends Props {
- $scope: string;
+ $scope: string;
}
let { content, class: className } = Astro.props as InternalProps;
let html = null;
let htmlContent = '';
-const { privateRenderMarkdownDoNotUse: renderMarkdown } = (Astro as any);
+const { privateRenderMarkdownDoNotUse: renderMarkdown } = Astro as any;
// If no content prop provided, use the slot.
if (!content) {
- const { privateRenderSlotDoNotUse: renderSlot } = (Astro as any);
- content = await renderSlot('default');
- if (content !== undefined && content !== null) {
- content = dedent(content);
- }
+ const { privateRenderSlotDoNotUse: renderSlot } = Astro as any;
+ content = await renderSlot('default');
+ if (content !== undefined && content !== null) {
+ content = dedent(content);
+ }
}
if (content) {
- htmlContent = await renderMarkdown(content, {
- mode: 'md',
- $: {
- scopedClassName: className
- }
- });
+ htmlContent = await renderMarkdown(content, {
+ mode: 'md',
+ $: {
+ scopedClassName: className,
+ },
+ });
}
html = htmlContent;
---
+
{html ? html : <slot />}
diff --git a/packages/astro/components/Prism.astro b/packages/astro/components/Prism.astro
index ba4b22ec3..9e9db3459 100644
--- a/packages/astro/components/Prism.astro
+++ b/packages/astro/components/Prism.astro
@@ -4,49 +4,46 @@ import { addAstro } from '@astrojs/prism';
import loadLanguages from 'prismjs/components/index.js';
export interface Props {
- class?: string;
- lang?: string;
- code: string;
+ class?: string;
+ lang?: string;
+ code: string;
}
const { class: className, lang, code } = Astro.props as Props;
-let classLanguage = `language-${lang}`
+let classLanguage = `language-${lang}`;
-const languageMap = new Map([
- ['ts', 'typescript']
-]);
+const languageMap = new Map([['ts', 'typescript']]);
if (lang == null) {
- console.warn('Prism.astro: No language provided.');
+ console.warn('Prism.astro: No language provided.');
}
-const ensureLoaded = lang => {
- if(lang && !Prism.languages[lang]) {
- loadLanguages([lang]);
- }
+const ensureLoaded = (lang) => {
+ if (lang && !Prism.languages[lang]) {
+ loadLanguages([lang]);
+ }
};
-if(languageMap.has(lang)) {
- ensureLoaded(languageMap.get(lang));
-} else if(lang === 'astro') {
- ensureLoaded('typescript');
- addAstro(Prism);
+if (languageMap.has(lang)) {
+ ensureLoaded(languageMap.get(lang));
+} else if (lang === 'astro') {
+ ensureLoaded('typescript');
+ addAstro(Prism);
} else {
- ensureLoaded('markup-templating'); // Prism expects this to exist for a number of other langs
- ensureLoaded(lang);
+ ensureLoaded('markup-templating'); // Prism expects this to exist for a number of other langs
+ ensureLoaded(lang);
}
-if(lang && !Prism.languages[lang]) {
- console.warn(`Unable to load the language: ${lang}`);
+if (lang && !Prism.languages[lang]) {
+ console.warn(`Unable to load the language: ${lang}`);
}
const grammar = Prism.languages[lang];
let html = code;
if (grammar) {
- html = Prism.highlight(code, grammar, lang);
+ html = Prism.highlight(code, grammar, lang);
}
-
---
<pre class={[className, classLanguage].join(' ')}><code class={classLanguage}>{html}</code></pre>
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index cdb2771d5..f6f47cd98 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -5,18 +5,18 @@ import type { AstroComponentFactory, Metadata } from '../runtime/server';
import type vite from '../core/vite';
export interface AstroBuiltinProps {
- 'client:load'?: boolean;
- 'client:idle'?: boolean;
- 'client:media'?: string;
- 'client:visible'?: boolean;
+ 'client:load'?: boolean;
+ 'client:idle'?: boolean;
+ 'client:media'?: string;
+ 'client:visible'?: boolean;
}
export interface AstroComponentMetadata {
- displayName: string;
- hydrate?: 'load' | 'idle' | 'visible' | 'media' | 'only';
- hydrateArgs?: any;
- componentUrl?: string;
- componentExport?: { value: string; namespace?: boolean };
+ displayName: string;
+ hydrate?: 'load' | 'idle' | 'visible' | 'media' | 'only';
+ hydrateArgs?: any;
+ componentUrl?: string;
+ componentExport?: { value: string; namespace?: boolean };
}
/**
@@ -24,25 +24,25 @@ export interface AstroComponentMetadata {
* Docs: https://docs.astro.build/reference/api-reference/#astro-global
*/
export interface AstroGlobal extends AstroGlobalPartial {
- /** set props for this astro component (along with default values) */
- props: Record<string, number | string | any>;
- /** get information about this page */
- request: {
- /** get the current page URL */
- url: URL;
- /** get the current canonical URL */
- canonicalURL: URL;
- /** get page params (dynamic pages only) */
- params: Params;
- };
- /** see if slots are used */
- slots: Record<string, true | undefined>;
+ /** set props for this astro component (along with default values) */
+ props: Record<string, number | string | any>;
+ /** get information about this page */
+ request: {
+ /** get the current page URL */
+ url: URL;
+ /** get the current canonical URL */
+ canonicalURL: URL;
+ /** get page params (dynamic pages only) */
+ params: Params;
+ };
+ /** see if slots are used */
+ slots: Record<string, true | undefined>;
}
export interface AstroGlobalPartial {
- fetchContent<T = any>(globStr: string): Promise<FetchContentResult<T>[]>;
- resolve: (path: string) => string;
- site: URL;
+ fetchContent<T = any>(globStr: string): Promise<FetchContentResult<T>[]>;
+ resolve: (path: string) => string;
+ site: URL;
}
/**
@@ -50,81 +50,81 @@ export interface AstroGlobalPartial {
* Docs: https://docs.astro.build/reference/configuration-reference/
*/
export interface AstroUserConfig {
- /**
- * Where to resolve all URLs relative to. Useful if you have a monorepo project.
- * Default: '.' (current working directory)
- */
- projectRoot?: string;
- /**
- * Path to the `astro build` output.
- * Default: './dist'
- */
- dist?: string;
- /**
- * Path to all of your Astro components, pages, and data.
- * Default: './src'
- */
- src?: string;
- /**
- * Path to your Astro/Markdown pages. Each file in this directory
- * becomes a page in your final build.
- * Default: './src/pages'
- */
- pages?: string;
- /**
- * Path to your public files. These are copied over into your build directory, untouched.
- * Useful for favicons, images, and other files that don't need processing.
- * Default: './public'
- */
- public?: string;
- /**
- * Framework component renderers enable UI framework rendering (static and dynamic).
- * When you define this in your configuration, all other defaults are disabled.
- * Default: [
- * '@astrojs/renderer-svelte',
- * '@astrojs/renderer-vue',
- * '@astrojs/renderer-react',
- * '@astrojs/renderer-preact',
- * ],
- */
- renderers?: string[];
- /** Options for rendering markdown content */
- markdownOptions?: {
- render?: [string | MarkdownParser, Record<string, any>];
- };
- /** Options specific to `astro build` */
- buildOptions?: {
- /** Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. */
- site?: string;
- /**
- * Generate an automatically-generated sitemap for your build.
- * Default: true
- */
- sitemap?: boolean;
- /**
- * Control the output file URL format of each page.
- * If 'file', Astro will generate a matching HTML file (ex: "/foo.html") instead of a directory.
- * If 'directory', Astro will generate a directory with a nested index.html (ex: "/foo/index.html") for each page.
- * Default: 'directory'
- */
- pageUrlFormat?: 'file' | 'directory';
- };
- /** Options for the development server run with `astro dev`. */
- devOptions?: {
- hostname?: string;
- /** The port to run the dev server on. */
- port?: number;
- /**
- * Configure The trailing slash behavior of URL route matching:
- * 'always' - Only match URLs that include a trailing slash (ex: "/foo/")
- * 'never' - Never match URLs that include a trailing slash (ex: "/foo")
- * 'ignore' - Match URLs regardless of whether a trailing "/" exists
- * Default: 'always'
- */
- trailingSlash?: 'always' | 'never' | 'ignore';
- };
- /** Pass configuration options to Vite */
- vite?: vite.InlineConfig;
+ /**
+ * Where to resolve all URLs relative to. Useful if you have a monorepo project.
+ * Default: '.' (current working directory)
+ */
+ projectRoot?: string;
+ /**
+ * Path to the `astro build` output.
+ * Default: './dist'
+ */
+ dist?: string;
+ /**
+ * Path to all of your Astro components, pages, and data.
+ * Default: './src'
+ */
+ src?: string;
+ /**
+ * Path to your Astro/Markdown pages. Each file in this directory
+ * becomes a page in your final build.
+ * Default: './src/pages'
+ */
+ pages?: string;
+ /**
+ * Path to your public files. These are copied over into your build directory, untouched.
+ * Useful for favicons, images, and other files that don't need processing.
+ * Default: './public'
+ */
+ public?: string;
+ /**
+ * Framework component renderers enable UI framework rendering (static and dynamic).
+ * When you define this in your configuration, all other defaults are disabled.
+ * Default: [
+ * '@astrojs/renderer-svelte',
+ * '@astrojs/renderer-vue',
+ * '@astrojs/renderer-react',
+ * '@astrojs/renderer-preact',
+ * ],
+ */
+ renderers?: string[];
+ /** Options for rendering markdown content */
+ markdownOptions?: {
+ render?: [string | MarkdownParser, Record<string, any>];
+ };
+ /** Options specific to `astro build` */
+ buildOptions?: {
+ /** Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. */
+ site?: string;
+ /**
+ * Generate an automatically-generated sitemap for your build.
+ * Default: true
+ */
+ sitemap?: boolean;
+ /**
+ * Control the output file URL format of each page.
+ * If 'file', Astro will generate a matching HTML file (ex: "/foo.html") instead of a directory.
+ * If 'directory', Astro will generate a directory with a nested index.html (ex: "/foo/index.html") for each page.
+ * Default: 'directory'
+ */
+ pageUrlFormat?: 'file' | 'directory';
+ };
+ /** Options for the development server run with `astro dev`. */
+ devOptions?: {
+ hostname?: string;
+ /** The port to run the dev server on. */
+ port?: number;
+ /**
+ * Configure The trailing slash behavior of URL route matching:
+ * 'always' - Only match URLs that include a trailing slash (ex: "/foo/")
+ * 'never' - Never match URLs that include a trailing slash (ex: "/foo")
+ * 'ignore' - Match URLs regardless of whether a trailing "/" exists
+ * Default: 'always'
+ */
+ trailingSlash?: 'always' | 'never' | 'ignore';
+ };
+ /** Pass configuration options to Vite */
+ vite?: vite.InlineConfig;
}
// NOTE(fks): We choose to keep our hand-generated AstroUserConfig interface so that
@@ -146,10 +146,10 @@ export type AsyncRendererComponentFn<U> = (Component: any, props: any, children:
/** Generic interface for a component (Astro, Svelte, React, etc.) */
export interface ComponentInstance {
- $$metadata: Metadata;
- default: AstroComponentFactory;
- css?: string[];
- getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
+ $$metadata: Metadata;
+ default: AstroComponentFactory;
+ css?: string[];
+ getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
}
/**
@@ -159,12 +159,12 @@ export interface ComponentInstance {
export type FetchContentResult<T> = FetchContentResultBase & T;
export type FetchContentResultBase = {
- astro: {
- headers: string[];
- source: string;
- html: string;
- };
- url: URL;
+ astro: {
+ headers: string[];
+ source: string;
+ html: string;
+ };
+ url: URL;
};
export type GetHydrateCallback = () => Promise<(element: Element, innerHTML: string | null) => void>;
@@ -173,41 +173,41 @@ export type GetHydrateCallback = () => Promise<(element: Element, innerHTML: str
* getStaticPaths() options
* Docs: https://docs.astro.build/reference/api-reference/#getstaticpaths
*/ export interface GetStaticPathsOptions {
- paginate?: PaginateFunction;
- rss?: (...args: any[]) => any;
+ paginate?: PaginateFunction;
+ rss?: (...args: any[]) => any;
}
export type GetStaticPathsResult = { params: Params; props?: Props }[];
export interface HydrateOptions {
- value?: string;
+ value?: string;
}
export interface JSXTransformConfig {
- /** Babel presets */
- presets?: babel.PluginItem[];
- /** Babel plugins */
- plugins?: babel.PluginItem[];
+ /** Babel presets */
+ presets?: babel.PluginItem[];
+ /** Babel plugins */
+ plugins?: babel.PluginItem[];
}
export type JSXTransformFn = (options: { mode: string; ssr: boolean }) => Promise<JSXTransformConfig>;
export interface ManifestData {
- routes: RouteData[];
+ routes: RouteData[];
}
export type MarkdownParser = (contents: string, options?: Record<string, any>) => MarkdownParserResponse | PromiseLike<MarkdownParserResponse>;
export interface MarkdownParserResponse {
- frontmatter: {
- [key: string]: any;
- };
- metadata: {
- headers: any[];
- source: string;
- html: string;
- };
- code: string;
+ frontmatter: {
+ [key: string]: any;
+ };
+ metadata: {
+ headers: any[];
+ source: string;
+ html: string;
+ };
+ code: string;
}
/**
@@ -215,12 +215,12 @@ export interface MarkdownParserResponse {
* Docs: https://docs.astro.build/guides/pagination/#calling-the-paginate-function
*/
export interface PaginateOptions {
- /** the number of items per-page (default: `10`) */
- pageSize?: number;
- /** key: value object of page params (ex: `{ tag: 'javascript' }`) */
- params?: Params;
- /** object of props to forward to `page` result */
- props?: Props;
+ /** the number of items per-page (default: `10`) */
+ pageSize?: number;
+ /** key: value object of page params (ex: `{ tag: 'javascript' }`) */
+ params?: Params;
+ /** object of props to forward to `page` result */
+ props?: Props;
}
/**
@@ -228,29 +228,29 @@ export interface PaginateOptions {
* Docs: https://docs.astro.build/guides/pagination/#using-the-page-prop
*/
export interface Page<T = any> {
- /** result */
- data: T[];
- /** metadata */
- /** the count of the first item on the page, starting from 0 */
- start: number;
- /** the count of the last item on the page, starting from 0 */
- end: number;
- /** total number of results */
- total: number;
- /** the current page number, starting from 1 */
- currentPage: number;
- /** number of items per page (default: 25) */
- size: number;
- /** number of last page */
- lastPage: number;
- url: {
- /** url of the current page */
- current: string;
- /** url of the previous page (if there is one) */
- prev: string | undefined;
- /** url of the next page (if there is one) */
- next: string | undefined;
- };
+ /** result */
+ data: T[];
+ /** metadata */
+ /** the count of the first item on the page, starting from 0 */
+ start: number;
+ /** the count of the last item on the page, starting from 0 */
+ end: number;
+ /** total number of results */
+ total: number;
+ /** the current page number, starting from 1 */
+ currentPage: number;
+ /** number of items per page (default: 25) */
+ size: number;
+ /** number of last page */
+ lastPage: number;
+ url: {
+ /** url of the current page */
+ current: string;
+ /** url of the previous page (if there is one) */
+ prev: string | undefined;
+ /** url of the next page (if there is one) */
+ next: string | undefined;
+ };
}
export type PaginateFunction = (data: [], args?: PaginateOptions) => GetStaticPathsResult;
@@ -260,14 +260,14 @@ export type Params = Record<string, string | undefined>;
export type Props = Record<string, unknown>;
export interface RenderPageOptions {
- request: {
- params?: Params;
- url: URL;
- canonicalURL: URL;
- };
- children: any[];
- props: Props;
- css?: string[];
+ request: {
+ params?: Params;
+ url: URL;
+ canonicalURL: URL;
+ };
+ children: any[];
+ props: Props;
+ css?: string[];
}
/**
@@ -275,40 +275,40 @@ export interface RenderPageOptions {
* Docs: https://docs.astro.build/reference/renderer-reference/
*/
export interface Renderer {
- /** Name of the renderer (required) */
- name: string;
- /** Import statement for renderer */
- source?: string;
- /** Scripts to be injected before component */
- polyfills?: string[];
- /** Polyfills that need to run before hydration ever occurs */
- hydrationPolyfills?: string[];
- /** JSX identifier (e.g. 'react' or 'solid-js') */
- jsxImportSource?: string;
- /** Babel transform options */
- jsxTransformOptions?: JSXTransformFn;
- /** Utilies for server-side rendering */
- ssr: {
- check: AsyncRendererComponentFn<boolean>;
- renderToStaticMarkup: AsyncRendererComponentFn<{
- html: string;
- }>;
- };
- /** Return configuration object for Vite ("options" should match https://vitejs.dev/guide/api-plugin.html#config) */
- viteConfig?: (options: { mode: 'string'; command: 'build' | 'serve' }) => Promise<vite.InlineConfig>;
- /** @deprecated Don’t try and build these dependencies for client (deprecated in 0.21) */
- external?: string[];
- /** @deprecated Clientside requirements (deprecated in 0.21) */
- knownEntrypoints?: string[];
+ /** Name of the renderer (required) */
+ name: string;
+ /** Import statement for renderer */
+ source?: string;
+ /** Scripts to be injected before component */
+ polyfills?: string[];
+ /** Polyfills that need to run before hydration ever occurs */
+ hydrationPolyfills?: string[];
+ /** JSX identifier (e.g. 'react' or 'solid-js') */
+ jsxImportSource?: string;
+ /** Babel transform options */
+ jsxTransformOptions?: JSXTransformFn;
+ /** Utilies for server-side rendering */
+ ssr: {
+ check: AsyncRendererComponentFn<boolean>;
+ renderToStaticMarkup: AsyncRendererComponentFn<{
+ html: string;
+ }>;
+ };
+ /** Return configuration object for Vite ("options" should match https://vitejs.dev/guide/api-plugin.html#config) */
+ viteConfig?: (options: { mode: 'string'; command: 'build' | 'serve' }) => Promise<vite.InlineConfig>;
+ /** @deprecated Don’t try and build these dependencies for client (deprecated in 0.21) */
+ external?: string[];
+ /** @deprecated Clientside requirements (deprecated in 0.21) */
+ knownEntrypoints?: string[];
}
export interface RouteData {
- component: string;
- generate: (data?: any) => string;
- params: string[];
- pathname?: string;
- pattern: RegExp;
- type: 'page';
+ component: string;
+ generate: (data?: any) => string;
+ params: string[];
+ pathname?: string;
+ pattern: RegExp;
+ type: 'page';
}
export type RouteCache = Record<string, GetStaticPathsResult>;
@@ -320,33 +320,33 @@ export type RuntimeMode = 'development' | 'production';
* Docs: https://docs.astro.build/reference/api-reference/#rss
*/
export interface RSS {
- /** (required) Title of the RSS Feed */
- title: string;
- /** (required) Description of the RSS Feed */
- description: string;
- /** Specify arbitrary metadata on opening <xml> tag */
- xmlns?: Record<string, string>;
- /** Specify custom data in opening of file */
- customData?: string;
- /**
- * Specify where the RSS xml file should be written.
- * Relative to final build directory. Example: '/foo/bar.xml'
- * Defaults to '/rss.xml'.
- */
- dest?: string;
- /** Return data about each item */
- items: {
- /** (required) Title of item */
- title: string;
- /** (required) Link to item */
- link: string;
- /** Publication date of item */
- pubDate?: Date;
- /** Item description */
- description?: string;
- /** Append some other XML-valid data to this item */
- customData?: string;
- }[];
+ /** (required) Title of the RSS Feed */
+ title: string;
+ /** (required) Description of the RSS Feed */
+ description: string;
+ /** Specify arbitrary metadata on opening <xml> tag */
+ xmlns?: Record<string, string>;
+ /** Specify custom data in opening of file */
+ customData?: string;
+ /**
+ * Specify where the RSS xml file should be written.
+ * Relative to final build directory. Example: '/foo/bar.xml'
+ * Defaults to '/rss.xml'.
+ */
+ dest?: string;
+ /** Return data about each item */
+ items: {
+ /** (required) Title of item */
+ title: string;
+ /** (required) Link to item */
+ link: string;
+ /** Publication date of item */
+ pubDate?: Date;
+ /** Item description */
+ description?: string;
+ /** Append some other XML-valid data to this item */
+ customData?: string;
+ }[];
}
export type RSSFunction = (args: RSS) => void;
@@ -356,20 +356,20 @@ export type RSSResult = { url: string; xml?: string };
export type SSRError = Error & vite.ErrorPayload['err'];
export interface SSRElement {
- props: Record<string, any>;
- children: string;
+ props: Record<string, any>;
+ children: string;
}
export interface SSRMetadata {
- renderers: Renderer[];
- pathname: string;
- experimentalStaticBuild: boolean;
+ renderers: Renderer[];
+ pathname: string;
+ experimentalStaticBuild: boolean;
}
export interface SSRResult {
- styles: Set<SSRElement>;
- scripts: Set<SSRElement>;
- links: Set<SSRElement>;
- createAstro(Astro: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null): AstroGlobal;
- _metadata: SSRMetadata;
+ styles: Set<SSRElement>;
+ scripts: Set<SSRElement>;
+ links: Set<SSRElement>;
+ createAstro(Astro: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null): AstroGlobal;
+ _metadata: SSRMetadata;
}
diff --git a/packages/astro/src/@types/shorthash.d.ts b/packages/astro/src/@types/shorthash.d.ts
index 02eb5ba51..8dc53e6ef 100644
--- a/packages/astro/src/@types/shorthash.d.ts
+++ b/packages/astro/src/@types/shorthash.d.ts
@@ -1,5 +1,5 @@
declare module 'shorthash' {
- function unique(string: string): string;
+ function unique(string: string): string;
- export default { unique };
+ export default { unique };
}
diff --git a/packages/astro/src/cli/check.ts b/packages/astro/src/cli/check.ts
index 5fe9e6d7b..499ac9afd 100644
--- a/packages/astro/src/cli/check.ts
+++ b/packages/astro/src/cli/check.ts
@@ -9,101 +9,101 @@ import { pathToFileURL } from 'url';
import * as fs from 'fs';
async function openAllDocuments(workspaceUri: URL, filePathsToIgnore: string[], checker: AstroCheck) {
- const files = await glob('**/*.astro', {
- cwd: workspaceUri.pathname,
- ignore: ['node_modules/**'].concat(filePathsToIgnore.map((ignore) => `${ignore}/**`)),
- });
- const absFilePaths = files.map((f) => path.resolve(workspaceUri.pathname, f));
-
- for (const absFilePath of absFilePaths) {
- const text = fs.readFileSync(absFilePath, 'utf-8');
- checker.upsertDocument({
- uri: pathToFileURL(absFilePath).toString(),
- text,
- });
- }
+ const files = await glob('**/*.astro', {
+ cwd: workspaceUri.pathname,
+ ignore: ['node_modules/**'].concat(filePathsToIgnore.map((ignore) => `${ignore}/**`)),
+ });
+ const absFilePaths = files.map((f) => path.resolve(workspaceUri.pathname, f));
+
+ for (const absFilePath of absFilePaths) {
+ const text = fs.readFileSync(absFilePath, 'utf-8');
+ checker.upsertDocument({
+ uri: pathToFileURL(absFilePath).toString(),
+ text,
+ });
+ }
}
interface Result {
- errors: number;
- warnings: number;
+ errors: number;
+ warnings: number;
}
function offsetAt({ line, character }: { line: number; character: number }, text: string) {
- let i = 0;
- let l = 0;
- let c = 0;
- while (i < text.length) {
- if (l === line && c === character) {
- break;
- }
-
- let char = text[i];
- switch (char) {
- case '\n': {
- l++;
- c = 0;
- break;
- }
- default: {
- c++;
- break;
- }
- }
-
- i++;
- }
-
- return i;
+ let i = 0;
+ let l = 0;
+ let c = 0;
+ while (i < text.length) {
+ if (l === line && c === character) {
+ break;
+ }
+
+ let char = text[i];
+ switch (char) {
+ case '\n': {
+ l++;
+ c = 0;
+ break;
+ }
+ default: {
+ c++;
+ break;
+ }
+ }
+
+ i++;
+ }
+
+ return i;
}
function pad(str: string, len: number) {
- return Array.from({ length: len }, () => str).join('');
+ return Array.from({ length: len }, () => str).join('');
}
export async function run() {}
export async function check(astroConfig: AstroConfig) {
- const root = astroConfig.projectRoot;
- let checker = new AstroCheck(root.toString());
- await openAllDocuments(root, [], checker);
-
- let diagnostics = await checker.getDiagnostics();
-
- let result: Result = {
- errors: 0,
- warnings: 0,
- };
-
- diagnostics.forEach((diag) => {
- diag.diagnostics.forEach((d) => {
- switch (d.severity) {
- case DiagnosticSeverity.Error: {
- console.error(`${bold(cyan(path.relative(root.pathname, diag.filePath)))}:${bold(yellow(d.range.start.line))}:${bold(yellow(d.range.start.character))} - ${d.message}`);
- let startOffset = offsetAt({ line: d.range.start.line, character: 0 }, diag.text);
- let endOffset = offsetAt({ line: d.range.start.line + 1, character: 0 }, diag.text);
- let str = diag.text.substring(startOffset, endOffset - 1);
- const lineNumStr = d.range.start.line.toString();
- const lineNumLen = lineNumStr.length;
- console.error(`${bgWhite(black(lineNumStr))} ${str}`);
- let tildes = pad('~', d.range.end.character - d.range.start.character);
- let spaces = pad(' ', d.range.start.character + lineNumLen - 1);
- console.error(` ${spaces}${bold(red(tildes))}\n`);
- result.errors++;
- break;
- }
- case DiagnosticSeverity.Warning: {
- result.warnings++;
- break;
- }
- }
- });
- });
-
- if (result.errors) {
- console.error(`Found ${result.errors} errors.`);
- }
-
- const exitCode = result.errors ? 1 : 0;
- return exitCode;
+ const root = astroConfig.projectRoot;
+ let checker = new AstroCheck(root.toString());
+ await openAllDocuments(root, [], checker);
+
+ let diagnostics = await checker.getDiagnostics();
+
+ let result: Result = {
+ errors: 0,
+ warnings: 0,
+ };
+
+ diagnostics.forEach((diag) => {
+ diag.diagnostics.forEach((d) => {
+ switch (d.severity) {
+ case DiagnosticSeverity.Error: {
+ console.error(`${bold(cyan(path.relative(root.pathname, diag.filePath)))}:${bold(yellow(d.range.start.line))}:${bold(yellow(d.range.start.character))} - ${d.message}`);
+ let startOffset = offsetAt({ line: d.range.start.line, character: 0 }, diag.text);
+ let endOffset = offsetAt({ line: d.range.start.line + 1, character: 0 }, diag.text);
+ let str = diag.text.substring(startOffset, endOffset - 1);
+ const lineNumStr = d.range.start.line.toString();
+ const lineNumLen = lineNumStr.length;
+ console.error(`${bgWhite(black(lineNumStr))} ${str}`);
+ let tildes = pad('~', d.range.end.character - d.range.start.character);
+ let spaces = pad(' ', d.range.start.character + lineNumLen - 1);
+ console.error(` ${spaces}${bold(red(tildes))}\n`);
+ result.errors++;
+ break;
+ }
+ case DiagnosticSeverity.Warning: {
+ result.warnings++;
+ break;
+ }
+ }
+ });
+ });
+
+ if (result.errors) {
+ console.error(`Found ${result.errors} errors.`);
+ }
+
+ const exitCode = result.errors ? 1 : 0;
+ return exitCode;
}
diff --git a/packages/astro/src/cli/index.ts b/packages/astro/src/cli/index.ts
index 092ff2198..60fe945a2 100644
--- a/packages/astro/src/cli/index.ts
+++ b/packages/astro/src/cli/index.ts
@@ -17,54 +17,54 @@ import { formatConfigError, loadConfig } from '../core/config.js';
type Arguments = yargs.Arguments;
type cliCommand = 'help' | 'version' | 'dev' | 'build' | 'preview' | 'reload' | 'check';
interface CLIState {
- cmd: cliCommand;
- options: {
- projectRoot?: string;
- site?: string;
- sitemap?: boolean;
- hostname?: string;
- port?: number;
- config?: string;
- experimentalStaticBuild?: boolean;
- };
+ cmd: cliCommand;
+ options: {
+ projectRoot?: string;
+ site?: string;
+ sitemap?: boolean;
+ hostname?: string;
+ port?: number;
+ config?: string;
+ experimentalStaticBuild?: boolean;
+ };
}
/** Determine which action the user requested */
function resolveArgs(flags: Arguments): CLIState {
- const options: CLIState['options'] = {
- projectRoot: typeof flags.projectRoot === 'string' ? flags.projectRoot : undefined,
- site: typeof flags.site === 'string' ? flags.site : undefined,
- sitemap: typeof flags.sitemap === 'boolean' ? flags.sitemap : undefined,
- port: typeof flags.port === 'number' ? flags.port : undefined,
- config: typeof flags.config === 'string' ? flags.config : undefined,
- hostname: typeof flags.hostname === 'string' ? flags.hostname : undefined,
- experimentalStaticBuild: typeof flags.experimentalStaticBuild === 'boolean' ? flags.experimentalStaticBuild : false,
- };
-
- if (flags.version) {
- return { cmd: 'version', options };
- } else if (flags.help) {
- return { cmd: 'help', options };
- }
-
- const cmd = flags._[2];
- switch (cmd) {
- case 'dev':
- return { cmd: 'dev', options };
- case 'build':
- return { cmd: 'build', options };
- case 'preview':
- return { cmd: 'preview', options };
- case 'check':
- return { cmd: 'check', options };
- default:
- return { cmd: 'help', options };
- }
+ const options: CLIState['options'] = {
+ projectRoot: typeof flags.projectRoot === 'string' ? flags.projectRoot : undefined,
+ site: typeof flags.site === 'string' ? flags.site : undefined,
+ sitemap: typeof flags.sitemap === 'boolean' ? flags.sitemap : undefined,
+ port: typeof flags.port === 'number' ? flags.port : undefined,
+ config: typeof flags.config === 'string' ? flags.config : undefined,
+ hostname: typeof flags.hostname === 'string' ? flags.hostname : undefined,
+ experimentalStaticBuild: typeof flags.experimentalStaticBuild === 'boolean' ? flags.experimentalStaticBuild : false,
+ };
+
+ if (flags.version) {
+ return { cmd: 'version', options };
+ } else if (flags.help) {
+ return { cmd: 'help', options };
+ }
+
+ const cmd = flags._[2];
+ switch (cmd) {
+ case 'dev':
+ return { cmd: 'dev', options };
+ case 'build':
+ return { cmd: 'build', options };
+ case 'preview':
+ return { cmd: 'preview', options };
+ case 'check':
+ return { cmd: 'check', options };
+ default:
+ return { cmd: 'help', options };
+ }
}
/** Display --help flag */
function printHelp() {
- console.log(` ${colors.bold('astro')} - Futuristic web development tool.
+ console.log(` ${colors.bold('astro')} - Futuristic web development tool.
${colors.bold('Commands:')}
astro dev Run Astro in development mode.
astro build Build a pre-compiled production version of your site.
@@ -85,100 +85,100 @@ function printHelp() {
/** Display --version flag */
async function printVersion() {
- const pkgURL = new URL('../../package.json', import.meta.url);
- const pkg = JSON.parse(await fs.promises.readFile(pkgURL, 'utf8'));
- const pkgVersion = pkg.version;
+ const pkgURL = new URL('../../package.json', import.meta.url);
+ const pkg = JSON.parse(await fs.promises.readFile(pkgURL, 'utf8'));
+ const pkgVersion = pkg.version;
- console.log(pkgVersion);
+ console.log(pkgVersion);
}
/** Merge CLI flags & config options (CLI flags take priority) */
function mergeCLIFlags(astroConfig: AstroConfig, flags: CLIState['options']) {
- if (typeof flags.sitemap === 'boolean') astroConfig.buildOptions.sitemap = flags.sitemap;
- if (typeof flags.site === 'string') astroConfig.buildOptions.site = flags.site;
- if (typeof flags.port === 'number') astroConfig.devOptions.port = flags.port;
- if (typeof flags.hostname === 'string') astroConfig.devOptions.hostname = flags.hostname;
- if (typeof flags.experimentalStaticBuild === 'boolean') astroConfig.buildOptions.experimentalStaticBuild = flags.experimentalStaticBuild;
+ if (typeof flags.sitemap === 'boolean') astroConfig.buildOptions.sitemap = flags.sitemap;
+ if (typeof flags.site === 'string') astroConfig.buildOptions.site = flags.site;
+ if (typeof flags.port === 'number') astroConfig.devOptions.port = flags.port;
+ if (typeof flags.hostname === 'string') astroConfig.devOptions.hostname = flags.hostname;
+ if (typeof flags.experimentalStaticBuild === 'boolean') astroConfig.buildOptions.experimentalStaticBuild = flags.experimentalStaticBuild;
}
/** The primary CLI action */
export async function cli(args: string[]) {
- const flags = yargs(args);
- const state = resolveArgs(flags);
- const options = { ...state.options };
- const projectRoot = options.projectRoot || flags._[3];
-
- switch (state.cmd) {
- case 'help':
- printHelp();
- return process.exit(0);
- case 'version':
- await printVersion();
- return process.exit(0);
- }
-
- // logLevel
- let logging: LogOptions = {
- dest: defaultLogDestination,
- level: 'info',
- };
-
- if (flags.verbose) logging.level = 'debug';
- if (flags.silent) logging.level = 'silent';
- let config: AstroConfig;
- try {
- config = await loadConfig({ cwd: projectRoot, filename: options.config });
- mergeCLIFlags(config, options);
- } catch (err) {
- if (err instanceof z.ZodError) {
- console.error(formatConfigError(err));
- } else {
- console.error(colors.red((err as any).toString() || err));
- }
- process.exit(1);
- }
-
- switch (state.cmd) {
- case 'dev': {
- try {
- await devServer(config, { logging });
-
- await new Promise(() => {}); // don’t close dev server
- } catch (err) {
- throwAndExit(err);
- }
-
- return;
- }
- case 'build': {
- try {
- await build(config, { logging });
- process.exit(0);
- } catch (err) {
- throwAndExit(err);
- }
- return;
- }
- case 'check': {
- const ret = await check(config);
- return process.exit(ret);
- }
- case 'preview': {
- try {
- await preview(config, { logging }); // this will keep running
- } catch (err) {
- throwAndExit(err);
- }
- return;
- }
- default: {
- throw new Error(`Error running ${state.cmd}`);
- }
- }
+ const flags = yargs(args);
+ const state = resolveArgs(flags);
+ const options = { ...state.options };
+ const projectRoot = options.projectRoot || flags._[3];
+
+ switch (state.cmd) {
+ case 'help':
+ printHelp();
+ return process.exit(0);
+ case 'version':
+ await printVersion();
+ return process.exit(0);
+ }
+
+ // logLevel
+ let logging: LogOptions = {
+ dest: defaultLogDestination,
+ level: 'info',
+ };
+
+ if (flags.verbose) logging.level = 'debug';
+ if (flags.silent) logging.level = 'silent';
+ let config: AstroConfig;
+ try {
+ config = await loadConfig({ cwd: projectRoot, filename: options.config });
+ mergeCLIFlags(config, options);
+ } catch (err) {
+ if (err instanceof z.ZodError) {
+ console.error(formatConfigError(err));
+ } else {
+ console.error(colors.red((err as any).toString() || err));
+ }
+ process.exit(1);
+ }
+
+ switch (state.cmd) {
+ case 'dev': {
+ try {
+ await devServer(config, { logging });
+
+ await new Promise(() => {}); // don’t close dev server
+ } catch (err) {
+ throwAndExit(err);
+ }
+
+ return;
+ }
+ case 'build': {
+ try {
+ await build(config, { logging });
+ process.exit(0);
+ } catch (err) {
+ throwAndExit(err);
+ }
+ return;
+ }
+ case 'check': {
+ const ret = await check(config);
+ return process.exit(ret);
+ }
+ case 'preview': {
+ try {
+ await preview(config, { logging }); // this will keep running
+ } catch (err) {
+ throwAndExit(err);
+ }
+ return;
+ }
+ default: {
+ throw new Error(`Error running ${state.cmd}`);
+ }
+ }
}
/** Display error and exit */
function throwAndExit(err: any) {
- console.error(colors.red(err.toString() || err));
- process.exit(1);
+ console.error(colors.red(err.toString() || err));
+ process.exit(1);
}
diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts
index 3789f9789..fd73e0d93 100644
--- a/packages/astro/src/core/build/index.ts
+++ b/packages/astro/src/core/build/index.ts
@@ -15,145 +15,145 @@ import { build as scanBasedBuild } from './scan-based-build.js';
import { staticBuild } from './static-build.js';
export interface BuildOptions {
- mode?: string;
- logging: LogOptions;
+ mode?: string;
+ logging: LogOptions;
}
/** `astro build` */
export default async function build(config: AstroConfig, options: BuildOptions = { logging: defaultLogOptions }): Promise<void> {
- // polyfill WebAPIs to globalThis for Node v12, Node v14, and Node v16
- polyfill(globalThis, {
- exclude: 'window document',
- });
+ // polyfill WebAPIs to globalThis for Node v12, Node v14, and Node v16
+ polyfill(globalThis, {
+ exclude: 'window document',
+ });
- const builder = new AstroBuilder(config, options);
- await builder.build();
+ const builder = new AstroBuilder(config, options);
+ await builder.build();
}
class AstroBuilder {
- private config: AstroConfig;
- private logging: LogOptions;
- private mode = 'production';
- private origin: string;
- private routeCache: RouteCache = {};
- private manifest: ManifestData;
- private viteServer?: ViteDevServer;
- private viteConfig?: ViteConfigWithSSR;
-
- constructor(config: AstroConfig, options: BuildOptions) {
- if (!config.buildOptions.site && config.buildOptions.sitemap !== false) {
- warn(options.logging, 'config', `Set "buildOptions.site" to generate correct canonical URLs and sitemap`);
- }
-
- if (options.mode) this.mode = options.mode;
- this.config = config;
- const port = config.devOptions.port; // no need to save this (don’t rely on port in builder)
- this.logging = options.logging;
- this.origin = config.buildOptions.site ? new URL(config.buildOptions.site).origin : `http://localhost:${port}`;
- this.manifest = createRouteManifest({ config }, this.logging);
- }
-
- async build() {
- const { logging, origin } = this;
- const timer: Record<string, number> = {};
- timer.init = performance.now();
- timer.viteStart = performance.now();
- const viteConfig = await createVite(
- vite.mergeConfig(
- {
- mode: this.mode,
- server: {
- hmr: { overlay: false },
- middlewareMode: 'ssr',
- },
- },
- this.config.vite || {}
- ),
- { astroConfig: this.config, logging }
- );
- this.viteConfig = viteConfig;
- const viteServer = await vite.createServer(viteConfig);
- this.viteServer = viteServer;
- debug(logging, 'build', timerMessage('Vite started', timer.viteStart));
-
- timer.loadStart = performance.now();
- const { assets, allPages } = await collectPagesData({
- astroConfig: this.config,
- logging: this.logging,
- manifest: this.manifest,
- origin,
- routeCache: this.routeCache,
- viteServer: this.viteServer,
- });
- debug(logging, 'build', timerMessage('All pages loaded', timer.loadStart));
-
- // The names of each pages
- const pageNames: string[] = [];
-
- // Bundle the assets in your final build: This currently takes the HTML output
- // of every page (stored in memory) and bundles the assets pointed to on those pages.
- timer.buildStart = performance.now();
-
- // Use the new faster static based build.
- if (this.config.buildOptions.experimentalStaticBuild) {
- await staticBuild({
- allPages,
- astroConfig: this.config,
- logging: this.logging,
- origin: this.origin,
- routeCache: this.routeCache,
- viteConfig: this.viteConfig,
- });
- } else {
- await scanBasedBuild({
- allPages,
- astroConfig: this.config,
- logging: this.logging,
- origin: this.origin,
- pageNames,
- routeCache: this.routeCache,
- viteConfig: this.viteConfig,
- viteServer: this.viteServer,
- });
- }
- debug(logging, 'build', timerMessage('Vite build finished', timer.buildStart));
-
- // Write any additionally generated assets to disk.
- timer.assetsStart = performance.now();
- Object.keys(assets).map((k) => {
- if (!assets[k]) return;
- const filePath = new URL(`file://${k}`);
- fs.mkdirSync(new URL('./', filePath), { recursive: true });
- fs.writeFileSync(filePath, assets[k], 'utf8');
- delete assets[k]; // free up memory
- });
- debug(logging, 'build', timerMessage('Additional assets copied', timer.assetsStart));
-
- // Build your final sitemap.
- timer.sitemapStart = performance.now();
- if (this.config.buildOptions.sitemap && this.config.buildOptions.site) {
- const sitemap = generateSitemap(pageNames.map((pageName) => new URL(`/${pageName}`, this.config.buildOptions.site).href));
- const sitemapPath = new URL('./sitemap.xml', this.config.dist);
- await fs.promises.mkdir(new URL('./', sitemapPath), { recursive: true });
- await fs.promises.writeFile(sitemapPath, sitemap, 'utf8');
- }
- debug(logging, 'build', timerMessage('Sitemap built', timer.sitemapStart));
-
- // You're done! Time to clean up.
- await viteServer.close();
- if (logging.level && levels[logging.level] <= levels['info']) {
- await this.printStats({ logging, timeStart: timer.init, pageCount: pageNames.length });
- }
- }
-
- /** Stats */
- private async printStats({ logging, timeStart, pageCount }: { logging: LogOptions; timeStart: number; pageCount: number }) {
- /* eslint-disable no-console */
- debug(logging, ''); // empty line for debug
- const buildTime = performance.now() - timeStart;
- const total = buildTime < 750 ? `${Math.round(buildTime)}ms` : `${(buildTime / 1000).toFixed(2)}s`;
- const perPage = `${Math.round(buildTime / pageCount)}ms`;
- info(logging, 'build', `${pageCount} pages built in ${colors.bold(total)} ${colors.dim(`(${perPage}/page)`)}`);
- info(logging, 'build', `🚀 ${colors.cyan(colors.bold('Done'))}`);
- }
+ private config: AstroConfig;
+ private logging: LogOptions;
+ private mode = 'production';
+ private origin: string;
+ private routeCache: RouteCache = {};
+ private manifest: ManifestData;
+ private viteServer?: ViteDevServer;
+ private viteConfig?: ViteConfigWithSSR;
+
+ constructor(config: AstroConfig, options: BuildOptions) {
+ if (!config.buildOptions.site && config.buildOptions.sitemap !== false) {
+ warn(options.logging, 'config', `Set "buildOptions.site" to generate correct canonical URLs and sitemap`);
+ }
+
+ if (options.mode) this.mode = options.mode;
+ this.config = config;
+ const port = config.devOptions.port; // no need to save this (don’t rely on port in builder)
+ this.logging = options.logging;
+ this.origin = config.buildOptions.site ? new URL(config.buildOptions.site).origin : `http://localhost:${port}`;
+ this.manifest = createRouteManifest({ config }, this.logging);
+ }
+
+ async build() {
+ const { logging, origin } = this;
+ const timer: Record<string, number> = {};
+ timer.init = performance.now();
+ timer.viteStart = performance.now();
+ const viteConfig = await createVite(
+ vite.mergeConfig(
+ {
+ mode: this.mode,
+ server: {
+ hmr: { overlay: false },
+ middlewareMode: 'ssr',
+ },
+ },
+ this.config.vite || {}
+ ),
+ { astroConfig: this.config, logging }
+ );
+ this.viteConfig = viteConfig;
+ const viteServer = await vite.createServer(viteConfig);
+ this.viteServer = viteServer;
+ debug(logging, 'build', timerMessage('Vite started', timer.viteStart));
+
+ timer.loadStart = performance.now();
+ const { assets, allPages } = await collectPagesData({
+ astroConfig: this.config,
+ logging: this.logging,
+ manifest: this.manifest,
+ origin,
+ routeCache: this.routeCache,
+ viteServer: this.viteServer,
+ });
+ debug(logging, 'build', timerMessage('All pages loaded', timer.loadStart));
+
+ // The names of each pages
+ const pageNames: string[] = [];
+
+ // Bundle the assets in your final build: This currently takes the HTML output
+ // of every page (stored in memory) and bundles the assets pointed to on those pages.
+ timer.buildStart = performance.now();
+
+ // Use the new faster static based build.
+ if (this.config.buildOptions.experimentalStaticBuild) {
+ await staticBuild({
+ allPages,
+ astroConfig: this.config,
+ logging: this.logging,
+ origin: this.origin,
+ routeCache: this.routeCache,
+ viteConfig: this.viteConfig,
+ });
+ } else {
+ await scanBasedBuild({
+ allPages,
+ astroConfig: this.config,
+ logging: this.logging,
+ origin: this.origin,
+ pageNames,
+ routeCache: this.routeCache,
+ viteConfig: this.viteConfig,
+ viteServer: this.viteServer,
+ });
+ }
+ debug(logging, 'build', timerMessage('Vite build finished', timer.buildStart));
+
+ // Write any additionally generated assets to disk.
+ timer.assetsStart = performance.now();
+ Object.keys(assets).map((k) => {
+ if (!assets[k]) return;
+ const filePath = new URL(`file://${k}`);
+ fs.mkdirSync(new URL('./', filePath), { recursive: true });
+ fs.writeFileSync(filePath, assets[k], 'utf8');
+ delete assets[k]; // free up memory
+ });
+ debug(logging, 'build', timerMessage('Additional assets copied', timer.assetsStart));
+
+ // Build your final sitemap.
+ timer.sitemapStart = performance.now();
+ if (this.config.buildOptions.sitemap && this.config.buildOptions.site) {
+ const sitemap = generateSitemap(pageNames.map((pageName) => new URL(`/${pageName}`, this.config.buildOptions.site).href));
+ const sitemapPath = new URL('./sitemap.xml', this.config.dist);
+ await fs.promises.mkdir(new URL('./', sitemapPath), { recursive: true });
+ await fs.promises.writeFile(sitemapPath, sitemap, 'utf8');
+ }
+ debug(logging, 'build', timerMessage('Sitemap built', timer.sitemapStart));
+
+ // You're done! Time to clean up.
+ await viteServer.close();
+ if (logging.level && levels[logging.level] <= levels['info']) {
+ await this.printStats({ logging, timeStart: timer.init, pageCount: pageNames.length });
+ }
+ }
+
+ /** Stats */
+ private async printStats({ logging, timeStart, pageCount }: { logging: LogOptions; timeStart: number; pageCount: number }) {
+ /* eslint-disable no-console */
+ debug(logging, ''); // empty line for debug
+ const buildTime = performance.now() - timeStart;
+ const total = buildTime < 750 ? `${Math.round(buildTime)}ms` : `${(buildTime / 1000).toFixed(2)}s`;
+ const perPage = `${Math.round(buildTime / pageCount)}ms`;
+ info(logging, 'build', `${pageCount} pages built in ${colors.bold(total)} ${colors.dim(`(${perPage}/page)`)}`);
+ info(logging, 'build', `🚀 ${colors.cyan(colors.bold('Done'))}`);
+ }
}
diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts
index 1028f3e4e..b30ea7ddf 100644
--- a/packages/astro/src/core/build/internal.ts
+++ b/packages/astro/src/core/build/internal.ts
@@ -1,19 +1,19 @@
import type { RenderedChunk } from 'rollup';
export interface BuildInternals {
- // Pure CSS chunks are chunks that only contain CSS.
- pureCSSChunks: Set<RenderedChunk>;
- // chunkToReferenceIdMap maps them to a hash id used to find the final file.
- chunkToReferenceIdMap: Map<string, string>;
+ // Pure CSS chunks are chunks that only contain CSS.
+ pureCSSChunks: Set<RenderedChunk>;
+ // chunkToReferenceIdMap maps them to a hash id used to find the final file.
+ chunkToReferenceIdMap: Map<string, string>;
- // This is a mapping of pathname to the string source of all collected
- // inline <style> for a page.
- astroStyleMap: Map<string, string>;
- // This is a virtual JS module that imports all dependent styles for a page.
- astroPageStyleMap: Map<string, string>;
+ // This is a mapping of pathname to the string source of all collected
+ // inline <style> for a page.
+ astroStyleMap: Map<string, string>;
+ // This is a virtual JS module that imports all dependent styles for a page.
+ astroPageStyleMap: Map<string, string>;
- // A mapping to entrypoints (facadeId) to assets (styles) that are added.
- facadeIdToAssetsMap: Map<string, string[]>;
+ // A mapping to entrypoints (facadeId) to assets (styles) that are added.
+ facadeIdToAssetsMap: Map<string, string[]>;
}
/**
@@ -21,25 +21,25 @@ export interface BuildInternals {
* @returns {BuildInternals}
*/
export function createBuildInternals(): BuildInternals {
- // Pure CSS chunks are chunks that only contain CSS.
- // This is all of them, and chunkToReferenceIdMap maps them to a hash id used to find the final file.
- const pureCSSChunks = new Set<RenderedChunk>();
- const chunkToReferenceIdMap = new Map<string, string>();
+ // Pure CSS chunks are chunks that only contain CSS.
+ // This is all of them, and chunkToReferenceIdMap maps them to a hash id used to find the final file.
+ const pureCSSChunks = new Set<RenderedChunk>();
+ const chunkToReferenceIdMap = new Map<string, string>();
- // This is a mapping of pathname to the string source of all collected
- // inline <style> for a page.
- const astroStyleMap = new Map<string, string>();
- // This is a virtual JS module that imports all dependent styles for a page.
- const astroPageStyleMap = new Map<string, string>();
+ // This is a mapping of pathname to the string source of all collected
+ // inline <style> for a page.
+ const astroStyleMap = new Map<string, string>();
+ // This is a virtual JS module that imports all dependent styles for a page.
+ const astroPageStyleMap = new Map<string, string>();
- // A mapping to entrypoints (facadeId) to assets (styles) that are added.
- const facadeIdToAssetsMap = new Map<string, string[]>();
+ // A mapping to entrypoints (facadeId) to assets (styles) that are added.
+ const facadeIdToAssetsMap = new Map<string, string[]>();
- return {
- pureCSSChunks,
- chunkToReferenceIdMap,
- astroStyleMap,
- astroPageStyleMap,
- facadeIdToAssetsMap,
- };
+ return {
+ pureCSSChunks,
+ chunkToReferenceIdMap,
+ astroStyleMap,
+ astroPageStyleMap,
+ facadeIdToAssetsMap,
+ };
}
diff --git a/packages/astro/src/core/build/page-data.ts b/packages/astro/src/core/build/page-data.ts
index aa6247537..8ba029ba3 100644
--- a/packages/astro/src/core/build/page-data.ts
+++ b/packages/astro/src/core/build/page-data.ts
@@ -12,111 +12,111 @@ import { generatePaginateFunction } from '../ssr/paginate.js';
import { generateRssFunction } from '../ssr/rss.js';
export interface CollectPagesDataOptions {
- astroConfig: AstroConfig;
- logging: LogOptions;
- manifest: ManifestData;
- origin: string;
- routeCache: RouteCache;
- viteServer: ViteDevServer;
+ astroConfig: AstroConfig;
+ logging: LogOptions;
+ manifest: ManifestData;
+ origin: string;
+ routeCache: RouteCache;
+ viteServer: ViteDevServer;
}
export interface CollectPagesDataResult {
- assets: Record<string, string>;
- allPages: AllPagesData;
+ assets: Record<string, string>;
+ allPages: AllPagesData;
}
// Examines the routes and returns a collection of information about each page.
export async function collectPagesData(opts: CollectPagesDataOptions): Promise<CollectPagesDataResult> {
- const { astroConfig, logging, manifest, origin, routeCache, viteServer } = opts;
+ const { astroConfig, logging, manifest, origin, routeCache, viteServer } = opts;
- const assets: Record<string, string> = {};
- const allPages: AllPagesData = {};
+ const assets: Record<string, string> = {};
+ const allPages: AllPagesData = {};
- // Collect all routes ahead-of-time, before we start the build.
- // NOTE: This enforces that `getStaticPaths()` is only called once per route,
- // and is then cached across all future SSR builds. In the past, we've had trouble
- // with parallelized builds without guaranteeing that this is called first.
- await Promise.all(
- manifest.routes.map(async (route) => {
- // static route:
- if (route.pathname) {
- allPages[route.component] = {
- route,
- paths: [route.pathname],
- preload: await ssrPreload({
- astroConfig,
- filePath: new URL(`./${route.component}`, astroConfig.projectRoot),
- logging,
- mode: 'production',
- origin,
- pathname: route.pathname,
- route,
- routeCache,
- viteServer,
- })
- .then((routes) => {
- const html = `${route.pathname}`.replace(/\/?$/, '/index.html');
- debug(logging, 'build', `├── ${colors.bold(colors.green('✔'))} ${route.component} → ${colors.yellow(html)}`);
- return routes;
- })
- .catch((err) => {
- debug(logging, 'build', `├── ${colors.bold(colors.red('✘'))} ${route.component}`);
- throw err;
- }),
- };
- return;
- }
- // dynamic route:
- const result = await getStaticPathsForRoute(opts, route)
- .then((routes) => {
- const label = routes.paths.length === 1 ? 'page' : 'pages';
- debug(logging, 'build', `├── ${colors.bold(colors.green('✔'))} ${route.component} → ${colors.magenta(`[${routes.paths.length} ${label}]`)}`);
- return routes;
- })
- .catch((err) => {
- debug(logging, 'build', `├── ${colors.bold(colors.red('✗'))} ${route.component}`);
- throw err;
- });
- if (result.rss?.xml) {
- const rssFile = new URL(result.rss.url.replace(/^\/?/, './'), astroConfig.dist);
- if (assets[fileURLToPath(rssFile)]) {
- throw new Error(`[getStaticPaths] RSS feed ${result.rss.url} already exists.\nUse \`rss(data, {url: '...'})\` to choose a unique, custom URL. (${route.component})`);
- }
- assets[fileURLToPath(rssFile)] = result.rss.xml;
- }
- allPages[route.component] = {
- route,
- paths: result.paths,
- preload: await ssrPreload({
- astroConfig,
- filePath: new URL(`./${route.component}`, astroConfig.projectRoot),
- logging,
- mode: 'production',
- origin,
- pathname: result.paths[0],
- route,
- routeCache,
- viteServer,
- }),
- };
- })
- );
+ // Collect all routes ahead-of-time, before we start the build.
+ // NOTE: This enforces that `getStaticPaths()` is only called once per route,
+ // and is then cached across all future SSR builds. In the past, we've had trouble
+ // with parallelized builds without guaranteeing that this is called first.
+ await Promise.all(
+ manifest.routes.map(async (route) => {
+ // static route:
+ if (route.pathname) {
+ allPages[route.component] = {
+ route,
+ paths: [route.pathname],
+ preload: await ssrPreload({
+ astroConfig,
+ filePath: new URL(`./${route.component}`, astroConfig.projectRoot),
+ logging,
+ mode: 'production',
+ origin,
+ pathname: route.pathname,
+ route,
+ routeCache,
+ viteServer,
+ })
+ .then((routes) => {
+ const html = `${route.pathname}`.replace(/\/?$/, '/index.html');
+ debug(logging, 'build', `├── ${colors.bold(colors.green('✔'))} ${route.component} → ${colors.yellow(html)}`);
+ return routes;
+ })
+ .catch((err) => {
+ debug(logging, 'build', `├── ${colors.bold(colors.red('✘'))} ${route.component}`);
+ throw err;
+ }),
+ };
+ return;
+ }
+ // dynamic route:
+ const result = await getStaticPathsForRoute(opts, route)
+ .then((routes) => {
+ const label = routes.paths.length === 1 ? 'page' : 'pages';
+ debug(logging, 'build', `├── ${colors.bold(colors.green('✔'))} ${route.component} → ${colors.magenta(`[${routes.paths.length} ${label}]`)}`);
+ return routes;
+ })
+ .catch((err) => {
+ debug(logging, 'build', `├── ${colors.bold(colors.red('✗'))} ${route.component}`);
+ throw err;
+ });
+ if (result.rss?.xml) {
+ const rssFile = new URL(result.rss.url.replace(/^\/?/, './'), astroConfig.dist);
+ if (assets[fileURLToPath(rssFile)]) {
+ throw new Error(`[getStaticPaths] RSS feed ${result.rss.url} already exists.\nUse \`rss(data, {url: '...'})\` to choose a unique, custom URL. (${route.component})`);
+ }
+ assets[fileURLToPath(rssFile)] = result.rss.xml;
+ }
+ allPages[route.component] = {
+ route,
+ paths: result.paths,
+ preload: await ssrPreload({
+ astroConfig,
+ filePath: new URL(`./${route.component}`, astroConfig.projectRoot),
+ logging,
+ mode: 'production',
+ origin,
+ pathname: result.paths[0],
+ route,
+ routeCache,
+ viteServer,
+ }),
+ };
+ })
+ );
- return { assets, allPages };
+ return { assets, allPages };
}
async function getStaticPathsForRoute(opts: CollectPagesDataOptions, route: RouteData): Promise<{ paths: string[]; rss?: RSSResult }> {
- const { astroConfig, logging, routeCache, viteServer } = opts;
- if (!viteServer) throw new Error(`vite.createServer() not called!`);
- const filePath = new URL(`./${route.component}`, astroConfig.projectRoot);
- const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
- validateGetStaticPathsModule(mod);
- const rss = generateRssFunction(astroConfig.buildOptions.site, route);
- const staticPaths: GetStaticPathsResult = (await mod.getStaticPaths!({ paginate: generatePaginateFunction(route), rss: rss.generator })).flat();
- routeCache[route.component] = staticPaths;
- validateGetStaticPathsResult(staticPaths, logging);
- return {
- paths: staticPaths.map((staticPath) => staticPath.params && route.generate(staticPath.params)).filter(Boolean),
- rss: rss.rss,
- };
+ const { astroConfig, logging, routeCache, viteServer } = opts;
+ if (!viteServer) throw new Error(`vite.createServer() not called!`);
+ const filePath = new URL(`./${route.component}`, astroConfig.projectRoot);
+ const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
+ validateGetStaticPathsModule(mod);
+ const rss = generateRssFunction(astroConfig.buildOptions.site, route);
+ const staticPaths: GetStaticPathsResult = (await mod.getStaticPaths!({ paginate: generatePaginateFunction(route), rss: rss.generator })).flat();
+ routeCache[route.component] = staticPaths;
+ validateGetStaticPathsResult(staticPaths, logging);
+ return {
+ paths: staticPaths.map((staticPath) => staticPath.params && route.generate(staticPath.params)).filter(Boolean),
+ rss: rss.rss,
+ };
}
diff --git a/packages/astro/src/core/build/scan-based-build.ts b/packages/astro/src/core/build/scan-based-build.ts
index 7bb787758..3a295c0db 100644
--- a/packages/astro/src/core/build/scan-based-build.ts
+++ b/packages/astro/src/core/build/scan-based-build.ts
@@ -11,58 +11,58 @@ import { rollupPluginAstroBuildHTML } from '../../vite-plugin-build-html/index.j
import { rollupPluginAstroBuildCSS } from '../../vite-plugin-build-css/index.js';
export interface ScanBasedBuildOptions {
- allPages: AllPagesData;
- astroConfig: AstroConfig;
- logging: LogOptions;
- origin: string;
- pageNames: string[];
- routeCache: RouteCache;
- viteConfig: ViteConfigWithSSR;
- viteServer: ViteDevServer;
+ allPages: AllPagesData;
+ astroConfig: AstroConfig;
+ logging: LogOptions;
+ origin: string;
+ pageNames: string[];
+ routeCache: RouteCache;
+ viteConfig: ViteConfigWithSSR;
+ viteServer: ViteDevServer;
}
export async function build(opts: ScanBasedBuildOptions) {
- const { allPages, astroConfig, logging, origin, pageNames, routeCache, viteConfig, viteServer } = opts;
+ const { allPages, astroConfig, logging, origin, pageNames, routeCache, viteConfig, viteServer } = opts;
- // Internal maps used to coordinate the HTML and CSS plugins.
- const internals = createBuildInternals();
+ // Internal maps used to coordinate the HTML and CSS plugins.
+ const internals = createBuildInternals();
- return await vite.build({
- logLevel: 'error',
- mode: 'production',
- build: {
- emptyOutDir: true,
- minify: 'esbuild', // significantly faster than "terser" but may produce slightly-bigger bundles
- outDir: fileURLToPath(astroConfig.dist),
- rollupOptions: {
- // The `input` will be populated in the build rollup plugin.
- input: [],
- output: {
- format: 'esm',
- },
- },
- target: 'es2020', // must match an esbuild target
- },
- plugins: [
- rollupPluginAstroBuildHTML({
- astroConfig,
- internals,
- logging,
- origin,
- allPages,
- pageNames,
- routeCache,
- viteServer,
- }),
- rollupPluginAstroBuildCSS({
- internals,
- }),
- ...(viteConfig.plugins || []),
- ],
- publicDir: viteConfig.publicDir,
- root: viteConfig.root,
- envPrefix: 'PUBLIC_',
- server: viteConfig.server,
- base: astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : '/',
- });
+ return await vite.build({
+ logLevel: 'error',
+ mode: 'production',
+ build: {
+ emptyOutDir: true,
+ minify: 'esbuild', // significantly faster than "terser" but may produce slightly-bigger bundles
+ outDir: fileURLToPath(astroConfig.dist),
+ rollupOptions: {
+ // The `input` will be populated in the build rollup plugin.
+ input: [],
+ output: {
+ format: 'esm',
+ },
+ },
+ target: 'es2020', // must match an esbuild target
+ },
+ plugins: [
+ rollupPluginAstroBuildHTML({
+ astroConfig,
+ internals,
+ logging,
+ origin,
+ allPages,
+ pageNames,
+ routeCache,
+ viteServer,
+ }),
+ rollupPluginAstroBuildCSS({
+ internals,
+ }),
+ ...(viteConfig.plugins || []),
+ ],
+ publicDir: viteConfig.publicDir,
+ root: viteConfig.root,
+ envPrefix: 'PUBLIC_',
+ server: viteConfig.server,
+ base: astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : '/',
+ });
}
diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts
index a6ed89e3c..191cdb987 100644
--- a/packages/astro/src/core/build/static-build.ts
+++ b/packages/astro/src/core/build/static-build.ts
@@ -17,179 +17,179 @@ import { rollupPluginAstroBuildCSS } from '../../vite-plugin-build-css/index.js'
import { renderComponent, getParamsAndProps } from '../ssr/index.js';
export interface StaticBuildOptions {
- allPages: AllPagesData;
- astroConfig: AstroConfig;
- logging: LogOptions;
- origin: string;
- routeCache: RouteCache;
- viteConfig: ViteConfigWithSSR;
+ allPages: AllPagesData;
+ astroConfig: AstroConfig;
+ logging: LogOptions;
+ origin: string;
+ routeCache: RouteCache;
+ viteConfig: ViteConfigWithSSR;
}
export async function staticBuild(opts: StaticBuildOptions) {
- const { allPages, astroConfig } = opts;
+ const { allPages, astroConfig } = opts;
- // The JavaScript entrypoints.
- const jsInput: Set<string> = new Set();
+ // The JavaScript entrypoints.
+ const jsInput: Set<string> = new Set();
- // A map of each page .astro file, to the PageBuildData which contains information
- // about that page, such as its paths.
- const facadeIdToPageDataMap = new Map<string, PageBuildData>();
+ // A map of each page .astro file, to the PageBuildData which contains information
+ // about that page, such as its paths.
+ const facadeIdToPageDataMap = new Map<string, PageBuildData>();
- for (const [component, pageData] of Object.entries(allPages)) {
- const [renderers, mod] = pageData.preload;
+ for (const [component, pageData] of Object.entries(allPages)) {
+ const [renderers, mod] = pageData.preload;
- // Hydrated components are statically identified.
- for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
- // Note that this part is not yet implemented in the static build.
- //jsInput.add(path);
- }
+ // Hydrated components are statically identified.
+ for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
+ // Note that this part is not yet implemented in the static build.
+ //jsInput.add(path);
+ }
- let astroModuleId = new URL('./' + component, astroConfig.projectRoot).pathname;
- jsInput.add(astroModuleId);
- facadeIdToPageDataMap.set(astroModuleId, pageData);
- }
+ let astroModuleId = new URL('./' + component, astroConfig.projectRoot).pathname;
+ jsInput.add(astroModuleId);
+ facadeIdToPageDataMap.set(astroModuleId, pageData);
+ }
- // Build internals needed by the CSS plugin
- const internals = createBuildInternals();
+ // Build internals needed by the CSS plugin
+ const internals = createBuildInternals();
- // Perform the SSR build
- const result = (await ssrBuild(opts, internals, jsInput)) as RollupOutput;
+ // Perform the SSR build
+ const result = (await ssrBuild(opts, internals, jsInput)) as RollupOutput;
- // Generate each of the pages.
- await generatePages(result, opts, internals, facadeIdToPageDataMap);
+ // Generate each of the pages.
+ await generatePages(result, opts, internals, facadeIdToPageDataMap);
}
async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
- const { astroConfig, viteConfig } = opts;
-
- return await vite.build({
- logLevel: 'error',
- mode: 'production',
- build: {
- emptyOutDir: true,
- minify: false, // 'esbuild', // significantly faster than "terser" but may produce slightly-bigger bundles
- outDir: fileURLToPath(astroConfig.dist),
- ssr: true,
- rollupOptions: {
- input: Array.from(input),
- output: {
- format: 'esm',
- },
- },
- target: 'es2020', // must match an esbuild target
- },
- plugins: [
- vitePluginNewBuild(),
- rollupPluginAstroBuildCSS({
- internals,
- }),
- ...(viteConfig.plugins || []),
- ],
- publicDir: viteConfig.publicDir,
- root: viteConfig.root,
- envPrefix: 'PUBLIC_',
- server: viteConfig.server,
- base: astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : '/',
- });
+ const { astroConfig, viteConfig } = opts;
+
+ return await vite.build({
+ logLevel: 'error',
+ mode: 'production',
+ build: {
+ emptyOutDir: true,
+ minify: false, // 'esbuild', // significantly faster than "terser" but may produce slightly-bigger bundles
+ outDir: fileURLToPath(astroConfig.dist),
+ ssr: true,
+ rollupOptions: {
+ input: Array.from(input),
+ output: {
+ format: 'esm',
+ },
+ },
+ target: 'es2020', // must match an esbuild target
+ },
+ plugins: [
+ vitePluginNewBuild(),
+ rollupPluginAstroBuildCSS({
+ internals,
+ }),
+ ...(viteConfig.plugins || []),
+ ],
+ publicDir: viteConfig.publicDir,
+ root: viteConfig.root,
+ envPrefix: 'PUBLIC_',
+ server: viteConfig.server,
+ base: astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : '/',
+ });
}
async function generatePages(result: RollupOutput, opts: StaticBuildOptions, internals: BuildInternals, facadeIdToPageDataMap: Map<string, PageBuildData>) {
- debug(opts.logging, 'generate', 'End build step, now generating');
- const generationPromises = [];
- for (let output of result.output) {
- if (output.type === 'chunk' && output.facadeModuleId && output.facadeModuleId.endsWith('.astro')) {
- generationPromises.push(generatePage(output, opts, internals, facadeIdToPageDataMap));
- }
- }
- await Promise.all(generationPromises);
+ debug(opts.logging, 'generate', 'End build step, now generating');
+ const generationPromises = [];
+ for (let output of result.output) {
+ if (output.type === 'chunk' && output.facadeModuleId && output.facadeModuleId.endsWith('.astro')) {
+ generationPromises.push(generatePage(output, opts, internals, facadeIdToPageDataMap));
+ }
+ }
+ await Promise.all(generationPromises);
}
async function generatePage(output: OutputChunk, opts: StaticBuildOptions, internals: BuildInternals, facadeIdToPageDataMap: Map<string, PageBuildData>) {
- const { astroConfig } = opts;
-
- let url = new URL('./' + output.fileName, astroConfig.dist);
- const facadeId: string = output.facadeModuleId as string;
- let pageData =
- facadeIdToPageDataMap.get(facadeId) ||
- // Check with a leading `/` because on Windows it doesn't have one.
- facadeIdToPageDataMap.get('/' + facadeId);
-
- if (!pageData) {
- throw new Error(`Unable to find a PageBuildData for the Astro page: ${facadeId}. There are the PageBuilDatas we have ${Array.from(facadeIdToPageDataMap.keys()).join(', ')}`);
- }
-
- let linkIds = internals.facadeIdToAssetsMap.get(facadeId) || [];
- let compiledModule = await import(url.toString());
- let Component = compiledModule.default;
-
- const generationOptions: Readonly<GeneratePathOptions> = {
- pageData,
- linkIds,
- Component,
- };
-
- const renderPromises = pageData.paths.map((path) => {
- return generatePath(path, opts, generationOptions);
- });
- return await Promise.all(renderPromises);
+ const { astroConfig } = opts;
+
+ let url = new URL('./' + output.fileName, astroConfig.dist);
+ const facadeId: string = output.facadeModuleId as string;
+ let pageData =
+ facadeIdToPageDataMap.get(facadeId) ||
+ // Check with a leading `/` because on Windows it doesn't have one.
+ facadeIdToPageDataMap.get('/' + facadeId);
+
+ if (!pageData) {
+ throw new Error(`Unable to find a PageBuildData for the Astro page: ${facadeId}. There are the PageBuilDatas we have ${Array.from(facadeIdToPageDataMap.keys()).join(', ')}`);
+ }
+
+ let linkIds = internals.facadeIdToAssetsMap.get(facadeId) || [];
+ let compiledModule = await import(url.toString());
+ let Component = compiledModule.default;
+
+ const generationOptions: Readonly<GeneratePathOptions> = {
+ pageData,
+ linkIds,
+ Component,
+ };
+
+ const renderPromises = pageData.paths.map((path) => {
+ return generatePath(path, opts, generationOptions);
+ });
+ return await Promise.all(renderPromises);
}
interface GeneratePathOptions {
- pageData: PageBuildData;
- linkIds: string[];
- Component: AstroComponentFactory;
+ pageData: PageBuildData;
+ linkIds: string[];
+ Component: AstroComponentFactory;
}
async function generatePath(path: string, opts: StaticBuildOptions, gopts: GeneratePathOptions) {
- const { astroConfig, logging, origin, routeCache } = opts;
- const { Component, linkIds, pageData } = gopts;
-
- const [renderers, mod] = pageData.preload;
-
- try {
- const [params, pageProps] = await getParamsAndProps({
- route: pageData.route,
- routeCache,
- logging,
- pathname: path,
- mod,
- });
-
- info(logging, 'generate', `Generating: ${path}`);
-
- const html = await renderComponent(renderers, Component, astroConfig, path, origin, params, pageProps, linkIds);
- const outFolder = new URL('.' + path + '/', astroConfig.dist);
- const outFile = new URL('./index.html', outFolder);
- await fs.promises.mkdir(outFolder, { recursive: true });
- await fs.promises.writeFile(outFile, html, 'utf-8');
- } catch (err) {
- error(opts.logging, 'build', `Error rendering:`, err);
- }
+ const { astroConfig, logging, origin, routeCache } = opts;
+ const { Component, linkIds, pageData } = gopts;
+
+ const [renderers, mod] = pageData.preload;
+
+ try {
+ const [params, pageProps] = await getParamsAndProps({
+ route: pageData.route,
+ routeCache,
+ logging,
+ pathname: path,
+ mod,
+ });
+
+ info(logging, 'generate', `Generating: ${path}`);
+
+ const html = await renderComponent(renderers, Component, astroConfig, path, origin, params, pageProps, linkIds);
+ const outFolder = new URL('.' + path + '/', astroConfig.dist);
+ const outFile = new URL('./index.html', outFolder);
+ await fs.promises.mkdir(outFolder, { recursive: true });
+ await fs.promises.writeFile(outFile, html, 'utf-8');
+ } catch (err) {
+ error(opts.logging, 'build', `Error rendering:`, err);
+ }
}
export function vitePluginNewBuild(): VitePlugin {
- return {
- name: '@astro/rollup-plugin-new-build',
-
- configResolved(resolvedConfig) {
- // Delete this hook because it causes assets not to be built
- const plugins = resolvedConfig.plugins as VitePlugin[];
- const viteAsset = plugins.find((p) => p.name === 'vite:asset');
- if (viteAsset) {
- delete viteAsset.generateBundle;
- }
- },
-
- outputOptions(outputOptions) {
- Object.assign(outputOptions, {
- entryFileNames(_chunk: PreRenderedChunk) {
- return 'assets/[name].[hash].mjs';
- },
- chunkFileNames(_chunk: PreRenderedChunk) {
- return 'assets/[name].[hash].mjs';
- },
- });
- return outputOptions;
- },
- };
+ return {
+ name: '@astro/rollup-plugin-new-build',
+
+ configResolved(resolvedConfig) {
+ // Delete this hook because it causes assets not to be built
+ const plugins = resolvedConfig.plugins as VitePlugin[];
+ const viteAsset = plugins.find((p) => p.name === 'vite:asset');
+ if (viteAsset) {
+ delete viteAsset.generateBundle;
+ }
+ },
+
+ outputOptions(outputOptions) {
+ Object.assign(outputOptions, {
+ entryFileNames(_chunk: PreRenderedChunk) {
+ return 'assets/[name].[hash].mjs';
+ },
+ chunkFileNames(_chunk: PreRenderedChunk) {
+ return 'assets/[name].[hash].mjs';
+ },
+ });
+ return outputOptions;
+ },
+ };
}
diff --git a/packages/astro/src/core/build/types.d.ts b/packages/astro/src/core/build/types.d.ts
index 198b94846..2606075e2 100644
--- a/packages/astro/src/core/build/types.d.ts
+++ b/packages/astro/src/core/build/types.d.ts
@@ -2,8 +2,8 @@ import type { ComponentPreload } from '../ssr/index';
import type { RouteData } from '../../@types/astro';
export interface PageBuildData {
- paths: string[];
- preload: ComponentPreload;
- route: RouteData;
+ paths: string[];
+ preload: ComponentPreload;
+ route: RouteData;
}
export type AllPagesData = Record<string, PageBuildData>;
diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts
index 38a4e8ea2..1f8827023 100644
--- a/packages/astro/src/core/config.ts
+++ b/packages/astro/src/core/config.ts
@@ -10,137 +10,137 @@ import loadTypeScript from '@proload/plugin-tsm';
load.use([loadTypeScript]);
export const AstroConfigSchema = z.object({
- projectRoot: z
- .string()
- .optional()
- .default('.')
- .transform((val) => new URL(val)),
- src: z
- .string()
- .optional()
- .default('./src')
- .transform((val) => new URL(val)),
- pages: z
- .string()
- .optional()
- .default('./src/pages')
- .transform((val) => new URL(val)),
- layouts: z
- .string()
- .optional()
- .default('./src/layouts')
- .transform((val) => new URL(val)),
- public: z
- .string()
- .optional()
- .default('./public')
- .transform((val) => new URL(val)),
- dist: z
- .string()
- .optional()
- .default('./dist')
- .transform((val) => new URL(val)),
- renderers: z.array(z.string()).optional().default(['@astrojs/renderer-svelte', '@astrojs/renderer-vue', '@astrojs/renderer-react', '@astrojs/renderer-preact']),
- markdownOptions: z
- .object({
- footnotes: z.boolean().optional(),
- gfm: z.boolean().optional(),
- render: z.any().optional().default(['@astrojs/markdown-remark', {}]),
- })
- .strict()
- .optional()
- .default({}),
- buildOptions: z
- .object({
- site: z.string().optional(),
- sitemap: z.boolean().optional().default(true),
- pageUrlFormat: z
- .union([z.literal('file'), z.literal('directory')])
- .optional()
- .default('directory'),
- experimentalStaticBuild: z.boolean().optional().default(false),
- })
- .optional()
- .default({}),
- devOptions: z
- .object({
- hostname: z.string().optional().default('localhost'),
- port: z.number().optional().default(3000),
- trailingSlash: z
- .union([z.literal('always'), z.literal('never'), z.literal('ignore')])
- .optional()
- .default('ignore'),
- })
- .optional()
- .default({}),
- vite: z.any().optional().default({}), // TODO: we don’t need validation, but can we get better type inference?
+ projectRoot: z
+ .string()
+ .optional()
+ .default('.')
+ .transform((val) => new URL(val)),
+ src: z
+ .string()
+ .optional()
+ .default('./src')
+ .transform((val) => new URL(val)),
+ pages: z
+ .string()
+ .optional()
+ .default('./src/pages')
+ .transform((val) => new URL(val)),
+ layouts: z
+ .string()
+ .optional()
+ .default('./src/layouts')
+ .transform((val) => new URL(val)),
+ public: z
+ .string()
+ .optional()
+ .default('./public')
+ .transform((val) => new URL(val)),
+ dist: z
+ .string()
+ .optional()
+ .default('./dist')
+ .transform((val) => new URL(val)),
+ renderers: z.array(z.string()).optional().default(['@astrojs/renderer-svelte', '@astrojs/renderer-vue', '@astrojs/renderer-react', '@astrojs/renderer-preact']),
+ markdownOptions: z
+ .object({
+ footnotes: z.boolean().optional(),
+ gfm: z.boolean().optional(),
+ render: z.any().optional().default(['@astrojs/markdown-remark', {}]),
+ })
+ .strict()
+ .optional()
+ .default({}),
+ buildOptions: z
+ .object({
+ site: z.string().optional(),
+ sitemap: z.boolean().optional().default(true),
+ pageUrlFormat: z
+ .union([z.literal('file'), z.literal('directory')])
+ .optional()
+ .default('directory'),
+ experimentalStaticBuild: z.boolean().optional().default(false),
+ })
+ .optional()
+ .default({}),
+ devOptions: z
+ .object({
+ hostname: z.string().optional().default('localhost'),
+ port: z.number().optional().default(3000),
+ trailingSlash: z
+ .union([z.literal('always'), z.literal('never'), z.literal('ignore')])
+ .optional()
+ .default('ignore'),
+ })
+ .optional()
+ .default({}),
+ vite: z.any().optional().default({}), // TODO: we don’t need validation, but can we get better type inference?
});
/** Turn raw config values into normalized values */
export async function validateConfig(userConfig: any, root: string): Promise<AstroConfig> {
- const fileProtocolRoot = pathToFileURL(root + path.sep);
- // We need to extend the global schema to add transforms that are relative to root.
- // This is type checked against the global schema to make sure we still match.
- const AstroConfigRelativeSchema = AstroConfigSchema.extend({
- projectRoot: z
- .string()
- .default('.')
- .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
- src: z
- .string()
- .default('./src')
- .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
- pages: z
- .string()
- .default('./src/pages')
- .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
- layouts: z
- .string()
- .default('./src/layouts')
- .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
- public: z
- .string()
- .default('./public')
- .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
- dist: z
- .string()
- .default('./dist')
- .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
- });
- return AstroConfigRelativeSchema.parseAsync(userConfig);
+ const fileProtocolRoot = pathToFileURL(root + path.sep);
+ // We need to extend the global schema to add transforms that are relative to root.
+ // This is type checked against the global schema to make sure we still match.
+ const AstroConfigRelativeSchema = AstroConfigSchema.extend({
+ projectRoot: z
+ .string()
+ .default('.')
+ .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
+ src: z
+ .string()
+ .default('./src')
+ .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
+ pages: z
+ .string()
+ .default('./src/pages')
+ .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
+ layouts: z
+ .string()
+ .default('./src/layouts')
+ .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
+ public: z
+ .string()
+ .default('./public')
+ .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
+ dist: z
+ .string()
+ .default('./dist')
+ .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
+ });
+ return AstroConfigRelativeSchema.parseAsync(userConfig);
}
/** Adds '/' to end of string but doesn’t double-up */
function addTrailingSlash(str: string): string {
- return str.replace(/\/*$/, '/');
+ return str.replace(/\/*$/, '/');
}
interface LoadConfigOptions {
- cwd?: string;
- filename?: string;
+ cwd?: string;
+ filename?: string;
}
/** Attempt to load an `astro.config.mjs` file */
export async function loadConfig(options: LoadConfigOptions): Promise<AstroConfig> {
- const root = options.cwd ? path.resolve(options.cwd) : process.cwd();
- let userConfig: AstroUserConfig = {};
- let userConfigPath: string | undefined;
+ const root = options.cwd ? path.resolve(options.cwd) : process.cwd();
+ let userConfig: AstroUserConfig = {};
+ let userConfigPath: string | undefined;
- if (options.filename) {
- userConfigPath = /^\.*\//.test(options.filename) ? options.filename : `./${options.filename}`;
- userConfigPath = fileURLToPath(new URL(userConfigPath, `file://${root}/`));
- }
- // Automatically load config file using Proload
- // If `userConfigPath` is `undefined`, Proload will search for `astro.config.[cm]?[jt]s`
- const config = await load('astro', { mustExist: false, cwd: root, filePath: userConfigPath });
- if (config) {
- userConfig = config.value;
- }
- // normalize, validate, and return
- return validateConfig(userConfig, root);
+ if (options.filename) {
+ userConfigPath = /^\.*\//.test(options.filename) ? options.filename : `./${options.filename}`;
+ userConfigPath = fileURLToPath(new URL(userConfigPath, `file://${root}/`));
+ }
+ // Automatically load config file using Proload
+ // If `userConfigPath` is `undefined`, Proload will search for `astro.config.[cm]?[jt]s`
+ const config = await load('astro', { mustExist: false, cwd: root, filePath: userConfigPath });
+ if (config) {
+ userConfig = config.value;
+ }
+ // normalize, validate, and return
+ return validateConfig(userConfig, root);
}
export function formatConfigError(err: z.ZodError) {
- const errorList = err.issues.map((issue) => ` ! ${colors.bold(issue.path.join('.'))} ${colors.red(issue.message + '.')}`);
- return `${colors.red('[config]')} Astro found issue(s) with your configuration:\n${errorList.join('\n')}`;
+ const errorList = err.issues.map((issue) => ` ! ${colors.bold(issue.path.join('.'))} ${colors.red(issue.message + '.')}`);
+ return `${colors.red('[config]')} Astro found issue(s) with your configuration:\n${errorList.join('\n')}`;
}
diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts
index 2001afc54..9e6dc419c 100644
--- a/packages/astro/src/core/create-vite.ts
+++ b/packages/astro/src/core/create-vite.ts
@@ -14,85 +14,85 @@ import { resolveDependency } from './util.js';
// Some packages are just external, and that’s the way it goes.
const ALWAYS_EXTERNAL = new Set([
- ...builtinModules.map((name) => `node:${name}`),
- '@sveltejs/vite-plugin-svelte',
- 'estree-util-value-to-estree',
- 'micromark-util-events-to-acorn',
- 'node-fetch',
- 'prismjs',
- 'shiki',
- 'shorthash',
- 'unified',
- 'whatwg-url',
+ ...builtinModules.map((name) => `node:${name}`),
+ '@sveltejs/vite-plugin-svelte',
+ 'estree-util-value-to-estree',
+ 'micromark-util-events-to-acorn',
+ 'node-fetch',
+ 'prismjs',
+ 'shiki',
+ 'shorthash',
+ 'unified',
+ 'whatwg-url',
]);
const ALWAYS_NOEXTERNAL = new Set([
- 'astro', // This is only because Vite's native ESM doesn't resolve "exports" correctly.
+ 'astro', // This is only because Vite's native ESM doesn't resolve "exports" correctly.
]);
// note: ssr is still an experimental API hence the type omission
export type ViteConfigWithSSR = vite.InlineConfig & { ssr?: { external?: string[]; noExternal?: string[] } };
interface CreateViteOptions {
- astroConfig: AstroConfig;
- devServer?: AstroDevServer;
- logging: LogOptions;
+ astroConfig: AstroConfig;
+ devServer?: AstroDevServer;
+ logging: LogOptions;
}
/** Return a common starting point for all Vite actions */
export async function createVite(inlineConfig: ViteConfigWithSSR, { astroConfig, logging, devServer }: CreateViteOptions): Promise<ViteConfigWithSSR> {
- // First, start with the Vite configuration that Astro core needs
- let viteConfig: ViteConfigWithSSR = {
- cacheDir: fileURLToPath(new URL('./node_modules/.vite/', astroConfig.projectRoot)), // using local caches allows Astro to be used in monorepos, etc.
- clearScreen: false, // we want to control the output, not Vite
- logLevel: 'error', // log errors only
- optimizeDeps: {
- entries: ['src/**/*'], // Try and scan a user’s project (won’t catch everything),
- },
- plugins: [
- configAliasVitePlugin({ config: astroConfig }),
- astroVitePlugin({ config: astroConfig, devServer }),
- markdownVitePlugin({ config: astroConfig, devServer }),
- jsxVitePlugin({ config: astroConfig, logging }),
- astroPostprocessVitePlugin({ config: astroConfig, devServer }),
- ],
- publicDir: fileURLToPath(astroConfig.public),
- root: fileURLToPath(astroConfig.projectRoot),
- envPrefix: 'PUBLIC_',
- server: {
- force: true, // force dependency rebuild (TODO: enabled only while next is unstable; eventually only call in "production" mode?)
- hmr: process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'production' ? false : undefined, // disable HMR for test
- // handle Vite URLs
- proxy: {
- // add proxies here
- },
- },
- // Note: SSR API is in beta (https://vitejs.dev/guide/ssr.html)
- ssr: {
- external: [...ALWAYS_EXTERNAL],
- noExternal: [...ALWAYS_NOEXTERNAL],
- },
- };
+ // First, start with the Vite configuration that Astro core needs
+ let viteConfig: ViteConfigWithSSR = {
+ cacheDir: fileURLToPath(new URL('./node_modules/.vite/', astroConfig.projectRoot)), // using local caches allows Astro to be used in monorepos, etc.
+ clearScreen: false, // we want to control the output, not Vite
+ logLevel: 'error', // log errors only
+ optimizeDeps: {
+ entries: ['src/**/*'], // Try and scan a user’s project (won’t catch everything),
+ },
+ plugins: [
+ configAliasVitePlugin({ config: astroConfig }),
+ astroVitePlugin({ config: astroConfig, devServer }),
+ markdownVitePlugin({ config: astroConfig, devServer }),
+ jsxVitePlugin({ config: astroConfig, logging }),
+ astroPostprocessVitePlugin({ config: astroConfig, devServer }),
+ ],
+ publicDir: fileURLToPath(astroConfig.public),
+ root: fileURLToPath(astroConfig.projectRoot),
+ envPrefix: 'PUBLIC_',
+ server: {
+ force: true, // force dependency rebuild (TODO: enabled only while next is unstable; eventually only call in "production" mode?)
+ hmr: process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'production' ? false : undefined, // disable HMR for test
+ // handle Vite URLs
+ proxy: {
+ // add proxies here
+ },
+ },
+ // Note: SSR API is in beta (https://vitejs.dev/guide/ssr.html)
+ ssr: {
+ external: [...ALWAYS_EXTERNAL],
+ noExternal: [...ALWAYS_NOEXTERNAL],
+ },
+ };
- // Add in Astro renderers, which will extend the base config
- for (const name of astroConfig.renderers) {
- try {
- const { default: renderer } = await import(resolveDependency(name, astroConfig));
- if (!renderer) continue;
- // if a renderer provides viteConfig(), call it and pass in results
- if (renderer.viteConfig) {
- if (typeof renderer.viteConfig !== 'function') {
- throw new Error(`${name}: viteConfig(options) must be a function! Got ${typeof renderer.viteConfig}.`);
- }
- const rendererConfig = await renderer.viteConfig({ mode: inlineConfig.mode, command: inlineConfig.mode === 'production' ? 'build' : 'serve' }); // is this command true?
- viteConfig = vite.mergeConfig(viteConfig, rendererConfig) as vite.InlineConfig;
- }
- } catch (err) {
- throw new Error(`${name}: ${err}`);
- }
- }
+ // Add in Astro renderers, which will extend the base config
+ for (const name of astroConfig.renderers) {
+ try {
+ const { default: renderer } = await import(resolveDependency(name, astroConfig));
+ if (!renderer) continue;
+ // if a renderer provides viteConfig(), call it and pass in results
+ if (renderer.viteConfig) {
+ if (typeof renderer.viteConfig !== 'function') {
+ throw new Error(`${name}: viteConfig(options) must be a function! Got ${typeof renderer.viteConfig}.`);
+ }
+ const rendererConfig = await renderer.viteConfig({ mode: inlineConfig.mode, command: inlineConfig.mode === 'production' ? 'build' : 'serve' }); // is this command true?
+ viteConfig = vite.mergeConfig(viteConfig, rendererConfig) as vite.InlineConfig;
+ }
+ } catch (err) {
+ throw new Error(`${name}: ${err}`);
+ }
+ }
- // Add in user settings last, followed by any Vite configuration passed in from the parent function (overrides)
- viteConfig = vite.mergeConfig(viteConfig, astroConfig.vite || {}); // merge in Vite config from astro.config.mjs
- viteConfig = vite.mergeConfig(viteConfig, inlineConfig); // merge in inline Vite config
- return viteConfig;
+ // Add in user settings last, followed by any Vite configuration passed in from the parent function (overrides)
+ viteConfig = vite.mergeConfig(viteConfig, astroConfig.vite || {}); // merge in Vite config from astro.config.mjs
+ viteConfig = vite.mergeConfig(viteConfig, inlineConfig); // merge in inline Vite config
+ return viteConfig;
}
diff --git a/packages/astro/src/core/dev/index.ts b/packages/astro/src/core/dev/index.ts
index bf007fb5d..a424c43f8 100644
--- a/packages/astro/src/core/dev/index.ts
+++ b/packages/astro/src/core/dev/index.ts
@@ -24,375 +24,375 @@ import notFoundTemplate, { subpathNotUsedTemplate } from './template/4xx.js';
import serverErrorTemplate from './template/5xx.js';
export interface DevOptions {
- logging: LogOptions;
+ logging: LogOptions;
}
export interface DevServer {
- hostname: string;
- port: number;
- server: connect.Server;
- stop(): Promise<void>;
+ hostname: string;
+ port: number;
+ server: connect.Server;
+ stop(): Promise<void>;
}
/** `astro dev` */
export default async function dev(config: AstroConfig, options: DevOptions = { logging: defaultLogOptions }): Promise<DevServer> {
- // polyfill WebAPIs to globalThis for Node v12, Node v14, and Node v16
- polyfill(globalThis, {
- exclude: 'window document',
- });
-
- // start dev server
- const server = new AstroDevServer(config, options);
- await server.start();
-
- // attempt shutdown
- process.on('SIGTERM', () => server.stop());
- return {
- hostname: server.hostname,
- port: server.port,
- server: server.app,
- stop: () => server.stop(),
- };
+ // polyfill WebAPIs to globalThis for Node v12, Node v14, and Node v16
+ polyfill(globalThis, {
+ exclude: 'window document',
+ });
+
+ // start dev server
+ const server = new AstroDevServer(config, options);
+ await server.start();
+
+ // attempt shutdown
+ process.on('SIGTERM', () => server.stop());
+ return {
+ hostname: server.hostname,
+ port: server.port,
+ server: server.app,
+ stop: () => server.stop(),
+ };
}
/** Dev server */
export class AstroDevServer {
- app: connect.Server = connect();
- config: AstroConfig;
- devRoot: string;
- hostname: string;
- httpServer: http.Server | undefined;
- logging: LogOptions;
- manifest: ManifestData;
- mostRecentRoute?: RouteData;
- origin: string;
- port: number;
- routeCache: RouteCache = {};
- site: URL | undefined;
- url: URL;
- viteServer: vite.ViteDevServer | undefined;
-
- constructor(config: AstroConfig, options: DevOptions) {
- this.config = config;
- this.hostname = config.devOptions.hostname || 'localhost';
- this.logging = options.logging;
- this.port = config.devOptions.port;
- this.origin = `http://${this.hostname}:${this.port}`;
- this.site = config.buildOptions.site ? new URL(config.buildOptions.site) : undefined;
- this.devRoot = this.site ? this.site.pathname : '/';
- this.url = new URL(this.devRoot, this.origin);
- this.manifest = createRouteManifest({ config }, this.logging);
- }
-
- async start() {
- const devStart = performance.now();
-
- // Setup the dev server and connect it to Vite (via middleware)
- this.viteServer = await this.createViteServer();
- this.app.use(this.viteServer.middlewares);
- this.app.use((req, res, next) => this.handleRequest(req, res, next));
- this.app.use((req, res, next) => this.renderError(req, res, next));
-
- // Listen on port (and retry if taken)
- await this.listen(devStart);
- }
-
- async stop() {
- if (this.viteServer) {
- await this.viteServer.close();
- }
- if (this.httpServer) {
- await promisify(this.httpServer.close.bind(this.httpServer))();
- }
- }
-
- public async handleHotUpdate({ file, modules }: HmrContext): Promise<void | ModuleNode[]> {
- const { viteServer } = this;
- if (!viteServer) throw new Error(`AstroDevServer.start() not called`);
-
- for (const module of modules) {
- viteServer.moduleGraph.invalidateModule(module);
- }
-
- const route = this.mostRecentRoute;
- const [pathname, search = undefined] = (route?.pathname ?? '/').split('?');
-
- if (!route) {
- viteServer.ws.send({
- type: 'full-reload',
- });
- return [];
- }
-
- try {
- const filePath = new URL(`./${route.component}`, this.config.projectRoot);
- // try to update the most recent route
- const html = await ssr({
- astroConfig: this.config,
- filePath,
- logging: this.logging,
- mode: 'development',
- origin: this.origin,
- pathname,
- route,
- routeCache: this.routeCache,
- viteServer,
- });
-
- // collect style tags to be reloaded (needed for Tailwind HMR, etc.)
- let invalidatedModules: vite.ModuleNode[] = [];
- await Promise.all(
- collectResources(html)
- .filter(({ href }) => {
- if (!href) return false;
- const ext = path.extname(href);
- return STYLE_EXTENSIONS.has(ext);
- })
- .map(async ({ href }) => {
- const viteModule =
- viteServer.moduleGraph.getModuleById(`${href}?direct`) ||
- (await viteServer.moduleGraph.getModuleByUrl(`${href}?direct`)) ||
- viteServer.moduleGraph.getModuleById(href) ||
- (await viteServer.moduleGraph.getModuleByUrl(href));
- if (viteModule) {
- invalidatedModules.push(viteModule);
- viteServer.moduleGraph.invalidateModule(viteModule);
- }
- })
- );
-
- // TODO: log update
- viteServer.ws.send({
- type: 'custom',
- event: 'astro:reload',
- data: { html },
- });
-
- for (const viteModule of invalidatedModules) {
- // Note: from the time viteServer.moduleGraph.invalidateModule() is called above until now, CSS
- // is building in the background. For things like Tailwind, this can take some time. If the
- // CSS is still processing by the time HMR fires, we’ll end up with stale styles on the page.
- // TODO: fix this hack by figuring out how to add these styles to the { modules } above
- setTimeout(() => {
- viteServer.ws.send({
- type: 'update',
- updates: [
- {
- type: viteModule.type === 'js' ? 'js-update' : 'css-update',
- path: viteModule.id || viteModule.file || viteModule.url,
- acceptedPath: viteModule.url,
- timestamp: Date.now(),
- },
- ],
- });
- }, 150);
- }
-
- return [];
- } catch (e) {
- const err = e as Error;
- // eslint-disable-next-line
- console.error(err.stack);
- viteServer.ws.send({
- type: 'full-reload',
- });
- return [];
- }
- }
-
- /** Expose dev server to this.port */
- public listen(devStart: number): Promise<void> {
- let showedPortTakenMsg = false;
- return new Promise<void>((resolve, reject) => {
- const appListen = () => {
- this.httpServer = this.app.listen(this.port, this.hostname, () => {
- info(this.logging, 'astro', msg.devStart({ startupTime: performance.now() - devStart }));
- info(this.logging, 'astro', msg.devHost({ host: `http://${this.hostname}:${this.port}${this.devRoot}` }));
- resolve();
- });
- this.httpServer?.on('error', onError);
- };
-
- const onError = (err: NodeJS.ErrnoException) => {
- if (err.code && err.code === 'EADDRINUSE') {
- if (!showedPortTakenMsg) {
- info(this.logging, 'astro', msg.portInUse({ port: this.port }));
- showedPortTakenMsg = true; // only print this once
- }
- this.port++;
- return appListen(); // retry
- } else {
- error(this.logging, 'astro', err.stack);
- this.httpServer?.removeListener('error', onError);
- reject(err); // reject
- }
- };
-
- appListen();
- });
- }
-
- private async createViteServer() {
- const viteConfig = await createVite(
- vite.mergeConfig(
- {
- mode: 'development',
- server: {
- middlewareMode: 'ssr',
- host: this.hostname,
- },
- },
- this.config.vite || {}
- ),
- { astroConfig: this.config, logging: this.logging, devServer: this }
- );
- const viteServer = await vite.createServer(viteConfig);
-
- const pagesDirectory = fileURLToPath(this.config.pages);
- viteServer.watcher.on('add', (file) => {
- // Only rebuild routes if new file is a page.
- if (!file.startsWith(pagesDirectory)) {
- return;
- }
- this.routeCache = {};
- this.manifest = createRouteManifest({ config: this.config }, this.logging);
- });
- viteServer.watcher.on('unlink', (file) => {
- // Only rebuild routes if deleted file is a page.
- if (!file.startsWith(pagesDirectory)) {
- return;
- }
- this.routeCache = {};
- this.manifest = createRouteManifest({ config: this.config }, this.logging);
- });
- viteServer.watcher.on('change', () => {
- // No need to rebuild routes on file content changes.
- // However, we DO want to clear the cache in case
- // the change caused a getStaticPaths() return to change.
- this.routeCache = {};
- });
-
- return viteServer;
- }
-
- /** The primary router (runs before Vite, in case we need to modify or intercept anything) */
- private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse, next: NextFunction) {
- if (!this.viteServer) throw new Error(`AstroDevServer.start() not called`);
-
- let [pathname, search = undefined] = (req.url || '/').split('?'); // original request
- const reqStart = performance.now();
- let filePath: URL | undefined;
- try {
- let routePathname: string = pathname;
- // If using a subpath, ensure that the user has included the pathname
- // such as /blog in the URL.
- if (this.devRoot !== '/') {
- if (pathname.startsWith(this.devRoot)) {
- // This includes the subpath, so strip off the subpath so that
- // matchRoute finds this route.
- routePathname = pathname.substr(this.devRoot.length) || '';
- if (!routePathname.startsWith('/')) {
- routePathname = '/' + routePathname;
- }
- } else {
- // Not using the subpath, so forward to Vite's middleware
- next();
- return;
- }
- }
-
- const route = matchRoute(routePathname, this.manifest);
-
- // 404: continue to Vite
- if (!route) {
- // Send through, stripping off the `/blog/` part so that Vite matches it.
- const newPathname = routePathname.startsWith('/') ? routePathname : '/' + routePathname;
- req.url = newPathname;
- next();
- return;
- }
- // handle .astro and .md pages
- filePath = new URL(`./${route.component}`, this.config.projectRoot);
- const html = await ssr({
- astroConfig: this.config,
- filePath,
- logging: this.logging,
- mode: 'development',
- origin: this.origin,
- pathname,
- route,
- routeCache: this.routeCache,
- viteServer: this.viteServer,
- });
- this.mostRecentRoute = route;
- info(this.logging, 'astro', msg.req({ url: pathname, statusCode: 200, reqTime: performance.now() - reqStart }));
- res.writeHead(200, {
- 'Content-Type': mime.getType('.html') as string,
- 'Content-Length': Buffer.byteLength(html, 'utf8'),
- });
- res.write(html);
- res.end();
- } catch (err: any) {
- const statusCode = 500;
- await this.viteServer.moduleGraph.invalidateAll();
- this.viteServer.ws.send({ type: 'error', err });
- let html = serverErrorTemplate({
- statusCode,
- title: 'Internal Error',
- tabTitle: '500: Error',
- message: stripAnsi(err.message),
- url: err.url || undefined,
- stack: stripAnsi(err.stack),
- });
- html = await this.viteServer.transformIndexHtml(pathname, html, pathname);
- info(this.logging, 'astro', msg.req({ url: pathname, statusCode: 500, reqTime: performance.now() - reqStart }));
- res.writeHead(statusCode, {
- 'Content-Type': mime.getType('.html') as string,
- 'Content-Length': Buffer.byteLength(html, 'utf8'),
- });
- res.write(html);
- res.end();
- }
- }
-
- /** Render error page */
- private async renderError(req: http.IncomingMessage, res: http.ServerResponse, next: NextFunction) {
- if (!this.viteServer) throw new Error(`AstroDevServer.start() not called`);
-
- const pathname = req.url || '/';
- const reqStart = performance.now();
- let html = '';
- const statusCode = 404;
-
- // attempt to load user-given page
- const relPages = this.config.pages.href.replace(this.config.projectRoot.href, '');
- const userDefined404 = this.manifest.routes.find((route) => route.component === relPages + '404.astro');
- if (userDefined404) {
- html = await ssr({
- astroConfig: this.config,
- filePath: new URL(`./${userDefined404.component}`, this.config.projectRoot),
- logging: this.logging,
- mode: 'development',
- pathname: `/${userDefined404.component}`,
- origin: this.origin,
- routeCache: this.routeCache,
- viteServer: this.viteServer,
- });
- }
- // if not found, fall back to default template
- else {
- if (pathname === '/' && !pathname.startsWith(this.devRoot)) {
- html = subpathNotUsedTemplate(this.devRoot, pathname);
- } else {
- html = notFoundTemplate({ statusCode, title: 'Not found', tabTitle: '404: Not Found', pathname });
- }
- }
- info(this.logging, 'astro', msg.req({ url: pathname, statusCode, reqTime: performance.now() - reqStart }));
- res.writeHead(statusCode, {
- 'Content-Type': mime.getType('.html') as string,
- 'Content-Length': Buffer.byteLength(html, 'utf8'),
- });
- res.write(html);
- res.end();
- }
+ app: connect.Server = connect();
+ config: AstroConfig;
+ devRoot: string;
+ hostname: string;
+ httpServer: http.Server | undefined;
+ logging: LogOptions;
+ manifest: ManifestData;
+ mostRecentRoute?: RouteData;
+ origin: string;
+ port: number;
+ routeCache: RouteCache = {};
+ site: URL | undefined;
+ url: URL;
+ viteServer: vite.ViteDevServer | undefined;
+
+ constructor(config: AstroConfig, options: DevOptions) {
+ this.config = config;
+ this.hostname = config.devOptions.hostname || 'localhost';
+ this.logging = options.logging;
+ this.port = config.devOptions.port;
+ this.origin = `http://${this.hostname}:${this.port}`;
+ this.site = config.buildOptions.site ? new URL(config.buildOptions.site) : undefined;
+ this.devRoot = this.site ? this.site.pathname : '/';
+ this.url = new URL(this.devRoot, this.origin);
+ this.manifest = createRouteManifest({ config }, this.logging);
+ }
+
+ async start() {
+ const devStart = performance.now();
+
+ // Setup the dev server and connect it to Vite (via middleware)
+ this.viteServer = await this.createViteServer();
+ this.app.use(this.viteServer.middlewares);
+ this.app.use((req, res, next) => this.handleRequest(req, res, next));
+ this.app.use((req, res, next) => this.renderError(req, res, next));
+
+ // Listen on port (and retry if taken)
+ await this.listen(devStart);
+ }
+
+ async stop() {
+ if (this.viteServer) {
+ await this.viteServer.close();
+ }
+ if (this.httpServer) {
+ await promisify(this.httpServer.close.bind(this.httpServer))();
+ }
+ }
+
+ public async handleHotUpdate({ file, modules }: HmrContext): Promise<void | ModuleNode[]> {
+ const { viteServer } = this;
+ if (!viteServer) throw new Error(`AstroDevServer.start() not called`);
+
+ for (const module of modules) {
+ viteServer.moduleGraph.invalidateModule(module);
+ }
+
+ const route = this.mostRecentRoute;
+ const [pathname, search = undefined] = (route?.pathname ?? '/').split('?');
+
+ if (!route) {
+ viteServer.ws.send({
+ type: 'full-reload',
+ });
+ return [];
+ }
+
+ try {
+ const filePath = new URL(`./${route.component}`, this.config.projectRoot);
+ // try to update the most recent route
+ const html = await ssr({
+ astroConfig: this.config,
+ filePath,
+ logging: this.logging,
+ mode: 'development',
+ origin: this.origin,
+ pathname,
+ route,
+ routeCache: this.routeCache,
+ viteServer,
+ });
+
+ // collect style tags to be reloaded (needed for Tailwind HMR, etc.)
+ let invalidatedModules: vite.ModuleNode[] = [];
+ await Promise.all(
+ collectResources(html)
+ .filter(({ href }) => {
+ if (!href) return false;
+ const ext = path.extname(href);
+ return STYLE_EXTENSIONS.has(ext);
+ })
+ .map(async ({ href }) => {
+ const viteModule =
+ viteServer.moduleGraph.getModuleById(`${href}?direct`) ||
+ (await viteServer.moduleGraph.getModuleByUrl(`${href}?direct`)) ||
+ viteServer.moduleGraph.getModuleById(href) ||
+ (await viteServer.moduleGraph.getModuleByUrl(href));
+ if (viteModule) {
+ invalidatedModules.push(viteModule);
+ viteServer.moduleGraph.invalidateModule(viteModule);
+ }
+ })
+ );
+
+ // TODO: log update
+ viteServer.ws.send({
+ type: 'custom',
+ event: 'astro:reload',
+ data: { html },
+ });
+
+ for (const viteModule of invalidatedModules) {
+ // Note: from the time viteServer.moduleGraph.invalidateModule() is called above until now, CSS
+ // is building in the background. For things like Tailwind, this can take some time. If the
+ // CSS is still processing by the time HMR fires, we’ll end up with stale styles on the page.
+ // TODO: fix this hack by figuring out how to add these styles to the { modules } above
+ setTimeout(() => {
+ viteServer.ws.send({
+ type: 'update',
+ updates: [
+ {
+ type: viteModule.type === 'js' ? 'js-update' : 'css-update',
+ path: viteModule.id || viteModule.file || viteModule.url,
+ acceptedPath: viteModule.url,
+ timestamp: Date.now(),
+ },
+ ],
+ });
+ }, 150);
+ }
+
+ return [];
+ } catch (e) {
+ const err = e as Error;
+ // eslint-disable-next-line
+ console.error(err.stack);
+ viteServer.ws.send({
+ type: 'full-reload',
+ });
+ return [];
+ }
+ }
+
+ /** Expose dev server to this.port */
+ public listen(devStart: number): Promise<void> {
+ let showedPortTakenMsg = false;
+ return new Promise<void>((resolve, reject) => {
+ const appListen = () => {
+ this.httpServer = this.app.listen(this.port, this.hostname, () => {
+ info(this.logging, 'astro', msg.devStart({ startupTime: performance.now() - devStart }));
+ info(this.logging, 'astro', msg.devHost({ host: `http://${this.hostname}:${this.port}${this.devRoot}` }));
+ resolve();
+ });
+ this.httpServer?.on('error', onError);
+ };
+
+ const onError = (err: NodeJS.ErrnoException) => {
+ if (err.code && err.code === 'EADDRINUSE') {
+ if (!showedPortTakenMsg) {
+ info(this.logging, 'astro', msg.portInUse({ port: this.port }));
+ showedPortTakenMsg = true; // only print this once
+ }
+ this.port++;
+ return appListen(); // retry
+ } else {
+ error(this.logging, 'astro', err.stack);
+ this.httpServer?.removeListener('error', onError);
+ reject(err); // reject
+ }
+ };
+
+ appListen();
+ });
+ }
+
+ private async createViteServer() {
+ const viteConfig = await createVite(
+ vite.mergeConfig(
+ {
+ mode: 'development',
+ server: {
+ middlewareMode: 'ssr',
+ host: this.hostname,
+ },
+ },
+ this.config.vite || {}
+ ),
+ { astroConfig: this.config, logging: this.logging, devServer: this }
+ );
+ const viteServer = await vite.createServer(viteConfig);
+
+ const pagesDirectory = fileURLToPath(this.config.pages);
+ viteServer.watcher.on('add', (file) => {
+ // Only rebuild routes if new file is a page.
+ if (!file.startsWith(pagesDirectory)) {
+ return;
+ }
+ this.routeCache = {};
+ this.manifest = createRouteManifest({ config: this.config }, this.logging);
+ });
+ viteServer.watcher.on('unlink', (file) => {
+ // Only rebuild routes if deleted file is a page.
+ if (!file.startsWith(pagesDirectory)) {
+ return;
+ }
+ this.routeCache = {};
+ this.manifest = createRouteManifest({ config: this.config }, this.logging);
+ });
+ viteServer.watcher.on('change', () => {
+ // No need to rebuild routes on file content changes.
+ // However, we DO want to clear the cache in case
+ // the change caused a getStaticPaths() return to change.
+ this.routeCache = {};
+ });
+
+ return viteServer;
+ }
+
+ /** The primary router (runs before Vite, in case we need to modify or intercept anything) */
+ private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse, next: NextFunction) {
+ if (!this.viteServer) throw new Error(`AstroDevServer.start() not called`);
+
+ let [pathname, search = undefined] = (req.url || '/').split('?'); // original request
+ const reqStart = performance.now();
+ let filePath: URL | undefined;
+ try {
+ let routePathname: string = pathname;
+ // If using a subpath, ensure that the user has included the pathname
+ // such as /blog in the URL.
+ if (this.devRoot !== '/') {
+ if (pathname.startsWith(this.devRoot)) {
+ // This includes the subpath, so strip off the subpath so that
+ // matchRoute finds this route.
+ routePathname = pathname.substr(this.devRoot.length) || '';
+ if (!routePathname.startsWith('/')) {
+ routePathname = '/' + routePathname;
+ }
+ } else {
+ // Not using the subpath, so forward to Vite's middleware
+ next();
+ return;
+ }
+ }
+
+ const route = matchRoute(routePathname, this.manifest);
+
+ // 404: continue to Vite
+ if (!route) {
+ // Send through, stripping off the `/blog/` part so that Vite matches it.
+ const newPathname = routePathname.startsWith('/') ? routePathname : '/' + routePathname;
+ req.url = newPathname;
+ next();
+ return;
+ }
+ // handle .astro and .md pages
+ filePath = new URL(`./${route.component}`, this.config.projectRoot);
+ const html = await ssr({
+ astroConfig: this.config,
+ filePath,
+ logging: this.logging,
+ mode: 'development',
+ origin: this.origin,
+ pathname,
+ route,
+ routeCache: this.routeCache,
+ viteServer: this.viteServer,
+ });
+ this.mostRecentRoute = route;
+ info(this.logging, 'astro', msg.req({ url: pathname, statusCode: 200, reqTime: performance.now() - reqStart }));
+ res.writeHead(200, {
+ 'Content-Type': mime.getType('.html') as string,
+ 'Content-Length': Buffer.byteLength(html, 'utf8'),
+ });
+ res.write(html);
+ res.end();
+ } catch (err: any) {
+ const statusCode = 500;
+ await this.viteServer.moduleGraph.invalidateAll();
+ this.viteServer.ws.send({ type: 'error', err });
+ let html = serverErrorTemplate({
+ statusCode,
+ title: 'Internal Error',
+ tabTitle: '500: Error',
+ message: stripAnsi(err.message),
+ url: err.url || undefined,
+ stack: stripAnsi(err.stack),
+ });
+ html = await this.viteServer.transformIndexHtml(pathname, html, pathname);
+ info(this.logging, 'astro', msg.req({ url: pathname, statusCode: 500, reqTime: performance.now() - reqStart }));
+ res.writeHead(statusCode, {
+ 'Content-Type': mime.getType('.html') as string,
+ 'Content-Length': Buffer.byteLength(html, 'utf8'),
+ });
+ res.write(html);
+ res.end();
+ }
+ }
+
+ /** Render error page */
+ private async renderError(req: http.IncomingMessage, res: http.ServerResponse, next: NextFunction) {
+ if (!this.viteServer) throw new Error(`AstroDevServer.start() not called`);
+
+ const pathname = req.url || '/';
+ const reqStart = performance.now();
+ let html = '';
+ const statusCode = 404;
+
+ // attempt to load user-given page
+ const relPages = this.config.pages.href.replace(this.config.projectRoot.href, '');
+ const userDefined404 = this.manifest.routes.find((route) => route.component === relPages + '404.astro');
+ if (userDefined404) {
+ html = await ssr({
+ astroConfig: this.config,
+ filePath: new URL(`./${userDefined404.component}`, this.config.projectRoot),
+ logging: this.logging,
+ mode: 'development',
+ pathname: `/${userDefined404.component}`,
+ origin: this.origin,
+ routeCache: this.routeCache,
+ viteServer: this.viteServer,
+ });
+ }
+ // if not found, fall back to default template
+ else {
+ if (pathname === '/' && !pathname.startsWith(this.devRoot)) {
+ html = subpathNotUsedTemplate(this.devRoot, pathname);
+ } else {
+ html = notFoundTemplate({ statusCode, title: 'Not found', tabTitle: '404: Not Found', pathname });
+ }
+ }
+ info(this.logging, 'astro', msg.req({ url: pathname, statusCode, reqTime: performance.now() - reqStart }));
+ res.writeHead(statusCode, {
+ 'Content-Type': mime.getType('.html') as string,
+ 'Content-Length': Buffer.byteLength(html, 'utf8'),
+ });
+ res.write(html);
+ res.end();
+ }
}
diff --git a/packages/astro/src/core/dev/messages.ts b/packages/astro/src/core/dev/messages.ts
index f62a59bad..b7247dec3 100644
--- a/packages/astro/src/core/dev/messages.ts
+++ b/packages/astro/src/core/dev/messages.ts
@@ -7,31 +7,31 @@ import { pad } from './util.js';
/** Display */
export function req({ url, statusCode, reqTime }: { url: string; statusCode: number; reqTime: number }): string {
- let color = dim;
- if (statusCode >= 500) color = magenta;
- else if (statusCode >= 400) color = yellow;
- else if (statusCode >= 300) color = dim;
- else if (statusCode >= 200) color = green;
- return `${color(statusCode)} ${pad(url, 40)} ${dim(Math.round(reqTime) + 'ms')}`;
+ let color = dim;
+ if (statusCode >= 500) color = magenta;
+ else if (statusCode >= 400) color = yellow;
+ else if (statusCode >= 300) color = dim;
+ else if (statusCode >= 200) color = green;
+ return `${color(statusCode)} ${pad(url, 40)} ${dim(Math.round(reqTime) + 'ms')}`;
}
/** Display */
export function reload({ url, reqTime }: { url: string; reqTime: number }): string {
- let color = yellow;
- return `${pad(url, 40)} ${dim(Math.round(reqTime) + 'ms')}`;
+ let color = yellow;
+ return `${pad(url, 40)} ${dim(Math.round(reqTime) + 'ms')}`;
}
/** Display dev server host and startup time */
export function devStart({ startupTime }: { startupTime: number }): string {
- return `${pad(`Server started`, 44)} ${dim(`${Math.round(startupTime)}ms`)}`;
+ return `${pad(`Server started`, 44)} ${dim(`${Math.round(startupTime)}ms`)}`;
}
/** Display dev server host */
export function devHost({ host }: { host: string }): string {
- return `Local: ${bold(magenta(host))}`;
+ return `Local: ${bold(magenta(host))}`;
}
/** Display port in use */
export function portInUse({ port }: { port: number }): string {
- return `Port ${port} in use. Trying a new one…`;
+ return `Port ${port} in use. Trying a new one…`;
}
diff --git a/packages/astro/src/core/dev/template/4xx.ts b/packages/astro/src/core/dev/template/4xx.ts
index e321a881a..af63feea7 100644
--- a/packages/astro/src/core/dev/template/4xx.ts
+++ b/packages/astro/src/core/dev/template/4xx.ts
@@ -2,21 +2,21 @@ import { encode } from 'html-entities';
import { baseCSS } from './css.js';
interface ErrorTemplateOptions {
- /** a short description of the error */
- pathname: string;
- /** HTTP error code */
- statusCode?: number;
- /** HTML <title> */
- tabTitle: string;
- /** page title */
- title: string;
- /** The body of the message, if one is provided */
- body?: string;
+ /** a short description of the error */
+ pathname: string;
+ /** HTTP error code */
+ statusCode?: number;
+ /** HTML <title> */
+ tabTitle: string;
+ /** page title */
+ title: string;
+ /** The body of the message, if one is provided */
+ body?: string;
}
/** Display all errors */
export default function template({ title, pathname, statusCode = 404, tabTitle, body }: ErrorTemplateOptions): string {
- return `<!doctype html>
+ return `<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
@@ -48,25 +48,25 @@ export default function template({ title, pathname, statusCode = 404, tabTitle,
<svg class="astro" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z" fill="white"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z" fill="#ff5d01"></path></svg>
<h1>${statusCode ? `<span class="statusCode">${statusCode}: </span> ` : ''}<span class="statusMessage">${title}</span></h1>
${
- body ||
- `
+ body ||
+ `
<pre>Path: ${encode(pathname)}</pre>
`
- }
+ }
</main>
</body>
</html>`;
}
export function subpathNotUsedTemplate(base: string, pathname: string) {
- return template({
- pathname,
- statusCode: 404,
- title: 'Not found',
- tabTitle: '404: Not Found',
- body: `
+ return template({
+ pathname,
+ statusCode: 404,
+ title: 'Not found',
+ tabTitle: '404: Not Found',
+ body: `
<p>In your <code>buildOptions.site</code> you have your base path set to <a href="${base}">${base}</a>. Do you want to go there instead?</p>
<p>Come to our <a href="https://astro.build/chat">Discord</a> if you need help.</p>
`,
- });
+ });
}
diff --git a/packages/astro/src/core/dev/template/5xx.ts b/packages/astro/src/core/dev/template/5xx.ts
index 75513ff0d..ee1f17fa6 100644
--- a/packages/astro/src/core/dev/template/5xx.ts
+++ b/packages/astro/src/core/dev/template/5xx.ts
@@ -2,24 +2,24 @@ import { encode } from 'html-entities';
import { baseCSS } from './css.js';
interface ErrorTemplateOptions {
- /** a short description of the error */
- message: string;
- /** information about where the error occurred */
- stack?: string;
- /** HTTP error code */
- statusCode?: number;
- /** HTML <title> */
- tabTitle: string;
- /** page title */
- title: string;
- /** show user a URL for more info or action to take */
- url?: string;
+ /** a short description of the error */
+ message: string;
+ /** information about where the error occurred */
+ stack?: string;
+ /** HTTP error code */
+ statusCode?: number;
+ /** HTML <title> */
+ tabTitle: string;
+ /** page title */
+ title: string;
+ /** show user a URL for more info or action to take */
+ url?: string;
}
/** Display all errors */
export default function template({ title, url, message, stack, statusCode, tabTitle }: ErrorTemplateOptions): string {
- let error = url ? message.replace(url, '') : message;
- return `<!doctype html>
+ let error = url ? message.replace(url, '') : message;
+ return `<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
diff --git a/packages/astro/src/core/dev/util.ts b/packages/astro/src/core/dev/util.ts
index 1f0a3c66b..1b942c3b8 100644
--- a/packages/astro/src/core/dev/util.ts
+++ b/packages/astro/src/core/dev/util.ts
@@ -1,8 +1,8 @@
/** Pad string () */
export function pad(input: string, minLength: number, dir?: 'left' | 'right'): string {
- let output = input;
- while (output.length < minLength) {
- output = dir === 'left' ? ' ' + output : output + ' ';
- }
- return output;
+ let output = input;
+ while (output.length < minLength) {
+ output = dir === 'left' ? ' ' + output : output + ' ';
+ }
+ return output;
}
diff --git a/packages/astro/src/core/logger.ts b/packages/astro/src/core/logger.ts
index eb8f6fef2..ed6240bcc 100644
--- a/packages/astro/src/core/logger.ts
+++ b/packages/astro/src/core/logger.ts
@@ -7,189 +7,189 @@ import stringWidth from 'string-width';
import { format as utilFormat } from 'util';
type ConsoleStream = Writable & {
- fd: 1 | 2;
+ fd: 1 | 2;
};
function getLoggerLocale(): string {
- const defaultLocale = 'en-US';
- if (process.env.LANG) {
- const extractedLocale = process.env.LANG.split('.')[0].replace(/_/g, '-');
- // Check if language code is atleast two characters long (ie. en, es).
- // NOTE: if "c" locale is encountered, the default locale will be returned.
- if (extractedLocale.length < 2) return defaultLocale;
- else return extractedLocale;
- } else return defaultLocale;
+ const defaultLocale = 'en-US';
+ if (process.env.LANG) {
+ const extractedLocale = process.env.LANG.split('.')[0].replace(/_/g, '-');
+ // Check if language code is atleast two characters long (ie. en, es).
+ // NOTE: if "c" locale is encountered, the default locale will be returned.
+ if (extractedLocale.length < 2) return defaultLocale;
+ else return extractedLocale;
+ } else return defaultLocale;
}
const dt = new Intl.DateTimeFormat(getLoggerLocale(), {
- hour: '2-digit',
- minute: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
});
export const defaultLogDestination = new Writable({
- objectMode: true,
- write(event: LogMessage, _, callback) {
- let dest: ConsoleStream = process.stderr;
- if (levels[event.level] < levels['error']) {
- dest = process.stdout;
- }
-
- dest.write(dim(dt.format(new Date()) + ' '));
-
- let type = event.type;
- if (type) {
- if (event.level === 'info') {
- type = bold(blue(type));
- } else if (event.level === 'warn') {
- type = bold(yellow(type));
- } else if (event.level === 'error') {
- type = bold(red(type));
- }
-
- dest.write(`[${type}] `);
- }
-
- dest.write(utilFormat(...event.args));
- dest.write('\n');
-
- callback();
- },
+ objectMode: true,
+ write(event: LogMessage, _, callback) {
+ let dest: ConsoleStream = process.stderr;
+ if (levels[event.level] < levels['error']) {
+ dest = process.stdout;
+ }
+
+ dest.write(dim(dt.format(new Date()) + ' '));
+
+ let type = event.type;
+ if (type) {
+ if (event.level === 'info') {
+ type = bold(blue(type));
+ } else if (event.level === 'warn') {
+ type = bold(yellow(type));
+ } else if (event.level === 'error') {
+ type = bold(red(type));
+ }
+
+ dest.write(`[${type}] `);
+ }
+
+ dest.write(utilFormat(...event.args));
+ dest.write('\n');
+
+ callback();
+ },
});
interface LogWritable<T> extends Writable {
- write: (chunk: T) => boolean;
+ write: (chunk: T) => boolean;
}
export type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'; // same as Pino
export type LoggerEvent = 'debug' | 'info' | 'warn' | 'error';
export interface LogOptions {
- dest?: LogWritable<LogMessage>;
- level?: LoggerLevel;
+ dest?: LogWritable<LogMessage>;
+ level?: LoggerLevel;
}
export const defaultLogOptions: Required<LogOptions> = {
- dest: defaultLogDestination,
- level: 'info',
+ dest: defaultLogDestination,
+ level: 'info',
};
export interface LogMessage {
- type: string | null;
- level: LoggerLevel;
- message: string;
- args: Array<any>;
+ type: string | null;
+ level: LoggerLevel;
+ message: string;
+ args: Array<any>;
}
export const levels: Record<LoggerLevel, number> = {
- debug: 20,
- info: 30,
- warn: 40,
- error: 50,
- silent: 90,
+ debug: 20,
+ info: 30,
+ warn: 40,
+ error: 50,
+ silent: 90,
};
/** Full logging API */
export function log(opts: LogOptions = {}, level: LoggerLevel, type: string | null, ...args: Array<any>) {
- const logLevel = opts.level ?? defaultLogOptions.level;
- const dest = opts.dest ?? defaultLogOptions.dest;
- const event: LogMessage = {
- type,
- level,
- args,
- message: '',
- };
-
- // test if this level is enabled or not
- if (levels[logLevel] > levels[level]) {
- return; // do nothing
- }
-
- dest.write(event);
+ const logLevel = opts.level ?? defaultLogOptions.level;
+ const dest = opts.dest ?? defaultLogOptions.dest;
+ const event: LogMessage = {
+ type,
+ level,
+ args,
+ message: '',
+ };
+
+ // test if this level is enabled or not
+ if (levels[logLevel] > levels[level]) {
+ return; // do nothing
+ }
+
+ dest.write(event);
}
/** Emit a message only shown in debug mode */
export function debug(opts: LogOptions, type: string | null, ...messages: Array<any>) {
- return log(opts, 'debug', type, ...messages);
+ return log(opts, 'debug', type, ...messages);
}
/** Emit a general info message (be careful using this too much!) */
export function info(opts: LogOptions, type: string | null, ...messages: Array<any>) {
- return log(opts, 'info', type, ...messages);
+ return log(opts, 'info', type, ...messages);
}
/** Emit a warning a user should be aware of */
export function warn(opts: LogOptions, type: string | null, ...messages: Array<any>) {
- return log(opts, 'warn', type, ...messages);
+ return log(opts, 'warn', type, ...messages);
}
/** Emit a fatal error message the user should address. */
export function error(opts: LogOptions, type: string | null, ...messages: Array<any>) {
- return log(opts, 'error', type, ...messages);
+ return log(opts, 'error', type, ...messages);
}
type LogFn = typeof debug | typeof info | typeof warn | typeof error;
export function table(opts: LogOptions, columns: number[]) {
- return function logTable(logFn: LogFn, ...input: Array<any>) {
- const messages = columns.map((len, i) => padStr(input[i].toString(), len));
- logFn(opts, null, ...messages);
- };
+ return function logTable(logFn: LogFn, ...input: Array<any>) {
+ const messages = columns.map((len, i) => padStr(input[i].toString(), len));
+ logFn(opts, null, ...messages);
+ };
}
/** Pretty format error for display */
export function parseError(opts: LogOptions, err: CompileError) {
- if (!err.frame) {
- return error(opts, 'parse-error', err.message || err);
- }
-
- let frame = err.frame
- // Switch colons for pipes
- .replace(/^([0-9]+)(:)/gm, `${bold('$1')} │`)
- // Make the caret red.
- .replace(/(?<=^\s+)(\^)/gm, bold(red(' ^')))
- // Add identation
- .replace(/^/gm, ' ');
-
- error(
- opts,
- 'parse-error',
- `
+ if (!err.frame) {
+ return error(opts, 'parse-error', err.message || err);
+ }
+
+ let frame = err.frame
+ // Switch colons for pipes
+ .replace(/^([0-9]+)(:)/gm, `${bold('$1')} │`)
+ // Make the caret red.
+ .replace(/(?<=^\s+)(\^)/gm, bold(red(' ^')))
+ // Add identation
+ .replace(/^/gm, ' ');
+
+ error(
+ opts,
+ 'parse-error',
+ `
${underline(bold(grey(`${err.filename || ''}:${err.start.line}:${err.start.column}`)))}
${bold(red(`𝘅 ${err.message}`))}
${frame}
`
- );
+ );
}
// A default logger for when too lazy to pass LogOptions around.
export const logger = {
- debug: debug.bind(null, defaultLogOptions),
- info: info.bind(null, defaultLogOptions),
- warn: warn.bind(null, defaultLogOptions),
- error: error.bind(null, defaultLogOptions),
+ debug: debug.bind(null, defaultLogOptions),
+ info: info.bind(null, defaultLogOptions),
+ warn: warn.bind(null, defaultLogOptions),
+ error: error.bind(null, defaultLogOptions),
};
function padStr(str: string, len: number) {
- const strLen = stringWidth(str);
- if (strLen > len) {
- return str.substring(0, len - 3) + '...';
- }
- const spaces = Array.from({ length: len - strLen }, () => ' ').join('');
- return str + spaces;
+ const strLen = stringWidth(str);
+ if (strLen > len) {
+ return str.substring(0, len - 3) + '...';
+ }
+ const spaces = Array.from({ length: len - strLen }, () => ' ').join('');
+ return str + spaces;
}
export let defaultLogLevel: LoggerLevel;
if (process.argv.includes('--verbose')) {
- defaultLogLevel = 'debug';
+ defaultLogLevel = 'debug';
} else if (process.argv.includes('--silent')) {
- defaultLogLevel = 'silent';
+ defaultLogLevel = 'silent';
} else {
- defaultLogLevel = 'info';
+ defaultLogLevel = 'info';
}
/** Print out a timer message for debug() */
export function timerMessage(message: string, startTime: number = performance.now()) {
- let timeDiff = performance.now() - startTime;
- let timeDisplay = timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`;
- return `${message} ${dim(timeDisplay)}`;
+ let timeDiff = performance.now() - startTime;
+ let timeDisplay = timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`;
+ return `${message} ${dim(timeDisplay)}`;
}
diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts
index f6cfd7ad3..e9e378981 100644
--- a/packages/astro/src/core/preview/index.ts
+++ b/packages/astro/src/core/preview/index.ts
@@ -10,83 +10,83 @@ import { error, info } from '../logger.js';
import { subpathNotUsedTemplate } from '../dev/template/4xx.js';
interface PreviewOptions {
- logging: LogOptions;
+ logging: LogOptions;
}
interface PreviewServer {
- hostname: string;
- port: number;
- server: http.Server;
- stop(): Promise<void>;
+ hostname: string;
+ port: number;
+ server: http.Server;
+ stop(): Promise<void>;
}
/** The primary dev action */
export default async function preview(config: AstroConfig, { logging }: PreviewOptions): Promise<PreviewServer> {
- const startServerTime = performance.now();
- const base = config.buildOptions.site ? new URL(config.buildOptions.site).pathname : '/';
+ const startServerTime = performance.now();
+ const base = config.buildOptions.site ? new URL(config.buildOptions.site).pathname : '/';
- // Create the preview server, send static files out of the `dist/` directory.
- const server = http.createServer((req, res) => {
- if (!req.url!.startsWith(base)) {
- res.statusCode = 404;
- res.end(subpathNotUsedTemplate(base, req.url!));
- return;
- }
+ // Create the preview server, send static files out of the `dist/` directory.
+ const server = http.createServer((req, res) => {
+ if (!req.url!.startsWith(base)) {
+ res.statusCode = 404;
+ res.end(subpathNotUsedTemplate(base, req.url!));
+ return;
+ }
- send(req, req.url!.substr(base.length - 1), {
- root: fileURLToPath(config.dist),
- }).pipe(res);
- });
+ send(req, req.url!.substr(base.length - 1), {
+ root: fileURLToPath(config.dist),
+ }).pipe(res);
+ });
- let { hostname, port } = config.devOptions;
+ let { hostname, port } = config.devOptions;
- let httpServer: http.Server;
+ let httpServer: http.Server;
- /** Expose dev server to `port` */
- function startServer(timerStart: number): Promise<void> {
- let showedPortTakenMsg = false;
- let showedListenMsg = false;
- return new Promise<void>((resolve, reject) => {
- const listen = () => {
- httpServer = server.listen(port, hostname, () => {
- if (!showedListenMsg) {
- info(logging, 'astro', msg.devStart({ startupTime: performance.now() - timerStart }));
- info(logging, 'astro', msg.devHost({ host: `http://${hostname}:${port}${base}` }));
- }
- showedListenMsg = true;
- resolve();
- });
- httpServer?.on('error', onError);
- };
+ /** Expose dev server to `port` */
+ function startServer(timerStart: number): Promise<void> {
+ let showedPortTakenMsg = false;
+ let showedListenMsg = false;
+ return new Promise<void>((resolve, reject) => {
+ const listen = () => {
+ httpServer = server.listen(port, hostname, () => {
+ if (!showedListenMsg) {
+ info(logging, 'astro', msg.devStart({ startupTime: performance.now() - timerStart }));
+ info(logging, 'astro', msg.devHost({ host: `http://${hostname}:${port}${base}` }));
+ }
+ showedListenMsg = true;
+ resolve();
+ });
+ httpServer?.on('error', onError);
+ };
- const onError = (err: NodeJS.ErrnoException) => {
- if (err.code && err.code === 'EADDRINUSE') {
- if (!showedPortTakenMsg) {
- info(logging, 'astro', msg.portInUse({ port }));
- showedPortTakenMsg = true; // only print this once
- }
- port++;
- return listen(); // retry
- } else {
- error(logging, 'astro', err.stack);
- httpServer?.removeListener('error', onError);
- reject(err); // reject
- }
- };
+ const onError = (err: NodeJS.ErrnoException) => {
+ if (err.code && err.code === 'EADDRINUSE') {
+ if (!showedPortTakenMsg) {
+ info(logging, 'astro', msg.portInUse({ port }));
+ showedPortTakenMsg = true; // only print this once
+ }
+ port++;
+ return listen(); // retry
+ } else {
+ error(logging, 'astro', err.stack);
+ httpServer?.removeListener('error', onError);
+ reject(err); // reject
+ }
+ };
- listen();
- });
- }
+ listen();
+ });
+ }
- // Start listening on `hostname:port`.
- await startServer(startServerTime);
+ // Start listening on `hostname:port`.
+ await startServer(startServerTime);
- return {
- hostname,
- port,
- server: httpServer!,
- stop: async () => {
- httpServer.close();
- },
- };
+ return {
+ hostname,
+ port,
+ server: httpServer!,
+ stop: async () => {
+ httpServer.close();
+ },
+ };
}
diff --git a/packages/astro/src/core/ssr/css.ts b/packages/astro/src/core/ssr/css.ts
index 774bf51fa..6c731c1ad 100644
--- a/packages/astro/src/core/ssr/css.ts
+++ b/packages/astro/src/core/ssr/css.ts
@@ -11,29 +11,29 @@ export const STYLE_EXTENSIONS = new Set(['.css', '.pcss', '.postcss', '.scss', '
* Given a filePath URL, crawl Vite’s module graph to find style files
*/
export function getStylesForURL(filePath: URL, viteServer: vite.ViteDevServer): Set<string> {
- const css = new Set<string>();
-
- // recursively crawl module graph to get all style files imported by parent id
- function crawlCSS(id: string, scanned = new Set<string>()) {
- // note: use .idToModuleMap() for lookups (.urlToModuleMap() may produce different
- // URLs for modules depending on conditions, making resolution difficult)
- const moduleName = viteServer.moduleGraph.idToModuleMap.get(id);
- if (!moduleName || !moduleName.id) return;
-
- scanned.add(moduleName.id);
-
- // scan importedModules
- for (const importedModule of moduleName.importedModules) {
- if (!importedModule.id || scanned.has(importedModule.id)) continue;
- const ext = path.extname(importedModule.url.toLowerCase());
- if (STYLE_EXTENSIONS.has(ext)) {
- css.add(importedModule.url); // note: return `url`s for HTML (not .id, which will break Windows)
- }
- crawlCSS(importedModule.id, scanned);
- }
- }
-
- crawlCSS(viteID(filePath));
-
- return css;
+ const css = new Set<string>();
+
+ // recursively crawl module graph to get all style files imported by parent id
+ function crawlCSS(id: string, scanned = new Set<string>()) {
+ // note: use .idToModuleMap() for lookups (.urlToModuleMap() may produce different
+ // URLs for modules depending on conditions, making resolution difficult)
+ const moduleName = viteServer.moduleGraph.idToModuleMap.get(id);
+ if (!moduleName || !moduleName.id) return;
+
+ scanned.add(moduleName.id);
+
+ // scan importedModules
+ for (const importedModule of moduleName.importedModules) {
+ if (!importedModule.id || scanned.has(importedModule.id)) continue;
+ const ext = path.extname(importedModule.url.toLowerCase());
+ if (STYLE_EXTENSIONS.has(ext)) {
+ css.add(importedModule.url); // note: return `url`s for HTML (not .id, which will break Windows)
+ }
+ crawlCSS(importedModule.id, scanned);
+ }
+ }
+
+ crawlCSS(viteID(filePath));
+
+ return css;
}
diff --git a/packages/astro/src/core/ssr/html.ts b/packages/astro/src/core/ssr/html.ts
index aa5a3d847..eb429b927 100644
--- a/packages/astro/src/core/ssr/html.ts
+++ b/packages/astro/src/core/ssr/html.ts
@@ -4,61 +4,61 @@ import htmlparser2 from 'htmlparser2';
/** Inject tags into HTML (note: for best performance, group as many tags as possible into as few calls as you can) */
export function injectTags(html: string, tags: vite.HtmlTagDescriptor[]): string {
- let output = html;
- if (!tags.length) return output;
-
- const pos = { 'head-prepend': -1, head: -1, 'body-prepend': -1, body: -1 };
-
- // parse html
- const parser = new htmlparser2.Parser({
- onopentag(tagname) {
- if (tagname === 'head') pos['head-prepend'] = parser.endIndex + 1;
- if (tagname === 'body') pos['body-prepend'] = parser.endIndex + 1;
- },
- onclosetag(tagname) {
- if (tagname === 'head') pos['head'] = parser.startIndex;
- if (tagname === 'body') pos['body'] = parser.startIndex;
- },
- });
- parser.write(html);
- parser.end();
-
- // inject
- const lastToFirst = Object.entries(pos).sort((a, b) => b[1] - a[1]);
- lastToFirst.forEach(([name, i]) => {
- if (i === -1) {
- // if page didn’t generate <head> or <body>, guess
- if (name === 'head-prepend' || name === 'head') i = 0;
- if (name === 'body-prepend' || name === 'body') i = html.length;
- }
- let selected = tags.filter(({ injectTo }) => {
- if (name === 'head-prepend' && !injectTo) {
- return true; // "head-prepend" is the default
- } else {
- return injectTo === name;
- }
- });
- if (!selected.length) return;
- output = output.substring(0, i) + serializeTags(selected) + html.substring(i);
- });
-
- return output;
+ let output = html;
+ if (!tags.length) return output;
+
+ const pos = { 'head-prepend': -1, head: -1, 'body-prepend': -1, body: -1 };
+
+ // parse html
+ const parser = new htmlparser2.Parser({
+ onopentag(tagname) {
+ if (tagname === 'head') pos['head-prepend'] = parser.endIndex + 1;
+ if (tagname === 'body') pos['body-prepend'] = parser.endIndex + 1;
+ },
+ onclosetag(tagname) {
+ if (tagname === 'head') pos['head'] = parser.startIndex;
+ if (tagname === 'body') pos['body'] = parser.startIndex;
+ },
+ });
+ parser.write(html);
+ parser.end();
+
+ // inject
+ const lastToFirst = Object.entries(pos).sort((a, b) => b[1] - a[1]);
+ lastToFirst.forEach(([name, i]) => {
+ if (i === -1) {
+ // if page didn’t generate <head> or <body>, guess
+ if (name === 'head-prepend' || name === 'head') i = 0;
+ if (name === 'body-prepend' || name === 'body') i = html.length;
+ }
+ let selected = tags.filter(({ injectTo }) => {
+ if (name === 'head-prepend' && !injectTo) {
+ return true; // "head-prepend" is the default
+ } else {
+ return injectTo === name;
+ }
+ });
+ if (!selected.length) return;
+ output = output.substring(0, i) + serializeTags(selected) + html.substring(i);
+ });
+
+ return output;
}
type Resource = Record<string, string>;
/** Collect resources (scans final, rendered HTML so expressions have been applied) */
export function collectResources(html: string): Resource[] {
- let resources: Resource[] = [];
- const parser = new htmlparser2.Parser({
- // <link> tags are self-closing, so only use onopentag (avoid onattribute or onclosetag)
- onopentag(tagname, attrs) {
- if (tagname === 'link') resources.push(attrs);
- },
- });
- parser.write(html);
- parser.end();
- return resources;
+ let resources: Resource[] = [];
+ const parser = new htmlparser2.Parser({
+ // <link> tags are self-closing, so only use onopentag (avoid onattribute or onclosetag)
+ onopentag(tagname, attrs) {
+ if (tagname === 'link') resources.push(attrs);
+ },
+ });
+ parser.write(html);
+ parser.end();
+ return resources;
}
// -------------------------------------------------------------------------------
@@ -95,34 +95,34 @@ export function collectResources(html: string): Resource[] {
const unaryTags = new Set(['link', 'meta', 'base']);
function serializeTag({ tag, attrs, children }: vite.HtmlTagDescriptor, indent = ''): string {
- if (unaryTags.has(tag)) {
- return `<${tag}${serializeAttrs(attrs)}>`;
- } else {
- return `<${tag}${serializeAttrs(attrs)}>${serializeTags(children, incrementIndent(indent))}</${tag}>`;
- }
+ if (unaryTags.has(tag)) {
+ return `<${tag}${serializeAttrs(attrs)}>`;
+ } else {
+ return `<${tag}${serializeAttrs(attrs)}>${serializeTags(children, incrementIndent(indent))}</${tag}>`;
+ }
}
function serializeTags(tags: vite.HtmlTagDescriptor['children'], indent = ''): string {
- if (typeof tags === 'string') {
- return tags;
- } else if (tags && tags.length) {
- return tags.map((tag) => `${indent}${serializeTag(tag, indent)}\n`).join('');
- }
- return '';
+ if (typeof tags === 'string') {
+ return tags;
+ } else if (tags && tags.length) {
+ return tags.map((tag) => `${indent}${serializeTag(tag, indent)}\n`).join('');
+ }
+ return '';
}
function serializeAttrs(attrs: vite.HtmlTagDescriptor['attrs']): string {
- let res = '';
- for (const key in attrs) {
- if (typeof attrs[key] === 'boolean') {
- res += attrs[key] ? ` ${key}` : ``;
- } else {
- res += ` ${key}=${JSON.stringify(attrs[key])}`;
- }
- }
- return res;
+ let res = '';
+ for (const key in attrs) {
+ if (typeof attrs[key] === 'boolean') {
+ res += attrs[key] ? ` ${key}` : ``;
+ } else {
+ res += ` ${key}=${JSON.stringify(attrs[key])}`;
+ }
+ }
+ return res;
}
function incrementIndent(indent = '') {
- return `${indent}${indent[0] === '\t' ? '\t' : ' '}`;
+ return `${indent}${indent[0] === '\t' ? '\t' : ' '}`;
}
diff --git a/packages/astro/src/core/ssr/index.ts b/packages/astro/src/core/ssr/index.ts
index 18d2e8c67..b53411b91 100644
--- a/packages/astro/src/core/ssr/index.ts
+++ b/packages/astro/src/core/ssr/index.ts
@@ -1,20 +1,20 @@
import type { BuildResult } from 'esbuild';
import type vite from '../vite';
import type {
- AstroConfig,
- AstroGlobal,
- AstroGlobalPartial,
- ComponentInstance,
- GetStaticPathsResult,
- Params,
- Props,
- Renderer,
- RouteCache,
- RouteData,
- RuntimeMode,
- SSRElement,
- SSRError,
- SSRResult,
+ AstroConfig,
+ AstroGlobal,
+ AstroGlobalPartial,
+ ComponentInstance,
+ GetStaticPathsResult,
+ Params,
+ Props,
+ Renderer,
+ RouteCache,
+ RouteData,
+ RuntimeMode,
+ SSRElement,
+ SSRError,
+ SSRResult,
} from '../../@types/astro';
import type { LogOptions } from '../logger';
import type { AstroComponentFactory } from '../../runtime/server/index';
@@ -33,388 +33,388 @@ import { getParams, validateGetStaticPathsModule, validateGetStaticPathsResult }
const svelteStylesRE = /svelte\?svelte&type=style/;
interface SSROptions {
- /** an instance of the AstroConfig */
- astroConfig: AstroConfig;
- /** location of file on disk */
- filePath: URL;
- /** logging options */
- logging: LogOptions;
- /** "development" or "production" */
- mode: RuntimeMode;
- /** production website, needed for some RSS & Sitemap functions */
- origin: string;
- /** the web request (needed for dynamic routes) */
- pathname: string;
- /** optional, in case we need to render something outside of a dev server */
- route?: RouteData;
- /** pass in route cache because SSR can’t manage cache-busting */
- routeCache: RouteCache;
- /** Vite instance */
- viteServer: vite.ViteDevServer;
+ /** an instance of the AstroConfig */
+ astroConfig: AstroConfig;
+ /** location of file on disk */
+ filePath: URL;
+ /** logging options */
+ logging: LogOptions;
+ /** "development" or "production" */
+ mode: RuntimeMode;
+ /** production website, needed for some RSS & Sitemap functions */
+ origin: string;
+ /** the web request (needed for dynamic routes) */
+ pathname: string;
+ /** optional, in case we need to render something outside of a dev server */
+ route?: RouteData;
+ /** pass in route cache because SSR can’t manage cache-busting */
+ routeCache: RouteCache;
+ /** Vite instance */
+ viteServer: vite.ViteDevServer;
}
const cache = new Map<string, Promise<Renderer>>();
// TODO: improve validation and error handling here.
async function resolveRenderer(viteServer: vite.ViteDevServer, renderer: string, astroConfig: AstroConfig) {
- const resolvedRenderer: any = {};
- // We can dynamically import the renderer by itself because it shouldn't have
- // any non-standard imports, the index is just meta info.
- // The other entrypoints need to be loaded through Vite.
- const {
- default: { name, client, polyfills, hydrationPolyfills, server },
- } = await import(resolveDependency(renderer, astroConfig));
-
- resolvedRenderer.name = name;
- if (client) resolvedRenderer.source = path.posix.join(renderer, client);
- if (Array.isArray(hydrationPolyfills)) resolvedRenderer.hydrationPolyfills = hydrationPolyfills.map((src: string) => path.posix.join(renderer, src));
- if (Array.isArray(polyfills)) resolvedRenderer.polyfills = polyfills.map((src: string) => path.posix.join(renderer, src));
- const { url } = await viteServer.moduleGraph.ensureEntryFromUrl(path.posix.join(renderer, server));
- const { default: rendererSSR } = await viteServer.ssrLoadModule(url);
- resolvedRenderer.ssr = rendererSSR;
-
- const completedRenderer: Renderer = resolvedRenderer;
- return completedRenderer;
+ const resolvedRenderer: any = {};
+ // We can dynamically import the renderer by itself because it shouldn't have
+ // any non-standard imports, the index is just meta info.
+ // The other entrypoints need to be loaded through Vite.
+ const {
+ default: { name, client, polyfills, hydrationPolyfills, server },
+ } = await import(resolveDependency(renderer, astroConfig));
+
+ resolvedRenderer.name = name;
+ if (client) resolvedRenderer.source = path.posix.join(renderer, client);
+ if (Array.isArray(hydrationPolyfills)) resolvedRenderer.hydrationPolyfills = hydrationPolyfills.map((src: string) => path.posix.join(renderer, src));
+ if (Array.isArray(polyfills)) resolvedRenderer.polyfills = polyfills.map((src: string) => path.posix.join(renderer, src));
+ const { url } = await viteServer.moduleGraph.ensureEntryFromUrl(path.posix.join(renderer, server));
+ const { default: rendererSSR } = await viteServer.ssrLoadModule(url);
+ resolvedRenderer.ssr = rendererSSR;
+
+ const completedRenderer: Renderer = resolvedRenderer;
+ return completedRenderer;
}
async function resolveRenderers(viteServer: vite.ViteDevServer, astroConfig: AstroConfig): Promise<Renderer[]> {
- const ids: string[] = astroConfig.renderers;
- const renderers = await Promise.all(
- ids.map((renderer) => {
- if (cache.has(renderer)) return cache.get(renderer)!;
- let promise = resolveRenderer(viteServer, renderer, astroConfig);
- cache.set(renderer, promise);
- return promise;
- })
- );
-
- return renderers;
+ const ids: string[] = astroConfig.renderers;
+ const renderers = await Promise.all(
+ ids.map((renderer) => {
+ if (cache.has(renderer)) return cache.get(renderer)!;
+ let promise = resolveRenderer(viteServer, renderer, astroConfig);
+ cache.set(renderer, promise);
+ return promise;
+ })
+ );
+
+ return renderers;
}
interface ErrorHandlerOptions {
- filePath: URL;
- viteServer: vite.ViteDevServer;
+ filePath: URL;
+ viteServer: vite.ViteDevServer;
}
async function errorHandler(e: unknown, { viteServer, filePath }: ErrorHandlerOptions) {
- // normalize error stack line-endings to \n
- if ((e as any).stack) {
- (e as any).stack = eol.lf((e as any).stack);
- }
-
- // fix stack trace with Vite (this searches its module graph for matches)
- if (e instanceof Error) {
- viteServer.ssrFixStacktrace(e);
- }
-
- // Astro error (thrown by esbuild so it needs to be formatted for Vite)
- if (Array.isArray((e as any).errors)) {
- const { location, pluginName, text } = (e as BuildResult).errors[0];
- const err = e as SSRError;
- if (location) err.loc = { file: location.file, line: location.line, column: location.column };
- let src = err.pluginCode;
- if (!src && err.id && fs.existsSync(err.id)) src = await fs.promises.readFile(err.id, 'utf8');
- if (!src) src = await fs.promises.readFile(filePath, 'utf8');
- err.frame = codeFrame(src, err.loc);
- err.id = location?.file;
- err.message = `${location?.file}: ${text}
+ // normalize error stack line-endings to \n
+ if ((e as any).stack) {
+ (e as any).stack = eol.lf((e as any).stack);
+ }
+
+ // fix stack trace with Vite (this searches its module graph for matches)
+ if (e instanceof Error) {
+ viteServer.ssrFixStacktrace(e);
+ }
+
+ // Astro error (thrown by esbuild so it needs to be formatted for Vite)
+ if (Array.isArray((e as any).errors)) {
+ const { location, pluginName, text } = (e as BuildResult).errors[0];
+ const err = e as SSRError;
+ if (location) err.loc = { file: location.file, line: location.line, column: location.column };
+ let src = err.pluginCode;
+ if (!src && err.id && fs.existsSync(err.id)) src = await fs.promises.readFile(err.id, 'utf8');
+ if (!src) src = await fs.promises.readFile(filePath, 'utf8');
+ err.frame = codeFrame(src, err.loc);
+ err.id = location?.file;
+ err.message = `${location?.file}: ${text}
${err.frame}
`;
- if (pluginName) err.plugin = pluginName;
- throw err;
- }
+ if (pluginName) err.plugin = pluginName;
+ throw err;
+ }
- // Generic error (probably from Vite, and already formatted)
- throw e;
+ // Generic error (probably from Vite, and already formatted)
+ throw e;
}
export type ComponentPreload = [Renderer[], ComponentInstance];
export async function preload({ astroConfig, filePath, viteServer }: SSROptions): Promise<ComponentPreload> {
- // Important: This needs to happen first, in case a renderer provides polyfills.
- const renderers = await resolveRenderers(viteServer, astroConfig);
- // Load the module from the Vite SSR Runtime.
- const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
+ // Important: This needs to happen first, in case a renderer provides polyfills.
+ const renderers = await resolveRenderers(viteServer, astroConfig);
+ // Load the module from the Vite SSR Runtime.
+ const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
- return [renderers, mod];
+ return [renderers, mod];
}
export async function renderComponent(
- renderers: Renderer[],
- Component: AstroComponentFactory,
- astroConfig: AstroConfig,
- pathname: string,
- origin: string,
- params: Params,
- pageProps: Props,
- links: string[] = []
+ renderers: Renderer[],
+ Component: AstroComponentFactory,
+ astroConfig: AstroConfig,
+ pathname: string,
+ origin: string,
+ params: Params,
+ pageProps: Props,
+ links: string[] = []
): Promise<string> {
- const _links = new Set<SSRElement>(
- links.map((href) => ({
- props: {
- rel: 'stylesheet',
- href,
- },
- children: '',
- }))
- );
- const result: SSRResult = {
- styles: new Set<SSRElement>(),
- scripts: new Set<SSRElement>(),
- links: _links,
- /** This function returns the `Astro` faux-global */
- createAstro(astroGlobal: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null) {
- const site = new URL(origin);
- const url = new URL('.' + pathname, site);
- const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin);
- return {
- __proto__: astroGlobal,
- props,
- request: {
- canonicalURL,
- params,
- url,
- },
- slots: Object.fromEntries(Object.entries(slots || {}).map(([slotName]) => [slotName, true])),
- // This is used for <Markdown> but shouldn't be used publicly
- privateRenderSlotDoNotUse(slotName: string) {
- return renderSlot(result, slots ? slots[slotName] : null);
- },
- // <Markdown> also needs the same `astroConfig.markdownOptions.render` as `.md` pages
- async privateRenderMarkdownDoNotUse(content: string, opts: any) {
- let mdRender = astroConfig.markdownOptions.render;
- let renderOpts = {};
- if (Array.isArray(mdRender)) {
- renderOpts = mdRender[1];
- mdRender = mdRender[0];
- }
- if (typeof mdRender === 'string') {
- ({ default: mdRender } = await import(mdRender));
- }
- const { code } = await mdRender(content, { ...renderOpts, ...(opts ?? {}) });
- return code;
- },
- } as unknown as AstroGlobal;
- },
- _metadata: {
- renderers,
- pathname,
- experimentalStaticBuild: astroConfig.buildOptions.experimentalStaticBuild,
- },
- };
-
- let html = await renderPage(result, Component, pageProps, null);
-
- return html;
+ const _links = new Set<SSRElement>(
+ links.map((href) => ({
+ props: {
+ rel: 'stylesheet',
+ href,
+ },
+ children: '',
+ }))
+ );
+ const result: SSRResult = {
+ styles: new Set<SSRElement>(),
+ scripts: new Set<SSRElement>(),
+ links: _links,
+ /** This function returns the `Astro` faux-global */
+ createAstro(astroGlobal: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null) {
+ const site = new URL(origin);
+ const url = new URL('.' + pathname, site);
+ const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin);
+ return {
+ __proto__: astroGlobal,
+ props,
+ request: {
+ canonicalURL,
+ params,
+ url,
+ },
+ slots: Object.fromEntries(Object.entries(slots || {}).map(([slotName]) => [slotName, true])),
+ // This is used for <Markdown> but shouldn't be used publicly
+ privateRenderSlotDoNotUse(slotName: string) {
+ return renderSlot(result, slots ? slots[slotName] : null);
+ },
+ // <Markdown> also needs the same `astroConfig.markdownOptions.render` as `.md` pages
+ async privateRenderMarkdownDoNotUse(content: string, opts: any) {
+ let mdRender = astroConfig.markdownOptions.render;
+ let renderOpts = {};
+ if (Array.isArray(mdRender)) {
+ renderOpts = mdRender[1];
+ mdRender = mdRender[0];
+ }
+ if (typeof mdRender === 'string') {
+ ({ default: mdRender } = await import(mdRender));
+ }
+ const { code } = await mdRender(content, { ...renderOpts, ...(opts ?? {}) });
+ return code;
+ },
+ } as unknown as AstroGlobal;
+ },
+ _metadata: {
+ renderers,
+ pathname,
+ experimentalStaticBuild: astroConfig.buildOptions.experimentalStaticBuild,
+ },
+ };
+
+ let html = await renderPage(result, Component, pageProps, null);
+
+ return html;
}
export async function getParamsAndProps({
- route,
- routeCache,
- logging,
- pathname,
- mod,
+ route,
+ routeCache,
+ logging,
+ pathname,
+ mod,
}: {
- route: RouteData | undefined;
- routeCache: RouteCache;
- pathname: string;
- mod: ComponentInstance;
- logging: LogOptions;
+ route: RouteData | undefined;
+ routeCache: RouteCache;
+ pathname: string;
+ mod: ComponentInstance;
+ logging: LogOptions;
}): Promise<[Params, Props]> {
- // Handle dynamic routes
- let params: Params = {};
- let pageProps: Props = {};
- if (route && !route.pathname) {
- if (route.params.length) {
- const paramsMatch = route.pattern.exec(pathname);
- if (paramsMatch) {
- params = getParams(route.params)(paramsMatch);
- }
- }
- validateGetStaticPathsModule(mod);
- if (!routeCache[route.component]) {
- routeCache[route.component] = await (
- await mod.getStaticPaths!({
- paginate: generatePaginateFunction(route),
- rss: () => {
- /* noop */
- },
- })
- ).flat();
- }
- validateGetStaticPathsResult(routeCache[route.component], logging);
- const routePathParams: GetStaticPathsResult = routeCache[route.component];
- const matchedStaticPath = routePathParams.find(({ params: _params }) => JSON.stringify(_params) === JSON.stringify(params));
- if (!matchedStaticPath) {
- throw new Error(`[getStaticPaths] route pattern matched, but no matching static path found. (${pathname})`);
- }
- pageProps = { ...matchedStaticPath.props } || {};
- }
- return [params, pageProps];
+ // Handle dynamic routes
+ let params: Params = {};
+ let pageProps: Props = {};
+ if (route && !route.pathname) {
+ if (route.params.length) {
+ const paramsMatch = route.pattern.exec(pathname);
+ if (paramsMatch) {
+ params = getParams(route.params)(paramsMatch);
+ }
+ }
+ validateGetStaticPathsModule(mod);
+ if (!routeCache[route.component]) {
+ routeCache[route.component] = await (
+ await mod.getStaticPaths!({
+ paginate: generatePaginateFunction(route),
+ rss: () => {
+ /* noop */
+ },
+ })
+ ).flat();
+ }
+ validateGetStaticPathsResult(routeCache[route.component], logging);
+ const routePathParams: GetStaticPathsResult = routeCache[route.component];
+ const matchedStaticPath = routePathParams.find(({ params: _params }) => JSON.stringify(_params) === JSON.stringify(params));
+ if (!matchedStaticPath) {
+ throw new Error(`[getStaticPaths] route pattern matched, but no matching static path found. (${pathname})`);
+ }
+ pageProps = { ...matchedStaticPath.props } || {};
+ }
+ return [params, pageProps];
}
/** use Vite to SSR */
export async function render(renderers: Renderer[], mod: ComponentInstance, ssrOpts: SSROptions): Promise<string> {
- const { astroConfig, filePath, logging, mode, origin, pathname, route, routeCache, viteServer } = ssrOpts;
-
- // Handle dynamic routes
- let params: Params = {};
- let pageProps: Props = {};
- if (route && !route.pathname) {
- if (route.params.length) {
- const paramsMatch = route.pattern.exec(pathname);
- if (paramsMatch) {
- params = getParams(route.params)(paramsMatch);
- }
- }
- validateGetStaticPathsModule(mod);
- if (!routeCache[route.component]) {
- routeCache[route.component] = await (
- await mod.getStaticPaths!({
- paginate: generatePaginateFunction(route),
- rss: () => {
- /* noop */
- },
- })
- ).flat();
- }
- validateGetStaticPathsResult(routeCache[route.component], logging);
- const routePathParams: GetStaticPathsResult = routeCache[route.component];
- const matchedStaticPath = routePathParams.find(({ params: _params }) => JSON.stringify(_params) === JSON.stringify(params));
- if (!matchedStaticPath) {
- throw new Error(`[getStaticPaths] route pattern matched, but no matching static path found. (${pathname})`);
- }
- pageProps = { ...matchedStaticPath.props } || {};
- }
-
- // Validate the page component before rendering the page
- const Component = await mod.default;
- if (!Component) throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`);
- if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`);
-
- // Create the result object that will be passed into the render function.
- // This object starts here as an empty shell (not yet the result) but then
- // calling the render() function will populate the object with scripts, styles, etc.
- const result: SSRResult = {
- styles: new Set<SSRElement>(),
- scripts: new Set<SSRElement>(),
- links: new Set<SSRElement>(),
- /** This function returns the `Astro` faux-global */
- createAstro(astroGlobal: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null) {
- const site = new URL(origin);
- const url = new URL('.' + pathname, site);
- const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin);
- return {
- __proto__: astroGlobal,
- props,
- request: {
- canonicalURL,
- params,
- url,
- },
- slots: Object.fromEntries(Object.entries(slots || {}).map(([slotName]) => [slotName, true])),
- // This is used for <Markdown> but shouldn't be used publicly
- privateRenderSlotDoNotUse(slotName: string) {
- return renderSlot(result, slots ? slots[slotName] : null);
- },
- // <Markdown> also needs the same `astroConfig.markdownOptions.render` as `.md` pages
- async privateRenderMarkdownDoNotUse(content: string, opts: any) {
- let mdRender = astroConfig.markdownOptions.render;
- let renderOpts = {};
- if (Array.isArray(mdRender)) {
- renderOpts = mdRender[1];
- mdRender = mdRender[0];
- }
- // ['rehype-toc', opts]
- if (typeof mdRender === 'string') {
- ({ default: mdRender } = await import(mdRender));
- }
- // [import('rehype-toc'), opts]
- else if (mdRender instanceof Promise) {
- ({ default: mdRender } = await mdRender);
- }
- const { code } = await mdRender(content, { ...renderOpts, ...(opts ?? {}) });
- return code;
- },
- } as unknown as AstroGlobal;
- },
- _metadata: {
- renderers,
- pathname,
- experimentalStaticBuild: astroConfig.buildOptions.experimentalStaticBuild,
- },
- };
-
- let html = await renderPage(result, Component, pageProps, null);
-
- // inject tags
- const tags: vite.HtmlTagDescriptor[] = [];
-
- // dev only: inject Astro HMR client
- if (mode === 'development') {
- tags.push({
- tag: 'script',
- attrs: { type: 'module' },
- // HACK: inject the direct contents of our `astro/runtime/client/hmr.js` to ensure
- // `import.meta.hot` is properly handled by Vite
- children: await getHmrScript(),
- injectTo: 'head',
- });
- }
-
- // inject CSS
- [...getStylesForURL(filePath, viteServer)].forEach((href) => {
- if (mode === 'development' && svelteStylesRE.test(href)) {
- tags.push({
- tag: 'script',
- attrs: { type: 'module', src: href },
- injectTo: 'head',
- });
- } else {
- tags.push({
- tag: 'link',
- attrs: {
- rel: 'stylesheet',
- href,
- 'data-astro-injected': true,
- },
- injectTo: 'head',
- });
- }
- });
-
- // add injected tags
- html = injectTags(html, tags);
-
- // run transformIndexHtml() in dev to run Vite dev transformations
- if (mode === 'development') {
- const relativeURL = filePath.href.replace(astroConfig.projectRoot.href, '/');
- html = await viteServer.transformIndexHtml(relativeURL, html, pathname);
- }
-
- // inject <!doctype html> if missing (TODO: is a more robust check needed for comments, etc.?)
- if (!/<!doctype html/i.test(html)) {
- html = '<!DOCTYPE html>\n' + html;
- }
-
- return html;
+ const { astroConfig, filePath, logging, mode, origin, pathname, route, routeCache, viteServer } = ssrOpts;
+
+ // Handle dynamic routes
+ let params: Params = {};
+ let pageProps: Props = {};
+ if (route && !route.pathname) {
+ if (route.params.length) {
+ const paramsMatch = route.pattern.exec(pathname);
+ if (paramsMatch) {
+ params = getParams(route.params)(paramsMatch);
+ }
+ }
+ validateGetStaticPathsModule(mod);
+ if (!routeCache[route.component]) {
+ routeCache[route.component] = await (
+ await mod.getStaticPaths!({
+ paginate: generatePaginateFunction(route),
+ rss: () => {
+ /* noop */
+ },
+ })
+ ).flat();
+ }
+ validateGetStaticPathsResult(routeCache[route.component], logging);
+ const routePathParams: GetStaticPathsResult = routeCache[route.component];
+ const matchedStaticPath = routePathParams.find(({ params: _params }) => JSON.stringify(_params) === JSON.stringify(params));
+ if (!matchedStaticPath) {
+ throw new Error(`[getStaticPaths] route pattern matched, but no matching static path found. (${pathname})`);
+ }
+ pageProps = { ...matchedStaticPath.props } || {};
+ }
+
+ // Validate the page component before rendering the page
+ const Component = await mod.default;
+ if (!Component) throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`);
+ if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`);
+
+ // Create the result object that will be passed into the render function.
+ // This object starts here as an empty shell (not yet the result) but then
+ // calling the render() function will populate the object with scripts, styles, etc.
+ const result: SSRResult = {
+ styles: new Set<SSRElement>(),
+ scripts: new Set<SSRElement>(),
+ links: new Set<SSRElement>(),
+ /** This function returns the `Astro` faux-global */
+ createAstro(astroGlobal: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null) {
+ const site = new URL(origin);
+ const url = new URL('.' + pathname, site);
+ const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin);
+ return {
+ __proto__: astroGlobal,
+ props,
+ request: {
+ canonicalURL,
+ params,
+ url,
+ },
+ slots: Object.fromEntries(Object.entries(slots || {}).map(([slotName]) => [slotName, true])),
+ // This is used for <Markdown> but shouldn't be used publicly
+ privateRenderSlotDoNotUse(slotName: string) {
+ return renderSlot(result, slots ? slots[slotName] : null);
+ },
+ // <Markdown> also needs the same `astroConfig.markdownOptions.render` as `.md` pages
+ async privateRenderMarkdownDoNotUse(content: string, opts: any) {
+ let mdRender = astroConfig.markdownOptions.render;
+ let renderOpts = {};
+ if (Array.isArray(mdRender)) {
+ renderOpts = mdRender[1];
+ mdRender = mdRender[0];
+ }
+ // ['rehype-toc', opts]
+ if (typeof mdRender === 'string') {
+ ({ default: mdRender } = await import(mdRender));
+ }
+ // [import('rehype-toc'), opts]
+ else if (mdRender instanceof Promise) {
+ ({ default: mdRender } = await mdRender);
+ }
+ const { code } = await mdRender(content, { ...renderOpts, ...(opts ?? {}) });
+ return code;
+ },
+ } as unknown as AstroGlobal;
+ },
+ _metadata: {
+ renderers,
+ pathname,
+ experimentalStaticBuild: astroConfig.buildOptions.experimentalStaticBuild,
+ },
+ };
+
+ let html = await renderPage(result, Component, pageProps, null);
+
+ // inject tags
+ const tags: vite.HtmlTagDescriptor[] = [];
+
+ // dev only: inject Astro HMR client
+ if (mode === 'development') {
+ tags.push({
+ tag: 'script',
+ attrs: { type: 'module' },
+ // HACK: inject the direct contents of our `astro/runtime/client/hmr.js` to ensure
+ // `import.meta.hot` is properly handled by Vite
+ children: await getHmrScript(),
+ injectTo: 'head',
+ });
+ }
+
+ // inject CSS
+ [...getStylesForURL(filePath, viteServer)].forEach((href) => {
+ if (mode === 'development' && svelteStylesRE.test(href)) {
+ tags.push({
+ tag: 'script',
+ attrs: { type: 'module', src: href },
+ injectTo: 'head',
+ });
+ } else {
+ tags.push({
+ tag: 'link',
+ attrs: {
+ rel: 'stylesheet',
+ href,
+ 'data-astro-injected': true,
+ },
+ injectTo: 'head',
+ });
+ }
+ });
+
+ // add injected tags
+ html = injectTags(html, tags);
+
+ // run transformIndexHtml() in dev to run Vite dev transformations
+ if (mode === 'development') {
+ const relativeURL = filePath.href.replace(astroConfig.projectRoot.href, '/');
+ html = await viteServer.transformIndexHtml(relativeURL, html, pathname);
+ }
+
+ // inject <!doctype html> if missing (TODO: is a more robust check needed for comments, etc.?)
+ if (!/<!doctype html/i.test(html)) {
+ html = '<!DOCTYPE html>\n' + html;
+ }
+
+ return html;
}
let hmrScript: string;
async function getHmrScript() {
- if (hmrScript) return hmrScript;
- const filePath = fileURLToPath(new URL('../../runtime/client/hmr.js', import.meta.url));
- const content = await fs.promises.readFile(filePath);
- hmrScript = content.toString();
- return hmrScript;
+ if (hmrScript) return hmrScript;
+ const filePath = fileURLToPath(new URL('../../runtime/client/hmr.js', import.meta.url));
+ const content = await fs.promises.readFile(filePath);
+ hmrScript = content.toString();
+ return hmrScript;
}
export async function ssr(ssrOpts: SSROptions): Promise<string> {
- try {
- const [renderers, mod] = await preload(ssrOpts);
- return await render(renderers, mod, ssrOpts); // note(drew): without "await", errors won’t get caught by errorHandler()
- } catch (e: unknown) {
- await errorHandler(e, { viteServer: ssrOpts.viteServer, filePath: ssrOpts.filePath });
- throw e;
- }
+ try {
+ const [renderers, mod] = await preload(ssrOpts);
+ return await render(renderers, mod, ssrOpts); // note(drew): without "await", errors won’t get caught by errorHandler()
+ } catch (e: unknown) {
+ await errorHandler(e, { viteServer: ssrOpts.viteServer, filePath: ssrOpts.filePath });
+ throw e;
+ }
}
diff --git a/packages/astro/src/core/ssr/paginate.ts b/packages/astro/src/core/ssr/paginate.ts
index 0b50c6237..96d8a435a 100644
--- a/packages/astro/src/core/ssr/paginate.ts
+++ b/packages/astro/src/core/ssr/paginate.ts
@@ -1,53 +1,53 @@
import { GetStaticPathsResult, Page, PaginateFunction, Params, Props, RouteData } from '../../@types/astro';
export function generatePaginateFunction(routeMatch: RouteData): PaginateFunction {
- return function paginateUtility(data: any[], args: { pageSize?: number; params?: Params; props?: Props } = {}) {
- let { pageSize: _pageSize, params: _params, props: _props } = args;
- const pageSize = _pageSize || 10;
- const paramName = 'page';
- const additionalParams = _params || {};
- const additionalProps = _props || {};
- let includesFirstPageNumber: boolean;
- if (routeMatch.params.includes(`...${paramName}`)) {
- includesFirstPageNumber = false;
- } else if (routeMatch.params.includes(`${paramName}`)) {
- includesFirstPageNumber = true;
- } else {
- throw new Error(
- `[paginate()] page number param \`${paramName}\` not found in your filepath.\nRename your file to \`[...page].astro\` or customize the param name via the \`paginate([], {param: '...'}\` option.`
- );
- }
- const lastPage = Math.max(1, Math.ceil(data.length / pageSize));
+ return function paginateUtility(data: any[], args: { pageSize?: number; params?: Params; props?: Props } = {}) {
+ let { pageSize: _pageSize, params: _params, props: _props } = args;
+ const pageSize = _pageSize || 10;
+ const paramName = 'page';
+ const additionalParams = _params || {};
+ const additionalProps = _props || {};
+ let includesFirstPageNumber: boolean;
+ if (routeMatch.params.includes(`...${paramName}`)) {
+ includesFirstPageNumber = false;
+ } else if (routeMatch.params.includes(`${paramName}`)) {
+ includesFirstPageNumber = true;
+ } else {
+ throw new Error(
+ `[paginate()] page number param \`${paramName}\` not found in your filepath.\nRename your file to \`[...page].astro\` or customize the param name via the \`paginate([], {param: '...'}\` option.`
+ );
+ }
+ const lastPage = Math.max(1, Math.ceil(data.length / pageSize));
- const result: GetStaticPathsResult = [...Array(lastPage).keys()].map((num) => {
- const pageNum = num + 1;
- const start = pageSize === Infinity ? 0 : (pageNum - 1) * pageSize; // currentPage is 1-indexed
- const end = Math.min(start + pageSize, data.length);
- const params = {
- ...additionalParams,
- [paramName]: includesFirstPageNumber || pageNum > 1 ? String(pageNum) : undefined,
- };
- return {
- params,
- props: {
- ...additionalProps,
- page: {
- data: data.slice(start, end),
- start,
- end: end - 1,
- size: pageSize,
- total: data.length,
- currentPage: pageNum,
- lastPage: lastPage,
- url: {
- current: routeMatch.generate({ ...params }),
- next: pageNum === lastPage ? undefined : routeMatch.generate({ ...params, page: String(pageNum + 1) }),
- prev: pageNum === 1 ? undefined : routeMatch.generate({ ...params, page: !includesFirstPageNumber && pageNum - 1 === 1 ? undefined : String(pageNum - 1) }),
- },
- } as Page,
- },
- };
- });
- return result;
- };
+ const result: GetStaticPathsResult = [...Array(lastPage).keys()].map((num) => {
+ const pageNum = num + 1;
+ const start = pageSize === Infinity ? 0 : (pageNum - 1) * pageSize; // currentPage is 1-indexed
+ const end = Math.min(start + pageSize, data.length);
+ const params = {
+ ...additionalParams,
+ [paramName]: includesFirstPageNumber || pageNum > 1 ? String(pageNum) : undefined,
+ };
+ return {
+ params,
+ props: {
+ ...additionalProps,
+ page: {
+ data: data.slice(start, end),
+ start,
+ end: end - 1,
+ size: pageSize,
+ total: data.length,
+ currentPage: pageNum,
+ lastPage: lastPage,
+ url: {
+ current: routeMatch.generate({ ...params }),
+ next: pageNum === lastPage ? undefined : routeMatch.generate({ ...params, page: String(pageNum + 1) }),
+ prev: pageNum === 1 ? undefined : routeMatch.generate({ ...params, page: !includesFirstPageNumber && pageNum - 1 === 1 ? undefined : String(pageNum - 1) }),
+ },
+ } as Page,
+ },
+ };
+ });
+ return result;
+ };
}
diff --git a/packages/astro/src/core/ssr/routing.ts b/packages/astro/src/core/ssr/routing.ts
index d3d20c2d1..b6a2cf1a4 100644
--- a/packages/astro/src/core/ssr/routing.ts
+++ b/packages/astro/src/core/ssr/routing.ts
@@ -14,337 +14,337 @@ import { warn } from '../logger.js';
* that turns a RegExpExecArray into ({ x, y, z })
*/
export function getParams(array: string[]) {
- const fn = (match: RegExpExecArray) => {
- const params: Params = {};
- array.forEach((key, i) => {
- if (key.startsWith('...')) {
- params[key.slice(3)] = match[i + 1] ? decodeURIComponent(match[i + 1]) : undefined;
- } else {
- params[key] = decodeURIComponent(match[i + 1]);
- }
- });
- return params;
- };
-
- return fn;
+ const fn = (match: RegExpExecArray) => {
+ const params: Params = {};
+ array.forEach((key, i) => {
+ if (key.startsWith('...')) {
+ params[key.slice(3)] = match[i + 1] ? decodeURIComponent(match[i + 1]) : undefined;
+ } else {
+ params[key] = decodeURIComponent(match[i + 1]);
+ }
+ });
+ return params;
+ };
+
+ return fn;
}
/** Find matching route from pathname */
export function matchRoute(pathname: string, manifest: ManifestData): RouteData | undefined {
- return manifest.routes.find((route) => route.pattern.test(pathname));
+ return manifest.routes.find((route) => route.pattern.test(pathname));
}
/** Throw error for deprecated/malformed APIs */
export function validateGetStaticPathsModule(mod: ComponentInstance) {
- if ((mod as any).createCollection) {
- throw new Error(`[createCollection] deprecated. Please use getStaticPaths() instead.`);
- }
- if (!mod.getStaticPaths) {
- throw new Error(`[getStaticPaths] getStaticPaths() function is required. Make sure that you \`export\` the function from your component.`);
- }
+ if ((mod as any).createCollection) {
+ throw new Error(`[createCollection] deprecated. Please use getStaticPaths() instead.`);
+ }
+ if (!mod.getStaticPaths) {
+ throw new Error(`[getStaticPaths] getStaticPaths() function is required. Make sure that you \`export\` the function from your component.`);
+ }
}
/** Throw error for malformed getStaticPaths() response */
export function validateGetStaticPathsResult(result: GetStaticPathsResult, logging: LogOptions) {
- if (!Array.isArray(result)) {
- throw new Error(`[getStaticPaths] invalid return value. Expected an array of path objects, but got \`${JSON.stringify(result)}\`.`);
- }
- result.forEach((pathObject) => {
- if (!pathObject.params) {
- warn(logging, 'getStaticPaths', `invalid path object. Expected an object with key \`params\`, but got \`${JSON.stringify(pathObject)}\`. Skipped.`);
- return;
- }
- for (const [key, val] of Object.entries(pathObject.params)) {
- if (!(typeof val === 'undefined' || typeof val === 'string')) {
- warn(logging, 'getStaticPaths', `invalid path param: ${key}. A string value was expected, but got \`${JSON.stringify(val)}\`.`);
- }
- if (val === '') {
- warn(logging, 'getStaticPaths', `invalid path param: ${key}. \`undefined\` expected for an optional param, but got empty string.`);
- }
- }
- });
+ if (!Array.isArray(result)) {
+ throw new Error(`[getStaticPaths] invalid return value. Expected an array of path objects, but got \`${JSON.stringify(result)}\`.`);
+ }
+ result.forEach((pathObject) => {
+ if (!pathObject.params) {
+ warn(logging, 'getStaticPaths', `invalid path object. Expected an object with key \`params\`, but got \`${JSON.stringify(pathObject)}\`. Skipped.`);
+ return;
+ }
+ for (const [key, val] of Object.entries(pathObject.params)) {
+ if (!(typeof val === 'undefined' || typeof val === 'string')) {
+ warn(logging, 'getStaticPaths', `invalid path param: ${key}. A string value was expected, but got \`${JSON.stringify(val)}\`.`);
+ }
+ if (val === '') {
+ warn(logging, 'getStaticPaths', `invalid path param: ${key}. \`undefined\` expected for an optional param, but got empty string.`);
+ }
+ }
+ });
}
interface Part {
- content: string;
- dynamic: boolean;
- spread: boolean;
+ content: string;
+ dynamic: boolean;
+ spread: boolean;
}
interface Item {
- basename: string;
- ext: string;
- parts: Part[];
- file: string;
- isDir: boolean;
- isIndex: boolean;
- isPage: boolean;
- routeSuffix: string;
+ basename: string;
+ ext: string;
+ parts: Part[];
+ file: string;
+ isDir: boolean;
+ isIndex: boolean;
+ isPage: boolean;
+ routeSuffix: string;
}
/** Create manifest of all static routes */
export function createRouteManifest({ config, cwd }: { config: AstroConfig; cwd?: string }, logging: LogOptions): ManifestData {
- const components: string[] = [];
- const routes: RouteData[] = [];
- const validExtensions: Set<string> = new Set(['.astro', '.md']);
-
- function walk(dir: string, parentSegments: Part[][], parentParams: string[]) {
- let items: Item[] = [];
- fs.readdirSync(dir).forEach((basename) => {
- const resolved = path.join(dir, basename);
- const file = slash(path.relative(cwd || fileURLToPath(config.projectRoot), resolved));
- const isDir = fs.statSync(resolved).isDirectory();
-
- const ext = path.extname(basename);
- const name = ext ? basename.slice(0, -ext.length) : basename;
-
- if (name[0] === '_') {
- return;
- }
- if (basename[0] === '.' && basename !== '.well-known') {
- return;
- }
- // filter out "foo.astro_tmp" files, etc
- if (!isDir && !validExtensions.has(ext)) {
- return;
- }
- const segment = isDir ? basename : name;
- if (/^\$/.test(segment)) {
- throw new Error(`Invalid route ${file} — Astro's Collections API has been replaced by dynamic route params.`);
- }
- if (/\]\[/.test(segment)) {
- throw new Error(`Invalid route ${file} — parameters must be separated`);
- }
- if (countOccurrences('[', segment) !== countOccurrences(']', segment)) {
- throw new Error(`Invalid route ${file} — brackets are unbalanced`);
- }
- if (/.+\[\.\.\.[^\]]+\]/.test(segment) || /\[\.\.\.[^\]]+\].+/.test(segment)) {
- throw new Error(`Invalid route ${file} — rest parameter must be a standalone segment`);
- }
-
- const parts = getParts(segment, file);
- const isIndex = isDir ? false : basename.startsWith('index.');
- const routeSuffix = basename.slice(basename.indexOf('.'), -ext.length);
-
- items.push({
- basename,
- ext,
- parts,
- file: slash(file),
- isDir,
- isIndex,
- isPage: true,
- routeSuffix,
- });
- });
- items = items.sort(comparator);
-
- items.forEach((item) => {
- const segments = parentSegments.slice();
-
- if (item.isIndex) {
- if (item.routeSuffix) {
- if (segments.length > 0) {
- const lastSegment = segments[segments.length - 1].slice();
- const lastPart = lastSegment[lastSegment.length - 1];
-
- if (lastPart.dynamic) {
- lastSegment.push({
- dynamic: false,
- spread: false,
- content: item.routeSuffix,
- });
- } else {
- lastSegment[lastSegment.length - 1] = {
- dynamic: false,
- spread: false,
- content: `${lastPart.content}${item.routeSuffix}`,
- };
- }
-
- segments[segments.length - 1] = lastSegment;
- } else {
- segments.push(item.parts);
- }
- }
- } else {
- segments.push(item.parts);
- }
-
- const params = parentParams.slice();
- params.push(...item.parts.filter((p) => p.dynamic).map((p) => p.content));
-
- if (item.isDir) {
- walk(path.join(dir, item.basename), segments, params);
- } else {
- components.push(item.file);
- const component = item.file;
- const pattern = getPattern(segments, config.devOptions.trailingSlash);
- const generate = getGenerator(segments, config.devOptions.trailingSlash);
- const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic) ? `/${segments.map((segment) => segment[0].content).join('/')}` : null;
-
- routes.push({
- type: 'page',
- pattern,
- params,
- component,
- generate,
- pathname: pathname || undefined,
- });
- }
- });
- }
-
- if (fs.existsSync(config.pages)) {
- walk(fileURLToPath(config.pages), [], []);
- } else {
- const pagesDirRootRelative = config.pages.href.slice(config.projectRoot.href.length);
-
- warn(logging, 'astro', `Missing pages directory: ${pagesDirRootRelative}`);
- }
-
- return {
- routes,
- };
+ const components: string[] = [];
+ const routes: RouteData[] = [];
+ const validExtensions: Set<string> = new Set(['.astro', '.md']);
+
+ function walk(dir: string, parentSegments: Part[][], parentParams: string[]) {
+ let items: Item[] = [];
+ fs.readdirSync(dir).forEach((basename) => {
+ const resolved = path.join(dir, basename);
+ const file = slash(path.relative(cwd || fileURLToPath(config.projectRoot), resolved));
+ const isDir = fs.statSync(resolved).isDirectory();
+
+ const ext = path.extname(basename);
+ const name = ext ? basename.slice(0, -ext.length) : basename;
+
+ if (name[0] === '_') {
+ return;
+ }
+ if (basename[0] === '.' && basename !== '.well-known') {
+ return;
+ }
+ // filter out "foo.astro_tmp" files, etc
+ if (!isDir && !validExtensions.has(ext)) {
+ return;
+ }
+ const segment = isDir ? basename : name;
+ if (/^\$/.test(segment)) {
+ throw new Error(`Invalid route ${file} — Astro's Collections API has been replaced by dynamic route params.`);
+ }
+ if (/\]\[/.test(segment)) {
+ throw new Error(`Invalid route ${file} — parameters must be separated`);
+ }
+ if (countOccurrences('[', segment) !== countOccurrences(']', segment)) {
+ throw new Error(`Invalid route ${file} — brackets are unbalanced`);
+ }
+ if (/.+\[\.\.\.[^\]]+\]/.test(segment) || /\[\.\.\.[^\]]+\].+/.test(segment)) {
+ throw new Error(`Invalid route ${file} — rest parameter must be a standalone segment`);
+ }
+
+ const parts = getParts(segment, file);
+ const isIndex = isDir ? false : basename.startsWith('index.');
+ const routeSuffix = basename.slice(basename.indexOf('.'), -ext.length);
+
+ items.push({
+ basename,
+ ext,
+ parts,
+ file: slash(file),
+ isDir,
+ isIndex,
+ isPage: true,
+ routeSuffix,
+ });
+ });
+ items = items.sort(comparator);
+
+ items.forEach((item) => {
+ const segments = parentSegments.slice();
+
+ if (item.isIndex) {
+ if (item.routeSuffix) {
+ if (segments.length > 0) {
+ const lastSegment = segments[segments.length - 1].slice();
+ const lastPart = lastSegment[lastSegment.length - 1];
+
+ if (lastPart.dynamic) {
+ lastSegment.push({
+ dynamic: false,
+ spread: false,
+ content: item.routeSuffix,
+ });
+ } else {
+ lastSegment[lastSegment.length - 1] = {
+ dynamic: false,
+ spread: false,
+ content: `${lastPart.content}${item.routeSuffix}`,
+ };
+ }
+
+ segments[segments.length - 1] = lastSegment;
+ } else {
+ segments.push(item.parts);
+ }
+ }
+ } else {
+ segments.push(item.parts);
+ }
+
+ const params = parentParams.slice();
+ params.push(...item.parts.filter((p) => p.dynamic).map((p) => p.content));
+
+ if (item.isDir) {
+ walk(path.join(dir, item.basename), segments, params);
+ } else {
+ components.push(item.file);
+ const component = item.file;
+ const pattern = getPattern(segments, config.devOptions.trailingSlash);
+ const generate = getGenerator(segments, config.devOptions.trailingSlash);
+ const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic) ? `/${segments.map((segment) => segment[0].content).join('/')}` : null;
+
+ routes.push({
+ type: 'page',
+ pattern,
+ params,
+ component,
+ generate,
+ pathname: pathname || undefined,
+ });
+ }
+ });
+ }
+
+ if (fs.existsSync(config.pages)) {
+ walk(fileURLToPath(config.pages), [], []);
+ } else {
+ const pagesDirRootRelative = config.pages.href.slice(config.projectRoot.href.length);
+
+ warn(logging, 'astro', `Missing pages directory: ${pagesDirRootRelative}`);
+ }
+
+ return {
+ routes,
+ };
}
function countOccurrences(needle: string, haystack: string) {
- let count = 0;
- for (let i = 0; i < haystack.length; i += 1) {
- if (haystack[i] === needle) count += 1;
- }
- return count;
+ let count = 0;
+ for (let i = 0; i < haystack.length; i += 1) {
+ if (haystack[i] === needle) count += 1;
+ }
+ return count;
}
function isSpread(str: string) {
- const spreadPattern = /\[\.{3}/g;
- return spreadPattern.test(str);
+ const spreadPattern = /\[\.{3}/g;
+ return spreadPattern.test(str);
}
function comparator(a: Item, b: Item) {
- if (a.isIndex !== b.isIndex) {
- if (a.isIndex) return isSpread(a.file) ? 1 : -1;
+ if (a.isIndex !== b.isIndex) {
+ if (a.isIndex) return isSpread(a.file) ? 1 : -1;
- return isSpread(b.file) ? -1 : 1;
- }
+ return isSpread(b.file) ? -1 : 1;
+ }
- const max = Math.max(a.parts.length, b.parts.length);
+ const max = Math.max(a.parts.length, b.parts.length);
- for (let i = 0; i < max; i += 1) {
- const aSubPart = a.parts[i];
- const bSubPart = b.parts[i];
+ for (let i = 0; i < max; i += 1) {
+ const aSubPart = a.parts[i];
+ const bSubPart = b.parts[i];
- if (!aSubPart) return 1; // b is more specific, so goes first
- if (!bSubPart) return -1;
+ if (!aSubPart) return 1; // b is more specific, so goes first
+ if (!bSubPart) return -1;
- // if spread && index, order later
- if (aSubPart.spread && bSubPart.spread) {
- return a.isIndex ? 1 : -1;
- }
+ // if spread && index, order later
+ if (aSubPart.spread && bSubPart.spread) {
+ return a.isIndex ? 1 : -1;
+ }
- // If one is ...spread order it later
- if (aSubPart.spread !== bSubPart.spread) return aSubPart.spread ? 1 : -1;
+ // If one is ...spread order it later
+ if (aSubPart.spread !== bSubPart.spread) return aSubPart.spread ? 1 : -1;
- if (aSubPart.dynamic !== bSubPart.dynamic) {
- return aSubPart.dynamic ? 1 : -1;
- }
+ if (aSubPart.dynamic !== bSubPart.dynamic) {
+ return aSubPart.dynamic ? 1 : -1;
+ }
- if (!aSubPart.dynamic && aSubPart.content !== bSubPart.content) {
- return bSubPart.content.length - aSubPart.content.length || (aSubPart.content < bSubPart.content ? -1 : 1);
- }
- }
+ if (!aSubPart.dynamic && aSubPart.content !== bSubPart.content) {
+ return bSubPart.content.length - aSubPart.content.length || (aSubPart.content < bSubPart.content ? -1 : 1);
+ }
+ }
- if (a.isPage !== b.isPage) {
- return a.isPage ? 1 : -1;
- }
+ if (a.isPage !== b.isPage) {
+ return a.isPage ? 1 : -1;
+ }
- // otherwise sort alphabetically
- return a.file < b.file ? -1 : 1;
+ // otherwise sort alphabetically
+ return a.file < b.file ? -1 : 1;
}
function getParts(part: string, file: string) {
- const result: Part[] = [];
- part.split(/\[(.+?\(.+?\)|.+?)\]/).map((str, i) => {
- if (!str) return;
- const dynamic = i % 2 === 1;
+ const result: Part[] = [];
+ part.split(/\[(.+?\(.+?\)|.+?)\]/).map((str, i) => {
+ if (!str) return;
+ const dynamic = i % 2 === 1;
- const [, content] = dynamic ? /([^(]+)$/.exec(str) || [null, null] : [null, str];
+ const [, content] = dynamic ? /([^(]+)$/.exec(str) || [null, null] : [null, str];
- if (!content || (dynamic && !/^(\.\.\.)?[a-zA-Z0-9_$]+$/.test(content))) {
- throw new Error(`Invalid route ${file} — parameter name must match /^[a-zA-Z0-9_$]+$/`);
- }
+ if (!content || (dynamic && !/^(\.\.\.)?[a-zA-Z0-9_$]+$/.test(content))) {
+ throw new Error(`Invalid route ${file} — parameter name must match /^[a-zA-Z0-9_$]+$/`);
+ }
- result.push({
- content,
- dynamic,
- spread: dynamic && /^\.{3}.+$/.test(content),
- });
- });
+ result.push({
+ content,
+ dynamic,
+ spread: dynamic && /^\.{3}.+$/.test(content),
+ });
+ });
- return result;
+ return result;
}
function getTrailingSlashPattern(addTrailingSlash: AstroConfig['devOptions']['trailingSlash']): string {
- if (addTrailingSlash === 'always') {
- return '\\/$';
- }
- if (addTrailingSlash === 'never') {
- return '$';
- }
- return '\\/?$';
+ if (addTrailingSlash === 'always') {
+ return '\\/$';
+ }
+ if (addTrailingSlash === 'never') {
+ return '$';
+ }
+ return '\\/?$';
}
function getPattern(segments: Part[][], addTrailingSlash: AstroConfig['devOptions']['trailingSlash']) {
- const pathname = segments
- .map((segment) => {
- return segment[0].spread
- ? '(?:\\/(.*?))?'
- : '\\/' +
- segment
- .map((part) => {
- if (part)
- return part.dynamic
- ? '([^/]+?)'
- : part.content
- .normalize()
- .replace(/\?/g, '%3F')
- .replace(/#/g, '%23')
- .replace(/%5B/g, '[')
- .replace(/%5D/g, ']')
- .replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- })
- .join('');
- })
- .join('');
-
- const trailing = addTrailingSlash && segments.length ? getTrailingSlashPattern(addTrailingSlash) : '$';
- return new RegExp(`^${pathname || '\\/'}${trailing}`);
+ const pathname = segments
+ .map((segment) => {
+ return segment[0].spread
+ ? '(?:\\/(.*?))?'
+ : '\\/' +
+ segment
+ .map((part) => {
+ if (part)
+ return part.dynamic
+ ? '([^/]+?)'
+ : part.content
+ .normalize()
+ .replace(/\?/g, '%3F')
+ .replace(/#/g, '%23')
+ .replace(/%5B/g, '[')
+ .replace(/%5D/g, ']')
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ })
+ .join('');
+ })
+ .join('');
+
+ const trailing = addTrailingSlash && segments.length ? getTrailingSlashPattern(addTrailingSlash) : '$';
+ return new RegExp(`^${pathname || '\\/'}${trailing}`);
}
function getGenerator(segments: Part[][], addTrailingSlash: AstroConfig['devOptions']['trailingSlash']) {
- const template = segments
- .map((segment) => {
- return segment[0].spread
- ? `/:${segment[0].content.substr(3)}(.*)?`
- : '/' +
- segment
- .map((part) => {
- if (part)
- return part.dynamic
- ? `:${part.content}`
- : part.content
- .normalize()
- .replace(/\?/g, '%3F')
- .replace(/#/g, '%23')
- .replace(/%5B/g, '[')
- .replace(/%5D/g, ']')
- .replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- })
- .join('');
- })
- .join('');
-
- const trailing = addTrailingSlash !== 'never' && segments.length ? '/' : '';
- const toPath = compile(template + trailing);
- return toPath;
+ const template = segments
+ .map((segment) => {
+ return segment[0].spread
+ ? `/:${segment[0].content.substr(3)}(.*)?`
+ : '/' +
+ segment
+ .map((part) => {
+ if (part)
+ return part.dynamic
+ ? `:${part.content}`
+ : part.content
+ .normalize()
+ .replace(/\?/g, '%3F')
+ .replace(/#/g, '%23')
+ .replace(/%5B/g, '[')
+ .replace(/%5D/g, ']')
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ })
+ .join('');
+ })
+ .join('');
+
+ const trailing = addTrailingSlash !== 'never' && segments.length ? '/' : '';
+ const toPath = compile(template + trailing);
+ return toPath;
}
diff --git a/packages/astro/src/core/ssr/rss.ts b/packages/astro/src/core/ssr/rss.ts
index 4a691565d..d1f1469b1 100644
--- a/packages/astro/src/core/ssr/rss.ts
+++ b/packages/astro/src/core/ssr/rss.ts
@@ -5,85 +5,85 @@ import { canonicalURL } from '../util.js';
/** Validates getStaticPaths.rss */
export function validateRSS(args: GenerateRSSArgs): void {
- const { rssData, srcFile } = args;
- if (!rssData.title) throw new Error(`[${srcFile}] rss.title required`);
- if (!rssData.description) throw new Error(`[${srcFile}] rss.description required`);
- if ((rssData as any).item) throw new Error(`[${srcFile}] \`item: Function\` should be \`items: Item[]\``);
- if (!Array.isArray(rssData.items)) throw new Error(`[${srcFile}] rss.items should be an array of items`);
+ const { rssData, srcFile } = args;
+ if (!rssData.title) throw new Error(`[${srcFile}] rss.title required`);
+ if (!rssData.description) throw new Error(`[${srcFile}] rss.description required`);
+ if ((rssData as any).item) throw new Error(`[${srcFile}] \`item: Function\` should be \`items: Item[]\``);
+ if (!Array.isArray(rssData.items)) throw new Error(`[${srcFile}] rss.items should be an array of items`);
}
type GenerateRSSArgs = { site: string; rssData: RSS; srcFile: string; feedURL: string };
/** Generate RSS 2.0 feed */
export function generateRSS(args: GenerateRSSArgs): string {
- validateRSS(args);
- const { srcFile, feedURL, rssData, site } = args;
- if ((rssData as any).item) throw new Error(`[${srcFile}] rss() \`item()\` function was deprecated, and is now \`items: object[]\`.`);
+ validateRSS(args);
+ const { srcFile, feedURL, rssData, site } = args;
+ if ((rssData as any).item) throw new Error(`[${srcFile}] rss() \`item()\` function was deprecated, and is now \`items: object[]\`.`);
- let xml = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"`;
+ let xml = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"`;
- // xmlns
- if (rssData.xmlns) {
- for (const [k, v] of Object.entries(rssData.xmlns)) {
- xml += ` xmlns:${k}="${v}"`;
- }
- }
- xml += `>`;
- xml += `<channel>`;
+ // xmlns
+ if (rssData.xmlns) {
+ for (const [k, v] of Object.entries(rssData.xmlns)) {
+ xml += ` xmlns:${k}="${v}"`;
+ }
+ }
+ xml += `>`;
+ xml += `<channel>`;
- // title, description, customData
- xml += `<title><![CDATA[${rssData.title}]]></title>`;
- xml += `<description><![CDATA[${rssData.description}]]></description>`;
- xml += `<link>${canonicalURL(feedURL, site).href}</link>`;
- if (typeof rssData.customData === 'string') xml += rssData.customData;
- // items
- for (const result of rssData.items) {
- xml += `<item>`;
- // validate
- if (typeof result !== 'object') throw new Error(`[${srcFile}] rss.items expected an object. got: "${JSON.stringify(result)}"`);
- if (!result.title) throw new Error(`[${srcFile}] rss.items required "title" property is missing. got: "${JSON.stringify(result)}"`);
- if (!result.link) throw new Error(`[${srcFile}] rss.items required "link" property is missing. got: "${JSON.stringify(result)}"`);
- xml += `<title><![CDATA[${result.title}]]></title>`;
- xml += `<link>${canonicalURL(result.link, site).href}</link>`;
- if (result.description) xml += `<description><![CDATA[${result.description}]]></description>`;
- if (result.pubDate) {
- // note: this should be a Date, but if user provided a string or number, we can work with that, too.
- if (typeof result.pubDate === 'number' || typeof result.pubDate === 'string') {
- result.pubDate = new Date(result.pubDate);
- } else if (result.pubDate instanceof Date === false) {
- throw new Error('[${filename}] rss.item().pubDate must be a Date');
- }
- xml += `<pubDate>${result.pubDate.toUTCString()}</pubDate>`;
- }
- if (typeof result.customData === 'string') xml += result.customData;
- xml += `</item>`;
- }
+ // title, description, customData
+ xml += `<title><![CDATA[${rssData.title}]]></title>`;
+ xml += `<description><![CDATA[${rssData.description}]]></description>`;
+ xml += `<link>${canonicalURL(feedURL, site).href}</link>`;
+ if (typeof rssData.customData === 'string') xml += rssData.customData;
+ // items
+ for (const result of rssData.items) {
+ xml += `<item>`;
+ // validate
+ if (typeof result !== 'object') throw new Error(`[${srcFile}] rss.items expected an object. got: "${JSON.stringify(result)}"`);
+ if (!result.title) throw new Error(`[${srcFile}] rss.items required "title" property is missing. got: "${JSON.stringify(result)}"`);
+ if (!result.link) throw new Error(`[${srcFile}] rss.items required "link" property is missing. got: "${JSON.stringify(result)}"`);
+ xml += `<title><![CDATA[${result.title}]]></title>`;
+ xml += `<link>${canonicalURL(result.link, site).href}</link>`;
+ if (result.description) xml += `<description><![CDATA[${result.description}]]></description>`;
+ if (result.pubDate) {
+ // note: this should be a Date, but if user provided a string or number, we can work with that, too.
+ if (typeof result.pubDate === 'number' || typeof result.pubDate === 'string') {
+ result.pubDate = new Date(result.pubDate);
+ } else if (result.pubDate instanceof Date === false) {
+ throw new Error('[${filename}] rss.item().pubDate must be a Date');
+ }
+ xml += `<pubDate>${result.pubDate.toUTCString()}</pubDate>`;
+ }
+ if (typeof result.customData === 'string') xml += result.customData;
+ xml += `</item>`;
+ }
- xml += `</channel></rss>`;
+ xml += `</channel></rss>`;
- // validate user’s inputs to see if it’s valid XML
- const isValid = XMLValidator.validate(xml);
- if (isValid !== true) {
- // If valid XML, isValid will be `true`. Otherwise, this will be an error object. Throw.
- throw new Error(isValid as any);
- }
+ // validate user’s inputs to see if it’s valid XML
+ const isValid = XMLValidator.validate(xml);
+ if (isValid !== true) {
+ // If valid XML, isValid will be `true`. Otherwise, this will be an error object. Throw.
+ throw new Error(isValid as any);
+ }
- return xml;
+ return xml;
}
/** Generated function to be run */
export function generateRssFunction(site: string | undefined, route: RouteData): { generator: RSSFunction; rss?: RSSResult } {
- let result: RSSResult = {} as any;
- return {
- generator: function rssUtility(args: any) {
- if (!site) {
- throw new Error(`[${route.component}] rss() tried to generate RSS but "buildOptions.site" missing in astro.config.mjs`);
- }
- const { dest, ...rssData } = args;
- const feedURL = dest || '/rss.xml';
- result.url = feedURL;
- result.xml = generateRSS({ rssData, site, srcFile: route.component, feedURL });
- },
- rss: result,
- };
+ let result: RSSResult = {} as any;
+ return {
+ generator: function rssUtility(args: any) {
+ if (!site) {
+ throw new Error(`[${route.component}] rss() tried to generate RSS but "buildOptions.site" missing in astro.config.mjs`);
+ }
+ const { dest, ...rssData } = args;
+ const feedURL = dest || '/rss.xml';
+ result.url = feedURL;
+ result.xml = generateRSS({ rssData, site, srcFile: route.component, feedURL });
+ },
+ rss: result,
+ };
}
diff --git a/packages/astro/src/core/ssr/sitemap.ts b/packages/astro/src/core/ssr/sitemap.ts
index facf49b1f..fd39098e7 100644
--- a/packages/astro/src/core/ssr/sitemap.ts
+++ b/packages/astro/src/core/ssr/sitemap.ts
@@ -1,17 +1,17 @@
/** Construct sitemap.xml given a set of URLs */
export function generateSitemap(pages: string[]): string {
- // TODO: find way to respect <link rel="canonical"> URLs here
+ // TODO: find way to respect <link rel="canonical"> URLs here
- // TODO: find way to exclude pages from sitemap
+ // TODO: find way to exclude pages from sitemap
- // copy just in case original copy is needed
- // make sure that 404 page is excluded
- const urls = [...pages].filter((url) => !url.endsWith('/404/index.html'));
- urls.sort((a, b) => a.localeCompare(b, 'en', { numeric: true })); // sort alphabetically so sitemap is same each time
- let sitemap = `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`;
- for (const url of urls) {
- sitemap += `<url><loc>${url}</loc></url>`;
- }
- sitemap += `</urlset>\n`;
- return sitemap;
+ // copy just in case original copy is needed
+ // make sure that 404 page is excluded
+ const urls = [...pages].filter((url) => !url.endsWith('/404/index.html'));
+ urls.sort((a, b) => a.localeCompare(b, 'en', { numeric: true })); // sort alphabetically so sitemap is same each time
+ let sitemap = `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`;
+ for (const url of urls) {
+ sitemap += `<url><loc>${url}</loc></url>`;
+ }
+ sitemap += `</urlset>\n`;
+ return sitemap;
}
diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts
index 51ba0db8a..83a0a9f40 100644
--- a/packages/astro/src/core/util.ts
+++ b/packages/astro/src/core/util.ts
@@ -8,69 +8,69 @@ import resolve from 'resolve';
/** Normalize URL to its canonical form */
export function canonicalURL(url: string, base?: string): URL {
- let pathname = url.replace(/\/index.html$/, ''); // index.html is not canonical
- pathname = pathname.replace(/\/1\/?$/, ''); // neither is a trailing /1/ (impl. detail of collections)
- if (!path.extname(pathname)) pathname = pathname.replace(/(\/+)?$/, '/'); // add trailing slash if there’s no extension
- pathname = pathname.replace(/\/+/g, '/'); // remove duplicate slashes (URL() won’t)
- return new URL(pathname, base);
+ let pathname = url.replace(/\/index.html$/, ''); // index.html is not canonical
+ pathname = pathname.replace(/\/1\/?$/, ''); // neither is a trailing /1/ (impl. detail of collections)
+ if (!path.extname(pathname)) pathname = pathname.replace(/(\/+)?$/, '/'); // add trailing slash if there’s no extension
+ pathname = pathname.replace(/\/+/g, '/'); // remove duplicate slashes (URL() won’t)
+ return new URL(pathname, base);
}
/** is a specifier an npm package? */
export function parseNpmName(spec: string): { scope?: string; name: string; subpath?: string } | undefined {
- // not an npm package
- if (!spec || spec[0] === '.' || spec[0] === '/') return undefined;
+ // not an npm package
+ if (!spec || spec[0] === '.' || spec[0] === '/') return undefined;
- let scope: string | undefined;
- let name = '';
+ let scope: string | undefined;
+ let name = '';
- let parts = spec.split('/');
- if (parts[0][0] === '@') {
- scope = parts[0];
- name = parts.shift() + '/';
- }
- name += parts.shift();
+ let parts = spec.split('/');
+ if (parts[0][0] === '@') {
+ scope = parts[0];
+ name = parts.shift() + '/';
+ }
+ name += parts.shift();
- let subpath = parts.length ? `./${parts.join('/')}` : undefined;
+ let subpath = parts.length ? `./${parts.join('/')}` : undefined;
- return {
- scope,
- name,
- subpath,
- };
+ return {
+ scope,
+ name,
+ subpath,
+ };
}
/** generate code frame from esbuild error */
export function codeFrame(src: string, loc: ErrorPayload['err']['loc']): string {
- if (!loc) return '';
- const lines = eol.lf(src).split('\n');
- // grab 2 lines before, and 3 lines after focused line
- const visibleLines = [];
- for (let n = -2; n <= 2; n++) {
- if (lines[loc.line + n]) visibleLines.push(loc.line + n);
- }
- // figure out gutter width
- let gutterWidth = 0;
- for (const lineNo of visibleLines) {
- let w = `> ${lineNo}`;
- if (w.length > gutterWidth) gutterWidth = w.length;
- }
- // print lines
- let output = '';
- for (const lineNo of visibleLines) {
- const isFocusedLine = lineNo === loc.line - 1;
- output += isFocusedLine ? '> ' : ' ';
- output += `${lineNo + 1} | ${lines[lineNo]}\n`;
- if (isFocusedLine) output += `${[...new Array(gutterWidth)].join(' ')} | ${[...new Array(loc.column)].join(' ')}^\n`;
- }
- return output;
+ if (!loc) return '';
+ const lines = eol.lf(src).split('\n');
+ // grab 2 lines before, and 3 lines after focused line
+ const visibleLines = [];
+ for (let n = -2; n <= 2; n++) {
+ if (lines[loc.line + n]) visibleLines.push(loc.line + n);
+ }
+ // figure out gutter width
+ let gutterWidth = 0;
+ for (const lineNo of visibleLines) {
+ let w = `> ${lineNo}`;
+ if (w.length > gutterWidth) gutterWidth = w.length;
+ }
+ // print lines
+ let output = '';
+ for (const lineNo of visibleLines) {
+ const isFocusedLine = lineNo === loc.line - 1;
+ output += isFocusedLine ? '> ' : ' ';
+ output += `${lineNo + 1} | ${lines[lineNo]}\n`;
+ if (isFocusedLine) output += `${[...new Array(gutterWidth)].join(' ')} | ${[...new Array(loc.column)].join(' ')}^\n`;
+ }
+ return output;
}
export function resolveDependency(dep: string, astroConfig: AstroConfig) {
- const resolved = resolve.sync(dep, {
- basedir: fileURLToPath(astroConfig.projectRoot),
- });
- // For Windows compat, we need a fully resolved `file://` URL string
- return pathToFileURL(resolved).toString();
+ const resolved = resolve.sync(dep, {
+ basedir: fileURLToPath(astroConfig.projectRoot),
+ });
+ // For Windows compat, we need a fully resolved `file://` URL string
+ return pathToFileURL(resolved).toString();
}
/**
@@ -80,5 +80,5 @@ export function resolveDependency(dep: string, astroConfig: AstroConfig) {
* Windows: C:/Users/astro/code/my-project/src/pages/index.astro
*/
export function viteID(filePath: URL): string {
- return slash(fileURLToPath(filePath));
+ return slash(fileURLToPath(filePath));
}
diff --git a/packages/astro/src/runtime/client/hmr.ts b/packages/astro/src/runtime/client/hmr.ts
index eb4574085..ca98ceeff 100644
--- a/packages/astro/src/runtime/client/hmr.ts
+++ b/packages/astro/src/runtime/client/hmr.ts
@@ -1,38 +1,38 @@
if (import.meta.hot) {
- const parser = new DOMParser();
- import.meta.hot.on('astro:reload', async ({ html }: { html: string }) => {
- const { default: morphdom } = await import('morphdom');
- const doc = parser.parseFromString(html, 'text/html');
+ const parser = new DOMParser();
+ import.meta.hot.on('astro:reload', async ({ html }: { html: string }) => {
+ const { default: morphdom } = await import('morphdom');
+ const doc = parser.parseFromString(html, 'text/html');
- morphdom(document.head, doc.head, {
- onBeforeElUpdated(fromEl, toEl) {
- // Do not update identical tags
- if (fromEl.isEqualNode(toEl)) {
- return false;
- }
+ morphdom(document.head, doc.head, {
+ onBeforeElUpdated(fromEl, toEl) {
+ // Do not update identical tags
+ if (fromEl.isEqualNode(toEl)) {
+ return false;
+ }
- // Do not update <link> or <script> tags
- // to avoid re-fetching their contents
- if (fromEl.tagName === toEl.tagName && (toEl.tagName === 'LINK' || toEl.tagName === 'SCRIPT')) {
- return false;
- }
+ // Do not update <link> or <script> tags
+ // to avoid re-fetching their contents
+ if (fromEl.tagName === toEl.tagName && (toEl.tagName === 'LINK' || toEl.tagName === 'SCRIPT')) {
+ return false;
+ }
- return true;
- },
- });
+ return true;
+ },
+ });
- morphdom(document.body, doc.body, {
- onBeforeElUpdated(fromEl, toEl) {
- if (fromEl.localName === 'astro-root') {
- return fromEl.getAttribute('uid') !== toEl.getAttribute('uid');
- }
+ morphdom(document.body, doc.body, {
+ onBeforeElUpdated(fromEl, toEl) {
+ if (fromEl.localName === 'astro-root') {
+ return fromEl.getAttribute('uid') !== toEl.getAttribute('uid');
+ }
- if (fromEl.isEqualNode(toEl)) {
- return false;
- }
+ if (fromEl.isEqualNode(toEl)) {
+ return false;
+ }
- return true;
- },
- });
- });
+ return true;
+ },
+ });
+ });
}
diff --git a/packages/astro/src/runtime/client/idle.ts b/packages/astro/src/runtime/client/idle.ts
index 6f35f6958..c3914cfbe 100644
--- a/packages/astro/src/runtime/client/idle.ts
+++ b/packages/astro/src/runtime/client/idle.ts
@@ -5,19 +5,19 @@ import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
* (or after a short delay, if `requestIdleCallback`) isn't supported
*/
export default async function onIdle(astroId: string, _options: HydrateOptions, getHydrateCallback: GetHydrateCallback) {
- const cb = async () => {
- const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
- const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
- const hydrate = await getHydrateCallback();
+ const cb = async () => {
+ const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
+ const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
+ const hydrate = await getHydrateCallback();
- for (const root of roots) {
- hydrate(root, innerHTML);
- }
- };
+ for (const root of roots) {
+ hydrate(root, innerHTML);
+ }
+ };
- if ('requestIdleCallback' in window) {
- (window as any).requestIdleCallback(cb);
- } else {
- setTimeout(cb, 200);
- }
+ if ('requestIdleCallback' in window) {
+ (window as any).requestIdleCallback(cb);
+ } else {
+ setTimeout(cb, 200);
+ }
}
diff --git a/packages/astro/src/runtime/client/load.ts b/packages/astro/src/runtime/client/load.ts
index 5d83f340d..c3fae489e 100644
--- a/packages/astro/src/runtime/client/load.ts
+++ b/packages/astro/src/runtime/client/load.ts
@@ -4,11 +4,11 @@ import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
* Hydrate this component immediately
*/
export default async function onLoad(astroId: string, _options: HydrateOptions, getHydrateCallback: GetHydrateCallback) {
- const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
- const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
- const hydrate = await getHydrateCallback();
+ const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
+ const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
+ const hydrate = await getHydrateCallback();
- for (const root of roots) {
- hydrate(root, innerHTML);
- }
+ for (const root of roots) {
+ hydrate(root, innerHTML);
+ }
}
diff --git a/packages/astro/src/runtime/client/media.ts b/packages/astro/src/runtime/client/media.ts
index c846db78f..ef2f65260 100644
--- a/packages/astro/src/runtime/client/media.ts
+++ b/packages/astro/src/runtime/client/media.ts
@@ -4,22 +4,22 @@ import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
* Hydrate this component when a matching media query is found
*/
export default async function onMedia(astroId: string, options: HydrateOptions, getHydrateCallback: GetHydrateCallback) {
- const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
- const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
+ const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
+ const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
- const cb = async () => {
- const hydrate = await getHydrateCallback();
- for (const root of roots) {
- hydrate(root, innerHTML);
- }
- };
+ const cb = async () => {
+ const hydrate = await getHydrateCallback();
+ for (const root of roots) {
+ hydrate(root, innerHTML);
+ }
+ };
- if (options.value) {
- const mql = matchMedia(options.value);
- if (mql.matches) {
- cb();
- } else {
- mql.addEventListener('change', cb, { once: true });
- }
- }
+ if (options.value) {
+ const mql = matchMedia(options.value);
+ if (mql.matches) {
+ cb();
+ } else {
+ mql.addEventListener('change', cb, { once: true });
+ }
+ }
}
diff --git a/packages/astro/src/runtime/client/only.ts b/packages/astro/src/runtime/client/only.ts
index 5d83f340d..c3fae489e 100644
--- a/packages/astro/src/runtime/client/only.ts
+++ b/packages/astro/src/runtime/client/only.ts
@@ -4,11 +4,11 @@ import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
* Hydrate this component immediately
*/
export default async function onLoad(astroId: string, _options: HydrateOptions, getHydrateCallback: GetHydrateCallback) {
- const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
- const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
- const hydrate = await getHydrateCallback();
+ const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
+ const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
+ const hydrate = await getHydrateCallback();
- for (const root of roots) {
- hydrate(root, innerHTML);
- }
+ for (const root of roots) {
+ hydrate(root, innerHTML);
+ }
}
diff --git a/packages/astro/src/runtime/client/visible.ts b/packages/astro/src/runtime/client/visible.ts
index 8abd6e1c4..e06aabab4 100644
--- a/packages/astro/src/runtime/client/visible.ts
+++ b/packages/astro/src/runtime/client/visible.ts
@@ -6,30 +6,30 @@ import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
* which doesn't work with IntersectionObserver
*/
export default async function onVisible(astroId: string, _options: HydrateOptions, getHydrateCallback: GetHydrateCallback) {
- const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
- const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
+ const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
+ const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
- const cb = async () => {
- const hydrate = await getHydrateCallback();
- for (const root of roots) {
- hydrate(root, innerHTML);
- }
- };
+ const cb = async () => {
+ const hydrate = await getHydrateCallback();
+ for (const root of roots) {
+ hydrate(root, innerHTML);
+ }
+ };
- const io = new IntersectionObserver((entries) => {
- for (const entry of entries) {
- if (!entry.isIntersecting) continue;
- // As soon as we hydrate, disconnect this IntersectionObserver for every `astro-root`
- io.disconnect();
- cb();
- break; // break loop on first match
- }
- });
+ const io = new IntersectionObserver((entries) => {
+ for (const entry of entries) {
+ if (!entry.isIntersecting) continue;
+ // As soon as we hydrate, disconnect this IntersectionObserver for every `astro-root`
+ io.disconnect();
+ cb();
+ break; // break loop on first match
+ }
+ });
- for (const root of roots) {
- for (let i = 0; i < root.children.length; i++) {
- const child = root.children[i];
- io.observe(child);
- }
- }
+ for (const root of roots) {
+ for (let i = 0; i < root.children.length; i++) {
+ const child = root.children[i];
+ io.observe(child);
+ }
+ }
}
diff --git a/packages/astro/src/runtime/server/hydration.ts b/packages/astro/src/runtime/server/hydration.ts
index 935e543a2..fd71287dd 100644
--- a/packages/astro/src/runtime/server/hydration.ts
+++ b/packages/astro/src/runtime/server/hydration.ts
@@ -11,127 +11,127 @@ const { generate, GENERATOR } = astring;
// A more robust version alternative to `JSON.stringify` that can handle most values
// see https://github.com/remcohaszing/estree-util-value-to-estree#readme
const customGenerator: astring.Generator = {
- ...GENERATOR,
- Literal(node, state) {
- if (node.raw != null) {
- // escape closing script tags in strings so browsers wouldn't interpret them as
- // closing the actual end tag in HTML
- state.write(node.raw.replace('</script>', '<\\/script>'));
- } else {
- GENERATOR.Literal(node, state);
- }
- },
+ ...GENERATOR,
+ Literal(node, state) {
+ if (node.raw != null) {
+ // escape closing script tags in strings so browsers wouldn't interpret them as
+ // closing the actual end tag in HTML
+ state.write(node.raw.replace('</script>', '<\\/script>'));
+ } else {
+ GENERATOR.Literal(node, state);
+ }
+ },
};
// Serializes props passed into a component so that they can be reused during hydration.
// The value is any
export function serializeProps(value: any) {
- return generate(valueToEstree(value), {
- generator: customGenerator,
- });
+ return generate(valueToEstree(value), {
+ generator: customGenerator,
+ });
}
const HydrationDirectives = ['load', 'idle', 'media', 'visible', 'only'];
interface ExtractedProps {
- hydration: {
- directive: string;
- value: string;
- componentUrl: string;
- componentExport: { value: string };
- } | null;
- props: Record<string | number, any>;
+ hydration: {
+ directive: string;
+ value: string;
+ componentUrl: string;
+ componentExport: { value: string };
+ } | null;
+ props: Record<string | number, any>;
}
// Used to extract the directives, aka `client:load` information about a component.
// Finds these special props and removes them from what gets passed into the component.
export function extractDirectives(inputProps: Record<string | number, any>): ExtractedProps {
- let extracted: ExtractedProps = {
- hydration: null,
- props: {},
- };
- for (const [key, value] of Object.entries(inputProps)) {
- if (key.startsWith('client:')) {
- if (!extracted.hydration) {
- extracted.hydration = {
- directive: '',
- value: '',
- componentUrl: '',
- componentExport: { value: '' },
- };
- }
- switch (key) {
- case 'client:component-path': {
- extracted.hydration.componentUrl = value;
- break;
- }
- case 'client:component-export': {
- extracted.hydration.componentExport.value = value;
- break;
- }
- default: {
- extracted.hydration.directive = key.split(':')[1];
- extracted.hydration.value = value;
-
- // throw an error if an invalid hydration directive was provided
- if (HydrationDirectives.indexOf(extracted.hydration.directive) < 0) {
- throw new Error(`Error: invalid hydration directive "${key}". Supported hydration methods: ${HydrationDirectives.map((d) => `"client:${d}"`).join(', ')}`);
- }
-
- // throw an error if the query wasn't provided for client:media
- if (extracted.hydration.directive === 'media' && typeof extracted.hydration.value !== 'string') {
- throw new Error('Error: Media query must be provided for "client:media", similar to client:media="(max-width: 600px)"');
- }
-
- break;
- }
- }
- } else if (key === 'class:list') {
- // support "class" from an expression passed into a component (#782)
- extracted.props[key.slice(0, -5)] = serializeListValue(value);
- } else {
- extracted.props[key] = value;
- }
- }
- return extracted;
+ let extracted: ExtractedProps = {
+ hydration: null,
+ props: {},
+ };
+ for (const [key, value] of Object.entries(inputProps)) {
+ if (key.startsWith('client:')) {
+ if (!extracted.hydration) {
+ extracted.hydration = {
+ directive: '',
+ value: '',
+ componentUrl: '',
+ componentExport: { value: '' },
+ };
+ }
+ switch (key) {
+ case 'client:component-path': {
+ extracted.hydration.componentUrl = value;
+ break;
+ }
+ case 'client:component-export': {
+ extracted.hydration.componentExport.value = value;
+ break;
+ }
+ default: {
+ extracted.hydration.directive = key.split(':')[1];
+ extracted.hydration.value = value;
+
+ // throw an error if an invalid hydration directive was provided
+ if (HydrationDirectives.indexOf(extracted.hydration.directive) < 0) {
+ throw new Error(`Error: invalid hydration directive "${key}". Supported hydration methods: ${HydrationDirectives.map((d) => `"client:${d}"`).join(', ')}`);
+ }
+
+ // throw an error if the query wasn't provided for client:media
+ if (extracted.hydration.directive === 'media' && typeof extracted.hydration.value !== 'string') {
+ throw new Error('Error: Media query must be provided for "client:media", similar to client:media="(max-width: 600px)"');
+ }
+
+ break;
+ }
+ }
+ } else if (key === 'class:list') {
+ // support "class" from an expression passed into a component (#782)
+ extracted.props[key.slice(0, -5)] = serializeListValue(value);
+ } else {
+ extracted.props[key] = value;
+ }
+ }
+ return extracted;
}
interface HydrateScriptOptions {
- renderer: any;
- astroId: string;
- props: Record<string | number, any>;
+ renderer: any;
+ astroId: string;
+ props: Record<string | number, any>;
}
/** For hydrated components, generate a <script type="module"> to load the component */
export async function generateHydrateScript(scriptOptions: HydrateScriptOptions, metadata: Required<AstroComponentMetadata>): Promise<SSRElement> {
- const { renderer, astroId, props } = scriptOptions;
- const { hydrate, componentUrl, componentExport } = metadata;
+ const { renderer, astroId, props } = scriptOptions;
+ const { hydrate, componentUrl, componentExport } = metadata;
- if (!componentExport) {
- throw new Error(`Unable to resolve a componentExport for "${metadata.displayName}"! Please open an issue.`);
- }
+ if (!componentExport) {
+ throw new Error(`Unable to resolve a componentExport for "${metadata.displayName}"! Please open an issue.`);
+ }
- let hydrationSource = '';
- if (renderer.hydrationPolyfills) {
- hydrationSource += `await Promise.all([${renderer.hydrationPolyfills.map((src: string) => `\n import("${src}")`).join(', ')}]);\n`;
- }
+ let hydrationSource = '';
+ if (renderer.hydrationPolyfills) {
+ hydrationSource += `await Promise.all([${renderer.hydrationPolyfills.map((src: string) => `\n import("${src}")`).join(', ')}]);\n`;
+ }
- hydrationSource += renderer.source
- ? `const [{ ${componentExport.value}: Component }, { default: hydrate }] = await Promise.all([import("${componentUrl}"), import("${renderer.source}")]);
+ hydrationSource += renderer.source
+ ? `const [{ ${componentExport.value}: Component }, { default: hydrate }] = await Promise.all([import("${componentUrl}"), import("${renderer.source}")]);
return (el, children) => hydrate(el)(Component, ${serializeProps(props)}, children);
`
- : `await import("${componentUrl}");
+ : `await import("${componentUrl}");
return () => {};
`;
- const hydrationScript = {
- props: { type: 'module', 'data-astro-component-hydration': true },
- children: `import setup from 'astro/client/${hydrate}.js';
+ const hydrationScript = {
+ props: { type: 'module', 'data-astro-component-hydration': true },
+ children: `import setup from 'astro/client/${hydrate}.js';
setup("${astroId}", {${metadata.hydrateArgs ? `value: ${JSON.stringify(metadata.hydrateArgs)}` : ''}}, async () => {
${hydrationSource}
});
`,
- };
+ };
- return hydrationScript;
+ return hydrationScript;
}
diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts
index 601dec4bc..7bbba1a4b 100644
--- a/packages/astro/src/runtime/server/index.ts
+++ b/packages/astro/src/runtime/server/index.ts
@@ -17,191 +17,191 @@ const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|l
// If these are intentional, add comments that these are intention and why.
// Or maybe type UserValue = any; ?
async function _render(child: any): Promise<any> {
- child = await child;
- if (Array.isArray(child)) {
- return (await Promise.all(child.map((value) => _render(value)))).join('');
- } else if (typeof child === 'function') {
- // Special: If a child is a function, call it automatically.
- // This lets you do {() => ...} without the extra boilerplate
- // of wrapping it in a function and calling it.
- return _render(child());
- } else if (typeof child === 'string') {
- return child;
- } else if (!child && child !== 0) {
- // do nothing, safe to ignore falsey values.
- }
- // Add a comment explaining why each of these are needed.
- // Maybe create clearly named function for what this is doing.
- else if (child instanceof AstroComponent || Object.prototype.toString.call(child) === '[object AstroComponent]') {
- return await renderAstroComponent(child);
- } else {
- return child;
- }
+ child = await child;
+ if (Array.isArray(child)) {
+ return (await Promise.all(child.map((value) => _render(value)))).join('');
+ } else if (typeof child === 'function') {
+ // Special: If a child is a function, call it automatically.
+ // This lets you do {() => ...} without the extra boilerplate
+ // of wrapping it in a function and calling it.
+ return _render(child());
+ } else if (typeof child === 'string') {
+ return child;
+ } else if (!child && child !== 0) {
+ // do nothing, safe to ignore falsey values.
+ }
+ // Add a comment explaining why each of these are needed.
+ // Maybe create clearly named function for what this is doing.
+ else if (child instanceof AstroComponent || Object.prototype.toString.call(child) === '[object AstroComponent]') {
+ return await renderAstroComponent(child);
+ } else {
+ return child;
+ }
}
// The return value when rendering a component.
// This is the result of calling render(), should this be named to RenderResult or...?
export class AstroComponent {
- private htmlParts: TemplateStringsArray;
- private expressions: any[];
+ private htmlParts: TemplateStringsArray;
+ private expressions: any[];
- constructor(htmlParts: TemplateStringsArray, expressions: any[]) {
- this.htmlParts = htmlParts;
- this.expressions = expressions;
- }
+ constructor(htmlParts: TemplateStringsArray, expressions: any[]) {
+ this.htmlParts = htmlParts;
+ this.expressions = expressions;
+ }
- get [Symbol.toStringTag]() {
- return 'AstroComponent';
- }
+ get [Symbol.toStringTag]() {
+ return 'AstroComponent';
+ }
- *[Symbol.iterator]() {
- const { htmlParts, expressions } = this;
+ *[Symbol.iterator]() {
+ const { htmlParts, expressions } = this;
- for (let i = 0; i < htmlParts.length; i++) {
- const html = htmlParts[i];
- const expression = expressions[i];
+ for (let i = 0; i < htmlParts.length; i++) {
+ const html = htmlParts[i];
+ const expression = expressions[i];
- yield _render(html);
- yield _render(expression);
- }
- }
+ yield _render(html);
+ yield _render(expression);
+ }
+ }
}
export async function render(htmlParts: TemplateStringsArray, ...expressions: any[]) {
- return new AstroComponent(htmlParts, expressions);
+ return new AstroComponent(htmlParts, expressions);
}
// The callback passed to to $$createComponent
export interface AstroComponentFactory {
- (result: any, props: any, slots: any): ReturnType<typeof render>;
- isAstroComponentFactory?: boolean;
+ (result: any, props: any, slots: any): ReturnType<typeof render>;
+ isAstroComponentFactory?: boolean;
}
// Used in creating the component. aka the main export.
export function createComponent(cb: AstroComponentFactory) {
- // Add a flag to this callback to mark it as an Astro component
- // INVESTIGATE does this need to cast
- (cb as any).isAstroComponentFactory = true;
- return cb;
+ // Add a flag to this callback to mark it as an Astro component
+ // INVESTIGATE does this need to cast
+ (cb as any).isAstroComponentFactory = true;
+ return cb;
}
export async function renderSlot(_result: any, slotted: string, fallback?: any) {
- if (slotted) {
- return _render(slotted);
- }
- return fallback;
+ if (slotted) {
+ return _render(slotted);
+ }
+ return fallback;
}
export const Fragment = Symbol('Astro.Fragment');
function guessRenderers(componentUrl?: string): string[] {
- const extname = componentUrl?.split('.').pop();
- switch (extname) {
- case 'svelte':
- return ['@astrojs/renderer-svelte'];
- case 'vue':
- return ['@astrojs/renderer-vue'];
- case 'jsx':
- case 'tsx':
- return ['@astrojs/renderer-react', '@astrojs/renderer-preact'];
- default:
- return ['@astrojs/renderer-react', '@astrojs/renderer-preact', '@astrojs/renderer-vue', '@astrojs/renderer-svelte'];
- }
+ const extname = componentUrl?.split('.').pop();
+ switch (extname) {
+ case 'svelte':
+ return ['@astrojs/renderer-svelte'];
+ case 'vue':
+ return ['@astrojs/renderer-vue'];
+ case 'jsx':
+ case 'tsx':
+ return ['@astrojs/renderer-react', '@astrojs/renderer-preact'];
+ default:
+ return ['@astrojs/renderer-react', '@astrojs/renderer-preact', '@astrojs/renderer-vue', '@astrojs/renderer-svelte'];
+ }
}
function formatList(values: string[]): string {
- if (values.length === 1) {
- return values[0];
- }
- return `${values.slice(0, -1).join(', ')} or ${values[values.length - 1]}`;
+ if (values.length === 1) {
+ return values[0];
+ }
+ return `${values.slice(0, -1).join(', ')} or ${values[values.length - 1]}`;
}
export async function renderComponent(result: SSRResult, displayName: string, Component: unknown, _props: Record<string | number, any>, slots: any = {}) {
- Component = await Component;
- const children = await renderSlot(result, slots?.default);
+ Component = await Component;
+ const children = await renderSlot(result, slots?.default);
- if (Component === Fragment) {
- return children;
- }
+ if (Component === Fragment) {
+ return children;
+ }
- if (Component && (Component as any).isAstroComponentFactory) {
- const output = await renderToString(result, Component as any, _props, slots);
- return output;
- }
+ if (Component && (Component as any).isAstroComponentFactory) {
+ const output = await renderToString(result, Component as any, _props, slots);
+ return output;
+ }
- if (Component === null && !_props['client:only']) {
- throw new Error(`Unable to render ${displayName} because it is ${Component}!\nDid you forget to import the component or is it possible there is a typo?`);
- }
+ if (Component === null && !_props['client:only']) {
+ throw new Error(`Unable to render ${displayName} because it is ${Component}!\nDid you forget to import the component or is it possible there is a typo?`);
+ }
- const { renderers } = result._metadata;
- const metadata: AstroComponentMetadata = { displayName };
+ const { renderers } = result._metadata;
+ const metadata: AstroComponentMetadata = { displayName };
- const { hydration, props } = extractDirectives(_props);
- let html = '';
+ const { hydration, props } = extractDirectives(_props);
+ let html = '';
- if (hydration) {
- metadata.hydrate = hydration.directive as AstroComponentMetadata['hydrate'];
- metadata.hydrateArgs = hydration.value;
- metadata.componentExport = hydration.componentExport;
- metadata.componentUrl = hydration.componentUrl;
- }
- const probableRendererNames = guessRenderers(metadata.componentUrl);
+ if (hydration) {
+ metadata.hydrate = hydration.directive as AstroComponentMetadata['hydrate'];
+ metadata.hydrateArgs = hydration.value;
+ metadata.componentExport = hydration.componentExport;
+ metadata.componentUrl = hydration.componentUrl;
+ }
+ const probableRendererNames = guessRenderers(metadata.componentUrl);
- if (Array.isArray(renderers) && renderers.length === 0 && typeof Component !== 'string') {
- const message = `Unable to render ${metadata.displayName}!
+ if (Array.isArray(renderers) && renderers.length === 0 && typeof Component !== 'string') {
+ const message = `Unable to render ${metadata.displayName}!
There are no \`renderers\` set in your \`astro.config.mjs\` file.
Did you mean to enable ${formatList(probableRendererNames.map((r) => '`' + r + '`'))}?`;
- throw new Error(message);
- }
-
- // Call the renderers `check` hook to see if any claim this component.
- let renderer: Renderer | undefined;
- if (metadata.hydrate !== 'only') {
- for (const r of renderers) {
- if (await r.ssr.check(Component, props, children)) {
- renderer = r;
- break;
- }
- }
- } else {
- // Attempt: use explicitly passed renderer name
- if (metadata.hydrateArgs) {
- const rendererName = metadata.hydrateArgs;
- renderer = renderers.filter(({ name }) => name === `@astrojs/renderer-${rendererName}` || name === rendererName)[0];
- }
- // Attempt: user only has a single renderer, default to that
- if (!renderer && renderers.length === 1) {
- renderer = renderers[0];
- }
- // Attempt: can we guess the renderer from the export extension?
- if (!renderer) {
- const extname = metadata.componentUrl?.split('.').pop();
- renderer = renderers.filter(({ name }) => name === `@astrojs/renderer-${extname}` || name === extname)[0];
- }
- }
-
- // If no one claimed the renderer
- if (!renderer) {
- if (metadata.hydrate === 'only') {
- // TODO: improve error message
- throw new Error(`Unable to render ${metadata.displayName}!
+ throw new Error(message);
+ }
+
+ // Call the renderers `check` hook to see if any claim this component.
+ let renderer: Renderer | undefined;
+ if (metadata.hydrate !== 'only') {
+ for (const r of renderers) {
+ if (await r.ssr.check(Component, props, children)) {
+ renderer = r;
+ break;
+ }
+ }
+ } else {
+ // Attempt: use explicitly passed renderer name
+ if (metadata.hydrateArgs) {
+ const rendererName = metadata.hydrateArgs;
+ renderer = renderers.filter(({ name }) => name === `@astrojs/renderer-${rendererName}` || name === rendererName)[0];
+ }
+ // Attempt: user only has a single renderer, default to that
+ if (!renderer && renderers.length === 1) {
+ renderer = renderers[0];
+ }
+ // Attempt: can we guess the renderer from the export extension?
+ if (!renderer) {
+ const extname = metadata.componentUrl?.split('.').pop();
+ renderer = renderers.filter(({ name }) => name === `@astrojs/renderer-${extname}` || name === extname)[0];
+ }
+ }
+
+ // If no one claimed the renderer
+ if (!renderer) {
+ if (metadata.hydrate === 'only') {
+ // TODO: improve error message
+ throw new Error(`Unable to render ${metadata.displayName}!
Using the \`client:only\` hydration strategy, Astro needs a hint to use the correct renderer.
Did you mean to pass <${metadata.displayName} client:only="${probableRendererNames.map((r) => r.replace('@astrojs/renderer-', '')).join('|')}" />
`);
- } else if (typeof Component !== 'string') {
- const matchingRenderers = renderers.filter((r) => probableRendererNames.includes(r.name));
- const plural = renderers.length > 1;
- if (matchingRenderers.length === 0) {
- throw new Error(`Unable to render ${metadata.displayName}!
+ } else if (typeof Component !== 'string') {
+ const matchingRenderers = renderers.filter((r) => probableRendererNames.includes(r.name));
+ const plural = renderers.length > 1;
+ if (matchingRenderers.length === 0) {
+ throw new Error(`Unable to render ${metadata.displayName}!
There ${plural ? 'are' : 'is'} ${renderers.length} renderer${plural ? 's' : ''} configured in your \`astro.config.mjs\` file,
but ${plural ? 'none were' : 'it was not'} able to server-side render ${metadata.displayName}.
Did you mean to enable ${formatList(probableRendererNames.map((r) => '`' + r + '`'))}?`);
- } else {
- throw new Error(`Unable to render ${metadata.displayName}!
+ } else {
+ throw new Error(`Unable to render ${metadata.displayName}!
This component likely uses ${formatList(probableRendererNames)},
but Astro encountered an error during server-side rendering.
@@ -212,232 +212,232 @@ Please ensure that ${metadata.displayName}:
2. Does not conditionally return \`null\` or \`undefined\` when rendered on the server.
If you're still stuck, please open an issue on GitHub or join us at https://astro.build/chat.`);
- }
- }
- } else {
- if (metadata.hydrate === 'only') {
- html = await renderSlot(result, slots?.fallback);
- } else {
- ({ html } = await renderer.ssr.renderToStaticMarkup(Component, props, children));
- }
- }
-
- // This is a custom element without a renderer. Because of that, render it
- // as a string and the user is responsible for adding a script tag for the component definition.
- if (!html && typeof Component === 'string') {
- html = await renderAstroComponent(
- await render`<${Component}${spreadAttributes(props)}${(children == null || children == '') && voidElementNames.test(Component) ? `/>` : `>${children}</${Component}>`}`
- );
- }
-
- // This is used to add polyfill scripts to the page, if the renderer needs them.
- if (renderer?.polyfills?.length) {
- for (const src of renderer.polyfills) {
- result.scripts.add({
- props: { type: 'module' },
- children: `import "${src}";`,
- });
- }
- }
-
- if (!hydration) {
- return html.replace(/\<\/?astro-fragment\>/g, '');
- }
-
- // Include componentExport name and componentUrl in hash to dedupe identical islands
- const astroId = shorthash.unique(`<!--${metadata.componentExport!.value}:${metadata.componentUrl}-->\n${html}`);
-
- // Rather than appending this inline in the page, puts this into the `result.scripts` set that will be appended to the head.
- // INVESTIGATE: This will likely be a problem in streaming because the `<head>` will be gone at this point.
- result.scripts.add(await generateHydrateScript({ renderer, astroId, props }, metadata as Required<AstroComponentMetadata>));
-
- return `<astro-root uid="${astroId}">${html ?? ''}</astro-root>`;
+ }
+ }
+ } else {
+ if (metadata.hydrate === 'only') {
+ html = await renderSlot(result, slots?.fallback);
+ } else {
+ ({ html } = await renderer.ssr.renderToStaticMarkup(Component, props, children));
+ }
+ }
+
+ // This is a custom element without a renderer. Because of that, render it
+ // as a string and the user is responsible for adding a script tag for the component definition.
+ if (!html && typeof Component === 'string') {
+ html = await renderAstroComponent(
+ await render`<${Component}${spreadAttributes(props)}${(children == null || children == '') && voidElementNames.test(Component) ? `/>` : `>${children}</${Component}>`}`
+ );
+ }
+
+ // This is used to add polyfill scripts to the page, if the renderer needs them.
+ if (renderer?.polyfills?.length) {
+ for (const src of renderer.polyfills) {
+ result.scripts.add({
+ props: { type: 'module' },
+ children: `import "${src}";`,
+ });
+ }
+ }
+
+ if (!hydration) {
+ return html.replace(/\<\/?astro-fragment\>/g, '');
+ }
+
+ // Include componentExport name and componentUrl in hash to dedupe identical islands
+ const astroId = shorthash.unique(`<!--${metadata.componentExport!.value}:${metadata.componentUrl}-->\n${html}`);
+
+ // Rather than appending this inline in the page, puts this into the `result.scripts` set that will be appended to the head.
+ // INVESTIGATE: This will likely be a problem in streaming because the `<head>` will be gone at this point.
+ result.scripts.add(await generateHydrateScript({ renderer, astroId, props }, metadata as Required<AstroComponentMetadata>));
+
+ return `<astro-root uid="${astroId}">${html ?? ''}</astro-root>`;
}
/** Create the Astro.fetchContent() runtime function. */
function createFetchContentFn(url: URL) {
- const fetchContent = (importMetaGlobResult: Record<string, any>) => {
- let allEntries = [...Object.entries(importMetaGlobResult)];
- if (allEntries.length === 0) {
- throw new Error(`[${url.pathname}] Astro.fetchContent() no matches found.`);
- }
- return allEntries
- .map(([spec, mod]) => {
- // Only return Markdown files for now.
- if (!mod.frontmatter) {
- return;
- }
- const urlSpec = new URL(spec, url).pathname;
- return {
- ...mod.frontmatter,
- Content: mod.default,
- content: mod.metadata,
- file: new URL(spec, url),
- url: urlSpec.includes('/pages/') ? urlSpec.replace(/^.*\/pages\//, '/').replace(/(\/index)?\.md$/, '') : undefined,
- };
- })
- .filter(Boolean);
- };
- // This has to be cast because the type of fetchContent is the type of the function
- // that receives the import.meta.glob result, but the user is using it as
- // another type.
- return fetchContent as unknown as AstroGlobalPartial['fetchContent'];
+ const fetchContent = (importMetaGlobResult: Record<string, any>) => {
+ let allEntries = [...Object.entries(importMetaGlobResult)];
+ if (allEntries.length === 0) {
+ throw new Error(`[${url.pathname}] Astro.fetchContent() no matches found.`);
+ }
+ return allEntries
+ .map(([spec, mod]) => {
+ // Only return Markdown files for now.
+ if (!mod.frontmatter) {
+ return;
+ }
+ const urlSpec = new URL(spec, url).pathname;
+ return {
+ ...mod.frontmatter,
+ Content: mod.default,
+ content: mod.metadata,
+ file: new URL(spec, url),
+ url: urlSpec.includes('/pages/') ? urlSpec.replace(/^.*\/pages\//, '/').replace(/(\/index)?\.md$/, '') : undefined,
+ };
+ })
+ .filter(Boolean);
+ };
+ // This has to be cast because the type of fetchContent is the type of the function
+ // that receives the import.meta.glob result, but the user is using it as
+ // another type.
+ return fetchContent as unknown as AstroGlobalPartial['fetchContent'];
}
// This is used to create the top-level Astro global; the one that you can use
// Inside of getStaticPaths.
export function createAstro(fileURLStr: string, site: string, projectRootStr: string): AstroGlobalPartial {
- const url = new URL(fileURLStr);
- const projectRoot = new URL(projectRootStr);
- const fetchContent = createFetchContentFn(url);
- return {
- site: new URL(site),
- fetchContent,
- // INVESTIGATE is there a use-case for multi args?
- resolve(...segments: string[]) {
- let resolved = segments.reduce((u, segment) => new URL(segment, u), url).pathname;
- // When inside of project root, remove the leading path so you are
- // left with only `/src/images/tower.png`
- if (resolved.startsWith(projectRoot.pathname)) {
- resolved = '/' + resolved.substr(projectRoot.pathname.length);
- }
- return resolved;
- },
- };
+ const url = new URL(fileURLStr);
+ const projectRoot = new URL(projectRootStr);
+ const fetchContent = createFetchContentFn(url);
+ return {
+ site: new URL(site),
+ fetchContent,
+ // INVESTIGATE is there a use-case for multi args?
+ resolve(...segments: string[]) {
+ let resolved = segments.reduce((u, segment) => new URL(segment, u), url).pathname;
+ // When inside of project root, remove the leading path so you are
+ // left with only `/src/images/tower.png`
+ if (resolved.startsWith(projectRoot.pathname)) {
+ resolved = '/' + resolved.substr(projectRoot.pathname.length);
+ }
+ return resolved;
+ },
+ };
}
const toAttributeString = (value: any) => String(value).replace(/&/g, '&#38;').replace(/"/g, '&#34;');
// A helper used to turn expressions into attribute key/value
export function addAttribute(value: any, key: string) {
- if (value == null || value === false) {
- return '';
- }
-
- // support "class" from an expression passed into an element (#782)
- if (key === 'class:list') {
- return ` ${key.slice(0, -5)}="${toAttributeString(serializeListValue(value))}"`;
- }
-
- // Boolean only needs the key
- if (value === true && key.startsWith('data-')) {
- return ` ${key}`;
- } else {
- return ` ${key}="${toAttributeString(value)}"`;
- }
+ if (value == null || value === false) {
+ return '';
+ }
+
+ // support "class" from an expression passed into an element (#782)
+ if (key === 'class:list') {
+ return ` ${key.slice(0, -5)}="${toAttributeString(serializeListValue(value))}"`;
+ }
+
+ // Boolean only needs the key
+ if (value === true && key.startsWith('data-')) {
+ return ` ${key}`;
+ } else {
+ return ` ${key}="${toAttributeString(value)}"`;
+ }
}
// Adds support for `<Component {...value} />
export function spreadAttributes(values: Record<any, any>) {
- let output = '';
- for (const [key, value] of Object.entries(values)) {
- output += addAttribute(value, key);
- }
- return output;
+ let output = '';
+ for (const [key, value] of Object.entries(values)) {
+ output += addAttribute(value, key);
+ }
+ return output;
}
// Adds CSS variables to an inline style tag
export function defineStyleVars(selector: string, vars: Record<any, any>) {
- let output = '\n';
- for (const [key, value] of Object.entries(vars)) {
- output += ` --${key}: ${value};\n`;
- }
- return `${selector} {${output}}`;
+ let output = '\n';
+ for (const [key, value] of Object.entries(vars)) {
+ output += ` --${key}: ${value};\n`;
+ }
+ return `${selector} {${output}}`;
}
// Adds variables to an inline script.
export function defineScriptVars(vars: Record<any, any>) {
- let output = '';
- for (const [key, value] of Object.entries(vars)) {
- output += `let ${key} = ${JSON.stringify(value)};\n`;
- }
- return output;
+ let output = '';
+ for (const [key, value] of Object.entries(vars)) {
+ output += `let ${key} = ${JSON.stringify(value)};\n`;
+ }
+ return output;
}
// Calls a component and renders it into a string of HTML
export async function renderToString(result: SSRResult, componentFactory: AstroComponentFactory, props: any, children: any) {
- const Component = await componentFactory(result, props, children);
- let template = await renderAstroComponent(Component);
- return template;
+ const Component = await componentFactory(result, props, children);
+ let template = await renderAstroComponent(Component);
+ return template;
}
// Filter out duplicate elements in our set
const uniqueElements = (item: any, index: number, all: any[]) => {
- const props = JSON.stringify(item.props);
- const children = item.children;
- return index === all.findIndex((i) => JSON.stringify(i.props) === props && i.children == children);
+ const props = JSON.stringify(item.props);
+ const children = item.children;
+ return index === all.findIndex((i) => JSON.stringify(i.props) === props && i.children == children);
};
// Renders a page to completion by first calling the factory callback, waiting for its result, and then appending
// styles and scripts into the head.
export async function renderPage(result: SSRResult, Component: AstroComponentFactory, props: any, children: any) {
- const template = await renderToString(result, Component, props, children);
- const styles = result._metadata.experimentalStaticBuild
- ? []
- : Array.from(result.styles)
- .filter(uniqueElements)
- .map((style) =>
- renderElement('style', {
- ...style,
- props: { ...style.props, 'astro-style': true },
- })
- );
- let needsHydrationStyles = false;
- const scripts = Array.from(result.scripts)
- .filter(uniqueElements)
- .map((script, i) => {
- if ('data-astro-component-hydration' in script.props) {
- needsHydrationStyles = true;
- }
- return renderElement('script', {
- ...script,
- props: { ...script.props, 'astro-script': result._metadata.pathname + '/script-' + i },
- });
- });
- if (needsHydrationStyles) {
- styles.push(renderElement('style', { props: { 'astro-style': true }, children: 'astro-root, astro-fragment { display: contents; }' }));
- }
-
- const links = Array.from(result.links)
- .filter(uniqueElements)
- .map((link) => renderElement('link', link));
-
- // inject styles & scripts at end of <head>
- let headPos = template.indexOf('</head>');
- if (headPos === -1) {
- return links.join('\n') + styles.join('\n') + scripts.join('\n') + template; // if no </head>, prepend styles & scripts
- }
- return template.substring(0, headPos) + links.join('\n') + styles.join('\n') + scripts.join('\n') + template.substring(headPos);
+ const template = await renderToString(result, Component, props, children);
+ const styles = result._metadata.experimentalStaticBuild
+ ? []
+ : Array.from(result.styles)
+ .filter(uniqueElements)
+ .map((style) =>
+ renderElement('style', {
+ ...style,
+ props: { ...style.props, 'astro-style': true },
+ })
+ );
+ let needsHydrationStyles = false;
+ const scripts = Array.from(result.scripts)
+ .filter(uniqueElements)
+ .map((script, i) => {
+ if ('data-astro-component-hydration' in script.props) {
+ needsHydrationStyles = true;
+ }
+ return renderElement('script', {
+ ...script,
+ props: { ...script.props, 'astro-script': result._metadata.pathname + '/script-' + i },
+ });
+ });
+ if (needsHydrationStyles) {
+ styles.push(renderElement('style', { props: { 'astro-style': true }, children: 'astro-root, astro-fragment { display: contents; }' }));
+ }
+
+ const links = Array.from(result.links)
+ .filter(uniqueElements)
+ .map((link) => renderElement('link', link));
+
+ // inject styles & scripts at end of <head>
+ let headPos = template.indexOf('</head>');
+ if (headPos === -1) {
+ return links.join('\n') + styles.join('\n') + scripts.join('\n') + template; // if no </head>, prepend styles & scripts
+ }
+ return template.substring(0, headPos) + links.join('\n') + styles.join('\n') + scripts.join('\n') + template.substring(headPos);
}
export async function renderAstroComponent(component: InstanceType<typeof AstroComponent>) {
- let template = '';
+ let template = '';
- for await (const value of component) {
- if (value || value === 0) {
- template += value;
- }
- }
+ for await (const value of component) {
+ if (value || value === 0) {
+ template += value;
+ }
+ }
- return template;
+ return template;
}
function renderElement(name: string, { props: _props, children = '' }: SSRElement) {
- // Do not print `hoist`, `lang`, `global`
- const { lang: _, 'data-astro-id': astroId, 'define:vars': defineVars, ...props } = _props;
- if (defineVars) {
- if (name === 'style') {
- if (props.global) {
- children = defineStyleVars(`:root`, defineVars) + '\n' + children;
- } else {
- children = defineStyleVars(`.astro-${astroId}`, defineVars) + '\n' + children;
- }
- delete props.global;
- }
- if (name === 'script') {
- delete props.hoist;
- children = defineScriptVars(defineVars) + '\n' + children;
- }
- }
- return `<${name}${spreadAttributes(props)}>${children}</${name}>`;
+ // Do not print `hoist`, `lang`, `global`
+ const { lang: _, 'data-astro-id': astroId, 'define:vars': defineVars, ...props } = _props;
+ if (defineVars) {
+ if (name === 'style') {
+ if (props.global) {
+ children = defineStyleVars(`:root`, defineVars) + '\n' + children;
+ } else {
+ children = defineStyleVars(`.astro-${astroId}`, defineVars) + '\n' + children;
+ }
+ delete props.global;
+ }
+ if (name === 'script') {
+ delete props.hoist;
+ children = defineScriptVars(defineVars) + '\n' + children;
+ }
+ }
+ return `<${name}${spreadAttributes(props)}>${children}</${name}>`;
}
diff --git a/packages/astro/src/runtime/server/metadata.ts b/packages/astro/src/runtime/server/metadata.ts
index e30a740a7..b4454e16f 100644
--- a/packages/astro/src/runtime/server/metadata.ts
+++ b/packages/astro/src/runtime/server/metadata.ts
@@ -1,94 +1,94 @@
interface ModuleInfo {
- module: Record<string, any>;
- specifier: string;
+ module: Record<string, any>;
+ specifier: string;
}
interface ComponentMetadata {
- componentExport: string;
- componentUrl: string;
+ componentExport: string;
+ componentUrl: string;
}
export class Metadata {
- public fileURL: URL;
- private metadataCache: Map<any, ComponentMetadata | null>;
- constructor(fileURL: string, public modules: ModuleInfo[], public hydratedComponents: any[], public hoisted: any[]) {
- this.fileURL = new URL(fileURL);
- this.metadataCache = new Map<any, ComponentMetadata | null>();
- }
+ public fileURL: URL;
+ private metadataCache: Map<any, ComponentMetadata | null>;
+ constructor(fileURL: string, public modules: ModuleInfo[], public hydratedComponents: any[], public hoisted: any[]) {
+ this.fileURL = new URL(fileURL);
+ this.metadataCache = new Map<any, ComponentMetadata | null>();
+ }
- resolvePath(specifier: string): string {
- return specifier.startsWith('.') ? new URL(specifier, this.fileURL).pathname : specifier;
- }
+ resolvePath(specifier: string): string {
+ return specifier.startsWith('.') ? new URL(specifier, this.fileURL).pathname : specifier;
+ }
- getPath(Component: any): string | null {
- const metadata = this.getComponentMetadata(Component);
- return metadata?.componentUrl || null;
- }
+ getPath(Component: any): string | null {
+ const metadata = this.getComponentMetadata(Component);
+ return metadata?.componentUrl || null;
+ }
- getExport(Component: any): string | null {
- const metadata = this.getComponentMetadata(Component);
- return metadata?.componentExport || null;
- }
+ getExport(Component: any): string | null {
+ const metadata = this.getComponentMetadata(Component);
+ return metadata?.componentExport || null;
+ }
- // Recursively collect all of the hydrated components' paths.
- getAllHydratedComponentPaths(): Set<string> {
- const paths = new Set<string>();
- for (const component of this.hydratedComponents) {
- const path = this.getPath(component);
- if (path) {
- paths.add(path);
- }
- }
+ // Recursively collect all of the hydrated components' paths.
+ getAllHydratedComponentPaths(): Set<string> {
+ const paths = new Set<string>();
+ for (const component of this.hydratedComponents) {
+ const path = this.getPath(component);
+ if (path) {
+ paths.add(path);
+ }
+ }
- for (const { module: mod } of this.modules) {
- if (typeof mod.$$metadata !== 'undefined') {
- for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
- paths.add(path);
- }
- }
- }
- return paths;
- }
+ for (const { module: mod } of this.modules) {
+ if (typeof mod.$$metadata !== 'undefined') {
+ for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
+ paths.add(path);
+ }
+ }
+ }
+ return paths;
+ }
- private getComponentMetadata(Component: any): ComponentMetadata | null {
- if (this.metadataCache.has(Component)) {
- return this.metadataCache.get(Component)!;
- }
- const metadata = this.findComponentMetadata(Component);
- this.metadataCache.set(Component, metadata);
- return metadata;
- }
+ private getComponentMetadata(Component: any): ComponentMetadata | null {
+ if (this.metadataCache.has(Component)) {
+ return this.metadataCache.get(Component)!;
+ }
+ const metadata = this.findComponentMetadata(Component);
+ this.metadataCache.set(Component, metadata);
+ return metadata;
+ }
- private findComponentMetadata(Component: any): ComponentMetadata | null {
- const isCustomElement = typeof Component === 'string';
- for (const { module, specifier } of this.modules) {
- const id = this.resolvePath(specifier);
- for (const [key, value] of Object.entries(module)) {
- if (isCustomElement) {
- if (key === 'tagName' && Component === value) {
- return {
- componentExport: key,
- componentUrl: id,
- };
- }
- } else if (Component === value) {
- return {
- componentExport: key,
- componentUrl: id,
- };
- }
- }
- }
- return null;
- }
+ private findComponentMetadata(Component: any): ComponentMetadata | null {
+ const isCustomElement = typeof Component === 'string';
+ for (const { module, specifier } of this.modules) {
+ const id = this.resolvePath(specifier);
+ for (const [key, value] of Object.entries(module)) {
+ if (isCustomElement) {
+ if (key === 'tagName' && Component === value) {
+ return {
+ componentExport: key,
+ componentUrl: id,
+ };
+ }
+ } else if (Component === value) {
+ return {
+ componentExport: key,
+ componentUrl: id,
+ };
+ }
+ }
+ }
+ return null;
+ }
}
interface CreateMetadataOptions {
- modules: ModuleInfo[];
- hydratedComponents: any[];
- hoisted: any[];
+ modules: ModuleInfo[];
+ hydratedComponents: any[];
+ hoisted: any[];
}
export function createMetadata(fileURL: string, options: CreateMetadataOptions) {
- return new Metadata(fileURL, options.modules, options.hydratedComponents, options.hoisted);
+ return new Metadata(fileURL, options.modules, options.hydratedComponents, options.hoisted);
}
diff --git a/packages/astro/src/runtime/server/util.ts b/packages/astro/src/runtime/server/util.ts
index 9de0dabab..62a0da388 100644
--- a/packages/astro/src/runtime/server/util.ts
+++ b/packages/astro/src/runtime/server/util.ts
@@ -1,29 +1,29 @@
export function serializeListValue(value: any) {
- const hash: Record<string, any> = {};
+ const hash: Record<string, any> = {};
- push(value);
+ push(value);
- return Object.keys(hash).join(' ');
+ return Object.keys(hash).join(' ');
- function push(item: any) {
- // push individual iteratables
- if (item && typeof item.forEach === 'function') item.forEach(push);
- // otherwise, push object value keys by truthiness
- else if (item === Object(item))
- Object.keys(item).forEach((name) => {
- if (item[name]) push(name);
- });
- // otherwise, push any other values as a string
- else {
- // get the item as a string
- item = item == null ? '' : String(item).trim();
+ function push(item: any) {
+ // push individual iteratables
+ if (item && typeof item.forEach === 'function') item.forEach(push);
+ // otherwise, push object value keys by truthiness
+ else if (item === Object(item))
+ Object.keys(item).forEach((name) => {
+ if (item[name]) push(name);
+ });
+ // otherwise, push any other values as a string
+ else {
+ // get the item as a string
+ item = item == null ? '' : String(item).trim();
- // add the item if it is filled
- if (item) {
- item.split(/\s+/).forEach((name: string) => {
- hash[name] = true;
- });
- }
- }
- }
+ // add the item if it is filled
+ if (item) {
+ item.split(/\s+/).forEach((name: string) => {
+ hash[name] = true;
+ });
+ }
+ }
+ }
}
diff --git a/packages/astro/src/vite-plugin-astro-postprocess/index.ts b/packages/astro/src/vite-plugin-astro-postprocess/index.ts
index 23481931f..96087966f 100644
--- a/packages/astro/src/vite-plugin-astro-postprocess/index.ts
+++ b/packages/astro/src/vite-plugin-astro-postprocess/index.ts
@@ -7,72 +7,72 @@ import * as babelTraverse from '@babel/traverse';
import * as babel from '@babel/core';
interface AstroPluginOptions {
- config: AstroConfig;
- devServer?: AstroDevServer;
+ config: AstroConfig;
+ devServer?: AstroDevServer;
}
// esbuild transforms the component-scoped Astro into Astro2, so need to check both.
const validAstroGlobalNames = new Set(['Astro', 'Astro2']);
export default function astro({ config, devServer }: AstroPluginOptions): Plugin {
- return {
- name: '@astrojs/vite-plugin-astro-postprocess',
- async transform(code, id) {
- // Currently only supported in ".astro" & ".md" files
- if (!id.endsWith('.astro') && !id.endsWith('.md')) {
- return null;
- }
+ return {
+ name: '@astrojs/vite-plugin-astro-postprocess',
+ async transform(code, id) {
+ // Currently only supported in ".astro" & ".md" files
+ if (!id.endsWith('.astro') && !id.endsWith('.md')) {
+ return null;
+ }
- // Optimization: only run on a probably match
- // Open this up if need for post-pass extends past fetchContent
- if (!code.includes('fetchContent')) {
- return null;
- }
+ // Optimization: only run on a probably match
+ // Open this up if need for post-pass extends past fetchContent
+ if (!code.includes('fetchContent')) {
+ return null;
+ }
- // Handle the second-pass JS AST Traversal
- const result = await babel.transformAsync(code, {
- sourceType: 'module',
- sourceMaps: true,
- plugins: [
- () => {
- return {
- visitor: {
- StringLiteral(path: babelTraverse.NodePath<t.StringLiteral>) {
- if (
- path.parent.type !== 'CallExpression' ||
- path.parent.callee.type !== 'MemberExpression' ||
- !validAstroGlobalNames.has((path.parent.callee.object as any).name) ||
- (path.parent.callee.property as any).name !== 'fetchContent'
- ) {
- return;
- }
- const { value } = path.node;
- if (/[a-z]\:\/\//.test(value)) {
- return;
- }
- path.replaceWith({
- type: 'CallExpression',
- callee: {
- type: 'MemberExpression',
- object: { type: 'MetaProperty', meta: { type: 'Identifier', name: 'import' }, property: { type: 'Identifier', name: 'meta' } },
- property: { type: 'Identifier', name: 'globEager' },
- computed: false,
- },
- arguments: [path.node],
- } as any);
- },
- },
- };
- },
- ],
- });
+ // Handle the second-pass JS AST Traversal
+ const result = await babel.transformAsync(code, {
+ sourceType: 'module',
+ sourceMaps: true,
+ plugins: [
+ () => {
+ return {
+ visitor: {
+ StringLiteral(path: babelTraverse.NodePath<t.StringLiteral>) {
+ if (
+ path.parent.type !== 'CallExpression' ||
+ path.parent.callee.type !== 'MemberExpression' ||
+ !validAstroGlobalNames.has((path.parent.callee.object as any).name) ||
+ (path.parent.callee.property as any).name !== 'fetchContent'
+ ) {
+ return;
+ }
+ const { value } = path.node;
+ if (/[a-z]\:\/\//.test(value)) {
+ return;
+ }
+ path.replaceWith({
+ type: 'CallExpression',
+ callee: {
+ type: 'MemberExpression',
+ object: { type: 'MetaProperty', meta: { type: 'Identifier', name: 'import' }, property: { type: 'Identifier', name: 'meta' } },
+ property: { type: 'Identifier', name: 'globEager' },
+ computed: false,
+ },
+ arguments: [path.node],
+ } as any);
+ },
+ },
+ };
+ },
+ ],
+ });
- // Undocumented baby behavior, but possible according to Babel types.
- if (!result || !result.code) {
- return null;
- }
+ // Undocumented baby behavior, but possible according to Babel types.
+ if (!result || !result.code) {
+ return null;
+ }
- return { code: result.code, map: result.map };
- },
- };
+ return { code: result.code, map: result.map };
+ },
+ };
}
diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts
index 71a08a94a..868230037 100644
--- a/packages/astro/src/vite-plugin-astro/compile.ts
+++ b/packages/astro/src/vite-plugin-astro/compile.ts
@@ -14,97 +14,97 @@ const configCache = new WeakMap<AstroConfig, CompilationCache>();
// https://github.com/vitejs/vite/discussions/5109#discussioncomment-1450726
function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {
- if (options === undefined) {
- return false;
- }
- if (typeof options === 'boolean') {
- return options;
- }
- if (typeof options == 'object') {
- return !!options.ssr;
- }
- return false;
+ if (options === undefined) {
+ return false;
+ }
+ if (typeof options === 'boolean') {
+ return options;
+ }
+ if (typeof options == 'object') {
+ return !!options.ssr;
+ }
+ return false;
}
async function compile(config: AstroConfig, filename: string, source: string, viteTransform: TransformHook, opts: boolean | undefined) {
- // pages and layouts should be transformed as full documents (implicit <head> <body> etc)
- // everything else is treated as a fragment
- const normalizedID = fileURLToPath(new URL(`file://${filename}`));
- const isPage = normalizedID.startsWith(fileURLToPath(config.pages)) || normalizedID.startsWith(fileURLToPath(config.layouts));
+ // pages and layouts should be transformed as full documents (implicit <head> <body> etc)
+ // everything else is treated as a fragment
+ const normalizedID = fileURLToPath(new URL(`file://${filename}`));
+ const isPage = normalizedID.startsWith(fileURLToPath(config.pages)) || normalizedID.startsWith(fileURLToPath(config.layouts));
- let cssTransformError: Error | undefined;
+ let cssTransformError: Error | undefined;
- // Transform from `.astro` to valid `.ts`
- // use `sourcemap: "both"` so that sourcemap is included in the code
- // result passed to esbuild, but also available in the catch handler.
- const transformResult = await transform(source, {
- as: isPage ? 'document' : 'fragment',
- projectRoot: config.projectRoot.toString(),
- site: config.buildOptions.site,
- sourcefile: filename,
- sourcemap: 'both',
- internalURL: 'astro/internal',
- experimentalStaticExtraction: config.buildOptions.experimentalStaticBuild,
- // TODO add experimental flag here
- preprocessStyle: async (value: string, attrs: Record<string, string>) => {
- const lang = `.${attrs?.lang || 'css'}`.toLowerCase();
- try {
- const result = await transformWithVite({
- value,
- lang,
- id: filename,
- transformHook: viteTransform,
- ssr: isSSR(opts),
- });
+ // Transform from `.astro` to valid `.ts`
+ // use `sourcemap: "both"` so that sourcemap is included in the code
+ // result passed to esbuild, but also available in the catch handler.
+ const transformResult = await transform(source, {
+ as: isPage ? 'document' : 'fragment',
+ projectRoot: config.projectRoot.toString(),
+ site: config.buildOptions.site,
+ sourcefile: filename,
+ sourcemap: 'both',
+ internalURL: 'astro/internal',
+ experimentalStaticExtraction: config.buildOptions.experimentalStaticBuild,
+ // TODO add experimental flag here
+ preprocessStyle: async (value: string, attrs: Record<string, string>) => {
+ const lang = `.${attrs?.lang || 'css'}`.toLowerCase();
+ try {
+ const result = await transformWithVite({
+ value,
+ lang,
+ id: filename,
+ transformHook: viteTransform,
+ ssr: isSSR(opts),
+ });
- let map: SourceMapInput | undefined;
- if (!result) return null as any; // TODO: add type in compiler to fix "any"
- if (result.map) {
- if (typeof result.map === 'string') {
- map = result.map;
- } else if (result.map.mappings) {
- map = result.map.toString();
- }
- }
- return { code: result.code, map };
- } catch (err) {
- // save error to throw in plugin context
- cssTransformError = err as any;
- return null;
- }
- },
- });
+ let map: SourceMapInput | undefined;
+ if (!result) return null as any; // TODO: add type in compiler to fix "any"
+ if (result.map) {
+ if (typeof result.map === 'string') {
+ map = result.map;
+ } else if (result.map.mappings) {
+ map = result.map.toString();
+ }
+ }
+ return { code: result.code, map };
+ } catch (err) {
+ // save error to throw in plugin context
+ cssTransformError = err as any;
+ return null;
+ }
+ },
+ });
- // throw CSS transform errors here if encountered
- if (cssTransformError) throw cssTransformError;
+ // throw CSS transform errors here if encountered
+ if (cssTransformError) throw cssTransformError;
- return transformResult;
+ return transformResult;
}
export function invalidateCompilation(config: AstroConfig, filename: string) {
- if (configCache.has(config)) {
- const cache = configCache.get(config)!;
- cache.delete(filename);
- }
+ if (configCache.has(config)) {
+ const cache = configCache.get(config)!;
+ cache.delete(filename);
+ }
}
export async function cachedCompilation(config: AstroConfig, filename: string, source: string | null, viteTransform: TransformHook, opts: boolean | undefined) {
- let cache: CompilationCache;
- if (!configCache.has(config)) {
- cache = new Map();
- configCache.set(config, cache);
- } else {
- cache = configCache.get(config)!;
- }
- if (cache.has(filename)) {
- return cache.get(filename)!;
- }
+ let cache: CompilationCache;
+ if (!configCache.has(config)) {
+ cache = new Map();
+ configCache.set(config, cache);
+ } else {
+ cache = configCache.get(config)!;
+ }
+ if (cache.has(filename)) {
+ return cache.get(filename)!;
+ }
- if (source === null) {
- const fileUrl = new URL(`file://${filename}`);
- source = await fs.promises.readFile(fileUrl, 'utf-8');
- }
- const transformResult = await compile(config, filename, source, viteTransform, opts);
- cache.set(filename, transformResult);
- return transformResult;
+ if (source === null) {
+ const fileUrl = new URL(`file://${filename}`);
+ source = await fs.promises.readFile(fileUrl, 'utf-8');
+ }
+ const transformResult = await compile(config, filename, source, viteTransform, opts);
+ cache.set(filename, transformResult);
+ return transformResult;
}
diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts
index 918d597a5..8fae5dc3b 100644
--- a/packages/astro/src/vite-plugin-astro/index.ts
+++ b/packages/astro/src/vite-plugin-astro/index.ts
@@ -10,99 +10,99 @@ import { cachedCompilation, invalidateCompilation } from './compile.js';
const FRONTMATTER_PARSE_REGEXP = /^\-\-\-(.*)^\-\-\-/ms;
interface AstroPluginOptions {
- config: AstroConfig;
- devServer?: AstroDevServer;
+ config: AstroConfig;
+ devServer?: AstroDevServer;
}
/** Transform .astro files for Vite */
export default function astro({ config }: AstroPluginOptions): vite.Plugin {
- let viteTransform: TransformHook;
- return {
- name: '@astrojs/vite-plugin-astro',
- enforce: 'pre', // run transforms before other plugins can
- configResolved(resolvedConfig) {
- viteTransform = getViteTransform(resolvedConfig);
- },
- // note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.globEager, etc.)
- async resolveId(id) {
- // serve sub-part requests (*?astro) as virtual modules
- if (parseAstroRequest(id).query.astro) {
- return id;
- }
- },
- async load(id, opts) {
- let { filename, query } = parseAstroRequest(id);
- if (query.astro) {
- if (query.type === 'style') {
- if (filename.startsWith('/') && !filename.startsWith(config.projectRoot.pathname)) {
- filename = new URL('.' + filename, config.projectRoot).pathname;
- }
- const transformResult = await cachedCompilation(config, filename, null, viteTransform, opts);
+ let viteTransform: TransformHook;
+ return {
+ name: '@astrojs/vite-plugin-astro',
+ enforce: 'pre', // run transforms before other plugins can
+ configResolved(resolvedConfig) {
+ viteTransform = getViteTransform(resolvedConfig);
+ },
+ // note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.globEager, etc.)
+ async resolveId(id) {
+ // serve sub-part requests (*?astro) as virtual modules
+ if (parseAstroRequest(id).query.astro) {
+ return id;
+ }
+ },
+ async load(id, opts) {
+ let { filename, query } = parseAstroRequest(id);
+ if (query.astro) {
+ if (query.type === 'style') {
+ if (filename.startsWith('/') && !filename.startsWith(config.projectRoot.pathname)) {
+ filename = new URL('.' + filename, config.projectRoot).pathname;
+ }
+ const transformResult = await cachedCompilation(config, filename, null, viteTransform, opts);
- if (typeof query.index === 'undefined') {
- throw new Error(`Requests for Astro CSS must include an index.`);
- }
+ if (typeof query.index === 'undefined') {
+ throw new Error(`Requests for Astro CSS must include an index.`);
+ }
- const csses = transformResult.css;
- const code = csses[query.index];
+ const csses = transformResult.css;
+ const code = csses[query.index];
- return {
- code,
- };
- }
- }
+ return {
+ code,
+ };
+ }
+ }
- return null;
- },
- async transform(source, id, opts) {
- if (!id.endsWith('.astro')) {
- return;
- }
+ return null;
+ },
+ async transform(source, id, opts) {
+ if (!id.endsWith('.astro')) {
+ return;
+ }
- try {
- const transformResult = await cachedCompilation(config, id, source, viteTransform, opts);
+ try {
+ const transformResult = await cachedCompilation(config, id, source, viteTransform, opts);
- // Compile all TypeScript to JavaScript.
- // Also, catches invalid JS/TS in the compiled output before returning.
- const { code, map } = await esbuild.transform(transformResult.code, {
- loader: 'ts',
- sourcemap: 'external',
- sourcefile: id,
- });
+ // Compile all TypeScript to JavaScript.
+ // Also, catches invalid JS/TS in the compiled output before returning.
+ const { code, map } = await esbuild.transform(transformResult.code, {
+ loader: 'ts',
+ sourcemap: 'external',
+ sourcefile: id,
+ });
- return {
- code,
- map,
- };
- } catch (err: any) {
- // Verify frontmatter: a common reason that this plugin fails is that
- // the user provided invalid JS/TS in the component frontmatter.
- // If the frontmatter is invalid, the `err` object may be a compiler
- // panic or some other vague/confusing compiled error message.
- //
- // Before throwing, it is better to verify the frontmatter here, and
- // let esbuild throw a more specific exception if the code is invalid.
- // If frontmatter is valid or cannot be parsed, then continue.
- const scannedFrontmatter = FRONTMATTER_PARSE_REGEXP.exec(source);
- if (scannedFrontmatter) {
- try {
- await esbuild.transform(scannedFrontmatter[1], { loader: 'ts', sourcemap: false, sourcefile: id });
- } catch (frontmatterErr: any) {
- // Improve the error by replacing the phrase "unexpected end of file"
- // with "unexpected end of frontmatter" in the esbuild error message.
- if (frontmatterErr && frontmatterErr.message) {
- frontmatterErr.message = frontmatterErr.message.replace('end of file', 'end of frontmatter');
- }
- throw frontmatterErr;
- }
- }
+ return {
+ code,
+ map,
+ };
+ } catch (err: any) {
+ // Verify frontmatter: a common reason that this plugin fails is that
+ // the user provided invalid JS/TS in the component frontmatter.
+ // If the frontmatter is invalid, the `err` object may be a compiler
+ // panic or some other vague/confusing compiled error message.
+ //
+ // Before throwing, it is better to verify the frontmatter here, and
+ // let esbuild throw a more specific exception if the code is invalid.
+ // If frontmatter is valid or cannot be parsed, then continue.
+ const scannedFrontmatter = FRONTMATTER_PARSE_REGEXP.exec(source);
+ if (scannedFrontmatter) {
+ try {
+ await esbuild.transform(scannedFrontmatter[1], { loader: 'ts', sourcemap: false, sourcefile: id });
+ } catch (frontmatterErr: any) {
+ // Improve the error by replacing the phrase "unexpected end of file"
+ // with "unexpected end of frontmatter" in the esbuild error message.
+ if (frontmatterErr && frontmatterErr.message) {
+ frontmatterErr.message = frontmatterErr.message.replace('end of file', 'end of frontmatter');
+ }
+ throw frontmatterErr;
+ }
+ }
- // improve compiler errors
- if (err.stack.includes('wasm-function')) {
- const search = new URLSearchParams({
- labels: 'compiler',
- title: '🐛 BUG: `@astrojs/compiler` panic',
- body: `### Describe the Bug
+ // improve compiler errors
+ if (err.stack.includes('wasm-function')) {
+ const search = new URLSearchParams({
+ labels: 'compiler',
+ title: '🐛 BUG: `@astrojs/compiler` panic',
+ body: `### Describe the Bug
\`@astrojs/compiler\` encountered an unrecoverable error when compiling the following file.
@@ -111,23 +111,23 @@ export default function astro({ config }: AstroPluginOptions): vite.Plugin {
${source}
\`\`\`
`,
- });
- err.url = `https://github.com/withastro/astro/issues/new?${search.toString()}`;
- err.message = `Error: Uh oh, the Astro compiler encountered an unrecoverable error!
+ });
+ err.url = `https://github.com/withastro/astro/issues/new?${search.toString()}`;
+ err.message = `Error: Uh oh, the Astro compiler encountered an unrecoverable error!
Please open
a GitHub issue using the link below:
${err.url}`;
- // TODO: remove stack replacement when compiler throws better errors
- err.stack = ` at ${id}`;
- }
+ // TODO: remove stack replacement when compiler throws better errors
+ err.stack = ` at ${id}`;
+ }
- throw err;
- }
- },
- async handleHotUpdate(context) {
- // Invalidate the compilation cache so it recompiles
- invalidateCompilation(config, context.file);
- },
- };
+ throw err;
+ }
+ },
+ async handleHotUpdate(context) {
+ // Invalidate the compilation cache so it recompiles
+ invalidateCompilation(config, context.file);
+ },
+ };
}
diff --git a/packages/astro/src/vite-plugin-astro/query.ts b/packages/astro/src/vite-plugin-astro/query.ts
index f6ea8414a..ae923c153 100644
--- a/packages/astro/src/vite-plugin-astro/query.ts
+++ b/packages/astro/src/vite-plugin-astro/query.ts
@@ -1,35 +1,35 @@
export interface AstroQuery {
- astro?: boolean;
- src?: boolean;
- type?: 'script' | 'template' | 'style' | 'custom';
- index?: number;
- lang?: string;
- raw?: boolean;
+ astro?: boolean;
+ src?: boolean;
+ type?: 'script' | 'template' | 'style' | 'custom';
+ index?: number;
+ lang?: string;
+ raw?: boolean;
}
// Parses an id to check if its an Astro request.
// CSS is imported like `import '/src/pages/index.astro?astro&type=style&index=0&lang.css';
// This parses those ids and returns an object representing what it found.
export function parseAstroRequest(id: string): {
- filename: string;
- query: AstroQuery;
+ filename: string;
+ query: AstroQuery;
} {
- const [filename, rawQuery] = id.split(`?`, 2);
- const query = Object.fromEntries(new URLSearchParams(rawQuery).entries()) as AstroQuery;
- if (query.astro != null) {
- query.astro = true;
- }
- if (query.src != null) {
- query.src = true;
- }
- if (query.index != null) {
- query.index = Number(query.index);
- }
- if (query.raw != null) {
- query.raw = true;
- }
- return {
- filename,
- query,
- };
+ const [filename, rawQuery] = id.split(`?`, 2);
+ const query = Object.fromEntries(new URLSearchParams(rawQuery).entries()) as AstroQuery;
+ if (query.astro != null) {
+ query.astro = true;
+ }
+ if (query.src != null) {
+ query.src = true;
+ }
+ if (query.index != null) {
+ query.index = Number(query.index);
+ }
+ if (query.raw != null) {
+ query.raw = true;
+ }
+ return {
+ filename,
+ query,
+ };
}
diff --git a/packages/astro/src/vite-plugin-astro/styles.ts b/packages/astro/src/vite-plugin-astro/styles.ts
index 96971f308..6ebcd0e0d 100644
--- a/packages/astro/src/vite-plugin-astro/styles.ts
+++ b/packages/astro/src/vite-plugin-astro/styles.ts
@@ -6,24 +6,24 @@ export type TransformHook = (code: string, id: string, ssr?: boolean) => Promise
/** Load vite:css’ transform() hook */
export function getViteTransform(viteConfig: vite.ResolvedConfig): TransformHook {
- const viteCSSPlugin = viteConfig.plugins.find(({ name }) => name === 'vite:css');
- if (!viteCSSPlugin) throw new Error(`vite:css plugin couldn’t be found`);
- if (!viteCSSPlugin.transform) throw new Error(`vite:css has no transform() hook`);
- return viteCSSPlugin.transform.bind(null as any) as any;
+ const viteCSSPlugin = viteConfig.plugins.find(({ name }) => name === 'vite:css');
+ if (!viteCSSPlugin) throw new Error(`vite:css plugin couldn’t be found`);
+ if (!viteCSSPlugin.transform) throw new Error(`vite:css has no transform() hook`);
+ return viteCSSPlugin.transform.bind(null as any) as any;
}
interface TransformWithViteOptions {
- value: string;
- lang: string;
- id: string;
- transformHook: TransformHook;
- ssr?: boolean;
+ value: string;
+ lang: string;
+ id: string;
+ transformHook: TransformHook;
+ ssr?: boolean;
}
/** Transform style using Vite hook */
export async function transformWithVite({ value, lang, transformHook, id, ssr }: TransformWithViteOptions): Promise<vite.TransformResult | null> {
- if (!STYLE_EXTENSIONS.has(lang)) {
- return null; // only preprocess langs supported by Vite
- }
- return transformHook(value, id + `?astro&type=style&lang${lang}`, ssr);
+ if (!STYLE_EXTENSIONS.has(lang)) {
+ return null; // only preprocess langs supported by Vite
+ }
+ return transformHook(value, id + `?astro&type=style&lang${lang}`, ssr);
}
diff --git a/packages/astro/src/vite-plugin-build-css/index.ts b/packages/astro/src/vite-plugin-build-css/index.ts
index cba865351..d67f973b9 100644
--- a/packages/astro/src/vite-plugin-build-css/index.ts
+++ b/packages/astro/src/vite-plugin-build-css/index.ts
@@ -16,168 +16,168 @@ const ASTRO_PAGE_STYLE_PREFIX = '@astro-page-all-styles';
const isCSSRequest = (request: string) => STYLE_EXTENSIONS.has(path.extname(request));
export function getAstroPageStyleId(pathname: string) {
- let styleId = ASTRO_PAGE_STYLE_PREFIX + pathname;
- if (styleId.endsWith('/')) {
- styleId += 'index';
- }
- styleId += '.js';
- return styleId;
+ let styleId = ASTRO_PAGE_STYLE_PREFIX + pathname;
+ if (styleId.endsWith('/')) {
+ styleId += 'index';
+ }
+ styleId += '.js';
+ return styleId;
}
export function getAstroStyleId(pathname: string) {
- let styleId = ASTRO_STYLE_PREFIX + pathname;
- if (styleId.endsWith('/')) {
- styleId += 'index';
- }
- styleId += '.css';
- return styleId;
+ let styleId = ASTRO_STYLE_PREFIX + pathname;
+ if (styleId.endsWith('/')) {
+ styleId += 'index';
+ }
+ styleId += '.css';
+ return styleId;
}
export function getAstroStylePathFromId(id: string) {
- return id.substr(ASTRO_STYLE_PREFIX.length + 1);
+ return id.substr(ASTRO_STYLE_PREFIX.length + 1);
}
function isStyleVirtualModule(id: string) {
- return id.startsWith(ASTRO_STYLE_PREFIX);
+ return id.startsWith(ASTRO_STYLE_PREFIX);
}
function isPageStyleVirtualModule(id: string) {
- return id.startsWith(ASTRO_PAGE_STYLE_PREFIX);
+ return id.startsWith(ASTRO_PAGE_STYLE_PREFIX);
}
interface PluginOptions {
- internals: BuildInternals;
+ internals: BuildInternals;
}
export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin {
- const { internals } = options;
- const styleSourceMap = new Map<string, string>();
-
- return {
- name: PLUGIN_NAME,
-
- configResolved(resolvedConfig) {
- // Our plugin needs to run before `vite:css-post` which does a lot of what we do
- // for bundling CSS, but since we need to control CSS we should go first.
- // We move to right before the vite:css-post plugin so that things like the
- // Vue plugin go before us.
- const plugins = resolvedConfig.plugins as VitePlugin[];
- const viteCSSPostIndex = resolvedConfig.plugins.findIndex((p) => p.name === 'vite:css-post');
- if (viteCSSPostIndex !== -1) {
- const viteCSSPost = plugins[viteCSSPostIndex];
- // Prevent this plugin's bundling behavior from running since we need to
- // do that ourselves in order to handle updating the HTML.
- delete viteCSSPost.renderChunk;
- delete viteCSSPost.generateBundle;
-
- // Move our plugin to be right before this one.
- const ourIndex = plugins.findIndex((p) => p.name === PLUGIN_NAME);
- const ourPlugin = plugins[ourIndex];
-
- // Remove us from where we are now and place us right before the viteCSSPost plugin
- plugins.splice(ourIndex, 1);
- plugins.splice(viteCSSPostIndex - 1, 0, ourPlugin);
- }
- },
-
- async resolveId(id) {
- if (isPageStyleVirtualModule(id)) {
- return id;
- }
- if (isStyleVirtualModule(id)) {
- return id;
- }
- return undefined;
- },
-
- async load(id) {
- if (isPageStyleVirtualModule(id)) {
- return internals.astroPageStyleMap.get(id) || null;
- }
- if (isStyleVirtualModule(id)) {
- return internals.astroStyleMap.get(id) || null;
- }
- return null;
- },
-
- async transform(value, id) {
- if (isStyleVirtualModule(id)) {
- styleSourceMap.set(id, value);
- }
- if (isCSSRequest(id)) {
- styleSourceMap.set(id, value);
- }
- return null;
- },
-
- async renderChunk(_code, chunk) {
- let chunkCSS = '';
- let isPureCSS = true;
- for (const [id] of Object.entries(chunk.modules)) {
- if (!isCSSRequest(id) && !isPageStyleVirtualModule(id)) {
- isPureCSS = false;
- }
- if (styleSourceMap.has(id)) {
- chunkCSS += styleSourceMap.get(id)!;
- }
- }
-
- // if (!chunkCSS) return null; // don’t output empty .css files
-
- if (isPureCSS) {
- internals.pureCSSChunks.add(chunk);
- }
-
- const { code: minifiedCSS } = await esbuild.transform(chunkCSS, {
- loader: 'css',
- minify: true,
- });
- const referenceId = this.emitFile({
- name: chunk.name + '.css',
- type: 'asset',
- source: minifiedCSS,
- });
-
- internals.chunkToReferenceIdMap.set(chunk.fileName, referenceId);
- if (chunk.type === 'chunk') {
- const facadeId = chunk.facadeModuleId!;
- if (!internals.facadeIdToAssetsMap.has(facadeId)) {
- internals.facadeIdToAssetsMap.set(facadeId, []);
- }
- internals.facadeIdToAssetsMap.get(facadeId)!.push(this.getFileName(referenceId));
- }
-
- return null;
- },
-
- // Delete CSS chunks so JS is not produced for them.
- generateBundle(opts, bundle) {
- if (internals.pureCSSChunks.size) {
- const pureChunkFilenames = new Set([...internals.pureCSSChunks].map((chunk) => chunk.fileName));
- const emptyChunkFiles = [...pureChunkFilenames]
- .map((file) => path.basename(file))
- .join('|')
- .replace(/\./g, '\\.');
- const emptyChunkRE = new RegExp(opts.format === 'es' ? `\\bimport\\s*"[^"]*(?:${emptyChunkFiles})";\n?` : `\\brequire\\(\\s*"[^"]*(?:${emptyChunkFiles})"\\);\n?`, 'g');
-
- for (const [chunkId, chunk] of Object.entries(bundle)) {
- if (chunk.type === 'chunk') {
- if (internals.pureCSSChunks.has(chunk)) {
- // Delete pure CSS chunks, these are JavaScript chunks that only import
- // other CSS files, so are empty at the end of bundling.
- delete bundle[chunkId];
- } else {
- // Remove any pure css chunk imports from JavaScript.
- // Note that this code comes from Vite's CSS build plugin.
- chunk.code = chunk.code.replace(
- emptyChunkRE,
- // remove css import while preserving source map location
- (m) => `/* empty css ${''.padEnd(m.length - 15)}*/`
- );
- }
- }
- }
- }
- },
- };
+ const { internals } = options;
+ const styleSourceMap = new Map<string, string>();
+
+ return {
+ name: PLUGIN_NAME,
+
+ configResolved(resolvedConfig) {
+ // Our plugin needs to run before `vite:css-post` which does a lot of what we do
+ // for bundling CSS, but since we need to control CSS we should go first.
+ // We move to right before the vite:css-post plugin so that things like the
+ // Vue plugin go before us.
+ const plugins = resolvedConfig.plugins as VitePlugin[];
+ const viteCSSPostIndex = resolvedConfig.plugins.findIndex((p) => p.name === 'vite:css-post');
+ if (viteCSSPostIndex !== -1) {
+ const viteCSSPost = plugins[viteCSSPostIndex];
+ // Prevent this plugin's bundling behavior from running since we need to
+ // do that ourselves in order to handle updating the HTML.
+ delete viteCSSPost.renderChunk;
+ delete viteCSSPost.generateBundle;
+
+ // Move our plugin to be right before this one.
+ const ourIndex = plugins.findIndex((p) => p.name === PLUGIN_NAME);
+ const ourPlugin = plugins[ourIndex];
+
+ // Remove us from where we are now and place us right before the viteCSSPost plugin
+ plugins.splice(ourIndex, 1);
+ plugins.splice(viteCSSPostIndex - 1, 0, ourPlugin);
+ }
+ },
+
+ async resolveId(id) {
+ if (isPageStyleVirtualModule(id)) {
+ return id;
+ }
+ if (isStyleVirtualModule(id)) {
+ return id;
+ }
+ return undefined;
+ },
+
+ async load(id) {
+ if (isPageStyleVirtualModule(id)) {
+ return internals.astroPageStyleMap.get(id) || null;
+ }
+ if (isStyleVirtualModule(id)) {
+ return internals.astroStyleMap.get(id) || null;
+ }
+ return null;
+ },
+
+ async transform(value, id) {
+ if (isStyleVirtualModule(id)) {
+ styleSourceMap.set(id, value);
+ }
+ if (isCSSRequest(id)) {
+ styleSourceMap.set(id, value);
+ }
+ return null;
+ },
+
+ async renderChunk(_code, chunk) {
+ let chunkCSS = '';
+ let isPureCSS = true;
+ for (const [id] of Object.entries(chunk.modules)) {
+ if (!isCSSRequest(id) && !isPageStyleVirtualModule(id)) {
+ isPureCSS = false;
+ }
+ if (styleSourceMap.has(id)) {
+ chunkCSS += styleSourceMap.get(id)!;
+ }
+ }
+
+ // if (!chunkCSS) return null; // don’t output empty .css files
+
+ if (isPureCSS) {
+ internals.pureCSSChunks.add(chunk);
+ }
+
+ const { code: minifiedCSS } = await esbuild.transform(chunkCSS, {
+ loader: 'css',
+ minify: true,
+ });
+ const referenceId = this.emitFile({
+ name: chunk.name + '.css',
+ type: 'asset',
+ source: minifiedCSS,
+ });
+
+ internals.chunkToReferenceIdMap.set(chunk.fileName, referenceId);
+ if (chunk.type === 'chunk') {
+ const facadeId = chunk.facadeModuleId!;
+ if (!internals.facadeIdToAssetsMap.has(facadeId)) {
+ internals.facadeIdToAssetsMap.set(facadeId, []);
+ }
+ internals.facadeIdToAssetsMap.get(facadeId)!.push(this.getFileName(referenceId));
+ }
+
+ return null;
+ },
+
+ // Delete CSS chunks so JS is not produced for them.
+ generateBundle(opts, bundle) {
+ if (internals.pureCSSChunks.size) {
+ const pureChunkFilenames = new Set([...internals.pureCSSChunks].map((chunk) => chunk.fileName));
+ const emptyChunkFiles = [...pureChunkFilenames]
+ .map((file) => path.basename(file))
+ .join('|')
+ .replace(/\./g, '\\.');
+ const emptyChunkRE = new RegExp(opts.format === 'es' ? `\\bimport\\s*"[^"]*(?:${emptyChunkFiles})";\n?` : `\\brequire\\(\\s*"[^"]*(?:${emptyChunkFiles})"\\);\n?`, 'g');
+
+ for (const [chunkId, chunk] of Object.entries(bundle)) {
+ if (chunk.type === 'chunk') {
+ if (internals.pureCSSChunks.has(chunk)) {
+ // Delete pure CSS chunks, these are JavaScript chunks that only import
+ // other CSS files, so are empty at the end of bundling.
+ delete bundle[chunkId];
+ } else {
+ // Remove any pure css chunk imports from JavaScript.
+ // Note that this code comes from Vite's CSS build plugin.
+ chunk.code = chunk.code.replace(
+ emptyChunkRE,
+ // remove css import while preserving source map location
+ (m) => `/* empty css ${''.padEnd(m.length - 15)}*/`
+ );
+ }
+ }
+ }
+ }
+ },
+ };
}
diff --git a/packages/astro/src/vite-plugin-build-css/resolve.ts b/packages/astro/src/vite-plugin-build-css/resolve.ts
index 72fef8532..6880f364e 100644
--- a/packages/astro/src/vite-plugin-build-css/resolve.ts
+++ b/packages/astro/src/vite-plugin-build-css/resolve.ts
@@ -2,27 +2,27 @@ import type { ResolveIdHook, LoadHook } from 'rollup';
import type { ResolvedConfig, Plugin as VitePlugin } from '../core/vite';
export function getVitePluginByName(viteConfig: ResolvedConfig, pluginName: string): VitePlugin {
- const plugin = viteConfig.plugins.find(({ name }) => name === pluginName);
- if (!plugin) throw new Error(`${pluginName} plugin couldn’t be found`);
- return plugin;
+ const plugin = viteConfig.plugins.find(({ name }) => name === pluginName);
+ if (!plugin) throw new Error(`${pluginName} plugin couldn’t be found`);
+ return plugin;
}
export function getViteResolvePlugin(viteConfig: ResolvedConfig): VitePlugin {
- return getVitePluginByName(viteConfig, 'vite:resolve');
+ return getVitePluginByName(viteConfig, 'vite:resolve');
}
export function getViteLoadFallbackPlugin(viteConfig: ResolvedConfig): VitePlugin {
- return getVitePluginByName(viteConfig, 'vite:load-fallback');
+ return getVitePluginByName(viteConfig, 'vite:load-fallback');
}
export function getViteResolve(viteConfig: ResolvedConfig): ResolveIdHook {
- const plugin = getViteResolvePlugin(viteConfig);
- if (!plugin.resolveId) throw new Error(`vite:resolve has no resolveId() hook`);
- return plugin.resolveId.bind(null as any) as any;
+ const plugin = getViteResolvePlugin(viteConfig);
+ if (!plugin.resolveId) throw new Error(`vite:resolve has no resolveId() hook`);
+ return plugin.resolveId.bind(null as any) as any;
}
export function getViteLoad(viteConfig: ResolvedConfig): LoadHook {
- const plugin = getViteLoadFallbackPlugin(viteConfig);
- if (!plugin.load) throw new Error(`vite:load-fallback has no load() hook`);
- return plugin.load.bind(null as any) as any;
+ const plugin = getViteLoadFallbackPlugin(viteConfig);
+ if (!plugin.load) throw new Error(`vite:load-fallback has no load() hook`);
+ return plugin.load.bind(null as any) as any;
}
diff --git a/packages/astro/src/vite-plugin-build-html/add-rollup-input.ts b/packages/astro/src/vite-plugin-build-html/add-rollup-input.ts
index 4e84fbfa9..79feb3a7d 100644
--- a/packages/astro/src/vite-plugin-build-html/add-rollup-input.ts
+++ b/packages/astro/src/vite-plugin-build-html/add-rollup-input.ts
@@ -1,43 +1,43 @@
import { InputOptions } from 'rollup';
function fromEntries<V>(entries: [string, V][]) {
- const obj: Record<string, V> = {};
- for (const [k, v] of entries) {
- obj[k] = v;
- }
- return obj;
+ const obj: Record<string, V> = {};
+ for (const [k, v] of entries) {
+ obj[k] = v;
+ }
+ return obj;
}
export function addRollupInput(inputOptions: InputOptions, newInputs: string[]): InputOptions {
- // Add input module ids to existing input option, whether it's a string, array or object
- // this way you can use multiple html plugins all adding their own inputs
- if (!inputOptions.input) {
- return { ...inputOptions, input: newInputs };
- }
+ // Add input module ids to existing input option, whether it's a string, array or object
+ // this way you can use multiple html plugins all adding their own inputs
+ if (!inputOptions.input) {
+ return { ...inputOptions, input: newInputs };
+ }
- if (typeof inputOptions.input === 'string') {
- return {
- ...inputOptions,
- input: [inputOptions.input, ...newInputs],
- };
- }
+ if (typeof inputOptions.input === 'string') {
+ return {
+ ...inputOptions,
+ input: [inputOptions.input, ...newInputs],
+ };
+ }
- if (Array.isArray(inputOptions.input)) {
- return {
- ...inputOptions,
- input: [...inputOptions.input, ...newInputs],
- };
- }
+ if (Array.isArray(inputOptions.input)) {
+ return {
+ ...inputOptions,
+ input: [...inputOptions.input, ...newInputs],
+ };
+ }
- if (typeof inputOptions.input === 'object') {
- return {
- ...inputOptions,
- input: {
- ...inputOptions.input,
- ...fromEntries(newInputs.map((i) => [i.split('/').slice(-1)[0].split('.')[0], i])),
- },
- };
- }
+ if (typeof inputOptions.input === 'object') {
+ return {
+ ...inputOptions,
+ input: {
+ ...inputOptions.input,
+ ...fromEntries(newInputs.map((i) => [i.split('/').slice(-1)[0].split('.')[0], i])),
+ },
+ };
+ }
- throw new Error(`Unknown rollup input type. Supported inputs are string, array and object.`);
+ throw new Error(`Unknown rollup input type. Supported inputs are string, array and object.`);
}
diff --git a/packages/astro/src/vite-plugin-build-html/extract-assets.ts b/packages/astro/src/vite-plugin-build-html/extract-assets.ts
index c323f6efb..5d9638630 100644
--- a/packages/astro/src/vite-plugin-build-html/extract-assets.ts
+++ b/packages/astro/src/vite-plugin-build-html/extract-assets.ts
@@ -8,209 +8,209 @@ const linkRels = [...hashedLinkRels, 'icon', 'manifest', 'apple-touch-icon', 'ma
const windowsPathRE = /^[A-Z]:\//;
function getSrcSetUrls(srcset: string) {
- if (!srcset) {
- return [];
- }
- const srcsetParts = srcset.includes(',') ? srcset.split(',') : [srcset];
- const urls = srcsetParts.map((url) => url.trim()).map((url) => (url.includes(' ') ? url.split(' ')[0] : url));
- return urls;
+ if (!srcset) {
+ return [];
+ }
+ const srcsetParts = srcset.includes(',') ? srcset.split(',') : [srcset];
+ const urls = srcsetParts.map((url) => url.trim()).map((url) => (url.includes(' ') ? url.split(' ')[0] : url));
+ return urls;
}
function extractFirstUrlOfSrcSet(node: Element) {
- const srcset = getAttribute(node, 'srcset');
- if (!srcset) {
- return '';
- }
- const urls = getSrcSetUrls(srcset);
- return urls[0];
+ const srcset = getAttribute(node, 'srcset');
+ if (!srcset) {
+ return '';
+ }
+ const urls = getSrcSetUrls(srcset);
+ return urls[0];
}
function isAsset(node: Element) {
- let path = '';
- switch (getTagName(node)) {
- case 'img':
- path = getAttribute(node, 'src') ?? '';
- break;
- case 'source':
- path = extractFirstUrlOfSrcSet(node) ?? '';
- break;
- case 'link':
- if (linkRels.includes(getAttribute(node, 'rel') ?? '')) {
- path = getAttribute(node, 'href') ?? '';
- }
- break;
- case 'meta':
- if (getAttribute(node, 'property') === 'og:image' && getAttribute(node, 'content')) {
- path = getAttribute(node, 'content') ?? '';
- }
- break;
- case 'script':
- if (getAttribute(node, 'type') !== 'module' && getAttribute(node, 'src')) {
- path = getAttribute(node, 'src') ?? '';
- }
- break;
- default:
- return false;
- }
- if (!path) {
- return false;
- }
- // Windows fix: if path starts with C:/, avoid URL parsing
- if (windowsPathRE.test(path)) {
- return true;
- }
- try {
- new URL(path);
- return false;
- } catch (e) {
- return true;
- }
+ let path = '';
+ switch (getTagName(node)) {
+ case 'img':
+ path = getAttribute(node, 'src') ?? '';
+ break;
+ case 'source':
+ path = extractFirstUrlOfSrcSet(node) ?? '';
+ break;
+ case 'link':
+ if (linkRels.includes(getAttribute(node, 'rel') ?? '')) {
+ path = getAttribute(node, 'href') ?? '';
+ }
+ break;
+ case 'meta':
+ if (getAttribute(node, 'property') === 'og:image' && getAttribute(node, 'content')) {
+ path = getAttribute(node, 'content') ?? '';
+ }
+ break;
+ case 'script':
+ if (getAttribute(node, 'type') !== 'module' && getAttribute(node, 'src')) {
+ path = getAttribute(node, 'src') ?? '';
+ }
+ break;
+ default:
+ return false;
+ }
+ if (!path) {
+ return false;
+ }
+ // Windows fix: if path starts with C:/, avoid URL parsing
+ if (windowsPathRE.test(path)) {
+ return true;
+ }
+ try {
+ new URL(path);
+ return false;
+ } catch (e) {
+ return true;
+ }
}
function isInlineScript(node: Element): boolean {
- switch (getTagName(node)) {
- case 'script':
- if (getAttribute(node, 'type') === 'module' && !getAttribute(node, 'src')) {
- return true;
- }
- return false;
- default:
- return false;
- }
+ switch (getTagName(node)) {
+ case 'script':
+ if (getAttribute(node, 'type') === 'module' && !getAttribute(node, 'src')) {
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
}
function isExternalScript(node: Element): boolean {
- switch (getTagName(node)) {
- case 'script':
- if (hasAttribute(node, 'src')) {
- return true;
- }
- return false;
- default:
- return false;
- }
+ switch (getTagName(node)) {
+ case 'script':
+ if (hasAttribute(node, 'src')) {
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
}
function isInlineStyle(node: Element): boolean {
- return getTagName(node) === 'style';
+ return getTagName(node) === 'style';
}
export function isStylesheetLink(node: Element): boolean {
- return getTagName(node) === 'link' && getAttribute(node, 'rel') === 'stylesheet';
+ return getTagName(node) === 'link' && getAttribute(node, 'rel') === 'stylesheet';
}
export function isHashedAsset(node: Element) {
- switch (getTagName(node)) {
- case 'img':
- return true;
- case 'source':
- return true;
- case 'script':
- return true;
- case 'link':
- return hashedLinkRels.includes(getAttribute(node, 'rel')!);
- case 'meta':
- return true;
- default:
- return false;
- }
+ switch (getTagName(node)) {
+ case 'img':
+ return true;
+ case 'source':
+ return true;
+ case 'script':
+ return true;
+ case 'link':
+ return hashedLinkRels.includes(getAttribute(node, 'rel')!);
+ case 'meta':
+ return true;
+ default:
+ return false;
+ }
}
export function resolveAssetFilePath(browserPath: string, htmlDir: string, projectRootDir: string, absolutePathPrefix?: string) {
- const _browserPath = absolutePathPrefix && browserPath[0] === '/' ? '/' + npath.posix.relative(absolutePathPrefix, browserPath) : browserPath;
- return npath.join(_browserPath.startsWith('/') ? projectRootDir : htmlDir, _browserPath.split('/').join(npath.sep));
+ const _browserPath = absolutePathPrefix && browserPath[0] === '/' ? '/' + npath.posix.relative(absolutePathPrefix, browserPath) : browserPath;
+ return npath.join(_browserPath.startsWith('/') ? projectRootDir : htmlDir, _browserPath.split('/').join(npath.sep));
}
export function getSourceAttribute(node: Element) {
- switch (getTagName(node)) {
- case 'img': {
- return 'src';
- }
- case 'source': {
- return 'srcset';
- }
- case 'link': {
- return 'href';
- }
- case 'script': {
- return 'src';
- }
- case 'meta': {
- return 'content';
- }
- default:
- throw new Error(`Unknown node with tagname ${getTagName(node)}`);
- }
+ switch (getTagName(node)) {
+ case 'img': {
+ return 'src';
+ }
+ case 'source': {
+ return 'srcset';
+ }
+ case 'link': {
+ return 'href';
+ }
+ case 'script': {
+ return 'src';
+ }
+ case 'meta': {
+ return 'content';
+ }
+ default:
+ throw new Error(`Unknown node with tagname ${getTagName(node)}`);
+ }
}
export interface Location {
- start: number;
- end: number;
+ start: number;
+ end: number;
}
export function getSourcePaths(node: Element) {
- const key = getSourceAttribute(node);
-
- let location: Location = { start: 0, end: 0 };
- const src = getAttribute(node, key);
- if (node.sourceCodeLocation) {
- let loc = node.sourceCodeLocation.attrs?.[key];
- if (loc) {
- location.start = loc.startOffset;
- location.end = loc.endOffset;
- }
- }
- if (typeof key !== 'string' || src === '') {
- throw new Error(`Missing attribute ${key} in element ${node.nodeName}`);
- }
-
- let paths: { path: string; location: Location }[] = [];
- if (src && key === 'srcset') {
- paths = getSrcSetUrls(src).map((path) => ({
- path,
- location,
- }));
- } else if (src) {
- paths.push({
- path: src,
- location,
- });
- }
-
- return paths;
+ const key = getSourceAttribute(node);
+
+ let location: Location = { start: 0, end: 0 };
+ const src = getAttribute(node, key);
+ if (node.sourceCodeLocation) {
+ let loc = node.sourceCodeLocation.attrs?.[key];
+ if (loc) {
+ location.start = loc.startOffset;
+ location.end = loc.endOffset;
+ }
+ }
+ if (typeof key !== 'string' || src === '') {
+ throw new Error(`Missing attribute ${key} in element ${node.nodeName}`);
+ }
+
+ let paths: { path: string; location: Location }[] = [];
+ if (src && key === 'srcset') {
+ paths = getSrcSetUrls(src).map((path) => ({
+ path,
+ location,
+ }));
+ } else if (src) {
+ paths.push({
+ path: src,
+ location,
+ });
+ }
+
+ return paths;
}
export function getTextContent(node: Node): string {
- if (adapter.isCommentNode(node)) {
- return node.data || '';
- }
- if (adapter.isTextNode(node)) {
- return node.value || '';
- }
- const subtree = findNodes(node, (n) => adapter.isTextNode(n));
- return subtree.map(getTextContent).join('');
+ if (adapter.isCommentNode(node)) {
+ return node.data || '';
+ }
+ if (adapter.isTextNode(node)) {
+ return node.value || '';
+ }
+ const subtree = findNodes(node, (n) => adapter.isTextNode(n));
+ return subtree.map(getTextContent).join('');
}
export function getAttributes(node: Element): Record<string, any> {
- return Object.fromEntries(node.attrs.map((attr) => [attr.name, attr.value]));
+ return Object.fromEntries(node.attrs.map((attr) => [attr.name, attr.value]));
}
export function findAssets(document: Document) {
- return findElements(document, isAsset);
+ return findElements(document, isAsset);
}
export function findInlineScripts(document: Document) {
- return findElements(document, isInlineScript);
+ return findElements(document, isInlineScript);
}
export function findExternalScripts(document: Document) {
- return findElements(document, isExternalScript);
+ return findElements(document, isExternalScript);
}
export function findInlineStyles(document: Document) {
- return findElements(document, isInlineStyle);
+ return findElements(document, isInlineStyle);
}
export function findStyleLinks(document: Document) {
- return findElements(document, isStylesheetLink);
+ return findElements(document, isStylesheetLink);
}
diff --git a/packages/astro/src/vite-plugin-build-html/index.ts b/packages/astro/src/vite-plugin-build-html/index.ts
index 90765c35d..d6472cb1a 100644
--- a/packages/astro/src/vite-plugin-build-html/index.ts
+++ b/packages/astro/src/vite-plugin-build-html/index.ts
@@ -26,471 +26,471 @@ const ASTRO_EMPTY = '@astro-empty';
const STATUS_CODE_RE = /^404$/;
interface PluginOptions {
- astroConfig: AstroConfig;
- internals: BuildInternals;
- logging: LogOptions;
- allPages: AllPagesData;
- pageNames: string[];
- origin: string;
- routeCache: RouteCache;
- viteServer: ViteDevServer;
+ astroConfig: AstroConfig;
+ internals: BuildInternals;
+ logging: LogOptions;
+ allPages: AllPagesData;
+ pageNames: string[];
+ origin: string;
+ routeCache: RouteCache;
+ viteServer: ViteDevServer;
}
export function rollupPluginAstroBuildHTML(options: PluginOptions): VitePlugin {
- const { astroConfig, internals, logging, origin, allPages, routeCache, viteServer, pageNames } = options;
-
- // The filepath root of the src folder
- const srcRoot = astroConfig.src.pathname;
- // The web path of the src folter
- const srcRootWeb = srcRoot.substr(astroConfig.projectRoot.pathname.length - 1);
-
- // A map of pages to rendered HTML
- const renderedPageMap = new Map<string, string>();
-
- //
- const astroScriptMap = new Map<string, string>();
- const astroPageMap = new Map<string, string>();
- const astroAssetMap = new Map<string, Promise<Buffer>>();
-
- const cssChunkMap = new Map<string, string[]>();
- const pageStyleImportOrder: string[] = [];
-
- return {
- name: PLUGIN_NAME,
-
- enforce: 'pre',
-
- async options(inputOptions) {
- const htmlInput: Set<string> = new Set();
- const assetInput: Set<string> = new Set();
- const jsInput: Set<string> = new Set();
-
- for (const [component, pageData] of Object.entries(allPages)) {
- const [renderers, mod] = pageData.preload;
-
- // Hydrated components are statically identified.
- for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
- jsInput.add(path);
- }
-
- for (const pathname of pageData.paths) {
- pageNames.push(pathname.replace(/\/?$/, '/index.html').replace(/^\//, ''));
- const id = ASTRO_PAGE_PREFIX + pathname;
- const html = await ssrRender(renderers, mod, {
- astroConfig,
- filePath: new URL(`./${component}`, astroConfig.projectRoot),
- logging,
- mode: 'production',
- origin,
- pathname,
- route: pageData.route,
- routeCache,
- viteServer,
- });
- renderedPageMap.set(id, html);
-
- const document = parse5.parse(html, {
- sourceCodeLocationInfo: true,
- });
-
- const frontEndImports = [];
- for (const script of findInlineScripts(document)) {
- const astroScript = getAttribute(script, 'astro-script');
- if (astroScript) {
- const js = getTextContent(script);
- const scriptId = ASTRO_SCRIPT_PREFIX + astroScript;
- frontEndImports.push(scriptId);
- astroScriptMap.set(scriptId, js);
- }
- }
-
- for (const script of findExternalScripts(document)) {
- if (isHoistedScript(script)) {
- const astroScript = getAttribute(script, 'astro-script');
- const src = getAttribute(script, 'src');
- if (astroScript) {
- const js = `import '${src}';`;
- const scriptId = ASTRO_SCRIPT_PREFIX + astroScript;
- frontEndImports.push(scriptId);
- astroScriptMap.set(scriptId, js);
- }
- } else if (isInSrcDirectory(script, 'src', srcRoot, srcRootWeb)) {
- const src = getAttribute(script, 'src');
- if (src) jsInput.add(src);
- }
- }
-
- let styles = '';
- for (const node of findInlineStyles(document)) {
- if (hasAttribute(node, 'astro-style')) {
- styles += getTextContent(node);
- }
- }
-
- const assetImports = [];
- for (let node of findAssets(document)) {
- if (isBuildableLink(node, srcRoot, srcRootWeb)) {
- const href = getAttribute(node, 'href')!;
- assetImports.push(href);
- }
-
- if (isBuildableImage(node, srcRoot, srcRootWeb)) {
- const src = getAttribute(node, 'src');
- if (src?.startsWith(srcRoot) && !astroAssetMap.has(src)) {
- astroAssetMap.set(src, fs.readFile(new URL(`file://${src}`)));
- } else if (src?.startsWith(srcRootWeb) && !astroAssetMap.has(src)) {
- const resolved = new URL('.' + src, astroConfig.projectRoot);
- astroAssetMap.set(src, fs.readFile(resolved));
- }
- }
-
- if (hasSrcSet(node)) {
- const candidates = matchSrcset(getAttribute(node, 'srcset')!);
- for (const { url } of candidates) {
- if (url.startsWith(srcRoot) && !astroAssetMap.has(url)) {
- astroAssetMap.set(url, fs.readFile(new URL(`file://${url}`)));
- } else if (url.startsWith(srcRootWeb) && !astroAssetMap.has(url)) {
- const resolved = new URL('.' + url, astroConfig.projectRoot);
- astroAssetMap.set(url, fs.readFile(resolved));
- }
- }
- }
- }
-
- if (styles) {
- const styleId = getAstroStyleId(pathname);
- internals.astroStyleMap.set(styleId, styles);
- // Put this at the front of imports
- assetImports.unshift(styleId);
- }
-
- if (frontEndImports.length) {
- htmlInput.add(id);
- const jsSource = frontEndImports.map((sid) => `import '${sid}';`).join('\n');
- astroPageMap.set(id, jsSource);
- }
-
- if (assetImports.length) {
- const pageStyleId = getAstroPageStyleId(pathname);
- const jsSource = assetImports.map((sid) => `import '${sid}';`).join('\n');
- internals.astroPageStyleMap.set(pageStyleId, jsSource);
- assetInput.add(pageStyleId);
-
- // preserve asset order in the order we encounter them
- for (const assetHref of assetImports) {
- if (!pageStyleImportOrder.includes(assetHref)) pageStyleImportOrder.push(assetHref);
- }
- }
- }
- }
-
- const allInputs = new Set([...jsInput, ...htmlInput, ...assetInput]);
- // You always need at least 1 input, so add an placeholder just so we can build HTML/CSS
- if (!allInputs.size) {
- allInputs.add(ASTRO_EMPTY);
- }
- const outOptions = addRollupInput(inputOptions, Array.from(allInputs));
- return outOptions;
- },
-
- async resolveId(id) {
- switch (true) {
- case astroScriptMap.has(id):
- case astroPageMap.has(id):
- case id === ASTRO_EMPTY: {
- return id;
- }
- }
-
- return undefined;
- },
-
- async load(id) {
- // Load pages
- if (astroPageMap.has(id)) {
- return astroPageMap.get(id)!;
- }
- // Load scripts
- if (astroScriptMap.has(id)) {
- return astroScriptMap.get(id)!;
- }
- // Give this module actual code so it doesnt warn about an empty chunk
- if (id === ASTRO_EMPTY) {
- return 'console.log("empty");';
- }
-
- return null;
- },
-
- outputOptions(outputOptions) {
- Object.assign(outputOptions, {
- entryFileNames(chunk: PreRenderedChunk) {
- // Removes the `@astro-page` prefix from JS chunk names.
- if (chunk.name.startsWith(ASTRO_PAGE_PREFIX)) {
- let pageName = chunk.name.substr(ASTRO_PAGE_PREFIX.length + 1);
- if (!pageName) {
- pageName = 'index';
- }
- return `assets/${pageName}.[hash].js`;
- }
- return 'assets/[name].[hash].js';
- },
- });
- return outputOptions;
- },
-
- async generateBundle(_options, bundle) {
- const facadeIdMap = new Map<string, string>();
- for (const [chunkId, output] of Object.entries(bundle)) {
- if (output.type === 'chunk') {
- const chunk = output as OutputChunk;
- const id = chunk.facadeModuleId;
- if (id === ASTRO_EMPTY) {
- delete bundle[chunkId];
- } else if (id) {
- facadeIdMap.set(id, chunk.fileName);
- }
- }
- }
-
- // Emit assets (images, etc)
- const assetIdMap = new Map<string, string>();
- for (const [assetPath, dataPromise] of astroAssetMap) {
- const referenceId = this.emitFile({
- type: 'asset',
- name: npath.basename(assetPath),
- source: await dataPromise,
- });
- assetIdMap.set(assetPath, referenceId);
- }
-
- // Sort CSS in order of appearance in HTML (pageStyleImportOrder)
- // This is the “global ordering” used below
- const sortedCSSChunks = [...internals.pureCSSChunks];
- sortedCSSChunks.sort((a, b) => {
- let aIndex = Math.min(
- ...Object.keys(a.modules).map((id) => {
- const i = pageStyleImportOrder.findIndex((url) => id.endsWith(url));
- return i >= 0 ? i : Infinity; // if -1 is encountered (unknown order), move to the end (Infinity)
- })
- );
- let bIndex = Math.min(
- ...Object.keys(b.modules).map((id) => {
- const i = pageStyleImportOrder.findIndex((url) => id.endsWith(url));
- return i >= 0 ? i : Infinity;
- })
- );
- return aIndex - bIndex;
- });
- const sortedChunkNames = sortedCSSChunks.map(({ fileName }) => fileName);
-
- // Create a mapping of chunks to dependent chunks, used to add the proper
- // link tags for CSS.
- for (const chunk of sortedCSSChunks) {
- const chunkModules = [chunk.fileName, ...chunk.imports];
- // For each HTML output, sort CSS in HTML order Note: here we actually
- // want -1 to be first. Since the last CSS “wins”, we want to load
- // “unknown” (-1) CSS ordering first, followed by “known” ordering at
- // the end so it takes priority
- chunkModules.sort((a, b) => sortedChunkNames.indexOf(a) - sortedChunkNames.indexOf(b));
-
- const referenceIDs: string[] = [];
- for (const chunkID of chunkModules) {
- const referenceID = internals.chunkToReferenceIdMap.get(chunkID);
- if (referenceID) referenceIDs.push(referenceID);
- }
- for (const id of Object.keys(chunk.modules)) {
- cssChunkMap.set(id, referenceIDs);
- }
- }
-
- // Keep track of links added so we don't do so twice.
- const linkChunksAdded = new Set<string>();
- const appendStyleChunksBefore = (ref: parse5.Element, pathname: string, referenceIds: string[] | undefined, attrs: Record<string, any> = {}) => {
- let added = false;
- if (referenceIds) {
- const lastNode = ref;
- for (const referenceId of referenceIds) {
- const chunkFileName = this.getFileName(referenceId);
- const relPath = npath.posix.relative(pathname, '/' + chunkFileName);
-
- // This prevents added links more than once per type.
- const key = pathname + relPath + attrs.rel || 'stylesheet';
- if (!linkChunksAdded.has(key)) {
- linkChunksAdded.add(key);
- insertBefore(
- lastNode.parentNode,
- createElement('link', {
- rel: 'stylesheet',
- ...attrs,
- href: relPath,
- }),
- lastNode
- );
- added = true;
- }
- }
- }
- return added;
- };
-
- for (const [id, html] of renderedPageMap) {
- const pathname = id.substr(ASTRO_PAGE_PREFIX.length);
- const document = parse5.parse(html, {
- sourceCodeLocationInfo: true,
- });
-
- // This is the module for the page-level bundle which includes
- // hoisted scripts and hydrated components.
- const pageAssetId = facadeIdMap.get(id);
- const bundlePath = '/' + pageAssetId;
-
- // Update scripts
- let pageBundleAdded = false;
-
- // Update inline scripts. These could be hydrated component scripts or hoisted inline scripts
- for (let script of findInlineScripts(document)) {
- if (getAttribute(script, 'astro-script') && typeof pageAssetId === 'string') {
- if (!pageBundleAdded) {
- pageBundleAdded = true;
- const relPath = npath.posix.relative(pathname, bundlePath);
- insertBefore(
- script.parentNode,
- createScript({
- type: 'module',
- src: relPath,
- }),
- script
- );
- }
- remove(script);
- }
- }
-
- // Update external scripts. These could be hoisted or in the src folder.
- for (let script of findExternalScripts(document)) {
- if (getAttribute(script, 'astro-script') && typeof pageAssetId === 'string') {
- if (!pageBundleAdded) {
- pageBundleAdded = true;
- const relPath = npath.posix.relative(pathname, bundlePath);
- insertBefore(
- script.parentNode,
- createScript({
- type: 'module',
- src: relPath,
- }),
- script
- );
- }
- remove(script);
- } else if (isInSrcDirectory(script, 'src', srcRoot, srcRootWeb)) {
- let src = getAttribute(script, 'src');
- // If this is projectRoot relative, get the fullpath to match the facadeId.
- if (src?.startsWith(srcRootWeb)) {
- src = new URL('.' + src, astroConfig.projectRoot).pathname;
- }
- // On windows the facadeId doesn't start with / but does not Unix :/
- if (src && (facadeIdMap.has(src) || facadeIdMap.has(src.substr(1)))) {
- const assetRootPath = '/' + (facadeIdMap.get(src) || facadeIdMap.get(src.substr(1)));
- const relPath = npath.posix.relative(pathname, assetRootPath);
- const attrs = getAttributes(script);
- insertBefore(
- script.parentNode,
- createScript({
- ...attrs,
- src: relPath,
- }),
- script
- );
- remove(script);
- }
- }
- }
-
- const styleId = getAstroPageStyleId(pathname);
- let pageCSSAdded = false;
- for (const node of findAssets(document)) {
- if (isBuildableLink(node, srcRoot, srcRootWeb)) {
- const rel = getAttribute(node, 'rel');
- switch (rel) {
- case 'stylesheet': {
- if (!pageCSSAdded) {
- const attrs = getAttributes(node);
- delete attrs['data-astro-injected'];
- pageCSSAdded = appendStyleChunksBefore(node, pathname, cssChunkMap.get(styleId), attrs);
- }
- remove(node);
- break;
- }
- case 'preload': {
- if (getAttribute(node, 'as') === 'style') {
- const attrs = getAttributes(node);
- appendStyleChunksBefore(node, pathname, cssChunkMap.get(styleId), attrs);
- remove(node);
- }
- }
- }
- }
-
- if (isBuildableImage(node, srcRoot, srcRootWeb)) {
- const src = getAttribute(node, 'src')!;
- const referenceId = assetIdMap.get(src);
- if (referenceId) {
- const fileName = this.getFileName(referenceId);
- const relPath = npath.posix.relative(pathname, '/' + fileName);
- setAttribute(node, 'src', relPath);
- }
- }
-
- // Could be a `source` or an `img`.
- if (hasSrcSet(node)) {
- const srcset = getAttribute(node, 'srcset')!;
- let changedSrcset = srcset;
- const urls = matchSrcset(srcset).map((c) => c.url);
- for (const url of urls) {
- if (assetIdMap.has(url)) {
- const referenceId = assetIdMap.get(url)!;
- const fileName = this.getFileName(referenceId);
- const relPath = npath.posix.relative(pathname, '/' + fileName);
- changedSrcset = changedSrcset.replace(url, relPath);
- }
- }
- // If anything changed, update it
- if (changedSrcset !== srcset) {
- setAttribute(node, 'srcset', changedSrcset);
- }
- }
- }
-
- // Page styles for <style> usage, if not already appended via links.
- for (const style of findInlineStyles(document)) {
- if (hasAttribute(style, 'astro-style')) {
- if (!pageCSSAdded) {
- pageCSSAdded = appendStyleChunksBefore(style, pathname, cssChunkMap.get(styleId));
- }
-
- remove(style);
- }
- }
-
- const outHTML = parse5.serialize(document);
- const name = pathname.substr(1);
- let outPath: string;
-
- // Output directly to 404.html rather than 400/index.html
- // Supports any other status codes, too
- if (name.match(STATUS_CODE_RE)) {
- outPath = npath.posix.join(`${name}.html`);
- } else {
- outPath = npath.posix.join(name, 'index.html');
- }
-
- this.emitFile({
- fileName: outPath,
- source: outHTML,
- type: 'asset',
- });
- }
- },
- };
+ const { astroConfig, internals, logging, origin, allPages, routeCache, viteServer, pageNames } = options;
+
+ // The filepath root of the src folder
+ const srcRoot = astroConfig.src.pathname;
+ // The web path of the src folter
+ const srcRootWeb = srcRoot.substr(astroConfig.projectRoot.pathname.length - 1);
+
+ // A map of pages to rendered HTML
+ const renderedPageMap = new Map<string, string>();
+
+ //
+ const astroScriptMap = new Map<string, string>();
+ const astroPageMap = new Map<string, string>();
+ const astroAssetMap = new Map<string, Promise<Buffer>>();
+
+ const cssChunkMap = new Map<string, string[]>();
+ const pageStyleImportOrder: string[] = [];
+
+ return {
+ name: PLUGIN_NAME,
+
+ enforce: 'pre',
+
+ async options(inputOptions) {
+ const htmlInput: Set<string> = new Set();
+ const assetInput: Set<string> = new Set();
+ const jsInput: Set<string> = new Set();
+
+ for (const [component, pageData] of Object.entries(allPages)) {
+ const [renderers, mod] = pageData.preload;
+
+ // Hydrated components are statically identified.
+ for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
+ jsInput.add(path);
+ }
+
+ for (const pathname of pageData.paths) {
+ pageNames.push(pathname.replace(/\/?$/, '/index.html').replace(/^\//, ''));
+ const id = ASTRO_PAGE_PREFIX + pathname;
+ const html = await ssrRender(renderers, mod, {
+ astroConfig,
+ filePath: new URL(`./${component}`, astroConfig.projectRoot),
+ logging,
+ mode: 'production',
+ origin,
+ pathname,
+ route: pageData.route,
+ routeCache,
+ viteServer,
+ });
+ renderedPageMap.set(id, html);
+
+ const document = parse5.parse(html, {
+ sourceCodeLocationInfo: true,
+ });
+
+ const frontEndImports = [];
+ for (const script of findInlineScripts(document)) {
+ const astroScript = getAttribute(script, 'astro-script');
+ if (astroScript) {
+ const js = getTextContent(script);
+ const scriptId = ASTRO_SCRIPT_PREFIX + astroScript;
+ frontEndImports.push(scriptId);
+ astroScriptMap.set(scriptId, js);
+ }
+ }
+
+ for (const script of findExternalScripts(document)) {
+ if (isHoistedScript(script)) {
+ const astroScript = getAttribute(script, 'astro-script');
+ const src = getAttribute(script, 'src');
+ if (astroScript) {
+ const js = `import '${src}';`;
+ const scriptId = ASTRO_SCRIPT_PREFIX + astroScript;
+ frontEndImports.push(scriptId);
+ astroScriptMap.set(scriptId, js);
+ }
+ } else if (isInSrcDirectory(script, 'src', srcRoot, srcRootWeb)) {
+ const src = getAttribute(script, 'src');
+ if (src) jsInput.add(src);
+ }
+ }
+
+ let styles = '';
+ for (const node of findInlineStyles(document)) {
+ if (hasAttribute(node, 'astro-style')) {
+ styles += getTextContent(node);
+ }
+ }
+
+ const assetImports = [];
+ for (let node of findAssets(document)) {
+ if (isBuildableLink(node, srcRoot, srcRootWeb)) {
+ const href = getAttribute(node, 'href')!;
+ assetImports.push(href);
+ }
+
+ if (isBuildableImage(node, srcRoot, srcRootWeb)) {
+ const src = getAttribute(node, 'src');
+ if (src?.startsWith(srcRoot) && !astroAssetMap.has(src)) {
+ astroAssetMap.set(src, fs.readFile(new URL(`file://${src}`)));
+ } else if (src?.startsWith(srcRootWeb) && !astroAssetMap.has(src)) {
+ const resolved = new URL('.' + src, astroConfig.projectRoot);
+ astroAssetMap.set(src, fs.readFile(resolved));
+ }
+ }
+
+ if (hasSrcSet(node)) {
+ const candidates = matchSrcset(getAttribute(node, 'srcset')!);
+ for (const { url } of candidates) {
+ if (url.startsWith(srcRoot) && !astroAssetMap.has(url)) {
+ astroAssetMap.set(url, fs.readFile(new URL(`file://${url}`)));
+ } else if (url.startsWith(srcRootWeb) && !astroAssetMap.has(url)) {
+ const resolved = new URL('.' + url, astroConfig.projectRoot);
+ astroAssetMap.set(url, fs.readFile(resolved));
+ }
+ }
+ }
+ }
+
+ if (styles) {
+ const styleId = getAstroStyleId(pathname);
+ internals.astroStyleMap.set(styleId, styles);
+ // Put this at the front of imports
+ assetImports.unshift(styleId);
+ }
+
+ if (frontEndImports.length) {
+ htmlInput.add(id);
+ const jsSource = frontEndImports.map((sid) => `import '${sid}';`).join('\n');
+ astroPageMap.set(id, jsSource);
+ }
+
+ if (assetImports.length) {
+ const pageStyleId = getAstroPageStyleId(pathname);
+ const jsSource = assetImports.map((sid) => `import '${sid}';`).join('\n');
+ internals.astroPageStyleMap.set(pageStyleId, jsSource);
+ assetInput.add(pageStyleId);
+
+ // preserve asset order in the order we encounter them
+ for (const assetHref of assetImports) {
+ if (!pageStyleImportOrder.includes(assetHref)) pageStyleImportOrder.push(assetHref);
+ }
+ }
+ }
+ }
+
+ const allInputs = new Set([...jsInput, ...htmlInput, ...assetInput]);
+ // You always need at least 1 input, so add an placeholder just so we can build HTML/CSS
+ if (!allInputs.size) {
+ allInputs.add(ASTRO_EMPTY);
+ }
+ const outOptions = addRollupInput(inputOptions, Array.from(allInputs));
+ return outOptions;
+ },
+
+ async resolveId(id) {
+ switch (true) {
+ case astroScriptMap.has(id):
+ case astroPageMap.has(id):
+ case id === ASTRO_EMPTY: {
+ return id;
+ }
+ }
+
+ return undefined;
+ },
+
+ async load(id) {
+ // Load pages
+ if (astroPageMap.has(id)) {
+ return astroPageMap.get(id)!;
+ }
+ // Load scripts
+ if (astroScriptMap.has(id)) {
+ return astroScriptMap.get(id)!;
+ }
+ // Give this module actual code so it doesnt warn about an empty chunk
+ if (id === ASTRO_EMPTY) {
+ return 'console.log("empty");';
+ }
+
+ return null;
+ },
+
+ outputOptions(outputOptions) {
+ Object.assign(outputOptions, {
+ entryFileNames(chunk: PreRenderedChunk) {
+ // Removes the `@astro-page` prefix from JS chunk names.
+ if (chunk.name.startsWith(ASTRO_PAGE_PREFIX)) {
+ let pageName = chunk.name.substr(ASTRO_PAGE_PREFIX.length + 1);
+ if (!pageName) {
+ pageName = 'index';
+ }
+ return `assets/${pageName}.[hash].js`;
+ }
+ return 'assets/[name].[hash].js';
+ },
+ });
+ return outputOptions;
+ },
+
+ async generateBundle(_options, bundle) {
+ const facadeIdMap = new Map<string, string>();
+ for (const [chunkId, output] of Object.entries(bundle)) {
+ if (output.type === 'chunk') {
+ const chunk = output as OutputChunk;
+ const id = chunk.facadeModuleId;
+ if (id === ASTRO_EMPTY) {
+ delete bundle[chunkId];
+ } else if (id) {
+ facadeIdMap.set(id, chunk.fileName);
+ }
+ }
+ }
+
+ // Emit assets (images, etc)
+ const assetIdMap = new Map<string, string>();
+ for (const [assetPath, dataPromise] of astroAssetMap) {
+ const referenceId = this.emitFile({
+ type: 'asset',
+ name: npath.basename(assetPath),
+ source: await dataPromise,
+ });
+ assetIdMap.set(assetPath, referenceId);
+ }
+
+ // Sort CSS in order of appearance in HTML (pageStyleImportOrder)
+ // This is the “global ordering” used below
+ const sortedCSSChunks = [...internals.pureCSSChunks];
+ sortedCSSChunks.sort((a, b) => {
+ let aIndex = Math.min(
+ ...Object.keys(a.modules).map((id) => {
+ const i = pageStyleImportOrder.findIndex((url) => id.endsWith(url));
+ return i >= 0 ? i : Infinity; // if -1 is encountered (unknown order), move to the end (Infinity)
+ })
+ );
+ let bIndex = Math.min(
+ ...Object.keys(b.modules).map((id) => {
+ const i = pageStyleImportOrder.findIndex((url) => id.endsWith(url));
+ return i >= 0 ? i : Infinity;
+ })
+ );
+ return aIndex - bIndex;
+ });
+ const sortedChunkNames = sortedCSSChunks.map(({ fileName }) => fileName);
+
+ // Create a mapping of chunks to dependent chunks, used to add the proper
+ // link tags for CSS.
+ for (const chunk of sortedCSSChunks) {
+ const chunkModules = [chunk.fileName, ...chunk.imports];
+ // For each HTML output, sort CSS in HTML order Note: here we actually
+ // want -1 to be first. Since the last CSS “wins”, we want to load
+ // “unknown” (-1) CSS ordering first, followed by “known” ordering at
+ // the end so it takes priority
+ chunkModules.sort((a, b) => sortedChunkNames.indexOf(a) - sortedChunkNames.indexOf(b));
+
+ const referenceIDs: string[] = [];
+ for (const chunkID of chunkModules) {
+ const referenceID = internals.chunkToReferenceIdMap.get(chunkID);
+ if (referenceID) referenceIDs.push(referenceID);
+ }
+ for (const id of Object.keys(chunk.modules)) {
+ cssChunkMap.set(id, referenceIDs);
+ }
+ }
+
+ // Keep track of links added so we don't do so twice.
+ const linkChunksAdded = new Set<string>();
+ const appendStyleChunksBefore = (ref: parse5.Element, pathname: string, referenceIds: string[] | undefined, attrs: Record<string, any> = {}) => {
+ let added = false;
+ if (referenceIds) {
+ const lastNode = ref;
+ for (const referenceId of referenceIds) {
+ const chunkFileName = this.getFileName(referenceId);
+ const relPath = npath.posix.relative(pathname, '/' + chunkFileName);
+
+ // This prevents added links more than once per type.
+ const key = pathname + relPath + attrs.rel || 'stylesheet';
+ if (!linkChunksAdded.has(key)) {
+ linkChunksAdded.add(key);
+ insertBefore(
+ lastNode.parentNode,
+ createElement('link', {
+ rel: 'stylesheet',
+ ...attrs,
+ href: relPath,
+ }),
+ lastNode
+ );
+ added = true;
+ }
+ }
+ }
+ return added;
+ };
+
+ for (const [id, html] of renderedPageMap) {
+ const pathname = id.substr(ASTRO_PAGE_PREFIX.length);
+ const document = parse5.parse(html, {
+ sourceCodeLocationInfo: true,
+ });
+
+ // This is the module for the page-level bundle which includes
+ // hoisted scripts and hydrated components.
+ const pageAssetId = facadeIdMap.get(id);
+ const bundlePath = '/' + pageAssetId;
+
+ // Update scripts
+ let pageBundleAdded = false;
+
+ // Update inline scripts. These could be hydrated component scripts or hoisted inline scripts
+ for (let script of findInlineScripts(document)) {
+ if (getAttribute(script, 'astro-script') && typeof pageAssetId === 'string') {
+ if (!pageBundleAdded) {
+ pageBundleAdded = true;
+ const relPath = npath.posix.relative(pathname, bundlePath);
+ insertBefore(
+ script.parentNode,
+ createScript({
+ type: 'module',
+ src: relPath,
+ }),
+ script
+ );
+ }
+ remove(script);
+ }
+ }
+
+ // Update external scripts. These could be hoisted or in the src folder.
+ for (let script of findExternalScripts(document)) {
+ if (getAttribute(script, 'astro-script') && typeof pageAssetId === 'string') {
+ if (!pageBundleAdded) {
+ pageBundleAdded = true;
+ const relPath = npath.posix.relative(pathname, bundlePath);
+ insertBefore(
+ script.parentNode,
+ createScript({
+ type: 'module',
+ src: relPath,
+ }),
+ script
+ );
+ }
+ remove(script);
+ } else if (isInSrcDirectory(script, 'src', srcRoot, srcRootWeb)) {
+ let src = getAttribute(script, 'src');
+ // If this is projectRoot relative, get the fullpath to match the facadeId.
+ if (src?.startsWith(srcRootWeb)) {
+ src = new URL('.' + src, astroConfig.projectRoot).pathname;
+ }
+ // On windows the facadeId doesn't start with / but does not Unix :/
+ if (src && (facadeIdMap.has(src) || facadeIdMap.has(src.substr(1)))) {
+ const assetRootPath = '/' + (facadeIdMap.get(src) || facadeIdMap.get(src.substr(1)));
+ const relPath = npath.posix.relative(pathname, assetRootPath);
+ const attrs = getAttributes(script);
+ insertBefore(
+ script.parentNode,
+ createScript({
+ ...attrs,
+ src: relPath,
+ }),
+ script
+ );
+ remove(script);
+ }
+ }
+ }
+
+ const styleId = getAstroPageStyleId(pathname);
+ let pageCSSAdded = false;
+ for (const node of findAssets(document)) {
+ if (isBuildableLink(node, srcRoot, srcRootWeb)) {
+ const rel = getAttribute(node, 'rel');
+ switch (rel) {
+ case 'stylesheet': {
+ if (!pageCSSAdded) {
+ const attrs = getAttributes(node);
+ delete attrs['data-astro-injected'];
+ pageCSSAdded = appendStyleChunksBefore(node, pathname, cssChunkMap.get(styleId), attrs);
+ }
+ remove(node);
+ break;
+ }
+ case 'preload': {
+ if (getAttribute(node, 'as') === 'style') {
+ const attrs = getAttributes(node);
+ appendStyleChunksBefore(node, pathname, cssChunkMap.get(styleId), attrs);
+ remove(node);
+ }
+ }
+ }
+ }
+
+ if (isBuildableImage(node, srcRoot, srcRootWeb)) {
+ const src = getAttribute(node, 'src')!;
+ const referenceId = assetIdMap.get(src);
+ if (referenceId) {
+ const fileName = this.getFileName(referenceId);
+ const relPath = npath.posix.relative(pathname, '/' + fileName);
+ setAttribute(node, 'src', relPath);
+ }
+ }
+
+ // Could be a `source` or an `img`.
+ if (hasSrcSet(node)) {
+ const srcset = getAttribute(node, 'srcset')!;
+ let changedSrcset = srcset;
+ const urls = matchSrcset(srcset).map((c) => c.url);
+ for (const url of urls) {
+ if (assetIdMap.has(url)) {
+ const referenceId = assetIdMap.get(url)!;
+ const fileName = this.getFileName(referenceId);
+ const relPath = npath.posix.relative(pathname, '/' + fileName);
+ changedSrcset = changedSrcset.replace(url, relPath);
+ }
+ }
+ // If anything changed, update it
+ if (changedSrcset !== srcset) {
+ setAttribute(node, 'srcset', changedSrcset);
+ }
+ }
+ }
+
+ // Page styles for <style> usage, if not already appended via links.
+ for (const style of findInlineStyles(document)) {
+ if (hasAttribute(style, 'astro-style')) {
+ if (!pageCSSAdded) {
+ pageCSSAdded = appendStyleChunksBefore(style, pathname, cssChunkMap.get(styleId));
+ }
+
+ remove(style);
+ }
+ }
+
+ const outHTML = parse5.serialize(document);
+ const name = pathname.substr(1);
+ let outPath: string;
+
+ // Output directly to 404.html rather than 400/index.html
+ // Supports any other status codes, too
+ if (name.match(STATUS_CODE_RE)) {
+ outPath = npath.posix.join(`${name}.html`);
+ } else {
+ outPath = npath.posix.join(name, 'index.html');
+ }
+
+ this.emitFile({
+ fileName: outPath,
+ source: outHTML,
+ type: 'asset',
+ });
+ }
+ },
+ };
}
diff --git a/packages/astro/src/vite-plugin-build-html/util.ts b/packages/astro/src/vite-plugin-build-html/util.ts
index 4f7b48f26..f42db5e72 100644
--- a/packages/astro/src/vite-plugin-build-html/util.ts
+++ b/packages/astro/src/vite-plugin-build-html/util.ts
@@ -5,47 +5,47 @@ import { isStylesheetLink } from './extract-assets.js';
const tagsWithSrcSet = new Set(['img', 'source']);
function startsWithSrcRoot(pathname: string, srcRoot: string, srcRootWeb: string): boolean {
- return (
- pathname.startsWith(srcRoot) || // /Users/user/project/src/styles/main.css
- pathname.startsWith(srcRootWeb) || // /src/styles/main.css
- `/${pathname}`.startsWith(srcRoot)
- ); // Windows fix: some paths are missing leading "/"
+ return (
+ pathname.startsWith(srcRoot) || // /Users/user/project/src/styles/main.css
+ pathname.startsWith(srcRootWeb) || // /src/styles/main.css
+ `/${pathname}`.startsWith(srcRoot)
+ ); // Windows fix: some paths are missing leading "/"
}
export function isInSrcDirectory(node: parse5.Element, attr: string, srcRoot: string, srcRootWeb: string): boolean {
- const value = getAttribute(node, attr);
- return value ? startsWithSrcRoot(value, srcRoot, srcRootWeb) : false;
+ const value = getAttribute(node, attr);
+ return value ? startsWithSrcRoot(value, srcRoot, srcRootWeb) : false;
}
export function isAstroInjectedLink(node: parse5.Element): boolean {
- return isStylesheetLink(node) && getAttribute(node, 'data-astro-injected') === '';
+ return isStylesheetLink(node) && getAttribute(node, 'data-astro-injected') === '';
}
export function isBuildableLink(node: parse5.Element, srcRoot: string, srcRootWeb: string): boolean {
- if (isAstroInjectedLink(node)) {
- return true;
- }
+ if (isAstroInjectedLink(node)) {
+ return true;
+ }
- const href = getAttribute(node, 'href');
- if (typeof href !== 'string' || !href.length) {
- return false;
- }
+ const href = getAttribute(node, 'href');
+ if (typeof href !== 'string' || !href.length) {
+ return false;
+ }
- return startsWithSrcRoot(href, srcRoot, srcRootWeb);
+ return startsWithSrcRoot(href, srcRoot, srcRootWeb);
}
export function isBuildableImage(node: parse5.Element, srcRoot: string, srcRootWeb: string): boolean {
- if (getTagName(node) === 'img') {
- const src = getAttribute(node, 'src');
- return src ? startsWithSrcRoot(src, srcRoot, srcRootWeb) : false;
- }
- return false;
+ if (getTagName(node) === 'img') {
+ const src = getAttribute(node, 'src');
+ return src ? startsWithSrcRoot(src, srcRoot, srcRootWeb) : false;
+ }
+ return false;
}
export function hasSrcSet(node: parse5.Element): boolean {
- return tagsWithSrcSet.has(getTagName(node)) && !!getAttribute(node, 'srcset');
+ return tagsWithSrcSet.has(getTagName(node)) && !!getAttribute(node, 'srcset');
}
export function isHoistedScript(node: parse5.Element): boolean {
- return getTagName(node) === 'script' && hasAttribute(node, 'hoist');
+ return getTagName(node) === 'script' && hasAttribute(node, 'hoist');
}
diff --git a/packages/astro/src/vite-plugin-config-alias/index.ts b/packages/astro/src/vite-plugin-config-alias/index.ts
index b1e421187..15ae0bffc 100644
--- a/packages/astro/src/vite-plugin-config-alias/index.ts
+++ b/packages/astro/src/vite-plugin-config-alias/index.ts
@@ -6,8 +6,8 @@ import type * as vite from '../core/vite';
/** Result of successfully parsed tsconfig.json or jsconfig.json. */
export declare interface Alias {
- find: RegExp;
- replacement: string;
+ find: RegExp;
+ replacement: string;
}
/** Returns a path with its slashes replaced with posix slashes. */
@@ -15,91 +15,91 @@ const normalize = (pathname: string) => String(pathname).split(path.sep).join(pa
/** Returns the results of a config file if it exists, otherwise null. */
const getExistingConfig = (searchName: string, cwd: string | undefined): tsr.TsConfigResultSuccess | null => {
- const config = tsr.tsconfigResolverSync({ cwd, searchName });
+ const config = tsr.tsconfigResolverSync({ cwd, searchName });
- return config.exists ? config : null;
+ return config.exists ? config : null;
};
/** Returns a list of compiled aliases. */
const getConfigAlias = (cwd: string | undefined): Alias[] | null => {
- /** Closest tsconfig.json or jsconfig.json */
- const config = getExistingConfig('tsconfig.json', cwd) || getExistingConfig('jsconfig.json', cwd);
+ /** Closest tsconfig.json or jsconfig.json */
+ const config = getExistingConfig('tsconfig.json', cwd) || getExistingConfig('jsconfig.json', cwd);
- // if no config was found, return null
- if (!config) return null;
+ // if no config was found, return null
+ if (!config) return null;
- /** Compiler options from tsconfig.json or jsconfig.json. */
- const compilerOptions = Object(config.config.compilerOptions);
+ /** Compiler options from tsconfig.json or jsconfig.json. */
+ const compilerOptions = Object(config.config.compilerOptions);
- // if no compilerOptions.baseUrl was defined, return null
- if (!compilerOptions.baseUrl) return null;
+ // if no compilerOptions.baseUrl was defined, return null
+ if (!compilerOptions.baseUrl) return null;
- // resolve the base url from the configuration file directory
- const baseUrl = path.posix.resolve(path.posix.dirname(normalize(config.path).replace(/^\/?/, '/')), normalize(compilerOptions.baseUrl));
+ // resolve the base url from the configuration file directory
+ const baseUrl = path.posix.resolve(path.posix.dirname(normalize(config.path).replace(/^\/?/, '/')), normalize(compilerOptions.baseUrl));
- /** List of compiled alias expressions. */
- const aliases: Alias[] = [];
+ /** List of compiled alias expressions. */
+ const aliases: Alias[] = [];
- // compile any alias expressions and push them to the list
- for (let [alias, values] of Object.entries(Object(compilerOptions.paths) as { [key: string]: string[] })) {
- values = [].concat(values as never);
+ // compile any alias expressions and push them to the list
+ for (let [alias, values] of Object.entries(Object(compilerOptions.paths) as { [key: string]: string[] })) {
+ values = [].concat(values as never);
- /** Regular Expression used to match a given path. */
- const find = new RegExp(`^${[...alias].map((segment) => (segment === '*' ? '(.+)' : segment.replace(/[\\^$*+?.()|[\]{}]/, '\\$&'))).join('')}$`);
+ /** Regular Expression used to match a given path. */
+ const find = new RegExp(`^${[...alias].map((segment) => (segment === '*' ? '(.+)' : segment.replace(/[\\^$*+?.()|[\]{}]/, '\\$&'))).join('')}$`);
- /** Internal index used to calculate the matching id in a replacement. */
- let matchId = 0;
+ /** Internal index used to calculate the matching id in a replacement. */
+ let matchId = 0;
- for (let value of values) {
- /** String used to replace a matched path. */
- const replacement = [...path.posix.resolve(baseUrl, value)].map((segment) => (segment === '*' ? `$${++matchId}` : segment === '$' ? '$$' : segment)).join('');
+ for (let value of values) {
+ /** String used to replace a matched path. */
+ const replacement = [...path.posix.resolve(baseUrl, value)].map((segment) => (segment === '*' ? `$${++matchId}` : segment === '$' ? '$$' : segment)).join('');
- aliases.push({ find, replacement });
- }
- }
+ aliases.push({ find, replacement });
+ }
+ }
- // compile the baseUrl expression and push it to the list
- // - `baseUrl` changes the way non-relative specifiers are resolved
- // - if `baseUrl` exists then all non-relative specifiers are resolved relative to it
- aliases.push({
- find: /^(?!\.*\/)(.+)$/,
- replacement: `${[...baseUrl].map((segment) => (segment === '$' ? '$$' : segment)).join('')}/$1`,
- });
+ // compile the baseUrl expression and push it to the list
+ // - `baseUrl` changes the way non-relative specifiers are resolved
+ // - if `baseUrl` exists then all non-relative specifiers are resolved relative to it
+ aliases.push({
+ find: /^(?!\.*\/)(.+)$/,
+ replacement: `${[...baseUrl].map((segment) => (segment === '$' ? '$$' : segment)).join('')}/$1`,
+ });
- return aliases;
+ return aliases;
};
/** Returns a Vite plugin used to alias pathes from tsconfig.json and jsconfig.json. */
export default function configAliasVitePlugin(astroConfig: { projectRoot?: URL; [key: string]: unknown }): vite.PluginOption {
- /** Aliases from the tsconfig.json or jsconfig.json configuration. */
- const configAlias = getConfigAlias(astroConfig.projectRoot && url.fileURLToPath(astroConfig.projectRoot));
-
- // if no config alias was found, bypass this plugin
- if (!configAlias) return {} as vite.PluginOption;
-
- return {
- name: '@astrojs/vite-plugin-config-alias',
- enforce: 'pre',
- async resolveId(sourceId: string, importer, options) {
- /** Resolved ID conditionally handled by any other resolver. (this gives priority to all other resolvers) */
- const resolvedId = await this.resolve(sourceId, importer, { skipSelf: true, ...options });
-
- // if any other resolver handles the file, return that resolution
- if (resolvedId) return resolvedId;
-
- // conditionally resolve the source ID from any matching alias or baseUrl
- for (const alias of configAlias) {
- if (alias.find.test(sourceId)) {
- /** Processed Source ID with our alias applied. */
- const aliasedSourceId = sourceId.replace(alias.find, alias.replacement);
-
- /** Resolved ID conditionally handled by any other resolver. (this also gives priority to all other resolvers) */
- const resolvedAliasedId = await this.resolve(aliasedSourceId, importer, { skipSelf: true, ...options });
-
- // if the existing resolvers find the file, return that resolution
- if (resolvedAliasedId) return resolvedAliasedId;
- }
- }
- },
- };
+ /** Aliases from the tsconfig.json or jsconfig.json configuration. */
+ const configAlias = getConfigAlias(astroConfig.projectRoot && url.fileURLToPath(astroConfig.projectRoot));
+
+ // if no config alias was found, bypass this plugin
+ if (!configAlias) return {} as vite.PluginOption;
+
+ return {
+ name: '@astrojs/vite-plugin-config-alias',
+ enforce: 'pre',
+ async resolveId(sourceId: string, importer, options) {
+ /** Resolved ID conditionally handled by any other resolver. (this gives priority to all other resolvers) */
+ const resolvedId = await this.resolve(sourceId, importer, { skipSelf: true, ...options });
+
+ // if any other resolver handles the file, return that resolution
+ if (resolvedId) return resolvedId;
+
+ // conditionally resolve the source ID from any matching alias or baseUrl
+ for (const alias of configAlias) {
+ if (alias.find.test(sourceId)) {
+ /** Processed Source ID with our alias applied. */
+ const aliasedSourceId = sourceId.replace(alias.find, alias.replacement);
+
+ /** Resolved ID conditionally handled by any other resolver. (this also gives priority to all other resolvers) */
+ const resolvedAliasedId = await this.resolve(aliasedSourceId, importer, { skipSelf: true, ...options });
+
+ // if the existing resolvers find the file, return that resolution
+ if (resolvedAliasedId) return resolvedAliasedId;
+ }
+ }
+ },
+ };
}
diff --git a/packages/astro/src/vite-plugin-fetch/index.ts b/packages/astro/src/vite-plugin-fetch/index.ts
index ce28faa94..19c355708 100644
--- a/packages/astro/src/vite-plugin-fetch/index.ts
+++ b/packages/astro/src/vite-plugin-fetch/index.ts
@@ -5,16 +5,16 @@ import { walk } from 'estree-walker';
// https://github.com/vitejs/vite/discussions/5109#discussioncomment-1450726
function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {
- if (options === undefined) {
- return false;
- }
- if (typeof options === 'boolean') {
- return options;
- }
- if (typeof options == 'object') {
- return !!options.ssr;
- }
- return false;
+ if (options === undefined) {
+ return false;
+ }
+ if (typeof options === 'boolean') {
+ return options;
+ }
+ if (typeof options == 'object') {
+ return !!options.ssr;
+ }
+ return false;
}
// This matches any JS-like file (that we know of)
@@ -24,61 +24,61 @@ const IGNORED_MODULES = [/astro\/dist\/runtime\/server/, /\/node-fetch\//];
const DEFINE_FETCH = `import fetch from 'node-fetch';\n`;
function isIdentifier(node: BaseNode): node is Identifier {
- return node.type === 'Identifier';
+ return node.type === 'Identifier';
}
export default function pluginFetch(): Plugin {
- return {
- name: '@astrojs/vite-plugin-fetch',
- enforce: 'post',
- async transform(code, id, opts) {
- const ssr = isSSR(opts);
- // If this isn't an SSR pass, `fetch` will already be available!
- if (!ssr) {
- return null;
- }
- // Only transform JS-like files
- if (!id.match(SUPPORTED_FILES)) {
- return null;
- }
- // Optimization: only run on probable matches
- if (!code.includes('fetch')) {
- return null;
- }
+ return {
+ name: '@astrojs/vite-plugin-fetch',
+ enforce: 'post',
+ async transform(code, id, opts) {
+ const ssr = isSSR(opts);
+ // If this isn't an SSR pass, `fetch` will already be available!
+ if (!ssr) {
+ return null;
+ }
+ // Only transform JS-like files
+ if (!id.match(SUPPORTED_FILES)) {
+ return null;
+ }
+ // Optimization: only run on probable matches
+ if (!code.includes('fetch')) {
+ return null;
+ }
- const ast = this.parse(code);
- let fetchDeclared = false;
- walk(ast, {
- enter(node, parent) {
- if (fetchDeclared) return this.skip();
- if (isIdentifier(node)) {
- // Identifier is OK in any type of Expression (CallExpression, UnaryExpression, etc)
- if (node.name === 'fetch' && !parent.type.endsWith('Expression')) {
- fetchDeclared = true;
- }
- }
- },
- });
+ const ast = this.parse(code);
+ let fetchDeclared = false;
+ walk(ast, {
+ enter(node, parent) {
+ if (fetchDeclared) return this.skip();
+ if (isIdentifier(node)) {
+ // Identifier is OK in any type of Expression (CallExpression, UnaryExpression, etc)
+ if (node.name === 'fetch' && !parent.type.endsWith('Expression')) {
+ fetchDeclared = true;
+ }
+ }
+ },
+ });
- // Fetch is already declared, do not inject a re-declaration!
- if (fetchDeclared) {
- return null;
- }
+ // Fetch is already declared, do not inject a re-declaration!
+ if (fetchDeclared) {
+ return null;
+ }
- // Ignore specific modules
- for (const ignored of IGNORED_MODULES) {
- if (id.match(ignored)) {
- return null;
- }
- }
- const s = new MagicString(code);
- s.prepend(DEFINE_FETCH);
- const result = s.toString();
- const map = s.generateMap({
- source: id,
- includeContent: true,
- });
- return { code: result, map };
- },
- };
+ // Ignore specific modules
+ for (const ignored of IGNORED_MODULES) {
+ if (id.match(ignored)) {
+ return null;
+ }
+ }
+ const s = new MagicString(code);
+ s.prepend(DEFINE_FETCH);
+ const result = s.toString();
+ const map = s.generateMap({
+ source: id,
+ includeContent: true,
+ });
+ return { code: result, map };
+ },
+ };
}
diff --git a/packages/astro/src/vite-plugin-jsx/index.ts b/packages/astro/src/vite-plugin-jsx/index.ts
index 23d31e0c2..a1efb6ffd 100644
--- a/packages/astro/src/vite-plugin-jsx/index.ts
+++ b/packages/astro/src/vite-plugin-jsx/index.ts
@@ -14,9 +14,9 @@ import { parseNpmName } from '../core/util.js';
const JSX_RENDERER_CACHE = new WeakMap<AstroConfig, Map<string, Renderer>>();
const JSX_EXTENSIONS = new Set(['.jsx', '.tsx']);
const IMPORT_STATEMENTS: Record<string, string> = {
- react: "import React from 'react'",
- preact: "import { h } from 'preact'",
- 'solid-js': "import 'solid-js/web'",
+ react: "import React from 'react'",
+ preact: "import { h } from 'preact'",
+ 'solid-js': "import 'solid-js/web'",
};
// A code snippet to inject into JS files to prevent esbuild reference bugs.
@@ -27,191 +27,191 @@ const PREVENT_UNUSED_IMPORTS = ';;(React,Fragment,h);';
// This check on a flexible "options" object is needed because Vite uses this flexible argument for ssr.
// More context: https://github.com/vitejs/vite/discussions/5109#discussioncomment-1450726
function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {
- if (options === undefined) {
- return false;
- }
- if (typeof options === 'boolean') {
- return options;
- }
- if (typeof options == 'object') {
- return !!options.ssr;
- }
- return false;
+ if (options === undefined) {
+ return false;
+ }
+ if (typeof options === 'boolean') {
+ return options;
+ }
+ if (typeof options == 'object') {
+ return !!options.ssr;
+ }
+ return false;
}
function getEsbuildLoader(fileExt: string): string {
- return fileExt.substr(1);
+ return fileExt.substr(1);
}
async function importJSXRenderers(rendererNames: string[]): Promise<Map<string, Renderer>> {
- const renderers = new Map<string, Renderer>();
- await Promise.all(
- rendererNames.map((name) =>
- import(name).then(({ default: renderer }) => {
- if (!renderer.jsxImportSource) return;
- renderers.set(renderer.jsxImportSource, renderer);
- })
- )
- );
- return renderers;
+ const renderers = new Map<string, Renderer>();
+ await Promise.all(
+ rendererNames.map((name) =>
+ import(name).then(({ default: renderer }) => {
+ if (!renderer.jsxImportSource) return;
+ renderers.set(renderer.jsxImportSource, renderer);
+ })
+ )
+ );
+ return renderers;
}
interface TransformJSXOptions {
- code: string;
- id: string;
- mode: string;
- renderer: Renderer;
- ssr: boolean;
+ code: string;
+ id: string;
+ mode: string;
+ renderer: Renderer;
+ ssr: boolean;
}
async function transformJSX({ code, mode, id, ssr, renderer }: TransformJSXOptions): Promise<TransformResult> {
- const { jsxTransformOptions } = renderer;
- const options = await jsxTransformOptions!({ mode, ssr });
- const plugins = [...(options.plugins || [])];
- const result = await babel.transformAsync(code, {
- presets: options.presets,
- plugins,
- cwd: process.cwd(),
- filename: id,
- ast: false,
- compact: false,
- sourceMaps: true,
- configFile: false,
- babelrc: false,
- });
- // TODO: Be more strict about bad return values here.
- // Should we throw an error instead? Should we never return `{code: ""}`?
- if (!result) return null;
- return {
- code: result.code || '',
- map: result.map,
- };
+ const { jsxTransformOptions } = renderer;
+ const options = await jsxTransformOptions!({ mode, ssr });
+ const plugins = [...(options.plugins || [])];
+ const result = await babel.transformAsync(code, {
+ presets: options.presets,
+ plugins,
+ cwd: process.cwd(),
+ filename: id,
+ ast: false,
+ compact: false,
+ sourceMaps: true,
+ configFile: false,
+ babelrc: false,
+ });
+ // TODO: Be more strict about bad return values here.
+ // Should we throw an error instead? Should we never return `{code: ""}`?
+ if (!result) return null;
+ return {
+ code: result.code || '',
+ map: result.map,
+ };
}
interface AstroPluginJSXOptions {
- config: AstroConfig;
- logging: LogOptions;
+ config: AstroConfig;
+ logging: LogOptions;
}
/** Use Astro config to allow for alternate or multiple JSX renderers (by default Vite will assume React) */
export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin {
- let viteConfig: ResolvedConfig;
-
- return {
- name: '@astrojs/vite-plugin-jsx',
- enforce: 'pre', // run transforms before other plugins
- configResolved(resolvedConfig) {
- viteConfig = resolvedConfig;
- },
- async transform(code, id, ssrOrOptions) {
- const ssr = isSSR(ssrOrOptions);
- if (!JSX_EXTENSIONS.has(path.extname(id))) {
- return null;
- }
-
- const { mode } = viteConfig;
- let jsxRenderers = JSX_RENDERER_CACHE.get(config);
-
- // load renderers (on first run only)
- if (!jsxRenderers) {
- jsxRenderers = new Map();
- const possibleRenderers = await importJSXRenderers(config.renderers);
- if (possibleRenderers.size === 0) {
- // note: we have filtered out all non-JSX files, so this error should only show if a JSX file is loaded with no matching renderers
- throw new Error(
- `${colors.yellow(
- id
- )}\nUnable to resolve a renderer that handles JSX transforms! Please include a \`renderer\` plugin which supports JSX in your \`astro.config.mjs\` file.`
- );
- }
- for (const [importSource, renderer] of possibleRenderers) {
- jsxRenderers.set(importSource, renderer);
- }
- JSX_RENDERER_CACHE.set(config, jsxRenderers);
- }
-
- // Attempt: Single JSX renderer
- // If we only have one renderer, we can skip a bunch of work!
- if (jsxRenderers.size === 1) {
- // downlevel any non-standard syntax, but preserve JSX
- const { code: jsxCode } = await esbuild.transform(code, {
- loader: getEsbuildLoader(path.extname(id)) as esbuild.Loader,
- jsx: 'preserve',
- sourcefile: id,
- sourcemap: 'inline',
- });
- return transformJSX({ code: jsxCode, id, renderer: [...jsxRenderers.values()][0], mode, ssr });
- }
-
- // Attempt: Multiple JSX renderers
- // we need valid JS to scan, so we can use `h` and `Fragment` as placeholders
- const { code: jsCode } = await esbuild.transform(code + PREVENT_UNUSED_IMPORTS, {
- loader: getEsbuildLoader(path.extname(id)) as esbuild.Loader,
- jsx: 'transform',
- jsxFactory: 'h',
- jsxFragment: 'Fragment',
- sourcefile: id,
- sourcemap: 'inline',
- });
-
- let imports: eslexer.ImportSpecifier[] = [];
- if (/import/.test(jsCode)) {
- let [i] = eslexer.parse(jsCode);
- imports = i as any;
- }
- let importSource: string | undefined;
- if (imports.length > 0) {
- for (let { n: spec } of imports) {
- const pkg = spec && parseNpmName(spec);
- if (!pkg) continue;
- if (jsxRenderers.has(pkg.name)) {
- importSource = pkg.name;
- break;
- }
- }
- }
-
- // if no imports were found, look for @jsxImportSource comment
- if (!importSource) {
- const multiline = code.match(/\/\*\*[\S\s]*\*\//gm) || [];
- for (const comment of multiline) {
- const [_, lib] = comment.match(/@jsxImportSource\s*(\S+)/) || [];
- if (lib) {
- importSource = lib;
- break;
- }
- }
- }
-
- // if JSX renderer found, then use that
- if (importSource) {
- const jsxRenderer = jsxRenderers.get(importSource);
- // if renderer not installed for this JSX source, throw error
- if (!jsxRenderer) {
- error(logging, 'renderer', `${colors.yellow(id)} No renderer installed for ${importSource}. Try adding \`@astrojs/renderer-${importSource}\` to your dependencies.`);
- return null;
- }
- // downlevel any non-standard syntax, but preserve JSX
- const { code: jsxCode } = await esbuild.transform(code, {
- loader: getEsbuildLoader(path.extname(id)) as esbuild.Loader,
- jsx: 'preserve',
- sourcefile: id,
- sourcemap: 'inline',
- });
- return await transformJSX({ code: jsxCode, id, renderer: jsxRenderers.get(importSource) as Renderer, mode, ssr });
- }
-
- // if we still can’t tell, throw error
- const defaultRenderer = [...jsxRenderers.keys()][0];
- error(
- logging,
- 'renderer',
- `${colors.yellow(id)}
+ let viteConfig: ResolvedConfig;
+
+ return {
+ name: '@astrojs/vite-plugin-jsx',
+ enforce: 'pre', // run transforms before other plugins
+ configResolved(resolvedConfig) {
+ viteConfig = resolvedConfig;
+ },
+ async transform(code, id, ssrOrOptions) {
+ const ssr = isSSR(ssrOrOptions);
+ if (!JSX_EXTENSIONS.has(path.extname(id))) {
+ return null;
+ }
+
+ const { mode } = viteConfig;
+ let jsxRenderers = JSX_RENDERER_CACHE.get(config);
+
+ // load renderers (on first run only)
+ if (!jsxRenderers) {
+ jsxRenderers = new Map();
+ const possibleRenderers = await importJSXRenderers(config.renderers);
+ if (possibleRenderers.size === 0) {
+ // note: we have filtered out all non-JSX files, so this error should only show if a JSX file is loaded with no matching renderers
+ throw new Error(
+ `${colors.yellow(
+ id
+ )}\nUnable to resolve a renderer that handles JSX transforms! Please include a \`renderer\` plugin which supports JSX in your \`astro.config.mjs\` file.`
+ );
+ }
+ for (const [importSource, renderer] of possibleRenderers) {
+ jsxRenderers.set(importSource, renderer);
+ }
+ JSX_RENDERER_CACHE.set(config, jsxRenderers);
+ }
+
+ // Attempt: Single JSX renderer
+ // If we only have one renderer, we can skip a bunch of work!
+ if (jsxRenderers.size === 1) {
+ // downlevel any non-standard syntax, but preserve JSX
+ const { code: jsxCode } = await esbuild.transform(code, {
+ loader: getEsbuildLoader(path.extname(id)) as esbuild.Loader,
+ jsx: 'preserve',
+ sourcefile: id,
+ sourcemap: 'inline',
+ });
+ return transformJSX({ code: jsxCode, id, renderer: [...jsxRenderers.values()][0], mode, ssr });
+ }
+
+ // Attempt: Multiple JSX renderers
+ // we need valid JS to scan, so we can use `h` and `Fragment` as placeholders
+ const { code: jsCode } = await esbuild.transform(code + PREVENT_UNUSED_IMPORTS, {
+ loader: getEsbuildLoader(path.extname(id)) as esbuild.Loader,
+ jsx: 'transform',
+ jsxFactory: 'h',
+ jsxFragment: 'Fragment',
+ sourcefile: id,
+ sourcemap: 'inline',
+ });
+
+ let imports: eslexer.ImportSpecifier[] = [];
+ if (/import/.test(jsCode)) {
+ let [i] = eslexer.parse(jsCode);
+ imports = i as any;
+ }
+ let importSource: string | undefined;
+ if (imports.length > 0) {
+ for (let { n: spec } of imports) {
+ const pkg = spec && parseNpmName(spec);
+ if (!pkg) continue;
+ if (jsxRenderers.has(pkg.name)) {
+ importSource = pkg.name;
+ break;
+ }
+ }
+ }
+
+ // if no imports were found, look for @jsxImportSource comment
+ if (!importSource) {
+ const multiline = code.match(/\/\*\*[\S\s]*\*\//gm) || [];
+ for (const comment of multiline) {
+ const [_, lib] = comment.match(/@jsxImportSource\s*(\S+)/) || [];
+ if (lib) {
+ importSource = lib;
+ break;
+ }
+ }
+ }
+
+ // if JSX renderer found, then use that
+ if (importSource) {
+ const jsxRenderer = jsxRenderers.get(importSource);
+ // if renderer not installed for this JSX source, throw error
+ if (!jsxRenderer) {
+ error(logging, 'renderer', `${colors.yellow(id)} No renderer installed for ${importSource}. Try adding \`@astrojs/renderer-${importSource}\` to your dependencies.`);
+ return null;
+ }
+ // downlevel any non-standard syntax, but preserve JSX
+ const { code: jsxCode } = await esbuild.transform(code, {
+ loader: getEsbuildLoader(path.extname(id)) as esbuild.Loader,
+ jsx: 'preserve',
+ sourcefile: id,
+ sourcemap: 'inline',
+ });
+ return await transformJSX({ code: jsxCode, id, renderer: jsxRenderers.get(importSource) as Renderer, mode, ssr });
+ }
+
+ // if we still can’t tell, throw error
+ const defaultRenderer = [...jsxRenderers.keys()][0];
+ error(
+ logging,
+ 'renderer',
+ `${colors.yellow(id)}
Unable to resolve a renderer that handles this file! With more than one renderer enabled, you should include an import or use a pragma comment.
Add ${colors.cyan(IMPORT_STATEMENTS[defaultRenderer] || `import '${defaultRenderer}';`)} or ${colors.cyan(`/* jsxImportSource: ${defaultRenderer} */`)} to this file.
`
- );
- return null;
- },
- };
+ );
+ return null;
+ },
+ };
}
diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts
index 0d868a7cd..e3d977066 100644
--- a/packages/astro/src/vite-plugin-markdown/index.ts
+++ b/packages/astro/src/vite-plugin-markdown/index.ts
@@ -7,73 +7,73 @@ import { transform } from '@astrojs/compiler';
import { AstroDevServer } from '../core/dev/index.js';
interface AstroPluginOptions {
- config: AstroConfig;
- devServer?: AstroDevServer;
+ config: AstroConfig;
+ devServer?: AstroDevServer;
}
/** Transform .astro files for Vite */
export default function markdown({ config }: AstroPluginOptions): Plugin {
- return {
- name: '@astrojs/vite-plugin-markdown',
- enforce: 'pre', // run transforms before other plugins can
- async load(id) {
- if (id.endsWith('.md')) {
- let source = await fs.promises.readFile(id, 'utf8');
+ return {
+ name: '@astrojs/vite-plugin-markdown',
+ enforce: 'pre', // run transforms before other plugins can
+ async load(id) {
+ if (id.endsWith('.md')) {
+ let source = await fs.promises.readFile(id, 'utf8');
- // Transform from `.md` to valid `.astro`
- let render = config.markdownOptions.render;
- let renderOpts = {};
- if (Array.isArray(render)) {
- renderOpts = render[1];
- render = render[0];
- }
- if (typeof render === 'string') {
- ({ default: render } = await import(render));
- }
- let renderResult = await render(source, renderOpts);
- let { frontmatter, metadata, code: astroResult } = renderResult;
+ // Transform from `.md` to valid `.astro`
+ let render = config.markdownOptions.render;
+ let renderOpts = {};
+ if (Array.isArray(render)) {
+ renderOpts = render[1];
+ render = render[0];
+ }
+ if (typeof render === 'string') {
+ ({ default: render } = await import(render));
+ }
+ let renderResult = await render(source, renderOpts);
+ let { frontmatter, metadata, code: astroResult } = renderResult;
- // Extract special frontmatter keys
- const { layout = '', components = '', setup = '', ...content } = frontmatter;
- content.astro = metadata;
- const prelude = `---
+ // Extract special frontmatter keys
+ const { layout = '', components = '', setup = '', ...content } = frontmatter;
+ content.astro = metadata;
+ const prelude = `---
${layout ? `import Layout from '${layout}';` : ''}
${components ? `import * from '${components}';` : ''}
${setup}
const $$content = ${JSON.stringify(content)}
---`;
- const imports = `${layout ? `import Layout from '${layout}';` : ''}
+ const imports = `${layout ? `import Layout from '${layout}';` : ''}
${setup}`.trim();
- // If the user imported "Layout", wrap the content in a Layout
- if (/\bLayout\b/.test(imports)) {
- astroResult = `${prelude}\n<Layout content={$$content}>\n\n${astroResult}\n\n</Layout>`;
- } else {
- astroResult = `${prelude}\n${astroResult}`;
- }
+ // If the user imported "Layout", wrap the content in a Layout
+ if (/\bLayout\b/.test(imports)) {
+ astroResult = `${prelude}\n<Layout content={$$content}>\n\n${astroResult}\n\n</Layout>`;
+ } else {
+ astroResult = `${prelude}\n${astroResult}`;
+ }
- // Transform from `.astro` to valid `.ts`
- let { code: tsResult } = await transform(astroResult, {
- projectRoot: config.projectRoot.toString(),
- site: config.buildOptions.site,
- sourcefile: id,
- sourcemap: 'inline',
- internalURL: 'astro/internal',
- });
+ // Transform from `.astro` to valid `.ts`
+ let { code: tsResult } = await transform(astroResult, {
+ projectRoot: config.projectRoot.toString(),
+ site: config.buildOptions.site,
+ sourcefile: id,
+ sourcemap: 'inline',
+ internalURL: 'astro/internal',
+ });
- tsResult = `\nexport const metadata = ${JSON.stringify(metadata)};
+ tsResult = `\nexport const metadata = ${JSON.stringify(metadata)};
export const frontmatter = ${JSON.stringify(content)};
${tsResult}`;
- // Compile from `.ts` to `.js`
- const { code, map } = await esbuild.transform(tsResult, { loader: 'ts', sourcemap: 'inline', sourcefile: id });
+ // Compile from `.ts` to `.js`
+ const { code, map } = await esbuild.transform(tsResult, { loader: 'ts', sourcemap: 'inline', sourcefile: id });
- return {
- code,
- map: null,
- };
- }
+ return {
+ code,
+ map: null,
+ };
+ }
- return null;
- },
- };
+ return null;
+ },
+ };
}
diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js
index 4387f2774..5acf83ad6 100644
--- a/packages/astro/test/0-css.test.js
+++ b/packages/astro/test/0-css.test.js
@@ -11,310 +11,310 @@ import { loadFixture } from './test-utils.js';
let fixture;
describe('CSS', function () {
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/0-css/',
- renderers: ['@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'],
- });
- });
-
- // test HTML and CSS contents for accuracy
- describe('build', () => {
- this.timeout(30000); // test needs a little more time in CI
-
- let $;
- let bundledCSS;
-
- before(async () => {
- await fixture.build();
-
- // get bundled CSS (will be hashed, hence DOM query)
- const html = await fixture.readFile('/index.html');
- $ = cheerio.load(html);
- const bundledCSSHREF = $('link[rel=stylesheet][href^=assets/]').attr('href');
- bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'));
- });
-
- describe('Astro Styles', () => {
- it('HTML and CSS scoped correctly', async () => {
- const el1 = $('#dynamic-class');
- const el2 = $('#dynamic-vis');
- const classes = $('#class').attr('class').split(' ');
- const scopedClass = classes.find((name) => /^astro-[A-Za-z0-9-]+/.test(name));
-
- // 1. check HTML
- expect(el1.attr('class')).to.equal(`blue ${scopedClass}`);
- expect(el2.attr('class')).to.equal(`visible ${scopedClass}`);
-
- // 2. check CSS
- expect(bundledCSS).to.include(`.blue.${scopedClass}{color:#b0e0e6}.color\\:blue.${scopedClass}{color:#b0e0e6}.visible.${scopedClass}{display:block}`);
- });
-
- it('No <style> skips scoping', async () => {
- // Astro component without <style> should not include scoped class
- expect($('#no-scope').attr('class')).to.equal(undefined);
- });
-
- it('Child inheritance', async () => {
- expect($('#passed-in').attr('class')).to.match(/outer astro-[A-Z0-9]+ astro-[A-Z0-9]+/);
- });
-
- it('Using hydrated components adds astro-root styles', async () => {
- expect(bundledCSS).to.include('display:contents');
- });
-
- it('<style lang="sass">', async () => {
- expect(bundledCSS).to.match(new RegExp('h1.astro-[^{]*{color:#90ee90}'));
- });
-
- it('<style lang="scss">', async () => {
- expect(bundledCSS).to.match(new RegExp('h1.astro-[^{]*{color:#ff69b4}'));
- });
- });
-
- describe('Styles in src/', () => {
- it('.css', async () => {
- expect(bundledCSS).to.match(new RegExp('.linked-css[^{]*{color:gold'));
- });
-
- it('.sass', async () => {
- expect(bundledCSS).to.match(new RegExp('.linked-sass[^{]*{color:#789'));
- });
-
- it('.scss', async () => {
- expect(bundledCSS).to.match(new RegExp('.linked-scss[^{]*{color:#6b8e23'));
- });
- });
-
- describe('JSX', () => {
- it('.css', async () => {
- const el = $('#react-css');
-
- // 1. check HTML
- expect(el.attr('class')).to.include('react-title');
-
- // 2. check CSS
- expect(bundledCSS).to.include('.react-title{');
- });
-
- it('.module.css', async () => {
- const el = $('#react-module-css');
- const classes = el.attr('class').split(' ');
- const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name));
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/0-css/',
+ renderers: ['@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'],
+ });
+ });
+
+ // test HTML and CSS contents for accuracy
+ describe('build', () => {
+ this.timeout(30000); // test needs a little more time in CI
+
+ let $;
+ let bundledCSS;
+
+ before(async () => {
+ await fixture.build();
+
+ // get bundled CSS (will be hashed, hence DOM query)
+ const html = await fixture.readFile('/index.html');
+ $ = cheerio.load(html);
+ const bundledCSSHREF = $('link[rel=stylesheet][href^=assets/]').attr('href');
+ bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'));
+ });
+
+ describe('Astro Styles', () => {
+ it('HTML and CSS scoped correctly', async () => {
+ const el1 = $('#dynamic-class');
+ const el2 = $('#dynamic-vis');
+ const classes = $('#class').attr('class').split(' ');
+ const scopedClass = classes.find((name) => /^astro-[A-Za-z0-9-]+/.test(name));
+
+ // 1. check HTML
+ expect(el1.attr('class')).to.equal(`blue ${scopedClass}`);
+ expect(el2.attr('class')).to.equal(`visible ${scopedClass}`);
+
+ // 2. check CSS
+ expect(bundledCSS).to.include(`.blue.${scopedClass}{color:#b0e0e6}.color\\:blue.${scopedClass}{color:#b0e0e6}.visible.${scopedClass}{display:block}`);
+ });
+
+ it('No <style> skips scoping', async () => {
+ // Astro component without <style> should not include scoped class
+ expect($('#no-scope').attr('class')).to.equal(undefined);
+ });
+
+ it('Child inheritance', async () => {
+ expect($('#passed-in').attr('class')).to.match(/outer astro-[A-Z0-9]+ astro-[A-Z0-9]+/);
+ });
+
+ it('Using hydrated components adds astro-root styles', async () => {
+ expect(bundledCSS).to.include('display:contents');
+ });
+
+ it('<style lang="sass">', async () => {
+ expect(bundledCSS).to.match(new RegExp('h1.astro-[^{]*{color:#90ee90}'));
+ });
+
+ it('<style lang="scss">', async () => {
+ expect(bundledCSS).to.match(new RegExp('h1.astro-[^{]*{color:#ff69b4}'));
+ });
+ });
+
+ describe('Styles in src/', () => {
+ it('.css', async () => {
+ expect(bundledCSS).to.match(new RegExp('.linked-css[^{]*{color:gold'));
+ });
+
+ it('.sass', async () => {
+ expect(bundledCSS).to.match(new RegExp('.linked-sass[^{]*{color:#789'));
+ });
+
+ it('.scss', async () => {
+ expect(bundledCSS).to.match(new RegExp('.linked-scss[^{]*{color:#6b8e23'));
+ });
+ });
+
+ describe('JSX', () => {
+ it('.css', async () => {
+ const el = $('#react-css');
+
+ // 1. check HTML
+ expect(el.attr('class')).to.include('react-title');
+
+ // 2. check CSS
+ expect(bundledCSS).to.include('.react-title{');
+ });
+
+ it('.module.css', async () => {
+ const el = $('#react-module-css');
+ const classes = el.attr('class').split(' ');
+ const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name));
- // 1. check HTML
- expect(el.attr('class')).to.include(moduleClass);
+ // 1. check HTML
+ expect(el.attr('class')).to.include(moduleClass);
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`));
- });
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`));
+ });
- it('.sass', async () => {
- const el = $('#react-sass');
-
- // 1. check HTML
- expect(el.attr('class')).to.include('react-sass-title');
+ it('.sass', async () => {
+ const el = $('#react-sass');
+
+ // 1. check HTML
+ expect(el.attr('class')).to.include('react-sass-title');
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.react-sass-title[^{]*{font-family:fantasy}`));
- });
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.react-sass-title[^{]*{font-family:fantasy}`));
+ });
- it('.scss', async () => {
- const el = $('#react-scss');
+ it('.scss', async () => {
+ const el = $('#react-scss');
- // 1. check HTML
- expect(el.attr('class')).to.include('react-scss-title');
+ // 1. check HTML
+ expect(el.attr('class')).to.include('react-scss-title');
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.react-scss-title[^{]*{font-family:fantasy}`));
- });
-
- it('.module.sass', async () => {
- const el = $('#react-module-sass');
- const classes = el.attr('class').split(' ');
- const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name));
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.react-scss-title[^{]*{font-family:fantasy}`));
+ });
+
+ it('.module.sass', async () => {
+ const el = $('#react-module-sass');
+ const classes = el.attr('class').split(' ');
+ const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name));
- // 1. check HTML
- expect(el.attr('class')).to.include(moduleClass);
+ // 1. check HTML
+ expect(el.attr('class')).to.include(moduleClass);
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`));
- });
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`));
+ });
- it('.module.scss', async () => {
- const el = $('#react-module-scss');
- const classes = el.attr('class').split(' ');
- const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name));
+ it('.module.scss', async () => {
+ const el = $('#react-module-scss');
+ const classes = el.attr('class').split(' ');
+ const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name));
- // 1. check HTML
- expect(el.attr('class')).to.include(moduleClass);
+ // 1. check HTML
+ expect(el.attr('class')).to.include(moduleClass);
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`));
- });
- });
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`));
+ });
+ });
- describe('Vue', () => {
- it('<style>', async () => {
- const el = $('#vue-css');
+ describe('Vue', () => {
+ it('<style>', async () => {
+ const el = $('#vue-css');
- // 1. check HTML
- expect(el.attr('class')).to.include('vue-css');
+ // 1. check HTML
+ expect(el.attr('class')).to.include('vue-css');
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.vue-css[^{]*{font-family:cursive`));
- });
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.vue-css[^{]*{font-family:cursive`));
+ });
- it('<style scoped>', async () => {
- const el = $('#vue-scoped');
+ it('<style scoped>', async () => {
+ const el = $('#vue-scoped');
- // find data-v-* attribute (how Vue CSS scoping works)
- const { attribs } = el.get(0);
- const scopeId = Object.keys(attribs).find((k) => k.startsWith('data-v-'));
- expect(scopeId).to.be.ok;
+ // find data-v-* attribute (how Vue CSS scoping works)
+ const { attribs } = el.get(0);
+ const scopeId = Object.keys(attribs).find((k) => k.startsWith('data-v-'));
+ expect(scopeId).to.be.ok;
- // 1. check HTML
- expect(el.attr('class')).to.include('vue-scoped');
+ // 1. check HTML
+ expect(el.attr('class')).to.include('vue-scoped');
- // 2. check CSS
- expect(bundledCSS).to.include(`.vue-scoped[${scopeId}]`);
- });
+ // 2. check CSS
+ expect(bundledCSS).to.include(`.vue-scoped[${scopeId}]`);
+ });
- it('<style module>', async () => {
- const el = $('#vue-modules');
- const classes = el.attr('class').split(' ');
- const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name));
+ it('<style module>', async () => {
+ const el = $('#vue-modules');
+ const classes = el.attr('class').split(' ');
+ const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name));
- // 1. check HTML
- expect(el.attr('class')).to.include(moduleClass);
+ // 1. check HTML
+ expect(el.attr('class')).to.include(moduleClass);
- // 2. check CSS
- expect(bundledCSS).to.include(`${moduleClass}{`);
- });
-
- it('<style lang="sass">', async () => {
- const el = $('#vue-sass');
-
- // 1. check HTML
- expect(el.attr('class')).to.include('vue-sass');
-
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.vue-sass[^{]*{font-family:cursive`));
- });
-
- it('<style lang="scss">', async () => {
- const el = $('#vue-scss');
-
- // 1. check HTML
- expect(el.attr('class')).to.include('vue-scss');
-
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.vue-scss[^{]*{font-family:cursive`));
- });
- });
-
- describe('Svelte', () => {
- it('<style>', async () => {
- const el = $('#svelte-css');
- const classes = el.attr('class').split(' ');
- const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name));
-
- // 1. check HTML
- expect(el.attr('class')).to.include('svelte-css');
-
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.svelte-css.${scopedClass}[^{]*{font-family:"Comic Sans MS"`));
- });
-
- it('<style lang="sass">', async () => {
- const el = $('#svelte-sass');
- const classes = el.attr('class').split(' ');
- const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name));
-
- // 1. check HTML
- expect(el.attr('class')).to.include('svelte-sass');
-
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.svelte-sass.${scopedClass}[^{]*{font-family:"Comic Sans MS"`));
- });
-
- it('<style lang="scss">', async () => {
- const el = $('#svelte-scss');
- const classes = el.attr('class').split(' ');
- const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name));
-
- // 1. check HTML
- expect(el.attr('class')).to.include('svelte-scss');
-
- // 2. check CSS
- expect(bundledCSS).to.match(new RegExp(`.svelte-scss.${scopedClass}[^{]*{font-family:"Comic Sans MS"`));
- });
- });
- });
-
- // with "build" handling CSS checking, the dev tests are mostly testing the paths resolve in dev
- describe('dev', () => {
- let devServer;
- let $;
-
- before(async () => {
- devServer = await fixture.startDevServer();
- const html = await fixture.fetch('/').then((res) => res.text());
- $ = cheerio.load(html);
- });
-
- after(async () => {
- devServer && (await devServer.stop());
- });
-
- it('resolves CSS in public/', async () => {
- const href = $('link[href="/global.css"]').attr('href');
- expect((await fixture.fetch(href)).status).to.equal(200);
- });
-
- it('resolves CSS in src/', async () => {
- const href = $('link[href$="linked.css"]').attr('href');
- expect((await fixture.fetch(href)).status).to.equal(200);
- });
-
- it('resolved imported CSS with ?url', async () => {
- const href = $('link[href$="imported-url.css"]').attr('href');
- expect((await fixture.fetch(href)).status).to.equal(200);
- });
-
- it('resolves Astro styles', async () => {
- const style = $('style[astro-style]');
- expect(style.length).to.not.equal(0);
- });
-
- it('resolves Styles from React', async () => {
- const styles = ['ReactCSS.css', 'ReactModules.module.css', 'ReactModules.module.scss', 'ReactModules.module.sass', 'ReactSass.sass', 'ReactScss.scss'];
- for (const style of styles) {
- const href = $(`link[href$="${style}"]`).attr('href');
- expect((await fixture.fetch(href)).status, style).to.equal(200);
- }
- });
-
- it('resolves CSS from Svelte', async () => {
- const scripts = ['SvelteCSS.svelte?svelte&type=style&lang.css', 'SvelteSass.svelte?svelte&type=style&lang.css', 'SvelteScss.svelte?svelte&type=style&lang.css'];
- for (const script of scripts) {
- const src = $(`script[src$="${script}"]`).attr('src');
- expect((await fixture.fetch(src)).status, script).to.equal(200);
- }
- });
-
- it('resolves CSS from Vue', async () => {
- const styles = [
- 'VueCSS.vue?vue&type=style&index=0&lang.css',
- 'VueModules.vue?vue&type=style&index=0&lang.module.scss',
- 'VueSass.vue?vue&type=style&index=0&lang.sass',
- 'VueScoped.vue?vue&type=style&index=0&scoped=true&lang.css',
- 'VueScss.vue?vue&type=style&index=0&lang.scss',
- ];
- for (const style of styles) {
- const href = $(`link[href$="${style}"]`).attr('href');
- expect((await fixture.fetch(href)).status, style).to.equal(200);
- }
- });
- });
+ // 2. check CSS
+ expect(bundledCSS).to.include(`${moduleClass}{`);
+ });
+
+ it('<style lang="sass">', async () => {
+ const el = $('#vue-sass');
+
+ // 1. check HTML
+ expect(el.attr('class')).to.include('vue-sass');
+
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.vue-sass[^{]*{font-family:cursive`));
+ });
+
+ it('<style lang="scss">', async () => {
+ const el = $('#vue-scss');
+
+ // 1. check HTML
+ expect(el.attr('class')).to.include('vue-scss');
+
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.vue-scss[^{]*{font-family:cursive`));
+ });
+ });
+
+ describe('Svelte', () => {
+ it('<style>', async () => {
+ const el = $('#svelte-css');
+ const classes = el.attr('class').split(' ');
+ const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name));
+
+ // 1. check HTML
+ expect(el.attr('class')).to.include('svelte-css');
+
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.svelte-css.${scopedClass}[^{]*{font-family:"Comic Sans MS"`));
+ });
+
+ it('<style lang="sass">', async () => {
+ const el = $('#svelte-sass');
+ const classes = el.attr('class').split(' ');
+ const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name));
+
+ // 1. check HTML
+ expect(el.attr('class')).to.include('svelte-sass');
+
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.svelte-sass.${scopedClass}[^{]*{font-family:"Comic Sans MS"`));
+ });
+
+ it('<style lang="scss">', async () => {
+ const el = $('#svelte-scss');
+ const classes = el.attr('class').split(' ');
+ const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name));
+
+ // 1. check HTML
+ expect(el.attr('class')).to.include('svelte-scss');
+
+ // 2. check CSS
+ expect(bundledCSS).to.match(new RegExp(`.svelte-scss.${scopedClass}[^{]*{font-family:"Comic Sans MS"`));
+ });
+ });
+ });
+
+ // with "build" handling CSS checking, the dev tests are mostly testing the paths resolve in dev
+ describe('dev', () => {
+ let devServer;
+ let $;
+
+ before(async () => {
+ devServer = await fixture.startDevServer();
+ const html = await fixture.fetch('/').then((res) => res.text());
+ $ = cheerio.load(html);
+ });
+
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
+
+ it('resolves CSS in public/', async () => {
+ const href = $('link[href="/global.css"]').attr('href');
+ expect((await fixture.fetch(href)).status).to.equal(200);
+ });
+
+ it('resolves CSS in src/', async () => {
+ const href = $('link[href$="linked.css"]').attr('href');
+ expect((await fixture.fetch(href)).status).to.equal(200);
+ });
+
+ it('resolved imported CSS with ?url', async () => {
+ const href = $('link[href$="imported-url.css"]').attr('href');
+ expect((await fixture.fetch(href)).status).to.equal(200);
+ });
+
+ it('resolves Astro styles', async () => {
+ const style = $('style[astro-style]');
+ expect(style.length).to.not.equal(0);
+ });
+
+ it('resolves Styles from React', async () => {
+ const styles = ['ReactCSS.css', 'ReactModules.module.css', 'ReactModules.module.scss', 'ReactModules.module.sass', 'ReactSass.sass', 'ReactScss.scss'];
+ for (const style of styles) {
+ const href = $(`link[href$="${style}"]`).attr('href');
+ expect((await fixture.fetch(href)).status, style).to.equal(200);
+ }
+ });
+
+ it('resolves CSS from Svelte', async () => {
+ const scripts = ['SvelteCSS.svelte?svelte&type=style&lang.css', 'SvelteSass.svelte?svelte&type=style&lang.css', 'SvelteScss.svelte?svelte&type=style&lang.css'];
+ for (const script of scripts) {
+ const src = $(`script[src$="${script}"]`).attr('src');
+ expect((await fixture.fetch(src)).status, script).to.equal(200);
+ }
+ });
+
+ it('resolves CSS from Vue', async () => {
+ const styles = [
+ 'VueCSS.vue?vue&type=style&index=0&lang.css',
+ 'VueModules.vue?vue&type=style&index=0&lang.module.scss',
+ 'VueSass.vue?vue&type=style&index=0&lang.sass',
+ 'VueScoped.vue?vue&type=style&index=0&scoped=true&lang.css',
+ 'VueScss.vue?vue&type=style&index=0&lang.scss',
+ ];
+ for (const style of styles) {
+ const href = $(`link[href$="${style}"]`).attr('href');
+ expect((await fixture.fetch(href)).status, style).to.equal(200);
+ }
+ });
+ });
});
diff --git a/packages/astro/test/astro-assets.test.js b/packages/astro/test/astro-assets.test.js
index 5c795c00d..4e17e7c50 100644
--- a/packages/astro/test/astro-assets.test.js
+++ b/packages/astro/test/astro-assets.test.js
@@ -8,54 +8,54 @@ const matchSrcset = srcsetParse.default;
// Asset bundling
describe('Assets', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-assets/' });
- await fixture.build();
- });
-
- it('built the base image', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- const imgPath = $('img').attr('src');
- const data = await fixture.readFile('/' + imgPath);
- expect(!!data).to.equal(true);
- });
-
- it('built the 2x image', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- const srcset = $('img').attr('srcset');
- const candidates = matchSrcset(srcset);
- const match = candidates.find((a) => a.density === 2);
- const data = await fixture.readFile('/' + match.url);
- expect(!!data).to.equal(true);
- });
-
- it('built the 3x image', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- const srcset = $('img').attr('srcset');
- const candidates = matchSrcset(srcset);
- const match = candidates.find((a) => a.density === 3);
- const data = await fixture.readFile('/' + match.url);
- expect(!!data).to.equal(true);
- });
-
- it('built image from an import specifier', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- const src = '/' + $('#import-no-url').attr('src');
- const data = await fixture.readFile(src);
- expect(!!data).to.equal(true);
- });
-
- it('built image from an import specifier using ?url', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- const src = '/' + $('#import-url').attr('src');
- const data = await fixture.readFile(src);
- expect(!!data).to.equal(true);
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-assets/' });
+ await fixture.build();
+ });
+
+ it('built the base image', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ const imgPath = $('img').attr('src');
+ const data = await fixture.readFile('/' + imgPath);
+ expect(!!data).to.equal(true);
+ });
+
+ it('built the 2x image', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ const srcset = $('img').attr('srcset');
+ const candidates = matchSrcset(srcset);
+ const match = candidates.find((a) => a.density === 2);
+ const data = await fixture.readFile('/' + match.url);
+ expect(!!data).to.equal(true);
+ });
+
+ it('built the 3x image', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ const srcset = $('img').attr('srcset');
+ const candidates = matchSrcset(srcset);
+ const match = candidates.find((a) => a.density === 3);
+ const data = await fixture.readFile('/' + match.url);
+ expect(!!data).to.equal(true);
+ });
+
+ it('built image from an import specifier', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ const src = '/' + $('#import-no-url').attr('src');
+ const data = await fixture.readFile(src);
+ expect(!!data).to.equal(true);
+ });
+
+ it('built image from an import specifier using ?url', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ const src = '/' + $('#import-url').attr('src');
+ const data = await fixture.readFile(src);
+ expect(!!data).to.equal(true);
+ });
});
diff --git a/packages/astro/test/astro-attrs.test.js b/packages/astro/test/astro-attrs.test.js
index 81b1fc716..173c1e2ec 100644
--- a/packages/astro/test/astro-attrs.test.js
+++ b/packages/astro/test/astro-attrs.test.js
@@ -3,55 +3,55 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Attributes', async () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-attrs/' });
- await fixture.build();
- });
-
- it('Passes attributes to elements as expected', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
-
- const attrs = {
- 'false-str': 'false',
- 'true-str': 'true',
- false: undefined,
- true: 'true',
- empty: '',
- null: undefined,
- undefined: undefined,
- };
-
- for (const [k, v] of Object.entries(attrs)) {
- const attr = $(`#${k}`).attr('attr');
- expect(attr).to.equal(v);
- }
- });
-
- it('Passes boolean attributes to components as expected', async () => {
- const html = await fixture.readFile('/component/index.html');
- const $ = cheerio.load(html);
-
- expect($('#true').attr('attr')).to.equal('attr-true');
- expect($('#true').attr('type')).to.equal('boolean');
- expect($('#false').attr('attr')).to.equal('attr-false');
- expect($('#false').attr('type')).to.equal('boolean');
- });
-
- it('Passes namespaced attributes as expected', async () => {
- const html = await fixture.readFile('/namespaced/index.html');
- const $ = cheerio.load(html);
-
- expect($('div').attr('xmlns:happy')).to.equal('https://example.com/schemas/happy');
- expect($('img').attr('happy:smile')).to.equal('sweet');
- });
-
- it('Passes namespaced attributes to components as expected', async () => {
- const html = await fixture.readFile('/namespaced-component/index.html');
- const $ = cheerio.load(html);
-
- expect($('span').attr('on:click')).to.deep.equal('(event) => console.log(event)');
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-attrs/' });
+ await fixture.build();
+ });
+
+ it('Passes attributes to elements as expected', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+
+ const attrs = {
+ 'false-str': 'false',
+ 'true-str': 'true',
+ false: undefined,
+ true: 'true',
+ empty: '',
+ null: undefined,
+ undefined: undefined,
+ };
+
+ for (const [k, v] of Object.entries(attrs)) {
+ const attr = $(`#${k}`).attr('attr');
+ expect(attr).to.equal(v);
+ }
+ });
+
+ it('Passes boolean attributes to components as expected', async () => {
+ const html = await fixture.readFile('/component/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#true').attr('attr')).to.equal('attr-true');
+ expect($('#true').attr('type')).to.equal('boolean');
+ expect($('#false').attr('attr')).to.equal('attr-false');
+ expect($('#false').attr('type')).to.equal('boolean');
+ });
+
+ it('Passes namespaced attributes as expected', async () => {
+ const html = await fixture.readFile('/namespaced/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('div').attr('xmlns:happy')).to.equal('https://example.com/schemas/happy');
+ expect($('img').attr('happy:smile')).to.equal('sweet');
+ });
+
+ it('Passes namespaced attributes to components as expected', async () => {
+ const html = await fixture.readFile('/namespaced-component/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('span').attr('on:click')).to.deep.equal('(event) => console.log(event)');
+ });
});
diff --git a/packages/astro/test/astro-basic.test.js b/packages/astro/test/astro-basic.test.js
index fc8c43cca..7e19715a8 100644
--- a/packages/astro/test/astro-basic.test.js
+++ b/packages/astro/test/astro-basic.test.js
@@ -3,120 +3,120 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Astro basics', () => {
- let fixture;
- let previewServer;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-basic/' });
- await fixture.build();
- previewServer = await fixture.preview();
- });
-
- // important: close preview server (free up port and connection)
- after(async () => {
- if (previewServer) await previewServer.stop();
- });
-
- describe('build', () => {
- it('Can load page', async () => {
- const html = await fixture.readFile(`/index.html`);
- const $ = cheerio.load(html);
-
- expect($('h1').text()).to.equal('Hello world!');
- });
-
- it('Correctly serializes boolean attributes', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
-
- expect($('h1').attr('data-something')).to.equal('');
- expect($('h2').attr('not-data-ok')).to.equal('');
- });
-
- it('Selector with an empty body', async () => {
- const html = await fixture.readFile('/empty-class/index.html');
- const $ = cheerio.load(html);
-
- expect($('.author')).to.have.lengthOf(1);
- });
-
- it('Allows forward-slashes in mustache tags (#407)', async () => {
- const html = await fixture.readFile('/forward-slash/index.html');
- const $ = cheerio.load(html);
-
- expect($('a[href="/post/one"]')).to.have.lengthOf(1);
- expect($('a[href="/post/two"]')).to.have.lengthOf(1);
- expect($('a[href="/post/three"]')).to.have.lengthOf(1);
- });
-
- it('Allows spread attributes (#521)', async () => {
- const html = await fixture.readFile('/spread/index.html');
- const $ = cheerio.load(html);
-
- expect($('#spread-leading')).to.have.lengthOf(1);
- expect($('#spread-leading').attr('a')).to.equal('0');
- expect($('#spread-leading').attr('b')).to.equal('1');
- expect($('#spread-leading').attr('c')).to.equal('2');
-
- expect($('#spread-trailing')).to.have.lengthOf(1);
- expect($('#spread-trailing').attr('a')).to.equal('0');
- expect($('#spread-trailing').attr('b')).to.equal('1');
- expect($('#spread-trailing').attr('c')).to.equal('2');
- });
-
- it('Allows spread attributes with TypeScript (#521)', async () => {
- const html = await fixture.readFile('/spread/index.html');
- const $ = cheerio.load(html);
-
- expect($('#spread-ts')).to.have.lengthOf(1);
- expect($('#spread-ts').attr('a')).to.equal('0');
- expect($('#spread-ts').attr('b')).to.equal('1');
- expect($('#spread-ts').attr('c')).to.equal('2');
- });
-
- it('Allows using the Fragment element to be used', async () => {
- const html = await fixture.readFile('/fragment/index.html');
- const $ = cheerio.load(html);
-
- // will be 1 if element rendered correctly
- expect($('#one')).to.have.lengthOf(1);
- });
-
- it('supports special chars in filename', async () => {
- // will have already erred by now, but add test anyway
- expect(await fixture.readFile('/special-“characters” -in-file/index.html')).to.be.ok;
- });
- });
-
- it('Supports void elements whose name is a string (#2062)', async () => {
- const html = await fixture.readFile('/input/index.html');
- const $ = cheerio.load(html);
-
- // <Input />
- expect($('body > :nth-child(1)').prop('outerHTML')).to.equal('<input>');
-
- // <Input type="password" />
- expect($('body > :nth-child(2)').prop('outerHTML')).to.equal('<input type="password">');
-
- // <Input type="text" />
- expect($('body > :nth-child(3)').prop('outerHTML')).to.equal('<input type="text">');
-
- // <Input type="select"><option>option</option></Input>
- expect($('body > :nth-child(4)').prop('outerHTML')).to.equal('<select><option>option</option></select>');
-
- // <Input type="textarea">textarea</Input>
- expect($('body > :nth-child(5)').prop('outerHTML')).to.equal('<textarea>textarea</textarea>');
- });
-
- describe('preview', () => {
- it('returns 200 for valid URLs', async () => {
- const result = await fixture.fetch('/');
- expect(result.status).to.equal(200);
- });
-
- it('returns 404 for invalid URLs', async () => {
- const result = await fixture.fetch('/bad-url');
- expect(result.status).to.equal(404);
- });
- });
+ let fixture;
+ let previewServer;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-basic/' });
+ await fixture.build();
+ previewServer = await fixture.preview();
+ });
+
+ // important: close preview server (free up port and connection)
+ after(async () => {
+ if (previewServer) await previewServer.stop();
+ });
+
+ describe('build', () => {
+ it('Can load page', async () => {
+ const html = await fixture.readFile(`/index.html`);
+ const $ = cheerio.load(html);
+
+ expect($('h1').text()).to.equal('Hello world!');
+ });
+
+ it('Correctly serializes boolean attributes', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('h1').attr('data-something')).to.equal('');
+ expect($('h2').attr('not-data-ok')).to.equal('');
+ });
+
+ it('Selector with an empty body', async () => {
+ const html = await fixture.readFile('/empty-class/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('.author')).to.have.lengthOf(1);
+ });
+
+ it('Allows forward-slashes in mustache tags (#407)', async () => {
+ const html = await fixture.readFile('/forward-slash/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('a[href="/post/one"]')).to.have.lengthOf(1);
+ expect($('a[href="/post/two"]')).to.have.lengthOf(1);
+ expect($('a[href="/post/three"]')).to.have.lengthOf(1);
+ });
+
+ it('Allows spread attributes (#521)', async () => {
+ const html = await fixture.readFile('/spread/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#spread-leading')).to.have.lengthOf(1);
+ expect($('#spread-leading').attr('a')).to.equal('0');
+ expect($('#spread-leading').attr('b')).to.equal('1');
+ expect($('#spread-leading').attr('c')).to.equal('2');
+
+ expect($('#spread-trailing')).to.have.lengthOf(1);
+ expect($('#spread-trailing').attr('a')).to.equal('0');
+ expect($('#spread-trailing').attr('b')).to.equal('1');
+ expect($('#spread-trailing').attr('c')).to.equal('2');
+ });
+
+ it('Allows spread attributes with TypeScript (#521)', async () => {
+ const html = await fixture.readFile('/spread/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#spread-ts')).to.have.lengthOf(1);
+ expect($('#spread-ts').attr('a')).to.equal('0');
+ expect($('#spread-ts').attr('b')).to.equal('1');
+ expect($('#spread-ts').attr('c')).to.equal('2');
+ });
+
+ it('Allows using the Fragment element to be used', async () => {
+ const html = await fixture.readFile('/fragment/index.html');
+ const $ = cheerio.load(html);
+
+ // will be 1 if element rendered correctly
+ expect($('#one')).to.have.lengthOf(1);
+ });
+
+ it('supports special chars in filename', async () => {
+ // will have already erred by now, but add test anyway
+ expect(await fixture.readFile('/special-“characters” -in-file/index.html')).to.be.ok;
+ });
+ });
+
+ it('Supports void elements whose name is a string (#2062)', async () => {
+ const html = await fixture.readFile('/input/index.html');
+ const $ = cheerio.load(html);
+
+ // <Input />
+ expect($('body > :nth-child(1)').prop('outerHTML')).to.equal('<input>');
+
+ // <Input type="password" />
+ expect($('body > :nth-child(2)').prop('outerHTML')).to.equal('<input type="password">');
+
+ // <Input type="text" />
+ expect($('body > :nth-child(3)').prop('outerHTML')).to.equal('<input type="text">');
+
+ // <Input type="select"><option>option</option></Input>
+ expect($('body > :nth-child(4)').prop('outerHTML')).to.equal('<select><option>option</option></select>');
+
+ // <Input type="textarea">textarea</Input>
+ expect($('body > :nth-child(5)').prop('outerHTML')).to.equal('<textarea>textarea</textarea>');
+ });
+
+ describe('preview', () => {
+ it('returns 200 for valid URLs', async () => {
+ const result = await fixture.fetch('/');
+ expect(result.status).to.equal(200);
+ });
+
+ it('returns 404 for invalid URLs', async () => {
+ const result = await fixture.fetch('/bad-url');
+ expect(result.status).to.equal(404);
+ });
+ });
});
diff --git a/packages/astro/test/astro-children.test.js b/packages/astro/test/astro-children.test.js
index 3017307aa..99c19387c 100644
--- a/packages/astro/test/astro-children.test.js
+++ b/packages/astro/test/astro-children.test.js
@@ -3,70 +3,70 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Component children', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-children/',
- renderers: ['@astrojs/renderer-preact', '@astrojs/renderer-vue', '@astrojs/renderer-svelte'],
- });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-children/',
+ renderers: ['@astrojs/renderer-preact', '@astrojs/renderer-vue', '@astrojs/renderer-svelte'],
+ });
+ await fixture.build();
+ });
- it('Passes string children to framework components', async () => {
- const html = await fixture.readFile('/strings/index.html');
- const $ = cheerio.load(html);
+ it('Passes string children to framework components', async () => {
+ const html = await fixture.readFile('/strings/index.html');
+ const $ = cheerio.load(html);
- // test 1: Can pass text to Preact components
- const $preact = $('#preact');
- expect($preact.text().trim()).to.equal('Hello world');
+ // test 1: Can pass text to Preact components
+ const $preact = $('#preact');
+ expect($preact.text().trim()).to.equal('Hello world');
- // test 2: Can pass text to Vue components
- const $vue = $('#vue');
- expect($vue.text().trim()).to.equal('Hello world');
+ // test 2: Can pass text to Vue components
+ const $vue = $('#vue');
+ expect($vue.text().trim()).to.equal('Hello world');
- // test 3: Can pass text to Svelte components
- const $svelte = $('#svelte');
- expect($svelte.text().trim()).to.equal('Hello world');
- });
+ // test 3: Can pass text to Svelte components
+ const $svelte = $('#svelte');
+ expect($svelte.text().trim()).to.equal('Hello world');
+ });
- it('Passes markup children to framework components', async () => {
- const html = await fixture.readFile('/markup/index.html');
- const $ = cheerio.load(html);
+ it('Passes markup children to framework components', async () => {
+ const html = await fixture.readFile('/markup/index.html');
+ const $ = cheerio.load(html);
- // test 1: Can pass markup to Preact components
- const $preact = $('#preact h1');
- expect($preact.text().trim()).to.equal('Hello world');
+ // test 1: Can pass markup to Preact components
+ const $preact = $('#preact h1');
+ expect($preact.text().trim()).to.equal('Hello world');
- // test 2: Can pass markup to Vue components
- const $vue = $('#vue h1');
- expect($vue.text().trim()).to.equal('Hello world');
+ // test 2: Can pass markup to Vue components
+ const $vue = $('#vue h1');
+ expect($vue.text().trim()).to.equal('Hello world');
- // test 3: Can pass markup to Svelte components
- const $svelte = $('#svelte h1');
- expect($svelte.text().trim()).to.equal('Hello world');
- });
+ // test 3: Can pass markup to Svelte components
+ const $svelte = $('#svelte h1');
+ expect($svelte.text().trim()).to.equal('Hello world');
+ });
- it('Passes multiple children to framework components', async () => {
- const html = await fixture.readFile('/multiple/index.html');
- const $ = cheerio.load(html);
+ it('Passes multiple children to framework components', async () => {
+ const html = await fixture.readFile('/multiple/index.html');
+ const $ = cheerio.load(html);
- // test 1: Can pass multiple children to Preact components
- const $preact = $('#preact');
- expect($preact.children()).to.have.lengthOf(2);
- expect($preact.children(':first-child').text().trim()).to.equal('Hello world');
- expect($preact.children(':last-child').text().trim()).to.equal('Goodbye world');
+ // test 1: Can pass multiple children to Preact components
+ const $preact = $('#preact');
+ expect($preact.children()).to.have.lengthOf(2);
+ expect($preact.children(':first-child').text().trim()).to.equal('Hello world');
+ expect($preact.children(':last-child').text().trim()).to.equal('Goodbye world');
- // test 2: Can pass multiple children to Vue components
- const $vue = $('#vue');
- expect($vue.children()).to.have.lengthOf(2);
- expect($vue.children(':first-child').text().trim()).to.equal('Hello world');
- expect($vue.children(':last-child').text().trim()).to.equal('Goodbye world');
+ // test 2: Can pass multiple children to Vue components
+ const $vue = $('#vue');
+ expect($vue.children()).to.have.lengthOf(2);
+ expect($vue.children(':first-child').text().trim()).to.equal('Hello world');
+ expect($vue.children(':last-child').text().trim()).to.equal('Goodbye world');
- // test 3: Can pass multiple children to Svelte components
- const $svelte = $('#svelte');
- expect($svelte.children()).to.have.lengthOf(2);
- expect($svelte.children(':first-child').text().trim()).to.equal('Hello world');
- expect($svelte.children(':last-child').text().trim()).to.equal('Goodbye world');
- });
+ // test 3: Can pass multiple children to Svelte components
+ const $svelte = $('#svelte');
+ expect($svelte.children()).to.have.lengthOf(2);
+ expect($svelte.children(':first-child').text().trim()).to.equal('Hello world');
+ expect($svelte.children(':last-child').text().trim()).to.equal('Goodbye world');
+ });
});
diff --git a/packages/astro/test/astro-class-list.test.js b/packages/astro/test/astro-class-list.test.js
index de8670520..46549788a 100644
--- a/packages/astro/test/astro-class-list.test.js
+++ b/packages/astro/test/astro-class-list.test.js
@@ -5,34 +5,34 @@ import { loadFixture } from './test-utils.js';
let fixture;
before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-class-list/' });
- await fixture.build();
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-class-list/' });
+ await fixture.build();
});
describe('Class List', async () => {
- it('Passes class:list attributes as expected to elements', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Passes class:list attributes as expected to elements', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- expect($('[class="test control"]')).to.have.lengthOf(1);
- expect($('[class="test expression"]')).to.have.lengthOf(1);
- expect($('[class="test true"]')).to.have.lengthOf(1);
- expect($('[class="test truthy"]')).to.have.lengthOf(1);
- expect($('[class="test set"]')).to.have.lengthOf(1);
- expect($('[class="hello goodbye world friend"]')).to.have.lengthOf(1);
+ expect($('[class="test control"]')).to.have.lengthOf(1);
+ expect($('[class="test expression"]')).to.have.lengthOf(1);
+ expect($('[class="test true"]')).to.have.lengthOf(1);
+ expect($('[class="test truthy"]')).to.have.lengthOf(1);
+ expect($('[class="test set"]')).to.have.lengthOf(1);
+ expect($('[class="hello goodbye world friend"]')).to.have.lengthOf(1);
- expect($('.false, .noshow1, .noshow2, .noshow3, .noshow4')).to.have.lengthOf(0);
- });
+ expect($('.false, .noshow1, .noshow2, .noshow3, .noshow4')).to.have.lengthOf(0);
+ });
- it('Passes class:list attributes as expected to components', async () => {
- const html = await fixture.readFile('/component/index.html');
- const $ = cheerio.load(html);
+ it('Passes class:list attributes as expected to components', async () => {
+ const html = await fixture.readFile('/component/index.html');
+ const $ = cheerio.load(html);
- expect($('[class="test control"]')).to.have.lengthOf(1);
- expect($('[class="test expression"]')).to.have.lengthOf(1);
- expect($('[class="test true"]')).to.have.lengthOf(1);
- expect($('[class="test truthy"]')).to.have.lengthOf(1);
- expect($('[class="test set"]')).to.have.lengthOf(1);
- expect($('[class="hello goodbye world friend"]')).to.have.lengthOf(1);
- });
+ expect($('[class="test control"]')).to.have.lengthOf(1);
+ expect($('[class="test expression"]')).to.have.lengthOf(1);
+ expect($('[class="test true"]')).to.have.lengthOf(1);
+ expect($('[class="test truthy"]')).to.have.lengthOf(1);
+ expect($('[class="test set"]')).to.have.lengthOf(1);
+ expect($('[class="hello goodbye world friend"]')).to.have.lengthOf(1);
+ });
});
diff --git a/packages/astro/test/astro-client-only.test.js b/packages/astro/test/astro-client-only.test.js
index 893db1c59..2f767ba5b 100644
--- a/packages/astro/test/astro-client-only.test.js
+++ b/packages/astro/test/astro-client-only.test.js
@@ -5,30 +5,30 @@ import { loadFixture } from './test-utils.js';
let fixture;
before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-client-only/' });
- await fixture.build();
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-client-only/' });
+ await fixture.build();
});
describe('Client only components', () => {
- it('Loads pages using client:only hydrator', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Loads pages using client:only hydrator', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- // test 1: <astro-root> is empty
- expect($('astro-root').html()).to.equal('');
- const src = $('script').attr('src');
+ // test 1: <astro-root> is empty
+ expect($('astro-root').html()).to.equal('');
+ const src = $('script').attr('src');
- const script = await fixture.readFile(src);
- // test 2: svelte renderer is on the page
- const exp = /import\("(.\/client.*)"\)/g;
- let match, svelteRenderer;
- while ((match = exp.exec(script))) {
- svelteRenderer = match[1].replace(/^\./, '/assets/');
- }
- expect(svelteRenderer).to.be.ok;
+ const script = await fixture.readFile(src);
+ // test 2: svelte renderer is on the page
+ const exp = /import\("(.\/client.*)"\)/g;
+ let match, svelteRenderer;
+ while ((match = exp.exec(script))) {
+ svelteRenderer = match[1].replace(/^\./, '/assets/');
+ }
+ expect(svelteRenderer).to.be.ok;
- // test 3: can load svelte renderer
- const svelteClient = await fixture.readFile(svelteRenderer);
- expect(svelteClient).to.be.ok;
- });
+ // test 3: can load svelte renderer
+ const svelteClient = await fixture.readFile(svelteRenderer);
+ expect(svelteClient).to.be.ok;
+ });
});
diff --git a/packages/astro/test/astro-component-code.test.js b/packages/astro/test/astro-component-code.test.js
index 9d8eb88f2..f4a13c041 100644
--- a/packages/astro/test/astro-component-code.test.js
+++ b/packages/astro/test/astro-component-code.test.js
@@ -5,80 +5,80 @@ import { loadFixture } from './test-utils.js';
let fixture;
before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-component-code/' });
- await fixture.build();
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-component-code/' });
+ await fixture.build();
});
describe('<Code', () => {
- it('<Code> without lang or theme', async () => {
- let html = await fixture.readFile('/no-lang/index.html');
- const $ = cheerio.load(html);
- expect($('pre')).to.have.lengthOf(1);
- expect($('pre').attr('style')).to.equal('background-color: #0d1117; overflow-x: auto;', 'applies default and overflow');
- expect($('pre > code')).to.have.lengthOf(1);
+ it('<Code> without lang or theme', async () => {
+ let html = await fixture.readFile('/no-lang/index.html');
+ const $ = cheerio.load(html);
+ expect($('pre')).to.have.lengthOf(1);
+ expect($('pre').attr('style')).to.equal('background-color: #0d1117; overflow-x: auto;', 'applies default and overflow');
+ expect($('pre > code')).to.have.lengthOf(1);
- // test: contains some generated spans
- expect($('pre > code span').length).to.be.greaterThan(1);
- });
+ // test: contains some generated spans
+ expect($('pre > code span').length).to.be.greaterThan(1);
+ });
- it('<Code lang="...">', async () => {
- let html = await fixture.readFile('/basic/index.html');
- const $ = cheerio.load(html);
- expect($('pre')).to.have.lengthOf(1);
- expect($('pre').attr('class'), 'astro-code');
- expect($('pre > code')).to.have.lengthOf(1);
- // test: contains many generated spans
- expect($('pre > code span').length).to.be.greaterThanOrEqual(6);
- });
+ it('<Code lang="...">', async () => {
+ let html = await fixture.readFile('/basic/index.html');
+ const $ = cheerio.load(html);
+ expect($('pre')).to.have.lengthOf(1);
+ expect($('pre').attr('class'), 'astro-code');
+ expect($('pre > code')).to.have.lengthOf(1);
+ // test: contains many generated spans
+ expect($('pre > code span').length).to.be.greaterThanOrEqual(6);
+ });
- it('<Code theme="...">', async () => {
- let html = await fixture.readFile('/custom-theme/index.html');
- const $ = cheerio.load(html);
- expect($('pre')).to.have.lengthOf(1);
- expect($('pre').attr('class')).to.equal('astro-code');
- expect($('pre').attr('style')).to.equal('background-color: #2e3440ff; overflow-x: auto;', 'applies custom theme');
- });
+ it('<Code theme="...">', async () => {
+ let html = await fixture.readFile('/custom-theme/index.html');
+ const $ = cheerio.load(html);
+ expect($('pre')).to.have.lengthOf(1);
+ expect($('pre').attr('class')).to.equal('astro-code');
+ expect($('pre').attr('style')).to.equal('background-color: #2e3440ff; overflow-x: auto;', 'applies custom theme');
+ });
- it('<Code wrap>', async () => {
- {
- let html = await fixture.readFile('/wrap-true/index.html');
- const $ = cheerio.load(html);
- expect($('pre')).to.have.lengthOf(1);
- // test: applies wrap overflow
- expect($('pre').attr('style')).to.equal('background-color: #0d1117; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;');
- }
- {
- let html = await fixture.readFile('/wrap-false/index.html');
- const $ = cheerio.load(html);
- expect($('pre')).to.have.lengthOf(1);
- // test: applies wrap overflow
- expect($('pre').attr('style')).to.equal('background-color: #0d1117; overflow-x: auto;');
- }
- {
- let html = await fixture.readFile('/wrap-null/index.html');
- const $ = cheerio.load(html);
- expect($('pre')).to.have.lengthOf(1);
- // test: applies wrap overflow
- expect($('pre').attr('style')).to.equal('background-color: #0d1117');
- }
- });
+ it('<Code wrap>', async () => {
+ {
+ let html = await fixture.readFile('/wrap-true/index.html');
+ const $ = cheerio.load(html);
+ expect($('pre')).to.have.lengthOf(1);
+ // test: applies wrap overflow
+ expect($('pre').attr('style')).to.equal('background-color: #0d1117; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;');
+ }
+ {
+ let html = await fixture.readFile('/wrap-false/index.html');
+ const $ = cheerio.load(html);
+ expect($('pre')).to.have.lengthOf(1);
+ // test: applies wrap overflow
+ expect($('pre').attr('style')).to.equal('background-color: #0d1117; overflow-x: auto;');
+ }
+ {
+ let html = await fixture.readFile('/wrap-null/index.html');
+ const $ = cheerio.load(html);
+ expect($('pre')).to.have.lengthOf(1);
+ // test: applies wrap overflow
+ expect($('pre').attr('style')).to.equal('background-color: #0d1117');
+ }
+ });
- it('<Code lang="..." theme="css-variables">', async () => {
- let html = await fixture.readFile('/css-theme/index.html');
- const $ = cheerio.load(html);
- expect($('pre')).to.have.lengthOf(1);
- expect($('pre').attr('class')).to.equal('astro-code');
- expect(
- $('pre, pre span')
- .map((i, f) => (f.attribs ? f.attribs.style : 'no style found'))
- .toArray()
- ).to.deep.equal([
- 'background-color: var(--astro-code-color-background); overflow-x: auto;',
- 'color: var(--astro-code-token-constant)',
- 'color: var(--astro-code-token-function)',
- 'color: var(--astro-code-color-text)',
- 'color: var(--astro-code-token-string-expression)',
- 'color: var(--astro-code-color-text)',
- ]);
- });
+ it('<Code lang="..." theme="css-variables">', async () => {
+ let html = await fixture.readFile('/css-theme/index.html');
+ const $ = cheerio.load(html);
+ expect($('pre')).to.have.lengthOf(1);
+ expect($('pre').attr('class')).to.equal('astro-code');
+ expect(
+ $('pre, pre span')
+ .map((i, f) => (f.attribs ? f.attribs.style : 'no style found'))
+ .toArray()
+ ).to.deep.equal([
+ 'background-color: var(--astro-code-color-background); overflow-x: auto;',
+ 'color: var(--astro-code-token-constant)',
+ 'color: var(--astro-code-token-function)',
+ 'color: var(--astro-code-color-text)',
+ 'color: var(--astro-code-token-string-expression)',
+ 'color: var(--astro-code-color-text)',
+ ]);
+ });
});
diff --git a/packages/astro/test/astro-css-bundling-import.test.js b/packages/astro/test/astro-css-bundling-import.test.js
index 3cbae3df1..9761de334 100644
--- a/packages/astro/test/astro-css-bundling-import.test.js
+++ b/packages/astro/test/astro-css-bundling-import.test.js
@@ -3,48 +3,48 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('CSS Bundling (ESM import)', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-css-bundling-import/' });
- await fixture.build();
- });
-
- it('CSS output in import order', async () => {
- // note: this test is a little confusing, but the main idea is that
- // page-2.astro contains all of page-1.astro, plus some unique styles.
- // we only test page-2 to ensure the proper order is observed.
- const html = await fixture.readFile('/page-2/index.html');
- const $ = cheerio.load(html);
-
- let css = '';
-
- for (const style of $('link[rel=stylesheet]')) {
- const href = style.attribs.href.replace(/^\.\./, '');
- if (!href) continue;
- css += await fixture.readFile(href);
- }
-
- // test 1: insure green comes after red (site.css)
- expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:red}'));
-
- // test 2: insure green comes after blue (page-1.css)
- expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:#00f}'));
- });
-
- // TODO: re-enable this
- it.skip('no empty CSS files', async () => {
- for (const page of ['/page-1/index.html', '/page-2/index.html']) {
- const html = await fixture.readFile(page);
- const $ = cheerio.load(html);
-
- for (const style of $('link[rel=stylesheet]')) {
- const href = style.attribs.href.replace(/^\.\./, '');
- if (!href) continue;
- const css = await fixture.readFile(href);
-
- expect(css).to.be.ok;
- }
- }
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-css-bundling-import/' });
+ await fixture.build();
+ });
+
+ it('CSS output in import order', async () => {
+ // note: this test is a little confusing, but the main idea is that
+ // page-2.astro contains all of page-1.astro, plus some unique styles.
+ // we only test page-2 to ensure the proper order is observed.
+ const html = await fixture.readFile('/page-2/index.html');
+ const $ = cheerio.load(html);
+
+ let css = '';
+
+ for (const style of $('link[rel=stylesheet]')) {
+ const href = style.attribs.href.replace(/^\.\./, '');
+ if (!href) continue;
+ css += await fixture.readFile(href);
+ }
+
+ // test 1: insure green comes after red (site.css)
+ expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:red}'));
+
+ // test 2: insure green comes after blue (page-1.css)
+ expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:#00f}'));
+ });
+
+ // TODO: re-enable this
+ it.skip('no empty CSS files', async () => {
+ for (const page of ['/page-1/index.html', '/page-2/index.html']) {
+ const html = await fixture.readFile(page);
+ const $ = cheerio.load(html);
+
+ for (const style of $('link[rel=stylesheet]')) {
+ const href = style.attribs.href.replace(/^\.\./, '');
+ if (!href) continue;
+ const css = await fixture.readFile(href);
+
+ expect(css).to.be.ok;
+ }
+ }
+ });
});
diff --git a/packages/astro/test/astro-css-bundling-nested-layouts.test.js b/packages/astro/test/astro-css-bundling-nested-layouts.test.js
index e85b78e2e..2d48a4058 100644
--- a/packages/astro/test/astro-css-bundling-nested-layouts.test.js
+++ b/packages/astro/test/astro-css-bundling-nested-layouts.test.js
@@ -3,36 +3,36 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('nested layouts', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-css-bundling-nested-layouts/' });
- await fixture.build();
- });
-
- it('page-1 has all CSS', async () => {
- const html = await fixture.readFile('/page-1/index.html');
- const $ = cheerio.load(html);
-
- const stylesheets = $('link[rel=stylesheet]')
- .toArray()
- .map((el) => el.attribs.href);
-
- // page-one.[hash].css exists
- expect(stylesheets.some((href) => /page-one\.\w+\.css/.test(href))).to.be.true;
- });
-
- it('page-2 has all CSS', async () => {
- const html = await fixture.readFile('/page-2/index.html');
- const $ = cheerio.load(html);
-
- const stylesheets = $('link[rel=stylesheet]')
- .toArray()
- .map((el) => el.attribs.href);
-
- // page-one.[hash].css exists
- expect(stylesheets.some((href) => /page-one\.\w+\.css/.test(href))).to.be.true;
- // page-2.[hash].css exists
- expect(stylesheets.some((href) => /page-2\.\w+\.css/.test(href))).to.be.true;
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-css-bundling-nested-layouts/' });
+ await fixture.build();
+ });
+
+ it('page-1 has all CSS', async () => {
+ const html = await fixture.readFile('/page-1/index.html');
+ const $ = cheerio.load(html);
+
+ const stylesheets = $('link[rel=stylesheet]')
+ .toArray()
+ .map((el) => el.attribs.href);
+
+ // page-one.[hash].css exists
+ expect(stylesheets.some((href) => /page-one\.\w+\.css/.test(href))).to.be.true;
+ });
+
+ it('page-2 has all CSS', async () => {
+ const html = await fixture.readFile('/page-2/index.html');
+ const $ = cheerio.load(html);
+
+ const stylesheets = $('link[rel=stylesheet]')
+ .toArray()
+ .map((el) => el.attribs.href);
+
+ // page-one.[hash].css exists
+ expect(stylesheets.some((href) => /page-one\.\w+\.css/.test(href))).to.be.true;
+ // page-2.[hash].css exists
+ expect(stylesheets.some((href) => /page-2\.\w+\.css/.test(href))).to.be.true;
+ });
});
diff --git a/packages/astro/test/astro-css-bundling.test.js b/packages/astro/test/astro-css-bundling.test.js
index ee080cc11..906ed03a6 100644
--- a/packages/astro/test/astro-css-bundling.test.js
+++ b/packages/astro/test/astro-css-bundling.test.js
@@ -5,63 +5,63 @@ import { loadFixture } from './test-utils.js';
// note: the hashes should be deterministic, but updating the file contents will change hashes
// be careful not to test that the HTML simply contains CSS, because it always will! filename and quanity matter here (bundling).
const EXPECTED_CSS = {
- '/index.html': ['assets/index', 'assets/typography'], // don’t match hashes, which change based on content
- '/one/index.html': ['../assets/one'],
- '/two/index.html': ['../assets/two', '../assets/typography'],
- '/preload/index.html': ['../assets/preload'],
- '/preload-merge/index.html': ['../assets/preload-merge'],
+ '/index.html': ['assets/index', 'assets/typography'], // don’t match hashes, which change based on content
+ '/one/index.html': ['../assets/one'],
+ '/two/index.html': ['../assets/two', '../assets/typography'],
+ '/preload/index.html': ['../assets/preload'],
+ '/preload-merge/index.html': ['../assets/preload-merge'],
};
const UNEXPECTED_CSS = ['/src/components/nav.css', '../css/typography.css', '../css/colors.css', '../css/page-index.css', '../css/page-one.css', '../css/page-two.css'];
describe('CSS Bundling', function () {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-css-bundling/' });
- await fixture.build({ mode: 'production' });
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-css-bundling/' });
+ await fixture.build({ mode: 'production' });
+ });
- it('Bundles CSS', async () => {
- const builtCSS = new Set();
+ it('Bundles CSS', async () => {
+ const builtCSS = new Set();
- // for all HTML files…
- for (const [filepath, css] of Object.entries(EXPECTED_CSS)) {
- const html = await fixture.readFile(filepath);
- const $ = cheerio.load(html);
+ // for all HTML files…
+ for (const [filepath, css] of Object.entries(EXPECTED_CSS)) {
+ const html = await fixture.readFile(filepath);
+ const $ = cheerio.load(html);
- // test 1: assert new bundled CSS is present
- for (const href of css) {
- const link = $(`link[rel="stylesheet"][href^="${href}"]`);
- expect(link).to.have.lengthOf(1);
- const outHref = link.attr('href');
- builtCSS.add(outHref.startsWith('../') ? outHref.substr(2) : outHref);
- }
+ // test 1: assert new bundled CSS is present
+ for (const href of css) {
+ const link = $(`link[rel="stylesheet"][href^="${href}"]`);
+ expect(link).to.have.lengthOf(1);
+ const outHref = link.attr('href');
+ builtCSS.add(outHref.startsWith('../') ? outHref.substr(2) : outHref);
+ }
- // test 2: assert old CSS was removed
- for (const href of UNEXPECTED_CSS) {
- const link = $(`link[rel="stylesheet"][href="${href}"]`);
- expect(link).to.have.lengthOf(0);
- }
+ // test 2: assert old CSS was removed
+ for (const href of UNEXPECTED_CSS) {
+ const link = $(`link[rel="stylesheet"][href="${href}"]`);
+ expect(link).to.have.lengthOf(0);
+ }
- // test 3: preload tags was not removed and attributes was preserved
- if (filepath === '/preload/index.html') {
- const stylesheet = $('link[rel="stylesheet"][href^="../assets/preload"]');
- const preload = $('link[rel="preload"][href^="../assets/preload"]');
- expect(stylesheet[0].attribs.media).to.equal('print');
- expect(preload).to.have.lengthOf(1); // Preload tag was removed
- }
+ // test 3: preload tags was not removed and attributes was preserved
+ if (filepath === '/preload/index.html') {
+ const stylesheet = $('link[rel="stylesheet"][href^="../assets/preload"]');
+ const preload = $('link[rel="preload"][href^="../assets/preload"]');
+ expect(stylesheet[0].attribs.media).to.equal('print');
+ expect(preload).to.have.lengthOf(1); // Preload tag was removed
+ }
- // test 4: preload tags was not removed and attributes was preserved
- if (filepath === '/preload-merge/index.html') {
- const preload = $('link[rel="preload"]');
- expect(preload).to.have.lengthOf(1);
- }
+ // test 4: preload tags was not removed and attributes was preserved
+ if (filepath === '/preload-merge/index.html') {
+ const preload = $('link[rel="preload"]');
+ expect(preload).to.have.lengthOf(1);
+ }
- // test 5: assert all bundled CSS was built and contains CSS
- for (const url of builtCSS.keys()) {
- const css = await fixture.readFile(url);
- expect(css).to.be.ok;
- }
- }
- });
+ // test 5: assert all bundled CSS was built and contains CSS
+ for (const url of builtCSS.keys()) {
+ const css = await fixture.readFile(url);
+ expect(css).to.be.ok;
+ }
+ }
+ });
});
diff --git a/packages/astro/test/astro-doctype.test.js b/packages/astro/test/astro-doctype.test.js
index 43087dcab..a5fdba6fd 100644
--- a/packages/astro/test/astro-doctype.test.js
+++ b/packages/astro/test/astro-doctype.test.js
@@ -5,70 +5,70 @@ import { loadFixture } from './test-utils.js';
let fixture;
before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-doctype/' });
- await fixture.build();
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-doctype/' });
+ await fixture.build();
});
describe('Doctype', () => {
- it('Automatically prepends the standards mode doctype', async () => {
- const html = await fixture.readFile('/prepend/index.html');
+ it('Automatically prepends the standards mode doctype', async () => {
+ const html = await fixture.readFile('/prepend/index.html');
- // test that Doctype always included
- expect(html).to.match(/^<!DOCTYPE html>/i);
- });
+ // test that Doctype always included
+ expect(html).to.match(/^<!DOCTYPE html>/i);
+ });
- it('No attributes added when doctype is provided by user', async () => {
- const html = await fixture.readFile('/provided/index.html');
+ it('No attributes added when doctype is provided by user', async () => {
+ const html = await fixture.readFile('/provided/index.html');
- // test that Doctype always included
- expect(html).to.match(/^<!DOCTYPE html>/i);
- });
+ // test that Doctype always included
+ expect(html).to.match(/^<!DOCTYPE html>/i);
+ });
- // Note: parse5 converts this to <!DOCTYPE html> (HTML5). Uncomment if we want to support legacy doctypes.
- //
- // it('Preserves user provided doctype', async () => {
- // const html = await fixture.readFile('/preserve/index.html');
+ // Note: parse5 converts this to <!DOCTYPE html> (HTML5). Uncomment if we want to support legacy doctypes.
+ //
+ // it('Preserves user provided doctype', async () => {
+ // const html = await fixture.readFile('/preserve/index.html');
- // // test that Doctype included was preserved
- // expect(html).to.match(new RegExp('^<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">', 'i'));
- // });
+ // // test that Doctype included was preserved
+ // expect(html).to.match(new RegExp('^<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">', 'i'));
+ // });
- it('User provided doctype is case insensitive', async () => {
- const html = await fixture.readFile('/capital/index.html');
+ it('User provided doctype is case insensitive', async () => {
+ const html = await fixture.readFile('/capital/index.html');
- // test 1: Doctype left alone
- expect(html).to.match(/^<!DOCTYPE html>/i);
+ // test 1: Doctype left alone
+ expect(html).to.match(/^<!DOCTYPE html>/i);
- // test 2: no closing tag
- expect(html).not.to.match(/<\/!DOCTYPE>/i);
- });
+ // test 2: no closing tag
+ expect(html).not.to.match(/<\/!DOCTYPE>/i);
+ });
- it('Doctype can be provided in a layout', async () => {
- const html = await fixture.readFile('/in-layout/index.html');
+ it('Doctype can be provided in a layout', async () => {
+ const html = await fixture.readFile('/in-layout/index.html');
- // test 1: doctype is at the front
- expect(html).to.match(/^<!DOCTYPE html>/i);
+ // test 1: doctype is at the front
+ expect(html).to.match(/^<!DOCTYPE html>/i);
- // test 2: A link inside of the head
- const $ = cheerio.load(html);
- expect($('head link')).to.have.lengthOf(1);
- });
+ // test 2: A link inside of the head
+ const $ = cheerio.load(html);
+ expect($('head link')).to.have.lengthOf(1);
+ });
- it('Doctype is added in a layout without one', async () => {
- const html = await fixture.readFile('/in-layout-no-doctype/index.html');
+ it('Doctype is added in a layout without one', async () => {
+ const html = await fixture.readFile('/in-layout-no-doctype/index.html');
- // test that doctype is at the front
- expect(html).to.match(/^<!DOCTYPE html>/i);
- });
+ // test that doctype is at the front
+ expect(html).to.match(/^<!DOCTYPE html>/i);
+ });
- it('Doctype is added in a layout used with markdown pages', async () => {
- const html = await fixture.readFile('/in-layout-article/index.html');
+ it('Doctype is added in a layout used with markdown pages', async () => {
+ const html = await fixture.readFile('/in-layout-article/index.html');
- // test 1: doctype is at the front
- expect(html).to.match(/^<!DOCTYPE html>/i);
+ // test 1: doctype is at the front
+ expect(html).to.match(/^<!DOCTYPE html>/i);
- // test 2: A link inside of the head
- const $ = cheerio.load(html);
- expect($('head link')).to.have.lengthOf(1);
- });
+ // test 2: A link inside of the head
+ const $ = cheerio.load(html);
+ expect($('head link')).to.have.lengthOf(1);
+ });
});
diff --git a/packages/astro/test/astro-dynamic.test.js b/packages/astro/test/astro-dynamic.test.js
index fedccfd5a..57cfadfd2 100644
--- a/packages/astro/test/astro-dynamic.test.js
+++ b/packages/astro/test/astro-dynamic.test.js
@@ -3,55 +3,55 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Dynamic components', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-dynamic/' });
- await fixture.build();
- });
-
- it('Loads packages that only run code in client', async () => {
- const html = await fixture.readFile('/index.html');
-
- const $ = cheerio.load(html);
- expect($('script').length).to.eq(1);
- });
-
- it('Loads pages using client:media hydrator', async () => {
- const root = new URL('http://example.com/media/index.html');
- const html = await fixture.readFile('/media/index.html');
- const $ = cheerio.load(html);
-
- // test 1: static value rendered
- let js = await fixture.readFile(new URL($('script').attr('src'), root).pathname);
- expect(js).to.include(`value:"(max-width: 700px)"`);
-
- // test 2: dynamic value rendered
- expect(js).to.include(`value:"(max-width: 600px)"`);
- });
-
- it('Loads pages using client:only hydrator', async () => {
- const html = await fixture.readFile('/client-only/index.html');
- const $ = cheerio.load(html);
-
- // test 1: <astro-root> is empty
- expect($('<astro-root>').html()).to.equal('');
- const script = $('script').text();
-
- // Grab the svelte import
- // const exp = /import\("(.+?)"\)/g;
- // let match, svelteRenderer;
- // while ((match = exp.exec(result.contents))) {
- // if (match[1].includes('renderers/renderer-svelte/client.js')) {
- // svelteRenderer = match[1];
- // }
- // }
-
- // test 2: Svelte renderer is on the page
- // expect(svelteRenderer).to.be.ok;
-
- // test 3: Can load svelte renderer
- // const result = await fixture.fetch(svelteRenderer);
- // expect(result.status).to.equal(200);
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-dynamic/' });
+ await fixture.build();
+ });
+
+ it('Loads packages that only run code in client', async () => {
+ const html = await fixture.readFile('/index.html');
+
+ const $ = cheerio.load(html);
+ expect($('script').length).to.eq(1);
+ });
+
+ it('Loads pages using client:media hydrator', async () => {
+ const root = new URL('http://example.com/media/index.html');
+ const html = await fixture.readFile('/media/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: static value rendered
+ let js = await fixture.readFile(new URL($('script').attr('src'), root).pathname);
+ expect(js).to.include(`value:"(max-width: 700px)"`);
+
+ // test 2: dynamic value rendered
+ expect(js).to.include(`value:"(max-width: 600px)"`);
+ });
+
+ it('Loads pages using client:only hydrator', async () => {
+ const html = await fixture.readFile('/client-only/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: <astro-root> is empty
+ expect($('<astro-root>').html()).to.equal('');
+ const script = $('script').text();
+
+ // Grab the svelte import
+ // const exp = /import\("(.+?)"\)/g;
+ // let match, svelteRenderer;
+ // while ((match = exp.exec(result.contents))) {
+ // if (match[1].includes('renderers/renderer-svelte/client.js')) {
+ // svelteRenderer = match[1];
+ // }
+ // }
+
+ // test 2: Svelte renderer is on the page
+ // expect(svelteRenderer).to.be.ok;
+
+ // test 3: Can load svelte renderer
+ // const result = await fixture.fetch(svelteRenderer);
+ // expect(result.status).to.equal(200);
+ });
});
diff --git a/packages/astro/test/astro-envs.test.js b/packages/astro/test/astro-envs.test.js
index 23946f097..56b0471da 100644
--- a/packages/astro/test/astro-envs.test.js
+++ b/packages/astro/test/astro-envs.test.js
@@ -2,43 +2,43 @@ import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
describe('Environment Variables', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-envs/' });
-
- await fixture.build();
- });
-
- it('builds without throwing', async () => {
- expect(true).to.equal(true);
- });
-
- it('does render public env, does not render private env', async () => {
- let indexHtml = await fixture.readFile('/index.html');
-
- expect(indexHtml).to.not.include('CLUB_33');
- expect(indexHtml).to.include('BLUE_BAYOU');
- });
-
- it('includes public env in client-side JS', async () => {
- let dirs = await fixture.readdir('/assets');
- let found = false;
-
- // Look in all of the .js files to see if the public env is inlined.
- // Testing this way prevents hardcoding expected js files.
- // If we find it in any of them that's good enough to know its working.
- await Promise.all(
- dirs.map(async (path) => {
- if (path.endsWith('.js')) {
- let js = await fixture.readFile(`/assets/${path}`);
- if (js.includes('BLUE_BAYOU')) {
- found = true;
- }
- }
- })
- );
-
- expect(found).to.equal(true, 'found the env variable in the JS build');
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-envs/' });
+
+ await fixture.build();
+ });
+
+ it('builds without throwing', async () => {
+ expect(true).to.equal(true);
+ });
+
+ it('does render public env, does not render private env', async () => {
+ let indexHtml = await fixture.readFile('/index.html');
+
+ expect(indexHtml).to.not.include('CLUB_33');
+ expect(indexHtml).to.include('BLUE_BAYOU');
+ });
+
+ it('includes public env in client-side JS', async () => {
+ let dirs = await fixture.readdir('/assets');
+ let found = false;
+
+ // Look in all of the .js files to see if the public env is inlined.
+ // Testing this way prevents hardcoding expected js files.
+ // If we find it in any of them that's good enough to know its working.
+ await Promise.all(
+ dirs.map(async (path) => {
+ if (path.endsWith('.js')) {
+ let js = await fixture.readFile(`/assets/${path}`);
+ if (js.includes('BLUE_BAYOU')) {
+ found = true;
+ }
+ }
+ })
+ );
+
+ expect(found).to.equal(true, 'found the env variable in the JS build');
+ });
});
diff --git a/packages/astro/test/astro-expr.test.js b/packages/astro/test/astro-expr.test.js
index f10040586..e08323f86 100644
--- a/packages/astro/test/astro-expr.test.js
+++ b/packages/astro/test/astro-expr.test.js
@@ -3,100 +3,100 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Expressions', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-expr/',
- renderers: ['@astrojs/renderer-preact'],
- });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-expr/',
+ renderers: ['@astrojs/renderer-preact'],
+ });
+ await fixture.build();
+ });
- it('Can load page', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Can load page', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- for (let col of ['red', 'yellow', 'blue']) {
- expect($('#' + col)).to.have.lengthOf(1);
- }
- });
+ for (let col of ['red', 'yellow', 'blue']) {
+ expect($('#' + col)).to.have.lengthOf(1);
+ }
+ });
- it('Ignores characters inside of strings', async () => {
- const html = await fixture.readFile('/strings/index.html');
- const $ = cheerio.load(html);
+ it('Ignores characters inside of strings', async () => {
+ const html = await fixture.readFile('/strings/index.html');
+ const $ = cheerio.load(html);
- for (let col of ['red', 'yellow', 'blue']) {
- expect($('#' + col)).to.have.lengthOf(1);
- }
- });
+ for (let col of ['red', 'yellow', 'blue']) {
+ expect($('#' + col)).to.have.lengthOf(1);
+ }
+ });
- it('Ignores characters inside of line comments', async () => {
- const html = await fixture.readFile('/line-comments/index.html');
- const $ = cheerio.load(html);
+ it('Ignores characters inside of line comments', async () => {
+ const html = await fixture.readFile('/line-comments/index.html');
+ const $ = cheerio.load(html);
- for (let col of ['red', 'yellow', 'blue']) {
- expect($('#' + col)).to.have.lengthOf(1);
- }
- });
+ for (let col of ['red', 'yellow', 'blue']) {
+ expect($('#' + col)).to.have.lengthOf(1);
+ }
+ });
- it('Ignores characters inside of multiline comments', async () => {
- const html = await fixture.readFile('/multiline-comments/index.html');
- const $ = cheerio.load(html);
+ it('Ignores characters inside of multiline comments', async () => {
+ const html = await fixture.readFile('/multiline-comments/index.html');
+ const $ = cheerio.load(html);
- for (let col of ['red', 'yellow', 'blue']) {
- expect($('#' + col)).to.have.lengthOf(1);
- }
- });
+ for (let col of ['red', 'yellow', 'blue']) {
+ expect($('#' + col)).to.have.lengthOf(1);
+ }
+ });
- it('Allows multiple JSX children in mustache', async () => {
- const html = await fixture.readFile('/multiple-children/index.html');
+ it('Allows multiple JSX children in mustache', async () => {
+ const html = await fixture.readFile('/multiple-children/index.html');
- expect(html).to.include('#f');
- expect(html).not.to.include('#t');
- });
+ expect(html).to.include('#f');
+ expect(html).not.to.include('#t');
+ });
- it('Allows <> Fragments in expressions', async () => {
- const html = await fixture.readFile('/multiple-children/index.html');
- const $ = cheerio.load(html);
+ it('Allows <> Fragments in expressions', async () => {
+ const html = await fixture.readFile('/multiple-children/index.html');
+ const $ = cheerio.load(html);
- expect($('#fragment').children()).to.have.lengthOf(3);
- expect($('#fragment').children('#a')).to.have.lengthOf(1);
- expect($('#fragment').children('#b')).to.have.lengthOf(1);
- expect($('#fragment').children('#c')).to.have.lengthOf(1);
- });
+ expect($('#fragment').children()).to.have.lengthOf(3);
+ expect($('#fragment').children('#a')).to.have.lengthOf(1);
+ expect($('#fragment').children('#b')).to.have.lengthOf(1);
+ expect($('#fragment').children('#c')).to.have.lengthOf(1);
+ });
- it('Does not render falsy values using &&', async () => {
- const html = await fixture.readFile('/falsy/index.html');
- const $ = cheerio.load(html);
+ it('Does not render falsy values using &&', async () => {
+ const html = await fixture.readFile('/falsy/index.html');
+ const $ = cheerio.load(html);
- // test 1: Expected {true && <span id="true" />} to render
- expect($('#true')).to.have.lengthOf(1);
+ // test 1: Expected {true && <span id="true" />} to render
+ expect($('#true')).to.have.lengthOf(1);
- // test 2: Expected {0 && "VALUE"} to render "0"
- expect($('#zero').text()).to.equal('0');
+ // test 2: Expected {0 && "VALUE"} to render "0"
+ expect($('#zero').text()).to.equal('0');
- // test 3: Expected {false && <span id="false" />} not to render
- expect($('#false')).to.have.lengthOf(0);
+ // test 3: Expected {false && <span id="false" />} not to render
+ expect($('#false')).to.have.lengthOf(0);
- // test 4: Expected {null && <span id="null" />} not to render
- expect($('#null')).to.have.lengthOf(0);
+ // test 4: Expected {null && <span id="null" />} not to render
+ expect($('#null')).to.have.lengthOf(0);
- // test 5: Expected {undefined && <span id="undefined" />} not to render
- expect($('#undefined')).to.have.lengthOf(0);
+ // test 5: Expected {undefined && <span id="undefined" />} not to render
+ expect($('#undefined')).to.have.lengthOf(0);
- // Inside of a component
+ // Inside of a component
- // test 6: Expected {true && <span id="true" />} to render
- expect($('#frag-true')).to.have.lengthOf(1);
+ // test 6: Expected {true && <span id="true" />} to render
+ expect($('#frag-true')).to.have.lengthOf(1);
- // test 7: Expected {false && <span id="false" />} not to render
- expect($('#frag-false')).to.have.lengthOf(0);
+ // test 7: Expected {false && <span id="false" />} not to render
+ expect($('#frag-false')).to.have.lengthOf(0);
- // test 8: Expected {null && <span id="null" />} not to render
- expect($('#frag-null')).to.have.lengthOf(0);
+ // test 8: Expected {null && <span id="null" />} not to render
+ expect($('#frag-null')).to.have.lengthOf(0);
- // test 9: Expected {undefined && <span id="undefined" />} not to render
- expect($('#frag-undefined')).to.have.lengthOf(0);
- });
+ // test 9: Expected {undefined && <span id="undefined" />} not to render
+ expect($('#frag-undefined')).to.have.lengthOf(0);
+ });
});
diff --git a/packages/astro/test/astro-fallback.test.js b/packages/astro/test/astro-fallback.test.js
index 515f3c854..0b07840d7 100644
--- a/packages/astro/test/astro-fallback.test.js
+++ b/packages/astro/test/astro-fallback.test.js
@@ -3,19 +3,19 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Dynamic component fallback', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-fallback',
- renderers: ['@astrojs/renderer-preact'],
- });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-fallback',
+ renderers: ['@astrojs/renderer-preact'],
+ });
+ await fixture.build();
+ });
- it('Shows static content', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- expect($('#fallback').text()).to.equal('static');
- });
+ it('Shows static content', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ expect($('#fallback').text()).to.equal('static');
+ });
});
diff --git a/packages/astro/test/astro-get-static-paths.test.js b/packages/astro/test/astro-get-static-paths.test.js
index 66091d562..13f1aa4ac 100644
--- a/packages/astro/test/astro-get-static-paths.test.js
+++ b/packages/astro/test/astro-get-static-paths.test.js
@@ -2,21 +2,21 @@ import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
describe('getStaticPaths()', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-get-static-paths/',
- buildOptions: {
- site: 'https://mysite.dev/blog/',
- sitemap: false,
- },
- });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-get-static-paths/',
+ buildOptions: {
+ site: 'https://mysite.dev/blog/',
+ sitemap: false,
+ },
+ });
+ await fixture.build();
+ });
- it('is only called once during build', () => {
- // useless expect; if build() throws in setup then this test fails
- expect(true).to.equal(true);
- });
+ it('is only called once during build', () => {
+ // useless expect; if build() throws in setup then this test fails
+ expect(true).to.equal(true);
+ });
});
diff --git a/packages/astro/test/astro-global.test.js b/packages/astro/test/astro-global.test.js
index 75d44e743..168d66bf9 100644
--- a/packages/astro/test/astro-global.test.js
+++ b/packages/astro/test/astro-global.test.js
@@ -3,55 +3,55 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Astro.*', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-global/',
- buildOptions: {
- site: 'https://mysite.dev/blog/',
- sitemap: false,
- },
- });
- await fixture.build();
- });
-
- it('Astro.request.url', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
-
- expect($('#pathname').text()).to.equal('/');
- expect($('#child-pathname').text()).to.equal('/');
- expect($('#nested-child-pathname').text()).to.equal('/');
- });
-
- it('Astro.request.canonicalURL', async () => {
- // given a URL, expect the following canonical URL
- const canonicalURLs = {
- '/index.html': 'https://mysite.dev/blog/',
- '/post/post/index.html': 'https://mysite.dev/blog/post/post/',
- '/posts/1/index.html': 'https://mysite.dev/blog/posts/',
- '/posts/2/index.html': 'https://mysite.dev/blog/posts/2/',
- };
-
- for (const [url, canonicalURL] of Object.entries(canonicalURLs)) {
- const html = await fixture.readFile(url);
-
- const $ = cheerio.load(html);
- expect($('link[rel="canonical"]').attr('href')).to.equal(canonicalURL);
- }
- });
-
- it('Astro.site', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- expect($('#site').attr('href')).to.equal('https://mysite.dev/blog/');
- });
-
- it('Astro.resolve built', async () => {
- const html = await fixture.readFile('/resolve/index.html');
- const $ = cheerio.load(html);
- expect($('img').attr('src')).to.include('assets/penguin.ccd44411.png'); // Main src/images
- expect($('#inner-child img').attr('src')).to.include('assets/penguin.b9ab122a.png');
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-global/',
+ buildOptions: {
+ site: 'https://mysite.dev/blog/',
+ sitemap: false,
+ },
+ });
+ await fixture.build();
+ });
+
+ it('Astro.request.url', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#pathname').text()).to.equal('/');
+ expect($('#child-pathname').text()).to.equal('/');
+ expect($('#nested-child-pathname').text()).to.equal('/');
+ });
+
+ it('Astro.request.canonicalURL', async () => {
+ // given a URL, expect the following canonical URL
+ const canonicalURLs = {
+ '/index.html': 'https://mysite.dev/blog/',
+ '/post/post/index.html': 'https://mysite.dev/blog/post/post/',
+ '/posts/1/index.html': 'https://mysite.dev/blog/posts/',
+ '/posts/2/index.html': 'https://mysite.dev/blog/posts/2/',
+ };
+
+ for (const [url, canonicalURL] of Object.entries(canonicalURLs)) {
+ const html = await fixture.readFile(url);
+
+ const $ = cheerio.load(html);
+ expect($('link[rel="canonical"]').attr('href')).to.equal(canonicalURL);
+ }
+ });
+
+ it('Astro.site', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ expect($('#site').attr('href')).to.equal('https://mysite.dev/blog/');
+ });
+
+ it('Astro.resolve built', async () => {
+ const html = await fixture.readFile('/resolve/index.html');
+ const $ = cheerio.load(html);
+ expect($('img').attr('src')).to.include('assets/penguin.ccd44411.png'); // Main src/images
+ expect($('#inner-child img').attr('src')).to.include('assets/penguin.b9ab122a.png');
+ });
});
diff --git a/packages/astro/test/astro-jsx.test.js b/packages/astro/test/astro-jsx.test.js
index b82d6618d..e8fbdb858 100644
--- a/packages/astro/test/astro-jsx.test.js
+++ b/packages/astro/test/astro-jsx.test.js
@@ -3,39 +3,39 @@ import { loadFixture } from './test-utils.js';
let cwd = './fixtures/astro-jsx/';
let orders = [
- ['preact', 'react', 'solid'],
- ['preact', 'solid', 'react'],
- ['react', 'preact', 'solid'],
- ['react', 'solid', 'preact'],
- ['solid', 'react', 'preact'],
- ['solid', 'preact', 'react'],
+ ['preact', 'react', 'solid'],
+ ['preact', 'solid', 'react'],
+ ['react', 'preact', 'solid'],
+ ['react', 'solid', 'preact'],
+ ['solid', 'react', 'preact'],
+ ['solid', 'preact', 'react'],
];
let fixtures = {};
before(async () => {
- await Promise.all(
- orders.map((renderers, n) =>
- loadFixture({
- projectRoot: cwd,
- renderers: renderers.map((name) => `@astrojs/renderer-${name}`),
- dist: new URL(`${cwd}dist-${n}/`, import.meta.url),
- }).then((fixture) => {
- fixtures[renderers.toString()] = fixture;
- return fixture.build();
- })
- )
- );
+ await Promise.all(
+ orders.map((renderers, n) =>
+ loadFixture({
+ projectRoot: cwd,
+ renderers: renderers.map((name) => `@astrojs/renderer-${name}`),
+ dist: new URL(`${cwd}dist-${n}/`, import.meta.url),
+ }).then((fixture) => {
+ fixtures[renderers.toString()] = fixture;
+ return fixture.build();
+ })
+ )
+ );
});
it('Renderer order', () => {
- it('JSX renderers can be defined in any order', async () => {
- if (!Object.values(fixtures).length) {
- throw new Error(`JSX renderers didn’t build properly`);
- }
+ it('JSX renderers can be defined in any order', async () => {
+ if (!Object.values(fixtures).length) {
+ throw new Error(`JSX renderers didn’t build properly`);
+ }
- for (const [name, fixture] of Object.entries(fixtures)) {
- const html = await fixture.readFile('/index.html');
- expect(html, name).to.be.ok;
- }
- });
+ for (const [name, fixture] of Object.entries(fixtures)) {
+ const html = await fixture.readFile('/index.html');
+ expect(html, name).to.be.ok;
+ }
+ });
});
diff --git a/packages/astro/test/astro-markdown-plugins.test.js b/packages/astro/test/astro-markdown-plugins.test.js
index d3f20b073..b293c1871 100644
--- a/packages/astro/test/astro-markdown-plugins.test.js
+++ b/packages/astro/test/astro-markdown-plugins.test.js
@@ -4,47 +4,47 @@ import { loadFixture } from './test-utils.js';
import markdownRemark from '@astrojs/markdown-remark';
describe('Astro Markdown plugins', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-markdown-plugins/',
- renderers: ['@astrojs/renderer-preact'],
- markdownOptions: {
- render: [
- markdownRemark,
- {
- remarkPlugins: ['remark-code-titles', ['rehype-autolink-headings', { behavior: 'prepend' }]],
- rehypePlugins: [[import('rehype-toc'), { headings: ['h2', 'h3'] }], ['rehype-add-classes', { 'h1,h2,h3': 'title' }], 'rehype-slug'],
- },
- ],
- },
- buildOptions: {
- sitemap: false,
- },
- });
- await fixture.build();
- });
-
- it('Can render markdown with plugins', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
-
- // test 1: Added a TOC
- expect($('.toc')).to.have.lengthOf(1);
-
- // teste 2: Added .title to h1
- expect($('#hello-world').hasClass('title')).to.equal(true);
- });
-
- it('Can render Astro <Markdown> with plugins', async () => {
- const html = await fixture.readFile('/astro/index.html');
- const $ = cheerio.load(html);
-
- // test 1: Added a TOC
- expect($('.toc')).to.have.lengthOf(1);
-
- // teste 2: Added .title to h1
- expect($('#hello-world').hasClass('title')).to.equal(true);
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-markdown-plugins/',
+ renderers: ['@astrojs/renderer-preact'],
+ markdownOptions: {
+ render: [
+ markdownRemark,
+ {
+ remarkPlugins: ['remark-code-titles', ['rehype-autolink-headings', { behavior: 'prepend' }]],
+ rehypePlugins: [[import('rehype-toc'), { headings: ['h2', 'h3'] }], ['rehype-add-classes', { 'h1,h2,h3': 'title' }], 'rehype-slug'],
+ },
+ ],
+ },
+ buildOptions: {
+ sitemap: false,
+ },
+ });
+ await fixture.build();
+ });
+
+ it('Can render markdown with plugins', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Added a TOC
+ expect($('.toc')).to.have.lengthOf(1);
+
+ // teste 2: Added .title to h1
+ expect($('#hello-world').hasClass('title')).to.equal(true);
+ });
+
+ it('Can render Astro <Markdown> with plugins', async () => {
+ const html = await fixture.readFile('/astro/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Added a TOC
+ expect($('.toc')).to.have.lengthOf(1);
+
+ // teste 2: Added .title to h1
+ expect($('#hello-world').hasClass('title')).to.equal(true);
+ });
});
diff --git a/packages/astro/test/astro-markdown.test.js b/packages/astro/test/astro-markdown.test.js
index 0d6aae978..8f1878914 100644
--- a/packages/astro/test/astro-markdown.test.js
+++ b/packages/astro/test/astro-markdown.test.js
@@ -3,147 +3,147 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Astro Markdown', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-markdown/',
- renderers: ['@astrojs/renderer-preact'],
- buildOptions: {
- sitemap: false,
- },
- });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-markdown/',
+ renderers: ['@astrojs/renderer-preact'],
+ buildOptions: {
+ sitemap: false,
+ },
+ });
+ await fixture.build();
+ });
- it('Can load markdown pages with Astro', async () => {
- const html = await fixture.readFile('/post/index.html');
- const $ = cheerio.load(html);
+ it('Can load markdown pages with Astro', async () => {
+ const html = await fixture.readFile('/post/index.html');
+ const $ = cheerio.load(html);
- // test 1: There is a div added in markdown
- expect($('#first').length).to.be.ok;
+ // test 1: There is a div added in markdown
+ expect($('#first').length).to.be.ok;
- // test 2: There is a div added via a component from markdown
- expect($('#test').length).to.be.ok;
- });
+ // test 2: There is a div added via a component from markdown
+ expect($('#test').length).to.be.ok;
+ });
- it('Can load more complex jsxy stuff', async () => {
- const html = await fixture.readFile('/complex/index.html');
- const $ = cheerio.load(html);
+ it('Can load more complex jsxy stuff', async () => {
+ const html = await fixture.readFile('/complex/index.html');
+ const $ = cheerio.load(html);
- expect($('#test').text()).to.equal('Hello world');
- });
+ expect($('#test').text()).to.equal('Hello world');
+ });
- it('Empty code blocks do not fail', async () => {
- const html = await fixture.readFile('/empty-code/index.html');
- const $ = cheerio.load(html);
+ it('Empty code blocks do not fail', async () => {
+ const html = await fixture.readFile('/empty-code/index.html');
+ const $ = cheerio.load(html);
- // test 1: There is not a `<code>` in the codeblock
- expect($('pre')[0].children).to.have.lengthOf(1);
+ // test 1: There is not a `<code>` in the codeblock
+ expect($('pre')[0].children).to.have.lengthOf(1);
- // test 2: The empty `<pre>` failed to render
- expect($('pre')[1].children).to.have.lengthOf(0);
- });
+ // test 2: The empty `<pre>` failed to render
+ expect($('pre')[1].children).to.have.lengthOf(0);
+ });
- it('Runs code blocks through syntax highlighter', async () => {
- const html = await fixture.readFile('/code/index.html');
- const $ = cheerio.load(html);
+ it('Runs code blocks through syntax highlighter', async () => {
+ const html = await fixture.readFile('/code/index.html');
+ const $ = cheerio.load(html);
- // test 1: There are child spans in code blocks
- expect($('code span').length).greaterThan(0);
- });
+ // test 1: There are child spans in code blocks
+ expect($('code span').length).greaterThan(0);
+ });
- it('Scoped styles should not break syntax highlight', async () => {
- const html = await fixture.readFile('/scopedStyles-code/index.html');
- const $ = cheerio.load(html);
+ it('Scoped styles should not break syntax highlight', async () => {
+ const html = await fixture.readFile('/scopedStyles-code/index.html');
+ const $ = cheerio.load(html);
- // test 1: <pre> tag has scopedStyle class passed down
- expect($('pre').is('[class]')).to.equal(true);
- expect($('pre').attr('class').split(' ').length).to.equal(2);
+ // test 1: <pre> tag has scopedStyle class passed down
+ expect($('pre').is('[class]')).to.equal(true);
+ expect($('pre').attr('class').split(' ').length).to.equal(2);
- // test 2: <pre> tag has correct language
- expect($('pre').hasClass('language-js')).to.equal(true);
+ // test 2: <pre> tag has correct language
+ expect($('pre').hasClass('language-js')).to.equal(true);
- // test 3: <code> tag has correct language
- expect($('code').hasClass('language-js')).to.equal(true);
+ // test 3: <code> tag has correct language
+ expect($('code').hasClass('language-js')).to.equal(true);
- // test 4: There are child spans in code blocks
- expect($('code span').length).to.be.greaterThan(0);
- });
+ // test 4: There are child spans in code blocks
+ expect($('code span').length).to.be.greaterThan(0);
+ });
- it('Renders correctly when deeply nested on a page', async () => {
- const html = await fixture.readFile('/deep/index.html');
- const $ = cheerio.load(html);
+ it('Renders correctly when deeply nested on a page', async () => {
+ const html = await fixture.readFile('/deep/index.html');
+ const $ = cheerio.load(html);
- // test 1: Rendered all children
- expect($('#deep').children()).to.have.lengthOf(3);
+ // test 1: Rendered all children
+ expect($('#deep').children()).to.have.lengthOf(3);
- // tests 2–4: Only rendered title in each section
- expect($('.a').children()).to.have.lengthOf(1);
- expect($('.b').children()).to.have.lengthOf(1);
- expect($('.c').children()).to.have.lengthOf(1);
+ // tests 2–4: Only rendered title in each section
+ expect($('.a').children()).to.have.lengthOf(1);
+ expect($('.b').children()).to.have.lengthOf(1);
+ expect($('.c').children()).to.have.lengthOf(1);
- // test 5–7: Rendered title in correct section
- expect($('.a > h2').text()).to.equal('A');
- expect($('.b > h2').text()).to.equal('B');
- expect($('.c > h2').text()).to.equal('C');
- });
+ // test 5–7: Rendered title in correct section
+ expect($('.a > h2').text()).to.equal('A');
+ expect($('.b > h2').text()).to.equal('B');
+ expect($('.c > h2').text()).to.equal('C');
+ });
- it('Renders dynamic content though the content attribute', async () => {
- const html = await fixture.readFile('/external/index.html');
- const $ = cheerio.load(html);
+ it('Renders dynamic content though the content attribute', async () => {
+ const html = await fixture.readFile('/external/index.html');
+ const $ = cheerio.load(html);
- // test 1: Rendered markdown content
- expect($('#outer')).to.have.lengthOf(1);
+ // test 1: Rendered markdown content
+ expect($('#outer')).to.have.lengthOf(1);
- // test 2: Nested markdown content
- expect($('#inner')).to.have.lengthOf(1);
+ // test 2: Nested markdown content
+ expect($('#inner')).to.have.lengthOf(1);
- // test 3: Scoped class passed down
- expect($('#inner').is('[class]')).to.equal(true);
- });
+ // test 3: Scoped class passed down
+ expect($('#inner').is('[class]')).to.equal(true);
+ });
- it('Renders curly braces correctly', async () => {
- const html = await fixture.readFile('/braces/index.html');
- const $ = cheerio.load(html);
-
- // test 1: Rendered curly braces markdown content
- expect($('code')).to.have.lengthOf(3);
+ it('Renders curly braces correctly', async () => {
+ const html = await fixture.readFile('/braces/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Rendered curly braces markdown content
+ expect($('code')).to.have.lengthOf(3);
- // test 2: Rendered curly braces markdown content
- expect($('code:first-child').text()).to.equal('({})');
+ // test 2: Rendered curly braces markdown content
+ expect($('code:first-child').text()).to.equal('({})');
- // test 3: Rendered curly braces markdown content
- expect($('code:nth-child(2)').text()).to.equal('{...props}');
+ // test 3: Rendered curly braces markdown content
+ expect($('code:nth-child(2)').text()).to.equal('{...props}');
- // test 4: Rendered curly braces markdown content
- expect($('code:last-child').text()).to.equal('{/* JavaScript */}');
- });
+ // test 4: Rendered curly braces markdown content
+ expect($('code:last-child').text()).to.equal('{/* JavaScript */}');
+ });
- it('Does not close parent early when using content attribute (#494)', async () => {
- const html = await fixture.readFile('/close/index.html');
- const $ = cheerio.load(html);
+ it('Does not close parent early when using content attribute (#494)', async () => {
+ const html = await fixture.readFile('/close/index.html');
+ const $ = cheerio.load(html);
- // test <Markdown content /> closed div#target early
- expect($('#target').children()).to.have.lengthOf(2);
- });
+ // test <Markdown content /> closed div#target early
+ expect($('#target').children()).to.have.lengthOf(2);
+ });
- it('Can render markdown with --- for horizontal rule', async () => {
- const html = await fixture.readFile('/dash/index.html');
- expect(!!html).to.equal(true);
- });
+ it('Can render markdown with --- for horizontal rule', async () => {
+ const html = await fixture.readFile('/dash/index.html');
+ expect(!!html).to.equal(true);
+ });
- it('Can render markdown content prop (#1259)', async () => {
- const html = await fixture.readFile('/content/index.html');
- const $ = cheerio.load(html);
+ it('Can render markdown content prop (#1259)', async () => {
+ const html = await fixture.readFile('/content/index.html');
+ const $ = cheerio.load(html);
- // test Markdown rendered correctly via content prop
- expect($('h1').text()).to.equal('Foo');
- });
+ // test Markdown rendered correctly via content prop
+ expect($('h1').text()).to.equal('Foo');
+ });
- it("doesn't occurs TypeError when no elements", async () => {
- const html = await fixture.readFile('/no-elements/index.html');
- // render html without error
- expect(html).to.be.ok;
- });
+ it("doesn't occurs TypeError when no elements", async () => {
+ const html = await fixture.readFile('/no-elements/index.html');
+ // render html without error
+ expect(html).to.be.ok;
+ });
});
diff --git a/packages/astro/test/astro-pageDirectoryUrl.test.js b/packages/astro/test/astro-pageDirectoryUrl.test.js
index e741b39a9..b0d0f3372 100644
--- a/packages/astro/test/astro-pageDirectoryUrl.test.js
+++ b/packages/astro/test/astro-pageDirectoryUrl.test.js
@@ -2,21 +2,21 @@ import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
describe('pageUrlFormat', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-page-directory-url',
- buildOptions: {
- pageUrlFormat: 'file',
- },
- });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-page-directory-url',
+ buildOptions: {
+ pageUrlFormat: 'file',
+ },
+ });
+ await fixture.build();
+ });
- it('outputs', async () => {
- expect(await fixture.readFile('/client/index.html')).to.be.ok;
- expect(await fixture.readFile('/nested-md/index.html')).to.be.ok;
- expect(await fixture.readFile('/nested-astro/index.html')).to.be.ok;
- });
+ it('outputs', async () => {
+ expect(await fixture.readFile('/client/index.html')).to.be.ok;
+ expect(await fixture.readFile('/nested-md/index.html')).to.be.ok;
+ expect(await fixture.readFile('/nested-astro/index.html')).to.be.ok;
+ });
});
diff --git a/packages/astro/test/astro-pages.test.js b/packages/astro/test/astro-pages.test.js
index 8637ac0f3..6cb0f8dfd 100644
--- a/packages/astro/test/astro-pages.test.js
+++ b/packages/astro/test/astro-pages.test.js
@@ -3,17 +3,17 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Pages', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-pages/' });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-pages/' });
+ await fixture.build();
+ });
- it('Can find page with "index" at the end file name', async () => {
- const html = await fixture.readFile('/posts/name-with-index/index.html');
- const $ = cheerio.load(html);
+ it('Can find page with "index" at the end file name', async () => {
+ const html = await fixture.readFile('/posts/name-with-index/index.html');
+ const $ = cheerio.load(html);
- expect($('h1').text()).to.equal('Name with index');
- });
+ expect($('h1').text()).to.equal('Name with index');
+ });
});
diff --git a/packages/astro/test/astro-pagination.test.js b/packages/astro/test/astro-pagination.test.js
index faccc2ecb..acdef93a4 100644
--- a/packages/astro/test/astro-pagination.test.js
+++ b/packages/astro/test/astro-pagination.test.js
@@ -5,43 +5,43 @@ import { loadFixture } from './test-utils.js';
let fixture;
before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-pagination/',
- buildOptions: {
- site: 'https://mysite.dev/blog/',
- sitemap: false,
- },
- });
- await fixture.build();
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-pagination/',
+ buildOptions: {
+ site: 'https://mysite.dev/blog/',
+ sitemap: false,
+ },
+ });
+ await fixture.build();
});
describe('Pagination', () => {
- it('optional root page', async () => {
- for (const file of ['/posts/optional-root-page/index.html', '/posts/optional-root-page/2/index.html', '/posts/optional-root-page/3/index.html']) {
- expect(await fixture.readFile(file)).to.be.ok;
- }
- });
+ it('optional root page', async () => {
+ for (const file of ['/posts/optional-root-page/index.html', '/posts/optional-root-page/2/index.html', '/posts/optional-root-page/3/index.html']) {
+ expect(await fixture.readFile(file)).to.be.ok;
+ }
+ });
- it('named root page', async () => {
- for (const file of ['/posts/named-root-page/1/index.html', '/posts/named-root-page/2/index.html', '/posts/named-root-page/3/index.html']) {
- expect(await fixture.readFile(file)).to.be.ok;
- }
- });
+ it('named root page', async () => {
+ for (const file of ['/posts/named-root-page/1/index.html', '/posts/named-root-page/2/index.html', '/posts/named-root-page/3/index.html']) {
+ expect(await fixture.readFile(file)).to.be.ok;
+ }
+ });
- it('multiple params', async () => {
- const params = [
- { color: 'red', p: '1' },
- { color: 'blue', p: '1' },
- { color: 'blue', p: '2' },
- ];
- await Promise.all(
- params.map(async ({ color, p }) => {
- const html = await fixture.readFile(`/posts/${color}/${p}/index.html`);
- const $ = cheerio.load(html);
- expect($('#page-a').text()).to.equal(p);
- expect($('#page-b').text()).to.equal(p);
- expect($('#filter').text()).to.equal(color);
- })
- );
- });
+ it('multiple params', async () => {
+ const params = [
+ { color: 'red', p: '1' },
+ { color: 'blue', p: '1' },
+ { color: 'blue', p: '2' },
+ ];
+ await Promise.all(
+ params.map(async ({ color, p }) => {
+ const html = await fixture.readFile(`/posts/${color}/${p}/index.html`);
+ const $ = cheerio.load(html);
+ expect($('#page-a').text()).to.equal(p);
+ expect($('#page-b').text()).to.equal(p);
+ expect($('#filter').text()).to.equal(color);
+ })
+ );
+ });
});
diff --git a/packages/astro/test/astro-partial-html.test.js b/packages/astro/test/astro-partial-html.test.js
index 45ad62038..255142940 100644
--- a/packages/astro/test/astro-partial-html.test.js
+++ b/packages/astro/test/astro-partial-html.test.js
@@ -3,39 +3,39 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Partial HTML ', async () => {
- let fixture;
- let devServer;
+ let fixture;
+ let devServer;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-partial-html/' });
- devServer = await fixture.startDevServer();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-partial-html/' });
+ devServer = await fixture.startDevServer();
+ });
- after(async () => {
- devServer && devServer.stop();
- });
+ after(async () => {
+ devServer && devServer.stop();
+ });
- it('injects Astro styles and scripts', async () => {
- const html = await fixture.fetch('/astro').then((res) => res.text());
- const $ = cheerio.load(html);
+ it('injects Astro styles and scripts', async () => {
+ const html = await fixture.fetch('/astro').then((res) => res.text());
+ const $ = cheerio.load(html);
- // test 1: Doctype first
- expect(html).to.match(/^<!DOCTYPE html/);
+ // test 1: Doctype first
+ expect(html).to.match(/^<!DOCTYPE html/);
- // test 2: correct CSS present
- const css = $('style[astro-style]').html();
- expect(css).to.match(/\.astro-[^{]+{color:red;}/);
- });
+ // test 2: correct CSS present
+ const css = $('style[astro-style]').html();
+ expect(css).to.match(/\.astro-[^{]+{color:red;}/);
+ });
- it('injects framework styles', async () => {
- const html = await fixture.fetch('/jsx').then((res) => res.text());
- const $ = cheerio.load(html);
+ it('injects framework styles', async () => {
+ const html = await fixture.fetch('/jsx').then((res) => res.text());
+ const $ = cheerio.load(html);
- // test 1: Doctype first
- expect(html).to.match(/^<!DOCTYPE html/);
+ // test 1: Doctype first
+ expect(html).to.match(/^<!DOCTYPE html/);
- // test 2: link tag present
- const href = $('link[rel=stylesheet][data-astro-injected]').attr('href');
- expect(href).to.be.ok;
- });
+ // test 2: link tag present
+ const href = $('link[rel=stylesheet][data-astro-injected]').attr('href');
+ expect(href).to.be.ok;
+ });
});
diff --git a/packages/astro/test/astro-public.test.js b/packages/astro/test/astro-public.test.js
index 6f7695ccd..56b407df5 100644
--- a/packages/astro/test/astro-public.test.js
+++ b/packages/astro/test/astro-public.test.js
@@ -2,17 +2,17 @@ import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
describe('Public', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-public/' });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-public/' });
+ await fixture.build();
+ });
- it('css and js files do not get bundled', async () => {
- let indexHtml = await fixture.readFile('/index.html');
- expect(indexHtml).to.include('<script src="/example.js"></script>');
- expect(indexHtml).to.include('<link href="/example.css" ref="stylesheet">');
- expect(indexHtml).to.include('<img src="/images/twitter.png">');
- });
+ it('css and js files do not get bundled', async () => {
+ let indexHtml = await fixture.readFile('/index.html');
+ expect(indexHtml).to.include('<script src="/example.js"></script>');
+ expect(indexHtml).to.include('<link href="/example.css" ref="stylesheet">');
+ expect(indexHtml).to.include('<img src="/images/twitter.png">');
+ });
});
diff --git a/packages/astro/test/astro-scripts.test.js b/packages/astro/test/astro-scripts.test.js
index 5d3e6cd96..3a16969ea 100644
--- a/packages/astro/test/astro-scripts.test.js
+++ b/packages/astro/test/astro-scripts.test.js
@@ -4,83 +4,83 @@ import path from 'path';
import { loadFixture } from './test-utils.js';
describe('Scripts (hoisted and not)', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-scripts/' });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-scripts/' });
+ await fixture.build();
+ });
- it('Moves external scripts up', async () => {
- const html = await fixture.readFile('/external/index.html');
- const $ = cheerio.load(html);
+ it('Moves external scripts up', async () => {
+ const html = await fixture.readFile('/external/index.html');
+ const $ = cheerio.load(html);
- expect($('head script[type="module"]:not([src="/regular_script.js"])')).to.have.lengthOf(1);
- expect($('body script')).to.have.lengthOf(0);
- });
+ expect($('head script[type="module"]:not([src="/regular_script.js"])')).to.have.lengthOf(1);
+ expect($('body script')).to.have.lengthOf(0);
+ });
- it('Moves inline scripts up', async () => {
- const html = await fixture.readFile('/inline/index.html');
- const $ = cheerio.load(html);
+ it('Moves inline scripts up', async () => {
+ const html = await fixture.readFile('/inline/index.html');
+ const $ = cheerio.load(html);
- expect($('head script[type="module"]')).to.have.lengthOf(1);
- expect($('body script')).to.have.lengthOf(0);
- });
+ expect($('head script[type="module"]')).to.have.lengthOf(1);
+ expect($('body script')).to.have.lengthOf(0);
+ });
- it('Inline page builds the scripts to a single bundle', async () => {
- // Inline page
- let inline = await fixture.readFile('/inline/index.html');
- let $ = cheerio.load(inline);
+ it('Inline page builds the scripts to a single bundle', async () => {
+ // Inline page
+ let inline = await fixture.readFile('/inline/index.html');
+ let $ = cheerio.load(inline);
- // test 1: Just one entry module
- expect($('script')).to.have.lengthOf(1);
+ // test 1: Just one entry module
+ expect($('script')).to.have.lengthOf(1);
- // test 2: attr removed
- expect($('script').attr('data-astro')).to.equal(undefined);
+ // test 2: attr removed
+ expect($('script').attr('data-astro')).to.equal(undefined);
- let entryURL = path.join('inline', $('script').attr('src'));
- let inlineEntryJS = await fixture.readFile(entryURL);
+ let entryURL = path.join('inline', $('script').attr('src'));
+ let inlineEntryJS = await fixture.readFile(entryURL);
- // test 3: the JS exists
- expect(inlineEntryJS).to.be.ok;
- });
+ // test 3: the JS exists
+ expect(inlineEntryJS).to.be.ok;
+ });
- it('External page builds the hoisted scripts to a single bundle', async () => {
- let external = await fixture.readFile('/external/index.html');
- let $ = cheerio.load(external);
+ it('External page builds the hoisted scripts to a single bundle', async () => {
+ let external = await fixture.readFile('/external/index.html');
+ let $ = cheerio.load(external);
- // test 1: there are two scripts
- expect($('script')).to.have.lengthOf(2);
+ // test 1: there are two scripts
+ expect($('script')).to.have.lengthOf(2);
- let el = $('script').get(1);
- let entryURL = path.join('external', $(el).attr('src'));
- let externalEntryJS = await fixture.readFile(entryURL);
+ let el = $('script').get(1);
+ let entryURL = path.join('external', $(el).attr('src'));
+ let externalEntryJS = await fixture.readFile(entryURL);
- // test 2: the JS exists
- expect(externalEntryJS).to.be.ok;
- });
+ // test 2: the JS exists
+ expect(externalEntryJS).to.be.ok;
+ });
- it('External page using non-hoist scripts that are modules are built standalone', async () => {
- let external = await fixture.readFile('/external-no-hoist/index.html');
- let $ = cheerio.load(external);
+ it('External page using non-hoist scripts that are modules are built standalone', async () => {
+ let external = await fixture.readFile('/external-no-hoist/index.html');
+ let $ = cheerio.load(external);
- // test 1: there is 1 scripts
- expect($('script')).to.have.lengthOf(1);
+ // test 1: there is 1 scripts
+ expect($('script')).to.have.lengthOf(1);
- // test 2: inside assets
- let entryURL = $('script').attr('src');
- expect(entryURL.includes('assets/')).to.equal(true);
- });
+ // test 2: inside assets
+ let entryURL = $('script').attr('src');
+ expect(entryURL.includes('assets/')).to.equal(true);
+ });
- it('External page using non-hoist scripts that are not modules are built standalone', async () => {
- let external = await fixture.readFile('/external-no-hoist-classic/index.html');
- let $ = cheerio.load(external);
+ it('External page using non-hoist scripts that are not modules are built standalone', async () => {
+ let external = await fixture.readFile('/external-no-hoist-classic/index.html');
+ let $ = cheerio.load(external);
- // test 1: there is 1 scripts
- expect($('script')).to.have.lengthOf(1);
+ // test 1: there is 1 scripts
+ expect($('script')).to.have.lengthOf(1);
- // test 2: inside assets
- let entryURL = $('script').attr('src');
- expect(entryURL.includes('assets/')).to.equal(true);
- });
+ // test 2: inside assets
+ let entryURL = $('script').attr('src');
+ expect(entryURL.includes('assets/')).to.equal(true);
+ });
});
diff --git a/packages/astro/test/astro-sitemap-rss.test.js b/packages/astro/test/astro-sitemap-rss.test.js
index c02b9282b..a39983196 100644
--- a/packages/astro/test/astro-sitemap-rss.test.js
+++ b/packages/astro/test/astro-sitemap-rss.test.js
@@ -2,35 +2,35 @@ import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
describe('Sitemaps', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/astro-sitemap-rss/',
- buildOptions: {
- site: 'https://astro.build/',
- sitemap: true,
- },
- });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/astro-sitemap-rss/',
+ buildOptions: {
+ site: 'https://astro.build/',
+ sitemap: true,
+ },
+ });
+ await fixture.build();
+ });
- describe('RSS Generation', () => {
- it('generates RSS correctly', async () => {
- const rss = await fixture.readFile('/custom/feed.xml');
- expect(rss).to.equal(
- `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title><![CDATA[MF Doomcast]]></title><description><![CDATA[The podcast about the things you find on a picnic, or at a picnic table]]></description><link>https://astro.build/custom/feed.xml</link><language>en-us</language><itunes:author>MF Doom</itunes:author><item><title><![CDATA[Rap Snitch Knishes (feat. Mr. Fantastik)]]></title><link>https://astro.build/episode/rap-snitch-knishes/</link><description><![CDATA[Complex named this song the “22nd funniest rap song of all time.”]]></description><pubDate>Tue, 16 Nov 2004 00:00:00 GMT</pubDate><itunes:episodeType>music</itunes:episodeType><itunes:duration>172</itunes:duration><itunes:explicit>true</itunes:explicit></item><item><title><![CDATA[Fazers]]></title><link>https://astro.build/episode/fazers/</link><description><![CDATA[Rhapsody ranked Take Me to Your Leader 17th on its list “Hip-Hop’s Best Albums of the Decade”]]></description><pubDate>Thu, 03 Jul 2003 00:00:00 GMT</pubDate><itunes:episodeType>music</itunes:episodeType><itunes:duration>197</itunes:duration><itunes:explicit>true</itunes:explicit></item><item><title><![CDATA[Rhymes Like Dimes (feat. Cucumber Slice)]]></title><link>https://astro.build/episode/rhymes-like-dimes/</link><description><![CDATA[Operation: Doomsday has been heralded as an underground classic that established MF Doom's rank within the underground hip-hop scene during the early to mid-2000s.
+ describe('RSS Generation', () => {
+ it('generates RSS correctly', async () => {
+ const rss = await fixture.readFile('/custom/feed.xml');
+ expect(rss).to.equal(
+ `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title><![CDATA[MF Doomcast]]></title><description><![CDATA[The podcast about the things you find on a picnic, or at a picnic table]]></description><link>https://astro.build/custom/feed.xml</link><language>en-us</language><itunes:author>MF Doom</itunes:author><item><title><![CDATA[Rap Snitch Knishes (feat. Mr. Fantastik)]]></title><link>https://astro.build/episode/rap-snitch-knishes/</link><description><![CDATA[Complex named this song the “22nd funniest rap song of all time.”]]></description><pubDate>Tue, 16 Nov 2004 00:00:00 GMT</pubDate><itunes:episodeType>music</itunes:episodeType><itunes:duration>172</itunes:duration><itunes:explicit>true</itunes:explicit></item><item><title><![CDATA[Fazers]]></title><link>https://astro.build/episode/fazers/</link><description><![CDATA[Rhapsody ranked Take Me to Your Leader 17th on its list “Hip-Hop’s Best Albums of the Decade”]]></description><pubDate>Thu, 03 Jul 2003 00:00:00 GMT</pubDate><itunes:episodeType>music</itunes:episodeType><itunes:duration>197</itunes:duration><itunes:explicit>true</itunes:explicit></item><item><title><![CDATA[Rhymes Like Dimes (feat. Cucumber Slice)]]></title><link>https://astro.build/episode/rhymes-like-dimes/</link><description><![CDATA[Operation: Doomsday has been heralded as an underground classic that established MF Doom's rank within the underground hip-hop scene during the early to mid-2000s.
]]></description><pubDate>Tue, 19 Oct 1999 00:00:00 GMT</pubDate><itunes:episodeType>music</itunes:episodeType><itunes:duration>259</itunes:duration><itunes:explicit>true</itunes:explicit></item></channel></rss>`
- );
- });
- });
+ );
+ });
+ });
- describe('Sitemap Generation', () => {
- it('Generates Sitemap correctly', async () => {
- let sitemap = await fixture.readFile('/sitemap.xml');
- expect(sitemap).to.equal(
- `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><url><loc>https://astro.build/episode/fazers/index.html</loc></url><url><loc>https://astro.build/episode/rap-snitch-knishes/index.html</loc></url><url><loc>https://astro.build/episode/rhymes-like-dimes/index.html</loc></url><url><loc>https://astro.build/episodes/index.html</loc></url></urlset>\n`
- );
- });
- });
+ describe('Sitemap Generation', () => {
+ it('Generates Sitemap correctly', async () => {
+ let sitemap = await fixture.readFile('/sitemap.xml');
+ expect(sitemap).to.equal(
+ `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><url><loc>https://astro.build/episode/fazers/index.html</loc></url><url><loc>https://astro.build/episode/rap-snitch-knishes/index.html</loc></url><url><loc>https://astro.build/episode/rhymes-like-dimes/index.html</loc></url><url><loc>https://astro.build/episodes/index.html</loc></url></urlset>\n`
+ );
+ });
+ });
});
diff --git a/packages/astro/test/astro-slots.test.js b/packages/astro/test/astro-slots.test.js
index 1e65ce737..f3b27aac3 100644
--- a/packages/astro/test/astro-slots.test.js
+++ b/packages/astro/test/astro-slots.test.js
@@ -3,113 +3,113 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Slots', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/astro-slots/' });
- await fixture.build();
- });
-
- it('Basic named slots work', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
-
- expect($('#a').text().trim()).to.equal('A');
- expect($('#b').text().trim()).to.equal('B');
- expect($('#c').text().trim()).to.equal('C');
- expect($('#default').text().trim()).to.equal('Default');
- });
-
- it('Dynamic named slots work', async () => {
- const html = await fixture.readFile('/dynamic/index.html');
- const $ = cheerio.load(html);
-
- expect($('#a').text().trim()).to.equal('A');
- expect($('#b').text().trim()).to.equal('B');
- expect($('#c').text().trim()).to.equal('C');
- expect($('#default').text().trim()).to.equal('Default');
- });
-
- it('Slots render fallback content by default', async () => {
- const html = await fixture.readFile('/fallback/index.html');
- const $ = cheerio.load(html);
-
- expect($('#default')).to.have.lengthOf(1);
- });
-
- it('Slots override fallback content', async () => {
- const html = await fixture.readFile('/fallback-override/index.html');
- const $ = cheerio.load(html);
-
- expect($('#override')).to.have.lengthOf(1);
- });
-
- it('Slots work with multiple elements', async () => {
- const html = await fixture.readFile('/multiple/index.html');
- const $ = cheerio.load(html);
-
- expect($('#a').text().trim()).to.equal('ABC');
- });
-
- it('Slots work on Components', async () => {
- const html = await fixture.readFile('/component/index.html');
- const $ = cheerio.load(html);
-
- // test 1: #a renders
- expect($('#a')).to.have.lengthOf(1);
-
- // test 2: Slotted component into #a
- expect($('#a').children('astro-component')).to.have.lengthOf(1);
-
- // test 3: Slotted component into default slot
- expect($('#default').children('astro-component')).to.have.lengthOf(1);
- });
-
- it('Slots API work on Components', async () => {
- // IDs will exist whether the slots are filled or not
- {
- const html = await fixture.readFile('/slottedapi-default/index.html');
- const $ = cheerio.load(html);
-
- expect($('#a')).to.have.lengthOf(1);
- expect($('#b')).to.have.lengthOf(1);
- expect($('#c')).to.have.lengthOf(1);
- expect($('#default')).to.have.lengthOf(1);
- }
-
- // IDs will not exist because the slots are not filled
- {
- const html = await fixture.readFile('/slottedapi-empty/index.html');
- const $ = cheerio.load(html);
-
- expect($('#a')).to.have.lengthOf(0);
- expect($('#b')).to.have.lengthOf(0);
- expect($('#c')).to.have.lengthOf(0);
- expect($('#default')).to.have.lengthOf(0);
- }
-
- // IDs will exist because the slots are filled
- {
- const html = await fixture.readFile('/slottedapi-filled/index.html');
- const $ = cheerio.load(html);
-
- expect($('#a')).to.have.lengthOf(1);
- expect($('#b')).to.have.lengthOf(1);
- expect($('#c')).to.have.lengthOf(1);
-
- expect($('#default')).to.have.lengthOf(0); // the default slot is not filled
- }
-
- // Default ID will exist because the default slot is filled
- {
- const html = await fixture.readFile('/slottedapi-default-filled/index.html');
- const $ = cheerio.load(html);
-
- expect($('#a')).to.have.lengthOf(0);
- expect($('#b')).to.have.lengthOf(0);
- expect($('#c')).to.have.lengthOf(0);
-
- expect($('#default')).to.have.lengthOf(1); // the default slot is filled
- }
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/astro-slots/' });
+ await fixture.build();
+ });
+
+ it('Basic named slots work', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#a').text().trim()).to.equal('A');
+ expect($('#b').text().trim()).to.equal('B');
+ expect($('#c').text().trim()).to.equal('C');
+ expect($('#default').text().trim()).to.equal('Default');
+ });
+
+ it('Dynamic named slots work', async () => {
+ const html = await fixture.readFile('/dynamic/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#a').text().trim()).to.equal('A');
+ expect($('#b').text().trim()).to.equal('B');
+ expect($('#c').text().trim()).to.equal('C');
+ expect($('#default').text().trim()).to.equal('Default');
+ });
+
+ it('Slots render fallback content by default', async () => {
+ const html = await fixture.readFile('/fallback/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#default')).to.have.lengthOf(1);
+ });
+
+ it('Slots override fallback content', async () => {
+ const html = await fixture.readFile('/fallback-override/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#override')).to.have.lengthOf(1);
+ });
+
+ it('Slots work with multiple elements', async () => {
+ const html = await fixture.readFile('/multiple/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#a').text().trim()).to.equal('ABC');
+ });
+
+ it('Slots work on Components', async () => {
+ const html = await fixture.readFile('/component/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: #a renders
+ expect($('#a')).to.have.lengthOf(1);
+
+ // test 2: Slotted component into #a
+ expect($('#a').children('astro-component')).to.have.lengthOf(1);
+
+ // test 3: Slotted component into default slot
+ expect($('#default').children('astro-component')).to.have.lengthOf(1);
+ });
+
+ it('Slots API work on Components', async () => {
+ // IDs will exist whether the slots are filled or not
+ {
+ const html = await fixture.readFile('/slottedapi-default/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#a')).to.have.lengthOf(1);
+ expect($('#b')).to.have.lengthOf(1);
+ expect($('#c')).to.have.lengthOf(1);
+ expect($('#default')).to.have.lengthOf(1);
+ }
+
+ // IDs will not exist because the slots are not filled
+ {
+ const html = await fixture.readFile('/slottedapi-empty/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#a')).to.have.lengthOf(0);
+ expect($('#b')).to.have.lengthOf(0);
+ expect($('#c')).to.have.lengthOf(0);
+ expect($('#default')).to.have.lengthOf(0);
+ }
+
+ // IDs will exist because the slots are filled
+ {
+ const html = await fixture.readFile('/slottedapi-filled/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#a')).to.have.lengthOf(1);
+ expect($('#b')).to.have.lengthOf(1);
+ expect($('#c')).to.have.lengthOf(1);
+
+ expect($('#default')).to.have.lengthOf(0); // the default slot is not filled
+ }
+
+ // Default ID will exist because the default slot is filled
+ {
+ const html = await fixture.readFile('/slottedapi-default-filled/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#a')).to.have.lengthOf(0);
+ expect($('#b')).to.have.lengthOf(0);
+ expect($('#c')).to.have.lengthOf(0);
+
+ expect($('#default')).to.have.lengthOf(1); // the default slot is filled
+ }
+ });
});
diff --git a/packages/astro/test/benchmark/benchmark.js b/packages/astro/test/benchmark/benchmark.js
index 8fb0ebb6c..d8b1c72fc 100644
--- a/packages/astro/test/benchmark/benchmark.js
+++ b/packages/astro/test/benchmark/benchmark.js
@@ -7,65 +7,65 @@ const MUST_BE_AT_LEAST_PERC_OF = 90;
const shouldSave = process.argv.includes('--save');
export class Benchmark {
- constructor(options) {
- this.options = options;
- this.setup = options.setup || Function.prototype;
- }
+ constructor(options) {
+ this.options = options;
+ this.setup = options.setup || Function.prototype;
+ }
- async execute() {
- const { run } = this.options;
- const start = performance.now();
- const end = await run(this.options);
- const time = Math.floor(end - start);
- return time;
- }
+ async execute() {
+ const { run } = this.options;
+ const start = performance.now();
+ const end = await run(this.options);
+ const time = Math.floor(end - start);
+ return time;
+ }
- async run() {
- const { file } = this.options;
+ async run() {
+ const { file } = this.options;
- await this.setup();
- const time = await this.execute();
+ await this.setup();
+ const time = await this.execute();
- if (existsSync(file)) {
- const raw = await fsPromises.readFile(file, 'utf-8');
- const data = JSON.parse(raw);
- if (Math.floor((data.time / time) * 100) > MUST_BE_AT_LEAST_PERC_OF) {
- this.withinPreviousRuns = true;
- } else {
- this.withinPreviousRuns = false;
- }
- }
- this.time = time;
- }
+ if (existsSync(file)) {
+ const raw = await fsPromises.readFile(file, 'utf-8');
+ const data = JSON.parse(raw);
+ if (Math.floor((data.time / time) * 100) > MUST_BE_AT_LEAST_PERC_OF) {
+ this.withinPreviousRuns = true;
+ } else {
+ this.withinPreviousRuns = false;
+ }
+ }
+ this.time = time;
+ }
- report() {
- const { name } = this.options;
- console.log(name, 'took', this.time, 'ms');
- }
+ report() {
+ const { name } = this.options;
+ console.log(name, 'took', this.time, 'ms');
+ }
- check() {
- assert.ok(this.withinPreviousRuns !== false, `${this.options.name} ran too slowly`);
- }
+ check() {
+ assert.ok(this.withinPreviousRuns !== false, `${this.options.name} ran too slowly`);
+ }
- async save() {
- const { file, name } = this.options;
- const data = JSON.stringify(
- {
- name,
- time: this.time,
- },
- null,
- ' '
- );
- await fsPromises.writeFile(file, data, 'utf-8');
- }
+ async save() {
+ const { file, name } = this.options;
+ const data = JSON.stringify(
+ {
+ name,
+ time: this.time,
+ },
+ null,
+ ' '
+ );
+ await fsPromises.writeFile(file, data, 'utf-8');
+ }
- async test() {
- await this.run();
- if (shouldSave) {
- await this.save();
- }
- this.report();
- this.check();
- }
+ async test() {
+ await this.run();
+ if (shouldSave) {
+ await this.save();
+ }
+ this.report();
+ this.check();
+ }
}
diff --git a/packages/astro/test/benchmark/build.bench.js b/packages/astro/test/benchmark/build.bench.js
index fbcbd1199..5a0f2111c 100644
--- a/packages/astro/test/benchmark/build.bench.js
+++ b/packages/astro/test/benchmark/build.bench.js
@@ -10,57 +10,57 @@ import { format as utilFormat } from 'util';
const snowpackExampleRoot = new URL('../../../../docs/', import.meta.url);
export const errorWritable = new Writable({
- objectMode: true,
- write(event, _, callback) {
- let dest = process.stderr;
- dest.write(utilFormat(...event.args));
- dest.write('\n');
+ objectMode: true,
+ write(event, _, callback) {
+ let dest = process.stderr;
+ dest.write(utilFormat(...event.args));
+ dest.write('\n');
- callback();
- },
+ callback();
+ },
});
let build;
async function setupBuild() {
- const astroConfig = await loadConfig(fileURLToPath(snowpackExampleRoot));
+ const astroConfig = await loadConfig(fileURLToPath(snowpackExampleRoot));
- const logging = {
- level: 'error',
- dest: errorWritable,
- };
+ const logging = {
+ level: 'error',
+ dest: errorWritable,
+ };
- build = () => astroBuild(astroConfig, logging);
+ build = () => astroBuild(astroConfig, logging);
}
async function runBuild() {
- await build();
- return performance.now();
+ await build();
+ return performance.now();
}
const benchmarks = [
- new Benchmark({
- name: 'Snowpack Example Build Uncached',
- root: snowpackExampleRoot,
- file: new URL('./build-uncached.json', import.meta.url),
- async setup() {
- process.chdir(new URL('../../../../', import.meta.url).pathname);
- const spcache = new URL('../../node_modules/.cache/', import.meta.url);
- await Promise.all([del(spcache.pathname, { force: true }), setupBuild()]);
- },
- run: runBuild,
- }),
- new Benchmark({
- name: 'Snowpack Example Build Cached',
- root: snowpackExampleRoot,
- file: new URL('./build-cached.json', import.meta.url),
- async setup() {
- process.chdir(new URL('../../../../', import.meta.url).pathname);
- await setupBuild();
- await this.execute();
- },
- run: runBuild,
- }),
- /*new Benchmark({
+ new Benchmark({
+ name: 'Snowpack Example Build Uncached',
+ root: snowpackExampleRoot,
+ file: new URL('./build-uncached.json', import.meta.url),
+ async setup() {
+ process.chdir(new URL('../../../../', import.meta.url).pathname);
+ const spcache = new URL('../../node_modules/.cache/', import.meta.url);
+ await Promise.all([del(spcache.pathname, { force: true }), setupBuild()]);
+ },
+ run: runBuild,
+ }),
+ new Benchmark({
+ name: 'Snowpack Example Build Cached',
+ root: snowpackExampleRoot,
+ file: new URL('./build-cached.json', import.meta.url),
+ async setup() {
+ process.chdir(new URL('../../../../', import.meta.url).pathname);
+ await setupBuild();
+ await this.execute();
+ },
+ run: runBuild,
+ }),
+ /*new Benchmark({
name: 'Snowpack Example Dev Server Cached',
root: snowpackExampleRoot,
file: new URL('./dev-server-cached.json', import.meta.url),
@@ -72,12 +72,12 @@ const benchmarks = [
];
async function run() {
- for (const b of benchmarks) {
- await b.test();
- }
+ for (const b of benchmarks) {
+ await b.test();
+ }
}
run().catch((err) => {
- console.error(err);
- process.exit(1);
+ console.error(err);
+ process.exit(1);
});
diff --git a/packages/astro/test/benchmark/dev.bench.js b/packages/astro/test/benchmark/dev.bench.js
index 1ade92c9a..97710d713 100644
--- a/packages/astro/test/benchmark/dev.bench.js
+++ b/packages/astro/test/benchmark/dev.bench.js
@@ -6,56 +6,56 @@ import del from 'del';
const docsExampleRoot = new URL('../../../../docs/', import.meta.url);
async function runToStarted(root) {
- const args = [];
- const process = runDevServer(root, args);
+ const args = [];
+ const process = runDevServer(root, args);
- let started = null;
- process.stdout.setEncoding('utf8');
- for await (const chunk of process.stdout) {
- if (/Server started/.test(chunk)) {
- started = performance.now();
- break;
- }
- }
+ let started = null;
+ process.stdout.setEncoding('utf8');
+ for await (const chunk of process.stdout) {
+ if (/Server started/.test(chunk)) {
+ started = performance.now();
+ break;
+ }
+ }
- process.kill();
- return started;
+ process.kill();
+ return started;
}
const benchmarks = [
- new Benchmark({
- name: 'Docs Site Example Dev Server Uncached',
- root: docsExampleRoot,
- file: new URL('./dev-server-uncached.json', import.meta.url),
- async setup() {
- const spcache = new URL('../../node_modules/.cache/', import.meta.url);
- await del(spcache.pathname);
- },
- run({ root }) {
- return runToStarted(root);
- },
- }),
- new Benchmark({
- name: 'Docs Site Example Dev Server Cached',
- root: docsExampleRoot,
- file: new URL('./dev-server-cached.json', import.meta.url),
- async setup() {
- // Execute once to make sure Docs Site is cached.
- await this.execute();
- },
- run({ root }) {
- return runToStarted(root);
- },
- }),
+ new Benchmark({
+ name: 'Docs Site Example Dev Server Uncached',
+ root: docsExampleRoot,
+ file: new URL('./dev-server-uncached.json', import.meta.url),
+ async setup() {
+ const spcache = new URL('../../node_modules/.cache/', import.meta.url);
+ await del(spcache.pathname);
+ },
+ run({ root }) {
+ return runToStarted(root);
+ },
+ }),
+ new Benchmark({
+ name: 'Docs Site Example Dev Server Cached',
+ root: docsExampleRoot,
+ file: new URL('./dev-server-cached.json', import.meta.url),
+ async setup() {
+ // Execute once to make sure Docs Site is cached.
+ await this.execute();
+ },
+ run({ root }) {
+ return runToStarted(root);
+ },
+ }),
];
async function run() {
- for (const b of benchmarks) {
- await b.test();
- }
+ for (const b of benchmarks) {
+ await b.test();
+ }
}
run().catch((err) => {
- console.error(err);
- process.exit(1);
+ console.error(err);
+ process.exit(1);
});
diff --git a/packages/astro/test/builtins.test.js b/packages/astro/test/builtins.test.js
index 8f26a450a..9d4cd0e42 100644
--- a/packages/astro/test/builtins.test.js
+++ b/packages/astro/test/builtins.test.js
@@ -5,23 +5,23 @@ import { loadFixture } from './test-utils.js';
let fixture;
before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/builtins/' });
- await fixture.build();
+ fixture = await loadFixture({ projectRoot: './fixtures/builtins/' });
+ await fixture.build();
});
describe('Node builtins', () => {
- it('Can be used with the node: prefix', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Can be used with the node: prefix', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- expect($('#version').text()).to.equal('1.2.0');
- expect($('#dep-version').text()).to.equal('0.0.1');
- });
+ expect($('#version').text()).to.equal('1.2.0');
+ expect($('#dep-version').text()).to.equal('0.0.1');
+ });
- it('Can also be used with the non-prefixed version', async () => {
- const html = await fixture.readFile('/bare/index.html');
- const $ = cheerio.load(html);
+ it('Can also be used with the non-prefixed version', async () => {
+ const html = await fixture.readFile('/bare/index.html');
+ const $ = cheerio.load(html);
- expect($('h1').text()).to.equal('true');
- });
+ expect($('h1').text()).to.equal('true');
+ });
});
diff --git a/packages/astro/test/cli.test.js b/packages/astro/test/cli.test.js
index 9b758ff02..c04cc6c2f 100644
--- a/packages/astro/test/cli.test.js
+++ b/packages/astro/test/cli.test.js
@@ -4,44 +4,44 @@ import { promises as fs } from 'fs';
import { fileURLToPath } from 'url';
describe('astro cli', () => {
- it('astro', async () => {
- const proc = await cli();
+ it('astro', async () => {
+ const proc = await cli();
- expect(proc.stdout).to.include('astro - Futuristic web development tool');
- });
+ expect(proc.stdout).to.include('astro - Futuristic web development tool');
+ });
- it('astro --version', async () => {
- const pkgURL = new URL('../package.json', import.meta.url);
- const pkgVersion = await fs.readFile(pkgURL, 'utf8').then((data) => JSON.parse(data).version);
+ it('astro --version', async () => {
+ const pkgURL = new URL('../package.json', import.meta.url);
+ const pkgVersion = await fs.readFile(pkgURL, 'utf8').then((data) => JSON.parse(data).version);
- const proc = await cli('--version');
+ const proc = await cli('--version');
- expect(proc.stdout).to.equal(pkgVersion);
- });
+ expect(proc.stdout).to.equal(pkgVersion);
+ });
- it('astro dev', async () => {
- const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
+ it('astro dev', async () => {
+ const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
- const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL));
+ const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL));
- let stdout = '';
+ let stdout = '';
- for await (const chunk of proc.stdout) {
- stdout += chunk;
+ for await (const chunk of proc.stdout) {
+ stdout += chunk;
- if (chunk.includes('Local:')) break;
- }
+ if (chunk.includes('Local:')) break;
+ }
- proc.kill();
+ proc.kill();
- expect(stdout).to.include('Server started');
- });
+ expect(stdout).to.include('Server started');
+ });
- it('astro build', async () => {
- const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
+ it('astro build', async () => {
+ const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
- const proc = await cli('build', '--project-root', fileURLToPath(projectRootURL));
+ const proc = await cli('build', '--project-root', fileURLToPath(projectRootURL));
- expect(proc.stdout).to.include('Done');
- });
+ expect(proc.stdout).to.include('Done');
+ });
});
diff --git a/packages/astro/test/config-validate.test.js b/packages/astro/test/config-validate.test.js
index d8e58f781..0b1bbcd43 100644
--- a/packages/astro/test/config-validate.test.js
+++ b/packages/astro/test/config-validate.test.js
@@ -4,45 +4,45 @@ import stripAnsi from 'strip-ansi';
import { formatConfigError, validateConfig } from '../dist/core/config.js';
describe('Config Validation', () => {
- it('empty user config is valid', async () => {
- expect(() => validateConfig({}, process.cwd()).catch((err) => err)).not.to.throw();
- });
+ it('empty user config is valid', async () => {
+ expect(() => validateConfig({}, process.cwd()).catch((err) => err)).not.to.throw();
+ });
- it('Zod errors are returned when invalid config is used', async () => {
- const configError = await validateConfig({ buildOptions: { sitemap: 42 } }, process.cwd()).catch((err) => err);
- expect(configError instanceof z.ZodError).to.equal(true);
- });
+ it('Zod errors are returned when invalid config is used', async () => {
+ const configError = await validateConfig({ buildOptions: { sitemap: 42 } }, process.cwd()).catch((err) => err);
+ expect(configError instanceof z.ZodError).to.equal(true);
+ });
- it('errors when an older markdownOptions format is used', async () => {
- const configError = await validateConfig({ markdownOptions: { rehypePlugins: ['rehype-autolink-headings'] } }, process.cwd()).catch((err) => err);
- expect(configError instanceof z.ZodError).to.equal(true);
- expect(configError.issues[0].message).to.equal("Unrecognized key(s) in object: 'rehypePlugins'");
- });
+ it('errors when an older markdownOptions format is used', async () => {
+ const configError = await validateConfig({ markdownOptions: { rehypePlugins: ['rehype-autolink-headings'] } }, process.cwd()).catch((err) => err);
+ expect(configError instanceof z.ZodError).to.equal(true);
+ expect(configError.issues[0].message).to.equal("Unrecognized key(s) in object: 'rehypePlugins'");
+ });
- it('A validation error can be formatted correctly', async () => {
- const configError = await validateConfig({ buildOptions: { sitemap: 42 } }, process.cwd()).catch((err) => err);
- expect(configError instanceof z.ZodError).to.equal(true);
- const formattedError = stripAnsi(formatConfigError(configError));
- expect(formattedError).to.equal(
- `[config] Astro found issue(s) with your configuration:
+ it('A validation error can be formatted correctly', async () => {
+ const configError = await validateConfig({ buildOptions: { sitemap: 42 } }, process.cwd()).catch((err) => err);
+ expect(configError instanceof z.ZodError).to.equal(true);
+ const formattedError = stripAnsi(formatConfigError(configError));
+ expect(formattedError).to.equal(
+ `[config] Astro found issue(s) with your configuration:
! buildOptions.sitemap Expected boolean, received number.`
- );
- });
+ );
+ });
- it('Multiple validation errors can be formatted correctly', async () => {
- const veryBadConfig = {
- renderers: [42],
- buildOptions: { pageUrlFormat: 'invalid' },
- pages: {},
- };
- const configError = await validateConfig(veryBadConfig, process.cwd()).catch((err) => err);
- expect(configError instanceof z.ZodError).to.equal(true);
- const formattedError = stripAnsi(formatConfigError(configError));
- expect(formattedError).to.equal(
- `[config] Astro found issue(s) with your configuration:
+ it('Multiple validation errors can be formatted correctly', async () => {
+ const veryBadConfig = {
+ renderers: [42],
+ buildOptions: { pageUrlFormat: 'invalid' },
+ pages: {},
+ };
+ const configError = await validateConfig(veryBadConfig, process.cwd()).catch((err) => err);
+ expect(configError instanceof z.ZodError).to.equal(true);
+ const formattedError = stripAnsi(formatConfigError(configError));
+ expect(formattedError).to.equal(
+ `[config] Astro found issue(s) with your configuration:
! pages Expected string, received object.
! renderers.0 Expected string, received number.
! buildOptions.pageUrlFormat Invalid input.`
- );
- });
+ );
+ });
});
diff --git a/packages/astro/test/config.test.js b/packages/astro/test/config.test.js
index 66e805add..aff6055cb 100644
--- a/packages/astro/test/config.test.js
+++ b/packages/astro/test/config.test.js
@@ -3,59 +3,59 @@ import { cli, loadFixture } from './test-utils.js';
import { fileURLToPath } from 'url';
describe('config', () => {
- let hostnameFixture;
- let portFixture;
+ let hostnameFixture;
+ let portFixture;
- before(async () => {
- [hostnameFixture, portFixture] = await Promise.all([loadFixture({ projectRoot: './fixtures/config-hostname/' }), loadFixture({ projectRoot: './fixtures/config-port/' })]);
- });
+ before(async () => {
+ [hostnameFixture, portFixture] = await Promise.all([loadFixture({ projectRoot: './fixtures/config-hostname/' }), loadFixture({ projectRoot: './fixtures/config-port/' })]);
+ });
- describe('hostname', () => {
- it('can be specified in astro.config.mjs', async () => {
- expect(hostnameFixture.config.devOptions.hostname).to.equal('0.0.0.0');
- });
+ describe('hostname', () => {
+ it('can be specified in astro.config.mjs', async () => {
+ expect(hostnameFixture.config.devOptions.hostname).to.equal('0.0.0.0');
+ });
- it('can be specified via --hostname flag', async () => {
- const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
- const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL), '--hostname', '127.0.0.1');
+ it('can be specified via --hostname flag', async () => {
+ const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
+ const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL), '--hostname', '127.0.0.1');
- let stdout = '';
+ let stdout = '';
- for await (const chunk of proc.stdout) {
- stdout += chunk;
+ for await (const chunk of proc.stdout) {
+ stdout += chunk;
- if (chunk.includes('Local:')) break;
- }
+ if (chunk.includes('Local:')) break;
+ }
- proc.kill();
+ proc.kill();
- expect(stdout).to.include('127.0.0.1');
- });
- });
+ expect(stdout).to.include('127.0.0.1');
+ });
+ });
- describe('path', () => {
- it('can be passed via --config', async () => {
- const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
- const configFileURL = new URL('./fixtures/config-path/config/my-config.mjs', import.meta.url);
- const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL), '--config', configFileURL.pathname);
+ describe('path', () => {
+ it('can be passed via --config', async () => {
+ const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
+ const configFileURL = new URL('./fixtures/config-path/config/my-config.mjs', import.meta.url);
+ const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL), '--config', configFileURL.pathname);
- let stdout = '';
+ let stdout = '';
- for await (const chunk of proc.stdout) {
- stdout += chunk;
+ for await (const chunk of proc.stdout) {
+ stdout += chunk;
- if (chunk.includes('Local:')) break;
- }
+ if (chunk.includes('Local:')) break;
+ }
- proc.kill();
+ proc.kill();
- expect(stdout).to.include('127.0.0.1');
- });
- });
+ expect(stdout).to.include('127.0.0.1');
+ });
+ });
- describe('port', () => {
- it('can be specified in astro.config.mjs', async () => {
- expect(portFixture.config.devOptions.port).to.deep.equal(5006);
- });
- });
+ describe('port', () => {
+ it('can be specified in astro.config.mjs', async () => {
+ expect(portFixture.config.devOptions.port).to.deep.equal(5006);
+ });
+ });
});
diff --git a/packages/astro/test/custom-elements.test.js b/packages/astro/test/custom-elements.test.js
index 177bc23f6..d0d7b5fb9 100644
--- a/packages/astro/test/custom-elements.test.js
+++ b/packages/astro/test/custom-elements.test.js
@@ -3,74 +3,74 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Custom Elements', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/custom-elements/',
- renderers: ['@astrojs/test-custom-element-renderer'],
- });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({
+ projectRoot: './fixtures/custom-elements/',
+ renderers: ['@astrojs/test-custom-element-renderer'],
+ });
+ await fixture.build();
+ });
- it('Work as constructors', async () => {
- const html = await fixture.readFile('/ctr/index.html');
- const $ = cheerio.load(html);
+ it('Work as constructors', async () => {
+ const html = await fixture.readFile('/ctr/index.html');
+ const $ = cheerio.load(html);
- // test 1: Element rendered
- expect($('my-element')).to.have.lengthOf(1);
+ // test 1: Element rendered
+ expect($('my-element')).to.have.lengthOf(1);
- // test 2: shadow rendererd
- expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1);
- });
+ // test 2: shadow rendererd
+ expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1);
+ });
- it('Works with exported tagName', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Works with exported tagName', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- // test 1: Element rendered
- expect($('my-element')).to.have.lengthOf(1);
+ // test 1: Element rendered
+ expect($('my-element')).to.have.lengthOf(1);
- // test 2: shadow rendered
- expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1);
- });
+ // test 2: shadow rendered
+ expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1);
+ });
- it('Hydration works with exported tagName', async () => {
- const html = await fixture.readFile('/load/index.html');
- const $ = cheerio.load(html);
+ it('Hydration works with exported tagName', async () => {
+ const html = await fixture.readFile('/load/index.html');
+ const $ = cheerio.load(html);
- // SSR
- // test 1: Element rendered
- expect($('my-element')).to.have.lengthOf(1);
+ // SSR
+ // test 1: Element rendered
+ expect($('my-element')).to.have.lengthOf(1);
- // test 2: shadow rendered
- expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1);
+ // test 2: shadow rendered
+ expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1);
- // Hydration
- // test 3: Component and polyfill scripts bundled together
- expect($('script[type=module]')).to.have.lengthOf(1);
- });
+ // Hydration
+ // test 3: Component and polyfill scripts bundled together
+ expect($('script[type=module]')).to.have.lengthOf(1);
+ });
- it('Polyfills are added even if not hydrating', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Polyfills are added even if not hydrating', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- expect($('script[type=module]')).to.have.lengthOf(1);
- });
+ expect($('script[type=module]')).to.have.lengthOf(1);
+ });
- it('Custom elements not claimed by renderer are rendered as regular HTML', async () => {
- const html = await fixture.readFile('/nossr/index.html');
- const $ = cheerio.load(html);
+ it('Custom elements not claimed by renderer are rendered as regular HTML', async () => {
+ const html = await fixture.readFile('/nossr/index.html');
+ const $ = cheerio.load(html);
- // test 1: Rendered the client-only element
- expect($('client-element')).to.have.lengthOf(1);
- });
+ // test 1: Rendered the client-only element
+ expect($('client-element')).to.have.lengthOf(1);
+ });
- it('Can import a client-only element that is nested in JSX', async () => {
- const html = await fixture.readFile('/nested/index.html');
- const $ = cheerio.load(html);
+ it('Can import a client-only element that is nested in JSX', async () => {
+ const html = await fixture.readFile('/nested/index.html');
+ const $ = cheerio.load(html);
- // test 1: Element rendered
- expect($('client-only-element')).to.have.lengthOf(1);
- });
+ // test 1: Element rendered
+ expect($('client-only-element')).to.have.lengthOf(1);
+ });
});
diff --git a/packages/astro/test/debug-component.test.js b/packages/astro/test/debug-component.test.js
index d4c230303..b590e25bd 100644
--- a/packages/astro/test/debug-component.test.js
+++ b/packages/astro/test/debug-component.test.js
@@ -6,24 +6,24 @@ import os from 'os';
const isMacOS = os.platform() === 'darwin';
describe('<Debug />', () => {
- if (isMacOS) return;
+ if (isMacOS) return;
- /** @type {import('./test-utils').Fixture} */
- let fixture;
- /** @type {import('./test-utils').DevServer} */
- let devServer;
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+ /** @type {import('./test-utils').DevServer} */
+ let devServer;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/debug-component/' });
- devServer = await fixture.startDevServer();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/debug-component/' });
+ devServer = await fixture.startDevServer();
+ });
- after(async () => {
- devServer && (await devServer.stop());
- });
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
- it('Works in markdown pages', async () => {
- const response = await fixture.fetch('/posts/first');
- expect(response.status).to.equal(200);
- });
+ it('Works in markdown pages', async () => {
+ const response = await fixture.fetch('/posts/first');
+ expect(response.status).to.equal(200);
+ });
});
diff --git a/packages/astro/test/dev-routing.test.js b/packages/astro/test/dev-routing.test.js
index 129942c83..2ed506335 100644
--- a/packages/astro/test/dev-routing.test.js
+++ b/packages/astro/test/dev-routing.test.js
@@ -3,132 +3,132 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Development Routing', () => {
- describe('No site config', () => {
- /** @type {import('./test-utils').Fixture} */
- let fixture;
- /** @type {import('./test-utils').DevServer} */
- let devServer;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/without-site-config/' });
- devServer = await fixture.startDevServer();
- });
-
- after(async () => {
- devServer && (await devServer.stop());
- });
-
- it('200 when loading /', async () => {
- const response = await fixture.fetch('/');
- expect(response.status).to.equal(200);
- });
-
- it('200 when adding search params', async () => {
- const response = await fixture.fetch('/?foo=bar');
- expect(response.status).to.equal(200);
- });
-
- it('200 when loading non-root page', async () => {
- const response = await fixture.fetch('/another');
- expect(response.status).to.equal(200);
- });
- });
-
- describe('No subpath used', () => {
- /** @type {import('./test-utils').Fixture} */
- let fixture;
- /** @type {import('./test-utils').DevServer} */
- let devServer;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/without-subpath/' });
- devServer = await fixture.startDevServer();
- });
-
- after(async () => {
- devServer && (await devServer.stop());
- });
-
- it('200 when loading /', async () => {
- const response = await fixture.fetch('/');
- expect(response.status).to.equal(200);
- });
-
- it('200 when loading non-root page', async () => {
- const response = await fixture.fetch('/another');
- expect(response.status).to.equal(200);
- });
- });
-
- describe('Subpath with trailing slash', () => {
- /** @type {import('./test-utils').Fixture} */
- let fixture;
- /** @type {import('./test-utils').DevServer} */
- let devServer;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/with-subpath-trailing-slash/' });
- devServer = await fixture.startDevServer();
- });
-
- after(async () => {
- devServer && (await devServer.stop());
- });
-
- it('404 when loading /', async () => {
- const response = await fixture.fetch('/');
- expect(response.status).to.equal(404);
- });
-
- it('200 when loading subpath root', async () => {
- const response = await fixture.fetch('/blog/');
- expect(response.status).to.equal(200);
- });
-
- it('404 when loading subpath root without trailing slash', async () => {
- const response = await fixture.fetch('/blog');
- expect(response.status).to.equal(404);
- });
-
- it('200 when loading another page with subpath used', async () => {
- const response = await fixture.fetch('/blog/another/');
- expect(response.status).to.equal(200);
- });
- });
-
- describe('Subpath without trailing slash', () => {
- /** @type {import('./test-utils').Fixture} */
- let fixture;
- /** @type {import('./test-utils').DevServer} */
- let devServer;
-
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/with-subpath-no-trailing-slash/' });
- devServer = await fixture.startDevServer();
- });
-
- after(async () => {
- devServer && (await devServer.stop());
- });
-
- it('404 when loading /', async () => {
- const response = await fixture.fetch('/');
- expect(response.status).to.equal(404);
- });
-
- it('200 when loading subpath root with trailing slash', async () => {
- const response = await fixture.fetch('/blog/');
- expect(response.status).to.equal(200);
- });
-
- it('200 when loading subpath root without trailing slash', async () => {
- const response = await fixture.fetch('/blog');
- expect(response.status).to.equal(200);
- });
-
- it('200 when loading another page with subpath used', async () => {
- const response = await fixture.fetch('/blog/another/');
- expect(response.status).to.equal(200);
- });
- });
+ describe('No site config', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+ /** @type {import('./test-utils').DevServer} */
+ let devServer;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/without-site-config/' });
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
+
+ it('200 when loading /', async () => {
+ const response = await fixture.fetch('/');
+ expect(response.status).to.equal(200);
+ });
+
+ it('200 when adding search params', async () => {
+ const response = await fixture.fetch('/?foo=bar');
+ expect(response.status).to.equal(200);
+ });
+
+ it('200 when loading non-root page', async () => {
+ const response = await fixture.fetch('/another');
+ expect(response.status).to.equal(200);
+ });
+ });
+
+ describe('No subpath used', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+ /** @type {import('./test-utils').DevServer} */
+ let devServer;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/without-subpath/' });
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
+
+ it('200 when loading /', async () => {
+ const response = await fixture.fetch('/');
+ expect(response.status).to.equal(200);
+ });
+
+ it('200 when loading non-root page', async () => {
+ const response = await fixture.fetch('/another');
+ expect(response.status).to.equal(200);
+ });
+ });
+
+ describe('Subpath with trailing slash', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+ /** @type {import('./test-utils').DevServer} */
+ let devServer;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/with-subpath-trailing-slash/' });
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
+
+ it('404 when loading /', async () => {
+ const response = await fixture.fetch('/');
+ expect(response.status).to.equal(404);
+ });
+
+ it('200 when loading subpath root', async () => {
+ const response = await fixture.fetch('/blog/');
+ expect(response.status).to.equal(200);
+ });
+
+ it('404 when loading subpath root without trailing slash', async () => {
+ const response = await fixture.fetch('/blog');
+ expect(response.status).to.equal(404);
+ });
+
+ it('200 when loading another page with subpath used', async () => {
+ const response = await fixture.fetch('/blog/another/');
+ expect(response.status).to.equal(200);
+ });
+ });
+
+ describe('Subpath without trailing slash', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+ /** @type {import('./test-utils').DevServer} */
+ let devServer;
+
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/with-subpath-no-trailing-slash/' });
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
+
+ it('404 when loading /', async () => {
+ const response = await fixture.fetch('/');
+ expect(response.status).to.equal(404);
+ });
+
+ it('200 when loading subpath root with trailing slash', async () => {
+ const response = await fixture.fetch('/blog/');
+ expect(response.status).to.equal(200);
+ });
+
+ it('200 when loading subpath root without trailing slash', async () => {
+ const response = await fixture.fetch('/blog');
+ expect(response.status).to.equal(200);
+ });
+
+ it('200 when loading another page with subpath used', async () => {
+ const response = await fixture.fetch('/blog/another/');
+ expect(response.status).to.equal(200);
+ });
+ });
});
diff --git a/packages/astro/test/errors.test.js b/packages/astro/test/errors.test.js
index 4d29361a4..32848851c 100644
--- a/packages/astro/test/errors.test.js
+++ b/packages/astro/test/errors.test.js
@@ -5,202 +5,202 @@ let fixture;
let devServer;
before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/errors',
- renderers: ['@astrojs/renderer-preact', '@astrojs/renderer-react', '@astrojs/renderer-solid', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'],
- vite: {
- optimizeDeps: false, // necessary to prevent Vite throwing on bad files
- },
- });
- devServer = await fixture.startDevServer();
+ fixture = await loadFixture({
+ projectRoot: './fixtures/errors',
+ renderers: ['@astrojs/renderer-preact', '@astrojs/renderer-react', '@astrojs/renderer-solid', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'],
+ vite: {
+ optimizeDeps: false, // necessary to prevent Vite throwing on bad files
+ },
+ });
+ devServer = await fixture.startDevServer();
});
describe('Error display', () => {
- describe('Astro', () => {
- it('syntax error in template', async () => {
- const res = await fixture.fetch('/astro-syntax-error');
+ describe('Astro', () => {
+ it('syntax error in template', async () => {
+ const res = await fixture.fetch('/astro-syntax-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Unexpected &quot;}&quot;');
- });
+ expect(body).to.include('Unexpected &quot;}&quot;');
+ });
- it('syntax error in frontmatter', async () => {
- const res = await fixture.fetch('/astro-frontmatter-syntax-error');
+ it('syntax error in frontmatter', async () => {
+ const res = await fixture.fetch('/astro-frontmatter-syntax-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Unexpected end of frontmatter');
- });
+ expect(body).to.include('Unexpected end of frontmatter');
+ });
- it('runtime error', async () => {
- const res = await fixture.fetch('/astro-runtime-error');
+ it('runtime error', async () => {
+ const res = await fixture.fetch('/astro-runtime-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('ReferenceError: title is not defined');
+ expect(body).to.include('ReferenceError: title is not defined');
- // TODO: improve and test stacktrace
- });
+ // TODO: improve and test stacktrace
+ });
- it('hydration error', async () => {
- const res = await fixture.fetch('/astro-hydration-error');
+ it('hydration error', async () => {
+ const res = await fixture.fetch('/astro-hydration-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Error: invalid hydration directive');
- });
+ expect(body).to.include('Error: invalid hydration directive');
+ });
- it('client:media error', async () => {
- const res = await fixture.fetch('/astro-client-media-error');
+ it('client:media error', async () => {
+ const res = await fixture.fetch('/astro-client-media-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Error: Media query must be provided');
- });
- });
+ expect(body).to.include('Error: Media query must be provided');
+ });
+ });
- describe('JS', () => {
- it('syntax error', async () => {
- const res = await fixture.fetch('/js-syntax-error');
+ describe('JS', () => {
+ it('syntax error', async () => {
+ const res = await fixture.fetch('/js-syntax-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Parse failure');
- });
+ expect(body).to.include('Parse failure');
+ });
- it('runtime error', async () => {
- const res = await fixture.fetch('/js-runtime-error');
+ it('runtime error', async () => {
+ const res = await fixture.fetch('/js-runtime-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('ReferenceError: undefinedvar is not defined');
- });
- });
+ expect(body).to.include('ReferenceError: undefinedvar is not defined');
+ });
+ });
- describe('Preact', () => {
- it('syntax error', async () => {
- const res = await fixture.fetch('/preact-syntax-error');
+ describe('Preact', () => {
+ it('syntax error', async () => {
+ const res = await fixture.fetch('/preact-syntax-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Syntax error');
- });
+ expect(body).to.include('Syntax error');
+ });
- it('runtime error', async () => {
- const res = await fixture.fetch('/preact-runtime-error');
+ it('runtime error', async () => {
+ const res = await fixture.fetch('/preact-runtime-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Error: PreactRuntimeError');
- });
- });
+ expect(body).to.include('Error: PreactRuntimeError');
+ });
+ });
- describe('React', () => {
- it('syntax error', async () => {
- const res = await fixture.fetch('/react-syntax-error');
+ describe('React', () => {
+ it('syntax error', async () => {
+ const res = await fixture.fetch('/react-syntax-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Syntax error');
- });
+ expect(body).to.include('Syntax error');
+ });
- it('runtime error', async () => {
- const res = await fixture.fetch('/react-runtime-error');
+ it('runtime error', async () => {
+ const res = await fixture.fetch('/react-runtime-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Error: ReactRuntimeError');
- });
- });
+ expect(body).to.include('Error: ReactRuntimeError');
+ });
+ });
- describe('Solid', () => {
- it('syntax error', async () => {
- const res = await fixture.fetch('/solid-syntax-error');
+ describe('Solid', () => {
+ it('syntax error', async () => {
+ const res = await fixture.fetch('/solid-syntax-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Syntax error');
- });
+ expect(body).to.include('Syntax error');
+ });
- it('runtime error', async () => {
- const res = await fixture.fetch('/solid-runtime-error');
+ it('runtime error', async () => {
+ const res = await fixture.fetch('/solid-runtime-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Error: SolidRuntimeError');
- });
- });
+ expect(body).to.include('Error: SolidRuntimeError');
+ });
+ });
- describe('Svelte', () => {
- it('syntax error', async () => {
- const res = await fixture.fetch('/svelte-syntax-error');
+ describe('Svelte', () => {
+ it('syntax error', async () => {
+ const res = await fixture.fetch('/svelte-syntax-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Internal Error');
- });
+ expect(body).to.include('Internal Error');
+ });
- it('runtime error', async () => {
- const res = await fixture.fetch('/svelte-runtime-error');
+ it('runtime error', async () => {
+ const res = await fixture.fetch('/svelte-runtime-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.include('Error: SvelteRuntimeError');
- });
- });
+ expect(body).to.include('Error: SvelteRuntimeError');
+ });
+ });
- describe('Vue', () => {
- it('syntax error', async () => {
- const res = await fixture.fetch('/vue-syntax-error');
- const body = await res.text();
+ describe('Vue', () => {
+ it('syntax error', async () => {
+ const res = await fixture.fetch('/vue-syntax-error');
+ const body = await res.text();
- expect(res.status).to.equal(500);
- expect(body).to.include('Parse failure');
- });
+ expect(res.status).to.equal(500);
+ expect(body).to.include('Parse failure');
+ });
- it('runtime error', async () => {
- const res = await fixture.fetch('/vue-runtime-error');
+ it('runtime error', async () => {
+ const res = await fixture.fetch('/vue-runtime-error');
- expect(res.status).to.equal(500);
+ expect(res.status).to.equal(500);
- const body = await res.text();
+ const body = await res.text();
- expect(body).to.match(/Cannot read.*undefined/); // note: error differs slightly between Node versions
- });
- });
+ expect(body).to.match(/Cannot read.*undefined/); // note: error differs slightly between Node versions
+ });
+ });
});
after(async () => {
- await devServer.stop();
+ await devServer.stop();
});
diff --git a/packages/astro/test/fetch.test.js b/packages/astro/test/fetch.test.js
index ad55749d0..ba76ce6a6 100644
--- a/packages/astro/test/fetch.test.js
+++ b/packages/astro/test/fetch.test.js
@@ -3,34 +3,34 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Global Fetch', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/fetch/' });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/fetch/' });
+ await fixture.build();
+ });
- it('Is available in Astro pages', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- expect($('#astro-page').text()).to.equal('function', 'Fetch supported in .astro page');
- });
- it('Is available in Astro components', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- expect($('#astro-component').text()).to.equal('function', 'Fetch supported in .astro components');
- });
- it('Is available in non-Astro components', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- expect($('#jsx').text()).to.equal('function', 'Fetch supported in .jsx');
- expect($('#svelte').text()).to.equal('function', 'Fetch supported in .svelte');
- expect($('#vue').text()).to.equal('function', 'Fetch supported in .vue');
- });
- it('Respects existing code', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- expect($('#already-imported').text()).to.equal('function', 'Existing fetch imports respected');
- expect($('#custom-declaration').text()).to.equal('number', 'Custom fetch declarations respected');
- });
+ it('Is available in Astro pages', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ expect($('#astro-page').text()).to.equal('function', 'Fetch supported in .astro page');
+ });
+ it('Is available in Astro components', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ expect($('#astro-component').text()).to.equal('function', 'Fetch supported in .astro components');
+ });
+ it('Is available in non-Astro components', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ expect($('#jsx').text()).to.equal('function', 'Fetch supported in .jsx');
+ expect($('#svelte').text()).to.equal('function', 'Fetch supported in .svelte');
+ expect($('#vue').text()).to.equal('function', 'Fetch supported in .vue');
+ });
+ it('Respects existing code', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ expect($('#already-imported').text()).to.equal('function', 'Existing fetch imports respected');
+ expect($('#custom-declaration').text()).to.equal('number', 'Custom fetch declarations respected');
+ });
});
diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/astro.astro b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/astro.astro
index a05a7c3ff..4ddb6dabd 100644
--- a/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/astro.astro
+++ b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/astro.astro
@@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
---
<Layout>
- <Markdown>
- # Hello world
- </Markdown>
+ <Markdown>
+ # Hello world
+ </Markdown>
</Layout>
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/braces.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/braces.astro
index ff56ef3ec..ee09e4433 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/braces.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/braces.astro
@@ -5,10 +5,9 @@ const description = 'This is a post about some stuff.';
---
<Markdown>
- ## Interesting Topic
+ ## Interesting Topic
- `({})`
- `{...props}`
- `{/* JavaScript */}`
-
+ `({})`
+ `{...props}`
+ `{/* JavaScript */}`
</Markdown>
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/code.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/code.astro
index a4671a34a..9ababd20c 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/code.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/code.astro
@@ -5,9 +5,9 @@ const description = 'This is a post about some stuff.';
---
<Markdown>
- ## Interesting Topic
+ ## Interesting Topic
- ```js
- const thing = () => {};
- ```
+ ```js
+ const thing = () => {};
+ ```
</Markdown>
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro
index 95b3e1d62..79098b4aa 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro
@@ -9,10 +9,10 @@ const description = 'This is a post about some stuff.';
---
<Layout>
- <Markdown>
- ## Interesting Topic
+ <Markdown>
+ ## Interesting Topic
- <Hello name={`world`} />
- <Counter client:load />
- </Markdown>
-</Layout> \ No newline at end of file
+ <Hello name={`world`} />
+ <Counter client:load />
+ </Markdown>
+</Layout>
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro
index fa2a747cd..96b3897e1 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro
@@ -9,21 +9,21 @@ const description = 'This is a post about some stuff.';
---
<div id="deep">
- <section class="a">
- <Markdown>
- ## A
- </Markdown>
- </section>
+ <section class="a">
+ <Markdown>
+ ## A
+ </Markdown>
+ </section>
- <section class="b">
- <Markdown>
- ## B
- </Markdown>
- </section>
+ <section class="b">
+ <Markdown>
+ ## B
+ </Markdown>
+ </section>
- <section class="c">
- <Markdown>
- ## C
- </Markdown>
- </section>
+ <section class="c">
+ <Markdown>
+ ## C
+ </Markdown>
+ </section>
</div>
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/external.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/external.astro
index cf4441c4c..a3f748497 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/external.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/external.astro
@@ -6,14 +6,14 @@ const inner = `## Inner`;
---
<style>
- #root { color: green; }
+ #root { color: green; }
</style>
<div id="root">
- <Markdown content={outer} />
+ <Markdown content={outer} />
- <Markdown>
- # Nested
+ <Markdown>
+ # Nested
- <Markdown content={inner} />
- </Markdown>
+ <Markdown content={inner} />
+ </Markdown>
</div> \ No newline at end of file
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/post.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/post.astro
index 20780d631..9bfcc847c 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/post.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/post.astro
@@ -8,9 +8,9 @@ const description = 'This is a post about some stuff.';
---
<Markdown>
- ## Interesting Topic
+ ## Interesting Topic
- <div id="first">Some content</div>
+ <div id="first">Some content</div>
- <Example></Example>
+ <Example></Example>
</Markdown>
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro
index ca0558e6d..1de76dfe6 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro
@@ -4,18 +4,18 @@ import Layout from '../layouts/content.astro';
---
<style>
- #root {
- color: green;
- }
+ #root {
+ color: green;
+ }
</style>
<Layout>
- <div id="root">
- <Markdown>
- ## Interesting Topic
+ <div id="root">
+ <Markdown>
+ ## Interesting Topic
- ```js
- const thing = () => {};
- ```
- </Markdown>
- </div>
+ ```js
+ const thing = () => {};
+ ```
+ </Markdown>
+ </div>
</Layout> \ No newline at end of file
diff --git a/packages/astro/test/lit-element.test.js b/packages/astro/test/lit-element.test.js
index d5be29a46..07367431f 100644
--- a/packages/astro/test/lit-element.test.js
+++ b/packages/astro/test/lit-element.test.js
@@ -8,73 +8,73 @@ const NODE_VERSION = parseFloat(process.versions.node);
const stripExpressionMarkers = (html) => html.replace(/<!--\/?lit-part-->/g, '');
before(async () => {
- // @lit-labs/ssr/ requires Node 13.9 or higher
- if (NODE_VERSION < 13.9) {
- return;
- }
- fixture = await loadFixture({
- projectRoot: './fixtures/lit-element/',
- renderers: ['@astrojs/renderer-lit'],
- });
- await fixture.build();
+ // @lit-labs/ssr/ requires Node 13.9 or higher
+ if (NODE_VERSION < 13.9) {
+ return;
+ }
+ fixture = await loadFixture({
+ projectRoot: './fixtures/lit-element/',
+ renderers: ['@astrojs/renderer-lit'],
+ });
+ await fixture.build();
});
describe('LitElement test', () => {
- it('Renders a custom element by tag name', async () => {
- // @lit-labs/ssr/ requires Node 13.9 or higher
- if (NODE_VERSION < 13.9) {
- return;
- }
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Renders a custom element by tag name', async () => {
+ // @lit-labs/ssr/ requires Node 13.9 or higher
+ if (NODE_VERSION < 13.9) {
+ return;
+ }
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- // test 1: attributes rendered – non reactive properties
- expect($('my-element').attr('foo')).to.equal('bar');
+ // test 1: attributes rendered – non reactive properties
+ expect($('my-element').attr('foo')).to.equal('bar');
- // test 2: shadow rendered
- expect($('my-element').html()).to.include(`<div>Testing...</div>`);
+ // test 2: shadow rendered
+ expect($('my-element').html()).to.include(`<div>Testing...</div>`);
- // test 3: string reactive property set
- expect(stripExpressionMarkers($('my-element').html())).to.include(`<div id="str">initialized</div>`);
+ // test 3: string reactive property set
+ expect(stripExpressionMarkers($('my-element').html())).to.include(`<div id="str">initialized</div>`);
- // test 4: boolean reactive property correctly set
- // <my-element bool="false"> Lit will equate to true because it uses
- // this.hasAttribute to determine its value
- expect(stripExpressionMarkers($('my-element').html())).to.include(`<div id="bool">B</div>`);
+ // test 4: boolean reactive property correctly set
+ // <my-element bool="false"> Lit will equate to true because it uses
+ // this.hasAttribute to determine its value
+ expect(stripExpressionMarkers($('my-element').html())).to.include(`<div id="bool">B</div>`);
- // test 5: object reactive property set
- // by default objects will be stringifed to [object Object]
- expect(stripExpressionMarkers($('my-element').html())).to.include(`<div id="data">data: 1</div>`);
+ // test 5: object reactive property set
+ // by default objects will be stringifed to [object Object]
+ expect(stripExpressionMarkers($('my-element').html())).to.include(`<div id="data">data: 1</div>`);
- // test 6: reactive properties are not rendered as attributes
- expect($('my-element').attr('obj')).to.equal(undefined);
- expect($('my-element').attr('bool')).to.equal(undefined);
- expect($('my-element').attr('str')).to.equal(undefined);
+ // test 6: reactive properties are not rendered as attributes
+ expect($('my-element').attr('obj')).to.equal(undefined);
+ expect($('my-element').attr('bool')).to.equal(undefined);
+ expect($('my-element').attr('str')).to.equal(undefined);
- // test 7: reflected reactive props are rendered as attributes
- expect($('my-element').attr('reflectedbool')).to.equal('');
- expect($('my-element').attr('reflected-str')).to.equal('default reflected string');
- expect($('my-element').attr('reflected-str-prop')).to.equal('initialized reflected');
- });
+ // test 7: reflected reactive props are rendered as attributes
+ expect($('my-element').attr('reflectedbool')).to.equal('');
+ expect($('my-element').attr('reflected-str')).to.equal('default reflected string');
+ expect($('my-element').attr('reflected-str-prop')).to.equal('initialized reflected');
+ });
- // Skipped because not supported by Lit
- it.skip('Renders a custom element by the constructor', async () => {
- const html = await fixture.fetch('/ctr/index.html');
- const $ = cheerio.load(html);
+ // Skipped because not supported by Lit
+ it.skip('Renders a custom element by the constructor', async () => {
+ const html = await fixture.fetch('/ctr/index.html');
+ const $ = cheerio.load(html);
- // test 1: attributes rendered
- expect($('my-element').attr('foo')).to.equal('bar');
+ // test 1: attributes rendered
+ expect($('my-element').attr('foo')).to.equal('bar');
- // test 2: shadow rendered
- expect($('my-element').html()).to.include(`<div>Testing...</div>`);
- });
+ // test 2: shadow rendered
+ expect($('my-element').html()).to.include(`<div>Testing...</div>`);
+ });
});
after(async () => {
- // The Lit renderer adds browser globals that interfere with other tests, so remove them now.
- const globals = Object.keys(globalThis.window || {});
- globals.splice(globals.indexOf('global'), 1);
- for (let name of globals) {
- delete globalThis[name];
- }
+ // The Lit renderer adds browser globals that interfere with other tests, so remove them now.
+ const globals = Object.keys(globalThis.window || {});
+ globals.splice(globals.indexOf('global'), 1);
+ for (let name of globals) {
+ delete globalThis[name];
+ }
});
diff --git a/packages/astro/test/markdown.test.js b/packages/astro/test/markdown.test.js
index 27882231f..aec486a19 100644
--- a/packages/astro/test/markdown.test.js
+++ b/packages/astro/test/markdown.test.js
@@ -5,30 +5,30 @@ import { loadFixture } from './test-utils.js';
let fixture;
before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/markdown/',
- buildOptions: {
- sitemap: false,
- },
- renderers: ['@astrojs/renderer-preact'],
- });
- await fixture.build();
+ fixture = await loadFixture({
+ projectRoot: './fixtures/markdown/',
+ buildOptions: {
+ sitemap: false,
+ },
+ renderers: ['@astrojs/renderer-preact'],
+ });
+ await fixture.build();
});
describe('Markdown tests', () => {
- it('Can load a simple markdown page with Astro', async () => {
- const html = await fixture.readFile('/post/index.html');
- const $ = cheerio.load(html);
+ it('Can load a simple markdown page with Astro', async () => {
+ const html = await fixture.readFile('/post/index.html');
+ const $ = cheerio.load(html);
- expect($('p').first().text()).to.equal('Hello world!');
- expect($('#first').text()).to.equal('Some content');
- expect($('#interesting-topic').text()).to.equal('Interesting Topic');
- });
+ expect($('p').first().text()).to.equal('Hello world!');
+ expect($('#first').text()).to.equal('Some content');
+ expect($('#interesting-topic').text()).to.equal('Interesting Topic');
+ });
- it('Can load a realworld markdown page with Astro', async () => {
- const html = await fixture.readFile('/realworld/index.html');
- const $ = cheerio.load(html);
+ it('Can load a realworld markdown page with Astro', async () => {
+ const html = await fixture.readFile('/realworld/index.html');
+ const $ = cheerio.load(html);
- expect($('pre')).to.have.lengthOf(7);
- });
+ expect($('pre')).to.have.lengthOf(7);
+ });
});
diff --git a/packages/astro/test/postcss.test.js b/packages/astro/test/postcss.test.js
index 27e89191a..fa91f498d 100644
--- a/packages/astro/test/postcss.test.js
+++ b/packages/astro/test/postcss.test.js
@@ -8,47 +8,47 @@ const PREFIXED_CSS = `{-webkit-appearance:none;appearance:none}`;
let fixture;
let bundledCSS;
before(async () => {
- fixture = await loadFixture({
- projectRoot: './fixtures/postcss',
- renderers: ['@astrojs/renderer-solid', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'],
- });
- await fixture.build();
-
- // get bundled CSS (will be hashed, hence DOM query)
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- const bundledCSSHREF = $('link[rel=stylesheet][href^=assets/]').attr('href');
- bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'));
+ fixture = await loadFixture({
+ projectRoot: './fixtures/postcss',
+ renderers: ['@astrojs/renderer-solid', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'],
+ });
+ await fixture.build();
+
+ // get bundled CSS (will be hashed, hence DOM query)
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ const bundledCSSHREF = $('link[rel=stylesheet][href^=assets/]').attr('href');
+ bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'));
});
describe('PostCSS', () => {
- it('works in Astro page styles', () => {
- expect(bundledCSS).to.match(new RegExp(`.astro-page.astro-[^{]+${PREFIXED_CSS}`));
- });
-
- it('works in Astro component styles', () => {
- expect(bundledCSS).to.match(new RegExp(`.astro-component.astro-[^{]+${PREFIXED_CSS}`));
- });
-
- it('works in <link>', () => {
- expect(bundledCSS).to.match(new RegExp(`.a-n${PREFIXED_CSS}`));
- });
-
- it('works in JSX', () => {
- expect(bundledCSS).to.match(new RegExp(`.solid${PREFIXED_CSS}`));
- });
-
- it('works in Vue', () => {
- expect(bundledCSS).to.match(new RegExp(`.vue${PREFIXED_CSS}`));
- });
-
- it('works in Svelte', () => {
- expect(bundledCSS).to.match(new RegExp(`.svelte.s[^{]+${PREFIXED_CSS}`));
- });
-
- it('ignores CSS in public/', async () => {
- const publicCSS = await fixture.readFile('/global.css');
- // neither minified nor prefixed
- expect(eol.lf(publicCSS.trim())).to.equal(`.global {\n appearance: none;\n}`);
- });
+ it('works in Astro page styles', () => {
+ expect(bundledCSS).to.match(new RegExp(`.astro-page.astro-[^{]+${PREFIXED_CSS}`));
+ });
+
+ it('works in Astro component styles', () => {
+ expect(bundledCSS).to.match(new RegExp(`.astro-component.astro-[^{]+${PREFIXED_CSS}`));
+ });
+
+ it('works in <link>', () => {
+ expect(bundledCSS).to.match(new RegExp(`.a-n${PREFIXED_CSS}`));
+ });
+
+ it('works in JSX', () => {
+ expect(bundledCSS).to.match(new RegExp(`.solid${PREFIXED_CSS}`));
+ });
+
+ it('works in Vue', () => {
+ expect(bundledCSS).to.match(new RegExp(`.vue${PREFIXED_CSS}`));
+ });
+
+ it('works in Svelte', () => {
+ expect(bundledCSS).to.match(new RegExp(`.svelte.s[^{]+${PREFIXED_CSS}`));
+ });
+
+ it('ignores CSS in public/', async () => {
+ const publicCSS = await fixture.readFile('/global.css');
+ // neither minified nor prefixed
+ expect(eol.lf(publicCSS.trim())).to.equal(`.global {\n appearance: none;\n}`);
+ });
});
diff --git a/packages/astro/test/preact-component.test.js b/packages/astro/test/preact-component.test.js
index 247232aef..4684d48e1 100644
--- a/packages/astro/test/preact-component.test.js
+++ b/packages/astro/test/preact-component.test.js
@@ -5,83 +5,83 @@ import { loadFixture } from './test-utils.js';
let fixture;
before(async () => {
- fixture = await loadFixture({
- devOptions: {
- port: 3009,
- },
- projectRoot: './fixtures/preact-component/',
- renderers: ['@astrojs/renderer-preact'],
- });
- await fixture.build();
+ fixture = await loadFixture({
+ devOptions: {
+ port: 3009,
+ },
+ projectRoot: './fixtures/preact-component/',
+ renderers: ['@astrojs/renderer-preact'],
+ });
+ await fixture.build();
});
describe('Preact component', () => {
- it('Can load class component', async () => {
- const html = await fixture.readFile('/class/index.html');
- const $ = cheerio.load(html);
-
- // test 1: Can use class components
- expect($('#class-component')).to.have.lengthOf(1);
- });
-
- it('Can load function component', async () => {
- const html = await fixture.readFile('/fn/index.html');
- const $ = cheerio.load(html);
-
- // test 1: Can use function components
- expect($('#fn-component')).to.have.lengthOf(1);
- // test 2: Can use function components
- expect($('#arrow-fn-component')).to.have.lengthOf(1);
- });
-
- it('Can load TS component', async () => {
- const html = await fixture.readFile('/ts-components/index.html');
- const $ = cheerio.load(html);
-
- // test 1: Can use TS components
- expect($('.ts-component')).to.have.lengthOf(1);
- });
-
- it('Can use hooks', async () => {
- const html = await fixture.readFile('/hooks/index.html');
- const $ = cheerio.load(html);
- expect($('#world')).to.have.lengthOf(1);
- });
-
- it('Can export a Fragment', async () => {
- const html = await fixture.readFile('/frag/index.html');
- const $ = cheerio.load(html);
-
- // test 1: nothing rendered but it didn’t throw
- expect($('body').children()).to.have.lengthOf(0);
- });
-
- it('Can use a pragma comment', async () => {
- const html = await fixture.readFile('/pragma-comment/index.html');
- const $ = cheerio.load(html);
-
- // test 1: rendered the PragmaComment component
- expect($('.pragma-comment')).to.have.lengthOf(1);
- expect($('.pragma-comment-tsx')).to.have.lengthOf(1);
- });
-
- // In moving over to Vite, the jsx-runtime import is now obscured. TODO: update the method of finding this.
- it.skip('Uses the new JSX transform', async () => {
- const html = await fixture.readFile('/pragma-comment/index.html');
-
- // Grab the imports
- const exp = /import\("(.+?)"\)/g;
- let match, componentUrl;
- while ((match = exp.exec(html))) {
- if (match[1].includes('PragmaComment.js')) {
- componentUrl = match[1];
- break;
- }
- }
- const component = await fixture.fetch(componentUrl).then((res) => res.text());
- const jsxRuntime = component.imports.filter((i) => i.specifier.includes('jsx-runtime'));
-
- // test 1: preact/jsx-runtime is used for the component
- expect(jsxRuntime).to.be.ok;
- });
+ it('Can load class component', async () => {
+ const html = await fixture.readFile('/class/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Can use class components
+ expect($('#class-component')).to.have.lengthOf(1);
+ });
+
+ it('Can load function component', async () => {
+ const html = await fixture.readFile('/fn/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Can use function components
+ expect($('#fn-component')).to.have.lengthOf(1);
+ // test 2: Can use function components
+ expect($('#arrow-fn-component')).to.have.lengthOf(1);
+ });
+
+ it('Can load TS component', async () => {
+ const html = await fixture.readFile('/ts-components/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Can use TS components
+ expect($('.ts-component')).to.have.lengthOf(1);
+ });
+
+ it('Can use hooks', async () => {
+ const html = await fixture.readFile('/hooks/index.html');
+ const $ = cheerio.load(html);
+ expect($('#world')).to.have.lengthOf(1);
+ });
+
+ it('Can export a Fragment', async () => {
+ const html = await fixture.readFile('/frag/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: nothing rendered but it didn’t throw
+ expect($('body').children()).to.have.lengthOf(0);
+ });
+
+ it('Can use a pragma comment', async () => {
+ const html = await fixture.readFile('/pragma-comment/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: rendered the PragmaComment component
+ expect($('.pragma-comment')).to.have.lengthOf(1);
+ expect($('.pragma-comment-tsx')).to.have.lengthOf(1);
+ });
+
+ // In moving over to Vite, the jsx-runtime import is now obscured. TODO: update the method of finding this.
+ it.skip('Uses the new JSX transform', async () => {
+ const html = await fixture.readFile('/pragma-comment/index.html');
+
+ // Grab the imports
+ const exp = /import\("(.+?)"\)/g;
+ let match, componentUrl;
+ while ((match = exp.exec(html))) {
+ if (match[1].includes('PragmaComment.js')) {
+ componentUrl = match[1];
+ break;
+ }
+ }
+ const component = await fixture.fetch(componentUrl).then((res) => res.text());
+ const jsxRuntime = component.imports.filter((i) => i.specifier.includes('jsx-runtime'));
+
+ // test 1: preact/jsx-runtime is used for the component
+ expect(jsxRuntime).to.be.ok;
+ });
});
diff --git a/packages/astro/test/react-component.test.js b/packages/astro/test/react-component.test.js
index 78af0d97c..4e237508c 100644
--- a/packages/astro/test/react-component.test.js
+++ b/packages/astro/test/react-component.test.js
@@ -5,127 +5,127 @@ import { loadFixture } from './test-utils.js';
let fixture;
describe('React Components', () => {
- before(async () => {
- fixture = await loadFixture({
- devOptions: {
- port: 3008,
- },
- projectRoot: './fixtures/react-component/',
- renderers: ['@astrojs/renderer-react', '@astrojs/renderer-vue'],
- });
- });
-
- describe('build', () => {
- before(async () => {
- await fixture.build();
- });
-
- it('Can load React', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
-
- // test 1: basic component renders
- expect($('#react-h2').text()).to.equal('Hello world!');
-
- // test 2: no reactroot
- expect($('#react-h2').attr('data-reactroot')).to.equal(undefined);
-
- // test 3: Can use function components
- expect($('#arrow-fn-component')).to.have.lengthOf(1);
-
- // test 4: Can use spread for components
- expect($('#component-spread-props')).to.have.lengthOf(1);
-
- // test 5: spread props renders
- expect($('#component-spread-props').text(), 'Hello world!');
-
- // test 6: Can use TS components
- expect($('.ts-component')).to.have.lengthOf(1);
-
- // test 7: Can use Pure components
- expect($('#pure')).to.have.lengthOf(1);
- });
-
- it('Can load Vue', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
- expect($('#vue-h2').text()).to.equal('Hasta la vista, baby');
- });
-
- it('Can use a pragma comment', async () => {
- const html = await fixture.readFile('/pragma-comment/index.html');
- const $ = cheerio.load(html);
-
- // test 1: rendered the PragmaComment component
- expect($('.pragma-comment')).to.have.lengthOf(2);
- });
-
- // TODO: is this still a relevant test?
- it.skip('Includes reactroot on hydrating components', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
-
- const div = $('#research');
-
- // test 1: has the hydration attr
- expect(div.attr('data-reactroot')).to.be.ok;
-
- // test 2: renders correctly
- expect(div.html()).to.equal('foo bar <!-- -->1');
- });
- });
-
- describe('dev', () => {
- let devServer;
-
- before(async () => {
- devServer = await fixture.startDevServer();
- });
-
- after(async () => {
- devServer && (await devServer.stop());
- });
-
- it('scripts proxy correctly', async () => {
- const html = await fixture.fetch('/').then((res) => res.text());
- const $ = cheerio.load(html);
-
- for (const script of $('script').toArray()) {
- const { src } = script.attribs;
- if (!src) continue;
- expect((await fixture.fetch(src)).status, `404: ${src}`).to.equal(200);
- }
- });
-
- // TODO: move this to separate dev test?
- it.skip('Throws helpful error message on window SSR', async () => {
- const html = await fixture.fetch('/window/index.html');
- expect(html).to.include(
- `[/window]
+ before(async () => {
+ fixture = await loadFixture({
+ devOptions: {
+ port: 3008,
+ },
+ projectRoot: './fixtures/react-component/',
+ renderers: ['@astrojs/renderer-react', '@astrojs/renderer-vue'],
+ });
+ });
+
+ describe('build', () => {
+ before(async () => {
+ await fixture.build();
+ });
+
+ it('Can load React', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: basic component renders
+ expect($('#react-h2').text()).to.equal('Hello world!');
+
+ // test 2: no reactroot
+ expect($('#react-h2').attr('data-reactroot')).to.equal(undefined);
+
+ // test 3: Can use function components
+ expect($('#arrow-fn-component')).to.have.lengthOf(1);
+
+ // test 4: Can use spread for components
+ expect($('#component-spread-props')).to.have.lengthOf(1);
+
+ // test 5: spread props renders
+ expect($('#component-spread-props').text(), 'Hello world!');
+
+ // test 6: Can use TS components
+ expect($('.ts-component')).to.have.lengthOf(1);
+
+ // test 7: Can use Pure components
+ expect($('#pure')).to.have.lengthOf(1);
+ });
+
+ it('Can load Vue', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ expect($('#vue-h2').text()).to.equal('Hasta la vista, baby');
+ });
+
+ it('Can use a pragma comment', async () => {
+ const html = await fixture.readFile('/pragma-comment/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: rendered the PragmaComment component
+ expect($('.pragma-comment')).to.have.lengthOf(2);
+ });
+
+ // TODO: is this still a relevant test?
+ it.skip('Includes reactroot on hydrating components', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+
+ const div = $('#research');
+
+ // test 1: has the hydration attr
+ expect(div.attr('data-reactroot')).to.be.ok;
+
+ // test 2: renders correctly
+ expect(div.html()).to.equal('foo bar <!-- -->1');
+ });
+ });
+
+ describe('dev', () => {
+ let devServer;
+
+ before(async () => {
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
+
+ it('scripts proxy correctly', async () => {
+ const html = await fixture.fetch('/').then((res) => res.text());
+ const $ = cheerio.load(html);
+
+ for (const script of $('script').toArray()) {
+ const { src } = script.attribs;
+ if (!src) continue;
+ expect((await fixture.fetch(src)).status, `404: ${src}`).to.equal(200);
+ }
+ });
+
+ // TODO: move this to separate dev test?
+ it.skip('Throws helpful error message on window SSR', async () => {
+ const html = await fixture.fetch('/window/index.html');
+ expect(html).to.include(
+ `[/window]
The window object is not available during server-side rendering (SSR).
Try using \`import.meta.env.SSR\` to write SSR-friendly code.
https://docs.astro.build/reference/api-reference/#importmeta`
- );
- });
-
- // In moving over to Vite, the jsx-runtime import is now obscured. TODO: update the method of finding this.
- it.skip('uses the new JSX transform', async () => {
- const html = await fixture.fetch('/index.html');
-
- // Grab the imports
- const exp = /import\("(.+?)"\)/g;
- let match, componentUrl;
- while ((match = exp.exec(html))) {
- if (match[1].includes('Research.js')) {
- componentUrl = match[1];
- break;
- }
- }
- const component = await fixture.readFile(componentUrl);
- const jsxRuntime = component.imports.filter((i) => i.specifier.includes('jsx-runtime'));
-
- // test 1: react/jsx-runtime is used for the component
- expect(jsxRuntime).to.be.ok;
- });
- });
+ );
+ });
+
+ // In moving over to Vite, the jsx-runtime import is now obscured. TODO: update the method of finding this.
+ it.skip('uses the new JSX transform', async () => {
+ const html = await fixture.fetch('/index.html');
+
+ // Grab the imports
+ const exp = /import\("(.+?)"\)/g;
+ let match, componentUrl;
+ while ((match = exp.exec(html))) {
+ if (match[1].includes('Research.js')) {
+ componentUrl = match[1];
+ break;
+ }
+ }
+ const component = await fixture.readFile(componentUrl);
+ const jsxRuntime = component.imports.filter((i) => i.specifier.includes('jsx-runtime'));
+
+ // test 1: react/jsx-runtime is used for the component
+ expect(jsxRuntime).to.be.ok;
+ });
+ });
});
diff --git a/packages/astro/test/route-manifest.test.js b/packages/astro/test/route-manifest.test.js
index 533bb7a30..730bc72b4 100644
--- a/packages/astro/test/route-manifest.test.js
+++ b/packages/astro/test/route-manifest.test.js
@@ -5,238 +5,238 @@ import { createRouteManifest } from '../dist/core/ssr/routing.js';
const cwd = new URL('./fixtures/route-manifest/', import.meta.url);
const create = (dir, trailingSlash) => {
- return createRouteManifest({
- config: {
- projectRoot: cwd,
- pages: new URL(dir, cwd),
- devOptions: {
- trailingSlash,
- },
- },
- cwd: fileURLToPath(cwd),
- });
+ return createRouteManifest({
+ config: {
+ projectRoot: cwd,
+ pages: new URL(dir, cwd),
+ devOptions: {
+ trailingSlash,
+ },
+ },
+ cwd: fileURLToPath(cwd),
+ });
};
function cleanRoutes(routes) {
- return routes.map((r) => {
- delete r.generate;
- return r;
- });
+ return routes.map((r) => {
+ delete r.generate;
+ return r;
+ });
}
describe('route manifest', () => {
- it('creates routes with trailingSlashes = always', () => {
- const { routes } = create('basic', 'always');
- expect(cleanRoutes(routes)).to.deep.equal([
- {
- type: 'page',
- pattern: /^\/$/,
- params: [],
- component: 'basic/index.astro',
- pathname: '/',
- },
-
- {
- type: 'page',
- pattern: /^\/about\/$/,
- params: [],
- component: 'basic/about.astro',
- pathname: '/about',
- },
-
- {
- type: 'page',
- pattern: /^\/blog\/$/,
- params: [],
- component: 'basic/blog/index.astro',
- pathname: '/blog',
- },
-
- {
- type: 'page',
- pattern: /^\/blog\/([^/]+?)\/$/,
- params: ['slug'],
- component: 'basic/blog/[slug].astro',
- pathname: undefined,
- },
- ]);
- });
-
- it('creates routes with trailingSlashes = never', () => {
- const { routes } = create('basic', 'never');
- expect(cleanRoutes(routes)).to.deep.equal([
- {
- type: 'page',
- pattern: /^\/$/,
- params: [],
- component: 'basic/index.astro',
- pathname: '/',
- },
-
- {
- type: 'page',
- pattern: /^\/about$/,
- params: [],
- component: 'basic/about.astro',
- pathname: '/about',
- },
-
- {
- type: 'page',
- pattern: /^\/blog$/,
- params: [],
- component: 'basic/blog/index.astro',
- pathname: '/blog',
- },
-
- {
- type: 'page',
- pattern: /^\/blog\/([^/]+?)$/,
- params: ['slug'],
- component: 'basic/blog/[slug].astro',
- pathname: undefined,
- },
- ]);
- });
-
- it('creates routes with trailingSlashes = ignore', () => {
- const { routes } = create('basic', 'ignore');
- expect(cleanRoutes(routes)).to.deep.equal([
- {
- type: 'page',
- pattern: /^\/$/,
- params: [],
- component: 'basic/index.astro',
- pathname: '/',
- },
-
- {
- type: 'page',
- pattern: /^\/about\/?$/,
- params: [],
- component: 'basic/about.astro',
- pathname: '/about',
- },
-
- {
- type: 'page',
- pattern: /^\/blog\/?$/,
- params: [],
- component: 'basic/blog/index.astro',
- pathname: '/blog',
- },
-
- {
- type: 'page',
- pattern: /^\/blog\/([^/]+?)\/?$/,
- params: ['slug'],
- component: 'basic/blog/[slug].astro',
- pathname: undefined,
- },
- ]);
- });
-
- it('encodes invalid characters', () => {
- const { routes } = create('encoding', 'always');
-
- // had to remove ? and " because windows
-
- // const quote = 'encoding/".astro';
- const hash = 'encoding/#.astro';
- // const question_mark = 'encoding/?.astro';
-
- expect(routes.map((p) => p.pattern)).to.deep.equal([
- // /^\/%22$/,
- /^\/%23\/$/,
- // /^\/%3F$/
- ]);
- });
-
- it('ignores files and directories with leading underscores', () => {
- const { routes } = create('hidden-underscore', 'always');
-
- expect(routes.map((r) => r.component).filter(Boolean)).to.deep.equal(['hidden-underscore/index.astro', 'hidden-underscore/e/f/g/h.astro']);
- });
-
- it('ignores files and directories with leading dots except .well-known', () => {
- const { routes } = create('hidden-dot', 'always');
-
- expect(routes.map((r) => r.component).filter(Boolean)).to.deep.equal(['hidden-dot/.well-known/dnt-policy.astro']);
- });
-
- it('fails if dynamic params are not separated', () => {
- expect(() => create('invalid-params', 'always')).to.throw('Invalid route invalid-params/[foo][bar].astro — parameters must be separated');
- });
-
- it('disallows rest parameters inside segments', () => {
- expect(() => create('invalid-rest', 'always')).to.throw('Invalid route invalid-rest/foo-[...rest]-bar.astro — rest parameter must be a standalone segment');
- });
-
- it('ignores things that look like lockfiles', () => {
- const { routes } = create('lockfiles', 'always');
- expect(cleanRoutes(routes)).to.deep.equal([
- {
- type: 'page',
- pattern: /^\/foo\/$/,
- params: [],
- component: 'lockfiles/foo.astro',
- pathname: '/foo',
- },
- ]);
- });
-
- it('ignores invalid route extensions', () => {
- const { routes } = create('invalid-extension', 'always');
- expect(cleanRoutes(routes)).to.deep.equal([
- {
- type: 'page',
- pattern: /^\/$/,
- params: [],
- component: 'invalid-extension/index.astro',
- pathname: '/',
- },
-
- {
- type: 'page',
- pattern: /^\/about\/$/,
- params: [],
- component: 'invalid-extension/about.astro',
- pathname: '/about',
- },
- ]);
- });
-
- it('allows multiple slugs', () => {
- const { routes } = create('multiple-slugs', 'always');
-
- expect(cleanRoutes(routes)).to.deep.equal([
- {
- type: 'page',
- pattern: /^\/([^/]+?)\.([^/]+?)\/$/,
- component: 'multiple-slugs/[file].[ext].astro',
- params: ['file', 'ext'],
- pathname: undefined,
- },
- ]);
- });
-
- it('sorts routes correctly', () => {
- const { routes } = create('sorting', 'always');
-
- expect(routes.map((p) => p.component)).to.deep.equal([
- 'sorting/index.astro',
- 'sorting/about.astro',
- 'sorting/post/index.astro',
- 'sorting/post/bar.astro',
- 'sorting/post/foo.astro',
- 'sorting/post/f[xx].astro',
- 'sorting/post/f[yy].astro',
- 'sorting/post/[id].astro',
- 'sorting/[wildcard].astro',
- 'sorting/[...rest]/deep/[...deep_rest]/xyz.astro',
- 'sorting/[...rest]/deep/[...deep_rest]/index.astro',
- 'sorting/[...rest]/deep/index.astro',
- 'sorting/[...rest]/abc.astro',
- 'sorting/[...rest]/index.astro',
- ]);
- });
+ it('creates routes with trailingSlashes = always', () => {
+ const { routes } = create('basic', 'always');
+ expect(cleanRoutes(routes)).to.deep.equal([
+ {
+ type: 'page',
+ pattern: /^\/$/,
+ params: [],
+ component: 'basic/index.astro',
+ pathname: '/',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/about\/$/,
+ params: [],
+ component: 'basic/about.astro',
+ pathname: '/about',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/blog\/$/,
+ params: [],
+ component: 'basic/blog/index.astro',
+ pathname: '/blog',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/blog\/([^/]+?)\/$/,
+ params: ['slug'],
+ component: 'basic/blog/[slug].astro',
+ pathname: undefined,
+ },
+ ]);
+ });
+
+ it('creates routes with trailingSlashes = never', () => {
+ const { routes } = create('basic', 'never');
+ expect(cleanRoutes(routes)).to.deep.equal([
+ {
+ type: 'page',
+ pattern: /^\/$/,
+ params: [],
+ component: 'basic/index.astro',
+ pathname: '/',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/about$/,
+ params: [],
+ component: 'basic/about.astro',
+ pathname: '/about',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/blog$/,
+ params: [],
+ component: 'basic/blog/index.astro',
+ pathname: '/blog',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/blog\/([^/]+?)$/,
+ params: ['slug'],
+ component: 'basic/blog/[slug].astro',
+ pathname: undefined,
+ },
+ ]);
+ });
+
+ it('creates routes with trailingSlashes = ignore', () => {
+ const { routes } = create('basic', 'ignore');
+ expect(cleanRoutes(routes)).to.deep.equal([
+ {
+ type: 'page',
+ pattern: /^\/$/,
+ params: [],
+ component: 'basic/index.astro',
+ pathname: '/',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/about\/?$/,
+ params: [],
+ component: 'basic/about.astro',
+ pathname: '/about',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/blog\/?$/,
+ params: [],
+ component: 'basic/blog/index.astro',
+ pathname: '/blog',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/blog\/([^/]+?)\/?$/,
+ params: ['slug'],
+ component: 'basic/blog/[slug].astro',
+ pathname: undefined,
+ },
+ ]);
+ });
+
+ it('encodes invalid characters', () => {
+ const { routes } = create('encoding', 'always');
+
+ // had to remove ? and " because windows
+
+ // const quote = 'encoding/".astro';
+ const hash = 'encoding/#.astro';
+ // const question_mark = 'encoding/?.astro';
+
+ expect(routes.map((p) => p.pattern)).to.deep.equal([
+ // /^\/%22$/,
+ /^\/%23\/$/,
+ // /^\/%3F$/
+ ]);
+ });
+
+ it('ignores files and directories with leading underscores', () => {
+ const { routes } = create('hidden-underscore', 'always');
+
+ expect(routes.map((r) => r.component).filter(Boolean)).to.deep.equal(['hidden-underscore/index.astro', 'hidden-underscore/e/f/g/h.astro']);
+ });
+
+ it('ignores files and directories with leading dots except .well-known', () => {
+ const { routes } = create('hidden-dot', 'always');
+
+ expect(routes.map((r) => r.component).filter(Boolean)).to.deep.equal(['hidden-dot/.well-known/dnt-policy.astro']);
+ });
+
+ it('fails if dynamic params are not separated', () => {
+ expect(() => create('invalid-params', 'always')).to.throw('Invalid route invalid-params/[foo][bar].astro — parameters must be separated');
+ });
+
+ it('disallows rest parameters inside segments', () => {
+ expect(() => create('invalid-rest', 'always')).to.throw('Invalid route invalid-rest/foo-[...rest]-bar.astro — rest parameter must be a standalone segment');
+ });
+
+ it('ignores things that look like lockfiles', () => {
+ const { routes } = create('lockfiles', 'always');
+ expect(cleanRoutes(routes)).to.deep.equal([
+ {
+ type: 'page',
+ pattern: /^\/foo\/$/,
+ params: [],
+ component: 'lockfiles/foo.astro',
+ pathname: '/foo',
+ },
+ ]);
+ });
+
+ it('ignores invalid route extensions', () => {
+ const { routes } = create('invalid-extension', 'always');
+ expect(cleanRoutes(routes)).to.deep.equal([
+ {
+ type: 'page',
+ pattern: /^\/$/,
+ params: [],
+ component: 'invalid-extension/index.astro',
+ pathname: '/',
+ },
+
+ {
+ type: 'page',
+ pattern: /^\/about\/$/,
+ params: [],
+ component: 'invalid-extension/about.astro',
+ pathname: '/about',
+ },
+ ]);
+ });
+
+ it('allows multiple slugs', () => {
+ const { routes } = create('multiple-slugs', 'always');
+
+ expect(cleanRoutes(routes)).to.deep.equal([
+ {
+ type: 'page',
+ pattern: /^\/([^/]+?)\.([^/]+?)\/$/,
+ component: 'multiple-slugs/[file].[ext].astro',
+ params: ['file', 'ext'],
+ pathname: undefined,
+ },
+ ]);
+ });
+
+ it('sorts routes correctly', () => {
+ const { routes } = create('sorting', 'always');
+
+ expect(routes.map((p) => p.component)).to.deep.equal([
+ 'sorting/index.astro',
+ 'sorting/about.astro',
+ 'sorting/post/index.astro',
+ 'sorting/post/bar.astro',
+ 'sorting/post/foo.astro',
+ 'sorting/post/f[xx].astro',
+ 'sorting/post/f[yy].astro',
+ 'sorting/post/[id].astro',
+ 'sorting/[wildcard].astro',
+ 'sorting/[...rest]/deep/[...deep_rest]/xyz.astro',
+ 'sorting/[...rest]/deep/[...deep_rest]/index.astro',
+ 'sorting/[...rest]/deep/index.astro',
+ 'sorting/[...rest]/abc.astro',
+ 'sorting/[...rest]/index.astro',
+ ]);
+ });
});
diff --git a/packages/astro/test/sass.test.js b/packages/astro/test/sass.test.js
index b00e12fb4..02ad938e5 100644
--- a/packages/astro/test/sass.test.js
+++ b/packages/astro/test/sass.test.js
@@ -5,22 +5,22 @@ import { loadFixture } from './test-utils.js';
// note: many Sass tests live in 0-css.test.js to test within context of a framework.
// these tests are independent of framework.
describe('Sass', () => {
- let fixture;
- let devServer;
+ let fixture;
+ let devServer;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/sass/' });
- devServer = await fixture.startDevServer();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/sass/' });
+ devServer = await fixture.startDevServer();
+ });
- after(async () => {
- devServer && (await devServer.stop());
- });
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
- // TODO: Sass cannot be found on macOS for some reason... Vite issue?
- const test = os.platform() === 'darwin' ? it.skip : it;
- test('shows helpful error on failure', async () => {
- const res = await fixture.fetch('/error').then((res) => res.text());
- expect(res).to.include('Undefined variable');
- });
+ // TODO: Sass cannot be found on macOS for some reason... Vite issue?
+ const test = os.platform() === 'darwin' ? it.skip : it;
+ test('shows helpful error on failure', async () => {
+ const res = await fixture.fetch('/error').then((res) => res.text());
+ expect(res).to.include('Undefined variable');
+ });
});
diff --git a/packages/astro/test/slots-preact.test.js b/packages/astro/test/slots-preact.test.js
index 1f8579475..a96bcea75 100644
--- a/packages/astro/test/slots-preact.test.js
+++ b/packages/astro/test/slots-preact.test.js
@@ -3,22 +3,22 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Slots: Preact', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/slots-preact/', renderers: ['@astrojs/renderer-preact'] });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/slots-preact/', renderers: ['@astrojs/renderer-preact'] });
+ await fixture.build();
+ });
- it('Renders default slot', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Renders default slot', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- expect($('#default-self-closing').text().trim()).to.equal('Fallback');
- expect($('#default-empty').text().trim()).to.equal('Fallback');
- expect($('#zero').text().trim()).to.equal('0');
- expect($('#false').text().trim()).to.equal('');
- expect($('#string').text().trim()).to.equal('');
- expect($('#content').text().trim()).to.equal('Hello world!');
- });
+ expect($('#default-self-closing').text().trim()).to.equal('Fallback');
+ expect($('#default-empty').text().trim()).to.equal('Fallback');
+ expect($('#zero').text().trim()).to.equal('0');
+ expect($('#false').text().trim()).to.equal('');
+ expect($('#string').text().trim()).to.equal('');
+ expect($('#content').text().trim()).to.equal('Hello world!');
+ });
});
diff --git a/packages/astro/test/slots-react.test.js b/packages/astro/test/slots-react.test.js
index d65411970..7a4f1863c 100644
--- a/packages/astro/test/slots-react.test.js
+++ b/packages/astro/test/slots-react.test.js
@@ -3,22 +3,22 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Slots: React', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/slots-react/', renderers: ['@astrojs/renderer-react'] });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/slots-react/', renderers: ['@astrojs/renderer-react'] });
+ await fixture.build();
+ });
- it('Renders default slot', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Renders default slot', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- expect($('#default-self-closing').text().trim()).to.equal('Fallback');
- expect($('#default-empty').text().trim()).to.equal('Fallback');
- expect($('#zero').text().trim()).to.equal('0');
- expect($('#false').text().trim()).to.equal('');
- expect($('#string').text().trim()).to.equal('');
- expect($('#content').text().trim()).to.equal('Hello world!');
- });
+ expect($('#default-self-closing').text().trim()).to.equal('Fallback');
+ expect($('#default-empty').text().trim()).to.equal('Fallback');
+ expect($('#zero').text().trim()).to.equal('0');
+ expect($('#false').text().trim()).to.equal('');
+ expect($('#string').text().trim()).to.equal('');
+ expect($('#content').text().trim()).to.equal('Hello world!');
+ });
});
diff --git a/packages/astro/test/slots-solid.test.js b/packages/astro/test/slots-solid.test.js
index 29c2005d4..d4f566930 100644
--- a/packages/astro/test/slots-solid.test.js
+++ b/packages/astro/test/slots-solid.test.js
@@ -3,22 +3,22 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Slots: Solid', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/slots-solid/', renderers: ['@astrojs/renderer-solid'] });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/slots-solid/', renderers: ['@astrojs/renderer-solid'] });
+ await fixture.build();
+ });
- it('Renders default slot', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Renders default slot', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- expect($('#default-self-closing').text().trim()).to.equal('Fallback');
- expect($('#default-empty').text().trim()).to.equal('Fallback');
- expect($('#zero').text().trim()).to.equal('0');
- expect($('#false').text().trim()).to.equal('');
- expect($('#string').text().trim()).to.equal('');
- expect($('#content').text().trim()).to.equal('Hello world!');
- });
+ expect($('#default-self-closing').text().trim()).to.equal('Fallback');
+ expect($('#default-empty').text().trim()).to.equal('Fallback');
+ expect($('#zero').text().trim()).to.equal('0');
+ expect($('#false').text().trim()).to.equal('');
+ expect($('#string').text().trim()).to.equal('');
+ expect($('#content').text().trim()).to.equal('Hello world!');
+ });
});
diff --git a/packages/astro/test/slots-svelte.test.js b/packages/astro/test/slots-svelte.test.js
index 08296f8a1..690a61b3d 100644
--- a/packages/astro/test/slots-svelte.test.js
+++ b/packages/astro/test/slots-svelte.test.js
@@ -3,22 +3,22 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Slots: Svelte', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/slots-svelte/', renderers: ['@astrojs/renderer-svelte'] });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/slots-svelte/', renderers: ['@astrojs/renderer-svelte'] });
+ await fixture.build();
+ });
- it('Renders default slot', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Renders default slot', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- expect($('#default-self-closing').text().trim()).to.equal('Fallback');
- expect($('#default-empty').text().trim()).to.equal('Fallback');
- expect($('#zero').text().trim()).to.equal('');
- expect($('#false').text().trim()).to.equal('');
- expect($('#string').text().trim()).to.equal('');
- expect($('#content').text().trim()).to.equal('Hello world!');
- });
+ expect($('#default-self-closing').text().trim()).to.equal('Fallback');
+ expect($('#default-empty').text().trim()).to.equal('Fallback');
+ expect($('#zero').text().trim()).to.equal('');
+ expect($('#false').text().trim()).to.equal('');
+ expect($('#string').text().trim()).to.equal('');
+ expect($('#content').text().trim()).to.equal('Hello world!');
+ });
});
diff --git a/packages/astro/test/slots-vue.test.js b/packages/astro/test/slots-vue.test.js
index 7a52f7aab..ba76c7cfd 100644
--- a/packages/astro/test/slots-vue.test.js
+++ b/packages/astro/test/slots-vue.test.js
@@ -3,22 +3,22 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Slots: Vue', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({ projectRoot: './fixtures/slots-vue/', renderers: ['@astrojs/renderer-vue'] });
- await fixture.build();
- });
+ before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/slots-vue/', renderers: ['@astrojs/renderer-vue'] });
+ await fixture.build();
+ });
- it('Renders default slot', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Renders default slot', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- expect($('#default-self-closing').text().trim()).to.equal('Fallback');
- expect($('#default-empty').text().trim()).to.equal('Fallback');
- expect($('#zero').text().trim()).to.equal('0');
- expect($('#false').text().trim()).to.equal('');
- expect($('#string').text().trim()).to.equal('');
- expect($('#content').text().trim()).to.equal('Hello world!');
- });
+ expect($('#default-self-closing').text().trim()).to.equal('Fallback');
+ expect($('#default-empty').text().trim()).to.equal('Fallback');
+ expect($('#zero').text().trim()).to.equal('0');
+ expect($('#false').text().trim()).to.equal('');
+ expect($('#string').text().trim()).to.equal('');
+ expect($('#content').text().trim()).to.equal('Hello world!');
+ });
});
diff --git a/packages/astro/test/solid-component.test.js b/packages/astro/test/solid-component.test.js
index 1166b1515..fa7172d31 100644
--- a/packages/astro/test/solid-component.test.js
+++ b/packages/astro/test/solid-component.test.js
@@ -3,52 +3,52 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Solid component', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- devOptions: {
- port: 3006,
- },
- projectRoot: './fixtures/solid-component/',
- renderers: ['@astrojs/renderer-solid'],
- });
- });
-
- describe('build', () => {
- before(async () => {
- await fixture.build();
- });
-
- it('Can load a component', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
-
- // test 1: Works
- expect($('.hello')).to.have.lengthOf(1);
- });
- });
-
- describe('dev', () => {
- let devServer;
-
- before(async () => {
- devServer = await fixture.startDevServer();
- });
-
- after(async () => {
- devServer & devServer.stop();
- });
-
- it('scripts proxy correctly', async () => {
- const html = await fixture.fetch('/').then((res) => res.text());
- const $ = cheerio.load(html);
-
- for (const script of $('script').toArray()) {
- const { src } = script.attribs;
- if (!src) continue;
- expect((await fixture.fetch(src)).status, `404: ${src}`).to.equal(200);
- }
- });
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ devOptions: {
+ port: 3006,
+ },
+ projectRoot: './fixtures/solid-component/',
+ renderers: ['@astrojs/renderer-solid'],
+ });
+ });
+
+ describe('build', () => {
+ before(async () => {
+ await fixture.build();
+ });
+
+ it('Can load a component', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Works
+ expect($('.hello')).to.have.lengthOf(1);
+ });
+ });
+
+ describe('dev', () => {
+ let devServer;
+
+ before(async () => {
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ devServer & devServer.stop();
+ });
+
+ it('scripts proxy correctly', async () => {
+ const html = await fixture.fetch('/').then((res) => res.text());
+ const $ = cheerio.load(html);
+
+ for (const script of $('script').toArray()) {
+ const { src } = script.attribs;
+ if (!src) continue;
+ expect((await fixture.fetch(src)).status, `404: ${src}`).to.equal(200);
+ }
+ });
+ });
});
diff --git a/packages/astro/test/svelte-component.test.js b/packages/astro/test/svelte-component.test.js
index 35da190ad..ef350af4d 100644
--- a/packages/astro/test/svelte-component.test.js
+++ b/packages/astro/test/svelte-component.test.js
@@ -3,53 +3,53 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Svelte component', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- devOptions: {
- port: 3007,
- },
- projectRoot: './fixtures/svelte-component/',
- renderers: ['@astrojs/renderer-svelte'],
- });
- });
-
- describe('build', () => {
- before(async () => {
- await fixture.build();
- });
-
- it('Works with TypeScript', async () => {
- const html = await fixture.readFile('/typescript/index.html');
- const $ = cheerio.load(html);
-
- expect($('#svelte-ts').text()).to.equal('Hello, TypeScript');
- });
- });
-
- describe('dev', () => {
- let devServer;
-
- before(async () => {
- devServer = await fixture.startDevServer();
- });
-
- after(async () => {
- devServer && (await devServer.stop());
- });
-
- it('scripts proxy correctly', async () => {
- const html = await fixture.fetch('/').then((res) => res.text());
- const $ = cheerio.load(html);
-
- for (const script of $('script').toArray()) {
- const { src } = script.attribs;
-
- if (!src) continue;
-
- expect((await fixture.fetch(src)).status, `404: ${src}`).to.equal(200);
- }
- });
- });
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ devOptions: {
+ port: 3007,
+ },
+ projectRoot: './fixtures/svelte-component/',
+ renderers: ['@astrojs/renderer-svelte'],
+ });
+ });
+
+ describe('build', () => {
+ before(async () => {
+ await fixture.build();
+ });
+
+ it('Works with TypeScript', async () => {
+ const html = await fixture.readFile('/typescript/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#svelte-ts').text()).to.equal('Hello, TypeScript');
+ });
+ });
+
+ describe('dev', () => {
+ let devServer;
+
+ before(async () => {
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
+
+ it('scripts proxy correctly', async () => {
+ const html = await fixture.fetch('/').then((res) => res.text());
+ const $ = cheerio.load(html);
+
+ for (const script of $('script').toArray()) {
+ const { src } = script.attribs;
+
+ if (!src) continue;
+
+ expect((await fixture.fetch(src)).status, `404: ${src}`).to.equal(200);
+ }
+ });
+ });
});
diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js
index e4eb2c336..49775c3b4 100644
--- a/packages/astro/test/test-utils.js
+++ b/packages/astro/test/test-utils.js
@@ -38,43 +38,43 @@ import preview from '../dist/core/preview/index.js';
* .preview() - Async. Starts a preview server. Note this can’t be running in same fixture as .dev() as they share ports. Also, you must call `server.close()` before test exit
*/
export async function loadFixture(inlineConfig) {
- if (!inlineConfig || !inlineConfig.projectRoot) throw new Error("Must provide { projectRoot: './fixtures/...' }");
+ if (!inlineConfig || !inlineConfig.projectRoot) throw new Error("Must provide { projectRoot: './fixtures/...' }");
- // load config
- let cwd = inlineConfig.projectRoot;
- if (typeof cwd === 'string') {
- try {
- cwd = new URL(cwd.replace(/\/?$/, '/'));
- } catch (err1) {
- cwd = new URL(cwd.replace(/\/?$/, '/'), import.meta.url);
- }
- }
+ // load config
+ let cwd = inlineConfig.projectRoot;
+ if (typeof cwd === 'string') {
+ try {
+ cwd = new URL(cwd.replace(/\/?$/, '/'));
+ } catch (err1) {
+ cwd = new URL(cwd.replace(/\/?$/, '/'), import.meta.url);
+ }
+ }
- // merge configs
- if (!inlineConfig.buildOptions) inlineConfig.buildOptions = {};
- if (inlineConfig.buildOptions.sitemap === undefined) inlineConfig.buildOptions.sitemap = false;
- if (!inlineConfig.devOptions) inlineConfig.devOptions = {};
- let config = await loadConfig({ cwd: fileURLToPath(cwd) });
- config = merge(config, { ...inlineConfig, projectRoot: cwd });
+ // merge configs
+ if (!inlineConfig.buildOptions) inlineConfig.buildOptions = {};
+ if (inlineConfig.buildOptions.sitemap === undefined) inlineConfig.buildOptions.sitemap = false;
+ if (!inlineConfig.devOptions) inlineConfig.devOptions = {};
+ let config = await loadConfig({ cwd: fileURLToPath(cwd) });
+ config = merge(config, { ...inlineConfig, projectRoot: cwd });
- return {
- build: (opts = {}) => build(config, { mode: 'development', logging: 'error', ...opts }),
- startDevServer: async (opts = {}) => {
- const devServer = await dev(config, { logging: 'error', ...opts });
- config.devOptions.port = devServer.port; // update port
- inlineConfig.devOptions.port = devServer.port;
- return devServer;
- },
- config,
- fetch: (url, init) => fetch(`http://${config.devOptions.hostname}:${config.devOptions.port}${url.replace(/^\/?/, '/')}`, init),
- preview: async (opts = {}) => {
- const previewServer = await preview(config, { logging: 'error', ...opts });
- inlineConfig.devOptions.port = previewServer.port; // update port for fetch
- return previewServer;
- },
- readFile: (filePath) => fs.promises.readFile(new URL(filePath.replace(/^\//, ''), config.dist), 'utf8'),
- readdir: (fp) => fs.promises.readdir(new URL(fp.replace(/^\//, ''), config.dist)),
- };
+ return {
+ build: (opts = {}) => build(config, { mode: 'development', logging: 'error', ...opts }),
+ startDevServer: async (opts = {}) => {
+ const devServer = await dev(config, { logging: 'error', ...opts });
+ config.devOptions.port = devServer.port; // update port
+ inlineConfig.devOptions.port = devServer.port;
+ return devServer;
+ },
+ config,
+ fetch: (url, init) => fetch(`http://${config.devOptions.hostname}:${config.devOptions.port}${url.replace(/^\/?/, '/')}`, init),
+ preview: async (opts = {}) => {
+ const previewServer = await preview(config, { logging: 'error', ...opts });
+ inlineConfig.devOptions.port = previewServer.port; // update port for fetch
+ return previewServer;
+ },
+ readFile: (filePath) => fs.promises.readFile(new URL(filePath.replace(/^\//, ''), config.dist), 'utf8'),
+ readdir: (fp) => fs.promises.readdir(new URL(fp.replace(/^\//, ''), config.dist)),
+ };
}
/**
@@ -84,28 +84,28 @@ export async function loadFixture(inlineConfig) {
* @returns {Object}
*/
function merge(a, b) {
- const allKeys = new Set([...Object.keys(a), ...Object.keys(b)]);
- const c = {};
- for (const k of allKeys) {
- const needsObjectMerge =
- typeof a[k] === 'object' && typeof b[k] === 'object' && (Object.keys(a[k]).length || Object.keys(b[k]).length) && !Array.isArray(a[k]) && !Array.isArray(b[k]);
- if (needsObjectMerge) {
- c[k] = merge(a[k] || {}, b[k] || {});
- continue;
- }
- c[k] = a[k];
- if (b[k] !== undefined) c[k] = b[k];
- }
- return c;
+ const allKeys = new Set([...Object.keys(a), ...Object.keys(b)]);
+ const c = {};
+ for (const k of allKeys) {
+ const needsObjectMerge =
+ typeof a[k] === 'object' && typeof b[k] === 'object' && (Object.keys(a[k]).length || Object.keys(b[k]).length) && !Array.isArray(a[k]) && !Array.isArray(b[k]);
+ if (needsObjectMerge) {
+ c[k] = merge(a[k] || {}, b[k] || {});
+ continue;
+ }
+ c[k] = a[k];
+ if (b[k] !== undefined) c[k] = b[k];
+ }
+ return c;
}
const cliPath = fileURLToPath(new URL('../astro.js', import.meta.url));
/** Returns a process running the Astro CLI. */
export function cli(/** @type {string[]} */ ...args) {
- const spawned = execa('node', [cliPath, ...args]);
+ const spawned = execa('node', [cliPath, ...args]);
- spawned.stdout.setEncoding('utf8');
+ spawned.stdout.setEncoding('utf8');
- return spawned;
+ return spawned;
}
diff --git a/packages/astro/test/vue-component.test.js b/packages/astro/test/vue-component.test.js
index d4928cf1f..a02a85a3f 100644
--- a/packages/astro/test/vue-component.test.js
+++ b/packages/astro/test/vue-component.test.js
@@ -3,66 +3,66 @@ import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Vue component', () => {
- let fixture;
+ let fixture;
- before(async () => {
- fixture = await loadFixture({
- devOptions: {
- port: 3005,
- },
- projectRoot: './fixtures/vue-component/',
- renderers: ['@astrojs/renderer-vue'],
- });
- });
+ before(async () => {
+ fixture = await loadFixture({
+ devOptions: {
+ port: 3005,
+ },
+ projectRoot: './fixtures/vue-component/',
+ renderers: ['@astrojs/renderer-vue'],
+ });
+ });
- describe('build', () => {
- before(async () => {
- await fixture.build();
- });
+ describe('build', () => {
+ before(async () => {
+ await fixture.build();
+ });
- it('Can load Vue', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerio.load(html);
+ it('Can load Vue', async () => {
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
- const allPreValues = $('pre')
- .toArray()
- .map((el) => $(el).text());
+ const allPreValues = $('pre')
+ .toArray()
+ .map((el) => $(el).text());
- // test 1: renders all components correctly
- expect(allPreValues).to.deep.equal(['0', '1', '10', '100', '1000']);
+ // test 1: renders all components correctly
+ expect(allPreValues).to.deep.equal(['0', '1', '10', '100', '1000']);
- // test 2: renders 3 <astro-root>s
- expect($('astro-root')).to.have.lengthOf(4);
+ // test 2: renders 3 <astro-root>s
+ expect($('astro-root')).to.have.lengthOf(4);
- // test 3: all <astro-root>s have uid attributes
- expect($('astro-root[uid]')).to.have.lengthOf(4);
+ // test 3: all <astro-root>s have uid attributes
+ expect($('astro-root[uid]')).to.have.lengthOf(4);
- // test 5: all <astro-root>s have unique uid attributes
- const uniqueRootUIDs = $('astro-root').map((i, el) => $(el).attr('uid'));
- expect(new Set(uniqueRootUIDs).size).to.equal(4);
- });
- });
+ // test 5: all <astro-root>s have unique uid attributes
+ const uniqueRootUIDs = $('astro-root').map((i, el) => $(el).attr('uid'));
+ expect(new Set(uniqueRootUIDs).size).to.equal(4);
+ });
+ });
- describe('dev', () => {
- let devServer;
+ describe('dev', () => {
+ let devServer;
- before(async () => {
- devServer = await fixture.startDevServer();
- });
+ before(async () => {
+ devServer = await fixture.startDevServer();
+ });
- after(async () => {
- devServer && (await devServer.stop());
- });
+ after(async () => {
+ devServer && (await devServer.stop());
+ });
- it('scripts proxy correctly', async () => {
- const html = await fixture.fetch('/').then((res) => res.text());
- const $ = cheerio.load(html);
+ it('scripts proxy correctly', async () => {
+ const html = await fixture.fetch('/').then((res) => res.text());
+ const $ = cheerio.load(html);
- for (const script of $('script').toArray()) {
- const { src } = script.attribs;
- if (!src) continue;
- expect((await fixture.fetch(src)).status, `404: ${src}`).to.equal(200);
- }
- });
- });
+ for (const script of $('script').toArray()) {
+ const { src } = script.attribs;
+ if (!src) continue;
+ expect((await fixture.fetch(src)).status, `404: ${src}`).to.equal(200);
+ }
+ });
+ });
});
diff --git a/packages/create-astro/create-astro.mjs b/packages/create-astro/create-astro.mjs
index 975b921dc..be032f3c2 100755
--- a/packages/create-astro/create-astro.mjs
+++ b/packages/create-astro/create-astro.mjs
@@ -6,9 +6,9 @@ const requiredMajorVersion = parseInt(currentVersion.split('.')[0], 10);
const minimumMajorVersion = 12;
if (requiredMajorVersion < minimumMajorVersion) {
- console.error(`Node.js v${currentVersion} is out of date and unsupported!`);
- console.error(`Please use Node.js v${minimumMajorVersion} or higher.`);
- process.exit(1);
+ console.error(`Node.js v${currentVersion} is out of date and unsupported!`);
+ console.error(`Please use Node.js v${minimumMajorVersion} or higher.`);
+ process.exit(1);
}
import('./dist/index.js').then(({ main }) => main());
diff --git a/packages/create-astro/src/config.ts b/packages/create-astro/src/config.ts
index c8698b2d8..d9f0cb28d 100644
--- a/packages/create-astro/src/config.ts
+++ b/packages/create-astro/src/config.ts
@@ -1,6 +1,6 @@
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.
// pages: './src/pages', // Path to Astro components, pages, and data
// dist: './dist', // When running \`astro build\`, path to final static output
@@ -13,11 +13,11 @@ export const createConfig = ({ renderers }: { renderers: string[] }) => {
// hostname: 'localhost', // The hostname to run the dev server on.
// port: 3000, // The port to run the dev server on.
},`,
- ` 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')
+ .map((ln, i) => (i !== 0 ? ` ${ln}` : ln))
+ .join('\n')},`,
+ `};
`,
- ].join('\n');
+ ].join('\n');
};
diff --git a/packages/create-astro/src/frameworks.ts b/packages/create-astro/src/frameworks.ts
index 5b1546d48..22a53a296 100644
--- a/packages/create-astro/src/frameworks.ts
+++ b/packages/create-astro/src/frameworks.ts
@@ -1,7 +1,7 @@
export const COUNTER_COMPONENTS = {
- '@astrojs/renderer-preact': {
- filename: `src/components/PreactCounter.jsx`,
- content: `import { useState } from 'preact/hooks';
+ '@astrojs/renderer-preact': {
+ filename: `src/components/PreactCounter.jsx`,
+ content: `import { useState } from 'preact/hooks';
export default function PreactCounter() {
const [count, setCount] = useState(0);
@@ -17,10 +17,10 @@ export default function PreactCounter() {
);
}
`,
- },
- '@astrojs/renderer-react': {
- filename: `src/components/ReactCounter.jsx`,
- content: `import { useState } from 'react';
+ },
+ '@astrojs/renderer-react': {
+ filename: `src/components/ReactCounter.jsx`,
+ content: `import { useState } from 'react';
export default function ReactCounter() {
const [count, setCount] = useState(0);
@@ -36,10 +36,10 @@ export default function ReactCounter() {
);
}
`,
- },
- '@astrojs/renderer-solid': {
- filename: `src/components/SolidCounter.jsx`,
- content: `import { createSignal } from "solid-js";
+ },
+ '@astrojs/renderer-solid': {
+ filename: `src/components/SolidCounter.jsx`,
+ content: `import { createSignal } from "solid-js";
export default function SolidCounter() {
const [count, setCount] = createSignal(0);
@@ -55,10 +55,10 @@ export default function SolidCounter() {
);
}
`,
- },
- '@astrojs/renderer-svelte': {
- filename: `src/components/SvelteCounter.svelte`,
- content: `<script>
+ },
+ '@astrojs/renderer-svelte': {
+ filename: `src/components/SvelteCounter.svelte`,
+ content: `<script>
let count = 0;
function add() {
@@ -76,10 +76,10 @@ export default function SolidCounter() {
<button on:click={add}>+</button>
</div>
`,
- },
- '@astrojs/renderer-vue': {
- filename: `src/components/VueCounter.vue`,
- content: `<template>
+ },
+ '@astrojs/renderer-vue': {
+ filename: `src/components/VueCounter.vue`,
+ content: `<template>
<div id="vue" class="counter">
<button @click="subtract()">-</button>
<pre>{{ count }}</pre>
@@ -104,28 +104,28 @@ export default {
}
</script>
`,
- },
+ },
};
export const FRAMEWORKS = [
- {
- title: 'Preact',
- value: '@astrojs/renderer-preact',
- },
- {
- title: 'React',
- value: '@astrojs/renderer-react',
- },
- {
- title: 'Solid',
- value: '@astrojs/renderer-solid',
- },
- {
- title: 'Svelte',
- value: '@astrojs/renderer-svelte',
- },
- {
- title: 'Vue',
- value: '@astrojs/renderer-vue',
- },
+ {
+ title: 'Preact',
+ value: '@astrojs/renderer-preact',
+ },
+ {
+ title: 'React',
+ value: '@astrojs/renderer-react',
+ },
+ {
+ title: 'Solid',
+ value: '@astrojs/renderer-solid',
+ },
+ {
+ title: 'Svelte',
+ value: '@astrojs/renderer-svelte',
+ },
+ {
+ title: 'Vue',
+ value: '@astrojs/renderer-vue',
+ },
];
diff --git a/packages/create-astro/src/index.ts b/packages/create-astro/src/index.ts
index d4bdb78ca..898d6b248 100644
--- a/packages/create-astro/src/index.ts
+++ b/packages/create-astro/src/index.ts
@@ -18,12 +18,12 @@ const args = yargs(cleanArgv, { array: ['renderers'] });
prompts.override(args);
export function mkdirp(dir: string) {
- try {
- fs.mkdirSync(dir, { recursive: true });
- } catch (e: any) {
- if (e.code === 'EEXIST') return;
- throw e;
- }
+ try {
+ fs.mkdirSync(dir, { recursive: true });
+ } catch (e: any) {
+ if (e.code === 'EEXIST') return;
+ throw e;
+ }
}
const { version } = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
@@ -31,174 +31,174 @@ const { version } = JSON.parse(fs.readFileSync(new URL('../package.json', import
const POSTPROCESS_FILES = ['package.json', 'astro.config.mjs', 'CHANGELOG.md']; // some files need processing after copying.
export async function main() {
- console.log(`\n${bold('Welcome to Astro!')} ${gray(`(create-astro v${version})`)}`);
- console.log(`If you encounter a problem, visit ${cyan('https://github.com/withastro/astro/issues')} to search or file a new issue.\n`);
-
- console.log(`${green(`>`)} ${gray(`Prepare for liftoff.`)}`);
- console.log(`${green(`>`)} ${gray(`Gathering mission details...`)}`);
-
- const cwd = args['_'][2] || '.';
- if (fs.existsSync(cwd)) {
- if (fs.readdirSync(cwd).length > 0) {
- const response = await prompts({
- type: 'confirm',
- name: 'forceOverwrite',
- message: 'Directory not empty. Continue [force overwrite]?',
- initial: false,
- });
- if (!response.forceOverwrite) {
- process.exit(1);
- }
- mkdirp(cwd);
- }
- } else {
- mkdirp(cwd);
- }
-
- const options = /** @type {import('./types/internal').Options} */ await prompts([
- {
- type: 'select',
- name: 'template',
- message: 'Which app template would you like to use?',
- choices: TEMPLATES,
- },
- ]);
-
- if (!options.template) {
- process.exit(1);
- }
-
- const hash = args.commit ? `#${args.commit}` : '';
-
- const templateTarget = options.template.includes('/') ? options.template : `withastro/astro/examples/${options.template}#latest`;
-
- const emitter = degit(`${templateTarget}${hash}`, {
- cache: false,
- force: true,
- verbose: false,
- });
-
- const selectedTemplate = TEMPLATES.find((template) => template.value === options.template);
- let renderers: string[] = [];
-
- if (selectedTemplate?.renderers === true) {
- const result = /** @type {import('./types/internal').Options} */ await prompts([
- {
- type: 'multiselect',
- name: 'renderers',
- message: 'Which frameworks would you like to use?',
- choices: FRAMEWORKS,
- },
- ]);
- renderers = result.renderers;
- } else if (selectedTemplate?.renderers && Array.isArray(selectedTemplate.renderers) && selectedTemplate.renderers.length) {
- renderers = selectedTemplate.renderers;
- const titles = renderers.map((renderer) => FRAMEWORKS.find((item) => item.value === renderer)?.title).join(', ');
- console.log(`${green(`✔`)} ${bold(`Using template's default renderers`)} ${gray('›')} ${titles}`);
- }
-
- // Copy
- try {
- // emitter.on('info', info => { console.log(info.message) });
- console.log(`${green(`>`)} ${gray(`Copying project files...`)}`);
- await emitter.clone(cwd);
- } catch (err: any) {
- // degit is compiled, so the stacktrace is pretty noisy. Just report the message.
- console.error(red(err.message));
-
- // Warning for issue #655
- if (err.message === 'zlib: unexpected end of file') {
- console.log(yellow("This seems to be a cache related problem. Remove the folder '~/.degit/github/snowpackjs' to fix this error."));
- console.log(yellow('For more information check out this issue: https://github.com/withastro/astro/issues/655'));
- }
-
- // Helpful message when encountering the "could not find commit hash for ..." error
- if (err.code === 'MISSING_REF') {
- console.log(yellow("This seems to be an issue with degit. Please check if you have 'git' installed on your system, and install it if you don't have (https://git-scm.com)."));
- console.log(yellow("If you do have 'git' installed, please file a new issue here: https://github.com/withastro/astro/issues"));
- }
- process.exit(1);
- }
-
- // Post-process in parallel
- await Promise.all(
- POSTPROCESS_FILES.map(async (file) => {
- const fileLoc = path.resolve(path.join(cwd, file));
-
- switch (file) {
- case 'CHANGELOG.md': {
- if (fs.existsSync(fileLoc)) {
- await fs.promises.unlink(fileLoc);
- }
- break;
- }
- case 'astro.config.mjs': {
- 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;
- }
- }
- })
- );
-
- // Inject framework components into starter template
- if (selectedTemplate?.value === 'starter') {
- let importStatements: string[] = [];
- let components: string[] = [];
- await Promise.all(
- renderers.map(async (renderer) => {
- const component = COUNTER_COMPONENTS[renderer as keyof typeof COUNTER_COMPONENTS];
- const componentName = path.basename(component.filename, path.extname(component.filename));
- const absFileLoc = path.resolve(cwd, component.filename);
- importStatements.push(`import ${componentName} from '${component.filename.replace(/^src/, '..')}';`);
- components.push(`<${componentName} client:visible />`);
- await fs.promises.writeFile(absFileLoc, component.content);
- })
- );
-
- const pageFileLoc = path.resolve(path.join(cwd, 'src', 'pages', 'index.astro'));
- const content = (await fs.promises.readFile(pageFileLoc)).toString();
- const newContent = content
- .replace(/^(\s*)\/\* ASTRO\:COMPONENT_IMPORTS \*\//gm, (_, indent) => {
- return indent + importStatements.join('\n');
- })
- .replace(/^(\s*)<!-- ASTRO:COMPONENT_MARKUP -->/gm, (_, indent) => {
- return components.map((ln) => indent + ln).join('\n');
- });
- await fs.promises.writeFile(pageFileLoc, newContent);
- }
-
- console.log(bold(green('✔') + ' Done!'));
-
- console.log('\nNext steps:');
- let i = 1;
-
- const relative = path.relative(process.cwd(), cwd);
- if (relative !== '') {
- console.log(` ${i++}: ${bold(cyan(`cd ${relative}`))}`);
- }
-
- console.log(` ${i++}: ${bold(cyan('npm install'))} (or pnpm install, yarn, etc)`);
- console.log(` ${i++}: ${bold(cyan('git init && git add -A && git commit -m "Initial commit"'))} (optional step)`);
- console.log(` ${i++}: ${bold(cyan('npm run dev'))} (or pnpm, yarn, etc)`);
-
- console.log(`\nTo close the dev server, hit ${bold(cyan('Ctrl-C'))}`);
- console.log(`\nStuck? Visit us at ${cyan('https://astro.build/chat')}\n`);
+ console.log(`\n${bold('Welcome to Astro!')} ${gray(`(create-astro v${version})`)}`);
+ console.log(`If you encounter a problem, visit ${cyan('https://github.com/withastro/astro/issues')} to search or file a new issue.\n`);
+
+ console.log(`${green(`>`)} ${gray(`Prepare for liftoff.`)}`);
+ console.log(`${green(`>`)} ${gray(`Gathering mission details...`)}`);
+
+ const cwd = args['_'][2] || '.';
+ if (fs.existsSync(cwd)) {
+ if (fs.readdirSync(cwd).length > 0) {
+ const response = await prompts({
+ type: 'confirm',
+ name: 'forceOverwrite',
+ message: 'Directory not empty. Continue [force overwrite]?',
+ initial: false,
+ });
+ if (!response.forceOverwrite) {
+ process.exit(1);
+ }
+ mkdirp(cwd);
+ }
+ } else {
+ mkdirp(cwd);
+ }
+
+ const options = /** @type {import('./types/internal').Options} */ await prompts([
+ {
+ type: 'select',
+ name: 'template',
+ message: 'Which app template would you like to use?',
+ choices: TEMPLATES,
+ },
+ ]);
+
+ if (!options.template) {
+ process.exit(1);
+ }
+
+ const hash = args.commit ? `#${args.commit}` : '';
+
+ const templateTarget = options.template.includes('/') ? options.template : `withastro/astro/examples/${options.template}#latest`;
+
+ const emitter = degit(`${templateTarget}${hash}`, {
+ cache: false,
+ force: true,
+ verbose: false,
+ });
+
+ const selectedTemplate = TEMPLATES.find((template) => template.value === options.template);
+ let renderers: string[] = [];
+
+ if (selectedTemplate?.renderers === true) {
+ const result = /** @type {import('./types/internal').Options} */ await prompts([
+ {
+ type: 'multiselect',
+ name: 'renderers',
+ message: 'Which frameworks would you like to use?',
+ choices: FRAMEWORKS,
+ },
+ ]);
+ renderers = result.renderers;
+ } else if (selectedTemplate?.renderers && Array.isArray(selectedTemplate.renderers) && selectedTemplate.renderers.length) {
+ renderers = selectedTemplate.renderers;
+ const titles = renderers.map((renderer) => FRAMEWORKS.find((item) => item.value === renderer)?.title).join(', ');
+ console.log(`${green(`✔`)} ${bold(`Using template's default renderers`)} ${gray('›')} ${titles}`);
+ }
+
+ // Copy
+ try {
+ // emitter.on('info', info => { console.log(info.message) });
+ console.log(`${green(`>`)} ${gray(`Copying project files...`)}`);
+ await emitter.clone(cwd);
+ } catch (err: any) {
+ // degit is compiled, so the stacktrace is pretty noisy. Just report the message.
+ console.error(red(err.message));
+
+ // Warning for issue #655
+ if (err.message === 'zlib: unexpected end of file') {
+ console.log(yellow("This seems to be a cache related problem. Remove the folder '~/.degit/github/snowpackjs' to fix this error."));
+ console.log(yellow('For more information check out this issue: https://github.com/withastro/astro/issues/655'));
+ }
+
+ // Helpful message when encountering the "could not find commit hash for ..." error
+ if (err.code === 'MISSING_REF') {
+ console.log(yellow("This seems to be an issue with degit. Please check if you have 'git' installed on your system, and install it if you don't have (https://git-scm.com)."));
+ console.log(yellow("If you do have 'git' installed, please file a new issue here: https://github.com/withastro/astro/issues"));
+ }
+ process.exit(1);
+ }
+
+ // Post-process in parallel
+ await Promise.all(
+ POSTPROCESS_FILES.map(async (file) => {
+ const fileLoc = path.resolve(path.join(cwd, file));
+
+ switch (file) {
+ case 'CHANGELOG.md': {
+ if (fs.existsSync(fileLoc)) {
+ await fs.promises.unlink(fileLoc);
+ }
+ break;
+ }
+ case 'astro.config.mjs': {
+ 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;
+ }
+ }
+ })
+ );
+
+ // Inject framework components into starter template
+ if (selectedTemplate?.value === 'starter') {
+ let importStatements: string[] = [];
+ let components: string[] = [];
+ await Promise.all(
+ renderers.map(async (renderer) => {
+ const component = COUNTER_COMPONENTS[renderer as keyof typeof COUNTER_COMPONENTS];
+ const componentName = path.basename(component.filename, path.extname(component.filename));
+ const absFileLoc = path.resolve(cwd, component.filename);
+ importStatements.push(`import ${componentName} from '${component.filename.replace(/^src/, '..')}';`);
+ components.push(`<${componentName} client:visible />`);
+ await fs.promises.writeFile(absFileLoc, component.content);
+ })
+ );
+
+ const pageFileLoc = path.resolve(path.join(cwd, 'src', 'pages', 'index.astro'));
+ const content = (await fs.promises.readFile(pageFileLoc)).toString();
+ const newContent = content
+ .replace(/^(\s*)\/\* ASTRO\:COMPONENT_IMPORTS \*\//gm, (_, indent) => {
+ return indent + importStatements.join('\n');
+ })
+ .replace(/^(\s*)<!-- ASTRO:COMPONENT_MARKUP -->/gm, (_, indent) => {
+ return components.map((ln) => indent + ln).join('\n');
+ });
+ await fs.promises.writeFile(pageFileLoc, newContent);
+ }
+
+ console.log(bold(green('✔') + ' Done!'));
+
+ console.log('\nNext steps:');
+ let i = 1;
+
+ const relative = path.relative(process.cwd(), cwd);
+ if (relative !== '') {
+ console.log(` ${i++}: ${bold(cyan(`cd ${relative}`))}`);
+ }
+
+ console.log(` ${i++}: ${bold(cyan('npm install'))} (or pnpm install, yarn, etc)`);
+ console.log(` ${i++}: ${bold(cyan('git init && git add -A && git commit -m "Initial commit"'))} (optional step)`);
+ console.log(` ${i++}: ${bold(cyan('npm run dev'))} (or pnpm, yarn, etc)`);
+
+ console.log(`\nTo close the dev server, hit ${bold(cyan('Ctrl-C'))}`);
+ console.log(`\nStuck? Visit us at ${cyan('https://astro.build/chat')}\n`);
}
diff --git a/packages/create-astro/src/templates.ts b/packages/create-astro/src/templates.ts
index fc39f5bcc..c4eef854b 100644
--- a/packages/create-astro/src/templates.ts
+++ b/packages/create-astro/src/templates.ts
@@ -1,32 +1,32 @@
export const TEMPLATES = [
- {
- title: 'Starter Kit (Generic)',
- value: 'starter',
- renderers: true,
- },
- {
- title: 'Blog',
- value: 'blog',
- renderers: ['@astrojs/renderer-preact'],
- },
- {
- title: 'Documentation',
- value: 'docs',
- renderers: ['@astrojs/renderer-preact'],
- },
- {
- title: 'Portfolio',
- value: 'portfolio',
- renderers: ['@astrojs/renderer-preact'],
- },
- {
- title: 'Portfolio Svelte',
- value: 'portfolio-svelte',
- renderers: ['@astrojs/renderer-svelte'],
- },
- {
- title: 'Minimal',
- value: 'minimal',
- renderers: [],
- },
+ {
+ title: 'Starter Kit (Generic)',
+ value: 'starter',
+ renderers: true,
+ },
+ {
+ title: 'Blog',
+ value: 'blog',
+ renderers: ['@astrojs/renderer-preact'],
+ },
+ {
+ title: 'Documentation',
+ value: 'docs',
+ renderers: ['@astrojs/renderer-preact'],
+ },
+ {
+ title: 'Portfolio',
+ value: 'portfolio',
+ renderers: ['@astrojs/renderer-preact'],
+ },
+ {
+ title: 'Portfolio Svelte',
+ value: 'portfolio-svelte',
+ renderers: ['@astrojs/renderer-svelte'],
+ },
+ {
+ title: 'Minimal',
+ value: 'minimal',
+ renderers: [],
+ },
];
diff --git a/packages/create-astro/test/create-astro.test.js b/packages/create-astro/test/create-astro.test.js
index 1395fe463..b856c640b 100644
--- a/packages/create-astro/test/create-astro.test.js
+++ b/packages/create-astro/test/create-astro.test.js
@@ -9,125 +9,125 @@ import { GITHUB_SHA, FIXTURES_DIR } from './helpers.js';
// helpers
async function fetch(url) {
- return new Promise((resolve, reject) => {
- http
- .get(url, (res) => {
- // not OK
- if (res.statusCode !== 200) {
- reject(res.statusCode);
- return;
- }
-
- // OK
- let body = '';
- res.on('data', (chunk) => {
- body += chunk;
- });
- res.on('end', () => resolve({ statusCode: res.statusCode, body }));
- })
- .on('error', (err) => {
- // other error
- reject(err);
- });
- });
+ return new Promise((resolve, reject) => {
+ http
+ .get(url, (res) => {
+ // not OK
+ if (res.statusCode !== 200) {
+ reject(res.statusCode);
+ return;
+ }
+
+ // OK
+ let body = '';
+ res.on('data', (chunk) => {
+ body += chunk;
+ });
+ res.on('end', () => resolve({ statusCode: res.statusCode, body }));
+ })
+ .on('error', (err) => {
+ // other error
+ reject(err);
+ });
+ });
}
function assert(a, b, message) {
- if (a !== b) throw new Error(red(`✘ ${message}`));
+ if (a !== b) throw new Error(red(`✘ ${message}`));
}
async function testTemplate(template) {
- const templateDir = path.join(FIXTURES_DIR, template);
-
- // test 1: install
- const DOES_HAVE = ['.gitignore', 'package.json', 'public', 'src'];
- const DOES_NOT_HAVE = ['.git', 'meta.json'];
-
- // test 1a: expect template contains essential files & folders
- for (const file of DOES_HAVE) {
- assert(fs.existsSync(path.join(templateDir, file)), true, `[${template}] has ${file}`);
- }
- // test 1b: expect template DOES NOT contain files supposed to be stripped away
- for (const file of DOES_NOT_HAVE) {
- assert(fs.existsSync(path.join(templateDir, file)), false, `[${template}] cleaned up ${file}`);
- }
-
- // test 2: build
- const MUST_HAVE_FILES = ['index.html', '_astro'];
- await execa('npm', ['run', 'build'], { cwd: templateDir });
- const builtFiles = await glob('**/*', { cwd: path.join(templateDir, 'dist') });
- // test 2a: expect all files built successfully
- for (const file of MUST_HAVE_FILES) {
- assert(builtFiles.includes(file), true, `[${template}] built ${file}`);
- }
-
- // test 3: dev server (should happen after build so dependency install can be reused)
-
- // TODO: fix dev server test in CI
- if (process.env.CI === true) {
- return;
- }
-
- // start dev server in background & wait until ready
- const templateIndex = TEMPLATES.findIndex(({ value }) => value === template);
- const port = 3000 + templateIndex; // use different port per-template
- const devServer = execa('npm', ['run', 'start', '--', '--port', port], { cwd: templateDir });
- let sigkill = setTimeout(() => {
- throw new Error(`Dev server failed to start`); // if 10s has gone by with no update, kill process
- }, 10000);
-
- // read stdout until "Server started" appears
- await new Promise((resolve, reject) => {
- devServer.stdout.on('data', (data) => {
- clearTimeout(sigkill);
- sigkill = setTimeout(() => {
- reject(`Dev server failed to start`);
- }, 10000);
- if (data.toString('utf8').includes('Server started')) resolve();
- });
- devServer.stderr.on('data', (data) => {
- reject(data.toString('utf8'));
- });
- });
- clearTimeout(sigkill); // done!
-
- // send request to dev server that should be ready
- const { statusCode, body } = (await fetch(`http://localhost:${port}`)) || {};
-
- // test 3a: expect 200 status code
- assert(statusCode, 200, `[${template}] 200 response`);
- // test 3b: expect non-empty response
- assert(body.length > 0, true, `[${template}] non-empty response`);
-
- // clean up
- devServer.kill();
+ const templateDir = path.join(FIXTURES_DIR, template);
+
+ // test 1: install
+ const DOES_HAVE = ['.gitignore', 'package.json', 'public', 'src'];
+ const DOES_NOT_HAVE = ['.git', 'meta.json'];
+
+ // test 1a: expect template contains essential files & folders
+ for (const file of DOES_HAVE) {
+ assert(fs.existsSync(path.join(templateDir, file)), true, `[${template}] has ${file}`);
+ }
+ // test 1b: expect template DOES NOT contain files supposed to be stripped away
+ for (const file of DOES_NOT_HAVE) {
+ assert(fs.existsSync(path.join(templateDir, file)), false, `[${template}] cleaned up ${file}`);
+ }
+
+ // test 2: build
+ const MUST_HAVE_FILES = ['index.html', '_astro'];
+ await execa('npm', ['run', 'build'], { cwd: templateDir });
+ const builtFiles = await glob('**/*', { cwd: path.join(templateDir, 'dist') });
+ // test 2a: expect all files built successfully
+ for (const file of MUST_HAVE_FILES) {
+ assert(builtFiles.includes(file), true, `[${template}] built ${file}`);
+ }
+
+ // test 3: dev server (should happen after build so dependency install can be reused)
+
+ // TODO: fix dev server test in CI
+ if (process.env.CI === true) {
+ return;
+ }
+
+ // start dev server in background & wait until ready
+ const templateIndex = TEMPLATES.findIndex(({ value }) => value === template);
+ const port = 3000 + templateIndex; // use different port per-template
+ const devServer = execa('npm', ['run', 'start', '--', '--port', port], { cwd: templateDir });
+ let sigkill = setTimeout(() => {
+ throw new Error(`Dev server failed to start`); // if 10s has gone by with no update, kill process
+ }, 10000);
+
+ // read stdout until "Server started" appears
+ await new Promise((resolve, reject) => {
+ devServer.stdout.on('data', (data) => {
+ clearTimeout(sigkill);
+ sigkill = setTimeout(() => {
+ reject(`Dev server failed to start`);
+ }, 10000);
+ if (data.toString('utf8').includes('Server started')) resolve();
+ });
+ devServer.stderr.on('data', (data) => {
+ reject(data.toString('utf8'));
+ });
+ });
+ clearTimeout(sigkill); // done!
+
+ // send request to dev server that should be ready
+ const { statusCode, body } = (await fetch(`http://localhost:${port}`)) || {};
+
+ // test 3a: expect 200 status code
+ assert(statusCode, 200, `[${template}] 200 response`);
+ // test 3b: expect non-empty response
+ assert(body.length > 0, true, `[${template}] non-empty response`);
+
+ // clean up
+ devServer.kill();
}
async function testAll() {
- // setup
- await Promise.all(
- TEMPLATES.map(async ({ value: template }) => {
- // setup: `npm init astro`
- await execa('../../create-astro.mjs', [template, '--template', template, '--commit', GITHUB_SHA, '--force-overwrite'], {
- cwd: FIXTURES_DIR,
- });
- // setup: `npm install` (note: running multiple `yarn`s in parallel in CI will conflict)
- await execa('npm', ['install', '--no-package-lock', '--silent'], { cwd: path.join(FIXTURES_DIR, template) });
- })
- );
-
- // test (note: not parallelized because Snowpack HMR reuses same port in dev)
- for (let n = 0; n < TEMPLATES.length; n += 1) {
- const template = TEMPLATES[n].value;
-
- try {
- await testTemplate(template);
- } catch (err) {
- console.error(red(`✘ [${template}]`));
- throw err;
- }
-
- console.info(green(`✔ [${template}] All tests passed (${n + 1}/${TEMPLATES.length})`));
- }
+ // setup
+ await Promise.all(
+ TEMPLATES.map(async ({ value: template }) => {
+ // setup: `npm init astro`
+ await execa('../../create-astro.mjs', [template, '--template', template, '--commit', GITHUB_SHA, '--force-overwrite'], {
+ cwd: FIXTURES_DIR,
+ });
+ // setup: `npm install` (note: running multiple `yarn`s in parallel in CI will conflict)
+ await execa('npm', ['install', '--no-package-lock', '--silent'], { cwd: path.join(FIXTURES_DIR, template) });
+ })
+ );
+
+ // test (note: not parallelized because Snowpack HMR reuses same port in dev)
+ for (let n = 0; n < TEMPLATES.length; n += 1) {
+ const template = TEMPLATES[n].value;
+
+ try {
+ await testTemplate(template);
+ } catch (err) {
+ console.error(red(`✘ [${template}]`));
+ throw err;
+ }
+
+ console.info(green(`✔ [${template}] All tests passed (${n + 1}/${TEMPLATES.length})`));
+ }
}
testAll();
diff --git a/packages/create-astro/test/external.test.js b/packages/create-astro/test/external.test.js
index 87c50338a..277e498e0 100644
--- a/packages/create-astro/test/external.test.js
+++ b/packages/create-astro/test/external.test.js
@@ -4,24 +4,24 @@ import { FIXTURES_URL } from './helpers.js';
import { existsSync } from 'fs';
async function run(outdir, template) {
- //--template cassidoo/shopify-react-astro
- await execa('../../create-astro.mjs', [outdir, '--template', template, '--force-overwrite'], {
- cwd: FIXTURES_URL.pathname,
- });
+ //--template cassidoo/shopify-react-astro
+ await execa('../../create-astro.mjs', [outdir, '--template', template, '--force-overwrite'], {
+ cwd: FIXTURES_URL.pathname,
+ });
}
const testCases = [['shopify', 'cassidoo/shopify-react-astro']];
async function tests() {
- for (let [dir, tmpl] of testCases) {
- await run(dir, tmpl);
+ for (let [dir, tmpl] of testCases) {
+ await run(dir, tmpl);
- const outPath = new URL('' + dir, FIXTURES_URL);
- assert.ok(existsSync(outPath));
- }
+ const outPath = new URL('' + dir, FIXTURES_URL);
+ assert.ok(existsSync(outPath));
+ }
}
tests().catch((err) => {
- console.error(err);
- process.exit(1);
+ console.error(err);
+ process.exit(1);
});
diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts
index 88033db14..e8242279a 100644
--- a/packages/markdown/remark/src/index.ts
+++ b/packages/markdown/remark/src/index.ts
@@ -23,9 +23,9 @@ export { AstroMarkdownOptions, MarkdownRenderingOptions };
/** Internal utility for rendering a full markdown file and extracting Frontmatter data */
export async function renderMarkdownWithFrontmatter(contents: string, opts?: MarkdownRenderingOptions | null) {
- const { data: frontmatter, content } = matter(contents);
- const value = await renderMarkdown(content, opts);
- return { ...value, frontmatter };
+ const { data: frontmatter, content } = matter(contents);
+ const value = await renderMarkdown(content, opts);
+ return { ...value, frontmatter };
}
export const DEFAULT_REMARK_PLUGINS = ['remark-gfm', 'remark-smartypants'];
@@ -34,63 +34,63 @@ export const DEFAULT_REHYPE_PLUGINS = ['rehype-slug'];
/** Shared utility for rendering markdown */
export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) {
- let { remarkPlugins = [], rehypePlugins = [] } = opts ?? {};
- const scopedClassName = opts?.$?.scopedClassName;
- const mode = opts?.mode ?? 'mdx';
- const isMDX = mode === 'mdx';
- const { headers, rehypeCollectHeaders } = createCollectHeaders();
-
- await Promise.all([loadRemarkExpressions(), loadRemarkJsx()]); // Vite bug: dynamically import() these because of CJS interop (this will cache)
-
- let parser = unified()
- .use(markdown)
- .use(isMDX ? [remarkJsx] : [])
- .use(isMDX ? [remarkExpressions] : [])
- .use([remarkUnwrap]);
-
- if (remarkPlugins.length === 0 && rehypePlugins.length === 0) {
- remarkPlugins = [...DEFAULT_REMARK_PLUGINS];
- rehypePlugins = [...DEFAULT_REHYPE_PLUGINS];
- }
-
- const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
- const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));
-
- loadedRemarkPlugins.forEach(([plugin, opts]) => {
- parser.use([[plugin, opts]]);
- });
-
- if (scopedClassName) {
- parser.use([scopedStyles(scopedClassName)]);
- }
-
- parser.use([remarkPrism(scopedClassName)]);
- parser.use([[markdownToHtml as any, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression', 'mdxJsxTextElement', 'mdxJsxFlowElement'] }]]);
-
- loadedRehypePlugins.forEach(([plugin, opts]) => {
- parser.use([[plugin, opts]]);
- });
-
- parser
- .use(isMDX ? [rehypeJsx] : [])
- .use(isMDX ? [rehypeExpressions] : [])
- .use(isMDX ? [] : [rehypeRaw])
- .use(isMDX ? [rehypeEscape] : [])
- .use(rehypeIslands);
-
- let result: string;
- try {
- const vfile = await parser.use([rehypeCollectHeaders]).use(rehypeStringify, { allowDangerousHtml: true }).process(content);
- result = vfile.toString();
- } catch (err) {
- console.error(err);
- throw err;
- }
-
- return {
- metadata: { headers, source: content, html: result.toString() },
- code: result.toString(),
- };
+ let { remarkPlugins = [], rehypePlugins = [] } = opts ?? {};
+ const scopedClassName = opts?.$?.scopedClassName;
+ const mode = opts?.mode ?? 'mdx';
+ const isMDX = mode === 'mdx';
+ const { headers, rehypeCollectHeaders } = createCollectHeaders();
+
+ await Promise.all([loadRemarkExpressions(), loadRemarkJsx()]); // Vite bug: dynamically import() these because of CJS interop (this will cache)
+
+ let parser = unified()
+ .use(markdown)
+ .use(isMDX ? [remarkJsx] : [])
+ .use(isMDX ? [remarkExpressions] : [])
+ .use([remarkUnwrap]);
+
+ if (remarkPlugins.length === 0 && rehypePlugins.length === 0) {
+ remarkPlugins = [...DEFAULT_REMARK_PLUGINS];
+ rehypePlugins = [...DEFAULT_REHYPE_PLUGINS];
+ }
+
+ const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
+ const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));
+
+ loadedRemarkPlugins.forEach(([plugin, opts]) => {
+ parser.use([[plugin, opts]]);
+ });
+
+ if (scopedClassName) {
+ parser.use([scopedStyles(scopedClassName)]);
+ }
+
+ parser.use([remarkPrism(scopedClassName)]);
+ parser.use([[markdownToHtml as any, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression', 'mdxJsxTextElement', 'mdxJsxFlowElement'] }]]);
+
+ loadedRehypePlugins.forEach(([plugin, opts]) => {
+ parser.use([[plugin, opts]]);
+ });
+
+ parser
+ .use(isMDX ? [rehypeJsx] : [])
+ .use(isMDX ? [rehypeExpressions] : [])
+ .use(isMDX ? [] : [rehypeRaw])
+ .use(isMDX ? [rehypeEscape] : [])
+ .use(rehypeIslands);
+
+ let result: string;
+ try {
+ const vfile = await parser.use([rehypeCollectHeaders]).use(rehypeStringify, { allowDangerousHtml: true }).process(content);
+ result = vfile.toString();
+ } catch (err) {
+ console.error(err);
+ throw err;
+ }
+
+ return {
+ metadata: { headers, source: content, html: result.toString() },
+ code: result.toString(),
+ };
}
export default renderMarkdownWithFrontmatter;
diff --git a/packages/markdown/remark/src/load-plugins.ts b/packages/markdown/remark/src/load-plugins.ts
index 6d30e8361..b1e72ae36 100644
--- a/packages/markdown/remark/src/load-plugins.ts
+++ b/packages/markdown/remark/src/load-plugins.ts
@@ -2,26 +2,26 @@ import * as unified from 'unified';
import type { Plugin, UnifiedPluginImport } from './types';
async function importPlugin(p: string | UnifiedPluginImport): UnifiedPluginImport {
- if (typeof p === 'string') {
- return await import(p);
- }
+ if (typeof p === 'string') {
+ return await import(p);
+ }
- return await p;
+ return await p;
}
export function loadPlugins(items: Plugin[]): Promise<[unified.Plugin] | [unified.Plugin, any]>[] {
- return items.map((p) => {
- return new Promise((resolve, reject) => {
- if (Array.isArray(p)) {
- const [plugin, opts] = p;
- return importPlugin(plugin)
- .then((m) => resolve([m.default, opts]))
- .catch((e) => reject(e));
- }
+ return items.map((p) => {
+ return new Promise((resolve, reject) => {
+ if (Array.isArray(p)) {
+ const [plugin, opts] = p;
+ return importPlugin(plugin)
+ .then((m) => resolve([m.default, opts]))
+ .catch((e) => reject(e));
+ }
- return importPlugin(p)
- .then((m) => resolve([m.default]))
- .catch((e) => reject(e));
- });
- });
+ return importPlugin(p)
+ .then((m) => resolve([m.default]))
+ .catch((e) => reject(e));
+ });
+ });
}
diff --git a/packages/markdown/remark/src/rehype-collect-headers.ts b/packages/markdown/remark/src/rehype-collect-headers.ts
index fc157c9b7..45138f624 100644
--- a/packages/markdown/remark/src/rehype-collect-headers.ts
+++ b/packages/markdown/remark/src/rehype-collect-headers.ts
@@ -4,35 +4,35 @@ import slugger from 'github-slugger';
/** */
export default function createCollectHeaders() {
- const headers: any[] = [];
+ const headers: any[] = [];
- function rehypeCollectHeaders() {
- return function (tree: Root) {
- visit(tree, (node) => {
- if (node.type !== 'element') return;
- const { tagName } = node;
- if (tagName[0] !== 'h') return;
- const [_, level] = tagName.match(/h([0-6])/) ?? [];
- if (!level) return;
- const depth = Number.parseInt(level);
+ function rehypeCollectHeaders() {
+ return function (tree: Root) {
+ visit(tree, (node) => {
+ if (node.type !== 'element') return;
+ const { tagName } = node;
+ if (tagName[0] !== 'h') return;
+ const [_, level] = tagName.match(/h([0-6])/) ?? [];
+ if (!level) return;
+ const depth = Number.parseInt(level);
- let text = '';
+ let text = '';
- visit(node, 'text', (child) => {
- text += child.value;
- });
+ visit(node, 'text', (child) => {
+ text += child.value;
+ });
- let slug = node?.properties?.id || slugger.slug(text);
+ let slug = node?.properties?.id || slugger.slug(text);
- node.properties = node.properties || {};
- node.properties.id = slug;
- headers.push({ depth, slug, text });
- });
- };
- }
+ node.properties = node.properties || {};
+ node.properties.id = slug;
+ headers.push({ depth, slug, text });
+ });
+ };
+ }
- return {
- headers,
- rehypeCollectHeaders,
- };
+ return {
+ headers,
+ rehypeCollectHeaders,
+ };
}
diff --git a/packages/markdown/remark/src/rehype-escape.ts b/packages/markdown/remark/src/rehype-escape.ts
index e0094b463..04c931fbf 100644
--- a/packages/markdown/remark/src/rehype-escape.ts
+++ b/packages/markdown/remark/src/rehype-escape.ts
@@ -1,12 +1,12 @@
import { SKIP, visit } from 'unist-util-visit';
export default function rehypeEscape(): any {
- return function (node: any): any {
- return visit(node, 'element', (el) => {
- if (el.tagName === 'code' || el.tagName === 'pre') {
- el.properties['data-astro-raw'] = true;
- }
- return el;
- });
- };
+ return function (node: any): any {
+ return visit(node, 'element', (el) => {
+ if (el.tagName === 'code' || el.tagName === 'pre') {
+ el.properties['data-astro-raw'] = true;
+ }
+ return el;
+ });
+ };
}
diff --git a/packages/markdown/remark/src/rehype-expressions.ts b/packages/markdown/remark/src/rehype-expressions.ts
index d296c2afe..26d04623d 100644
--- a/packages/markdown/remark/src/rehype-expressions.ts
+++ b/packages/markdown/remark/src/rehype-expressions.ts
@@ -1,12 +1,12 @@
import { map } from 'unist-util-map';
export default function rehypeExpressions(): any {
- return function (node: any): any {
- return map(node, (child) => {
- if (child.type === 'mdxTextExpression') {
- return { type: 'text', value: `{${(child as any).value}}` };
- }
- return child;
- });
- };
+ return function (node: any): any {
+ return map(node, (child) => {
+ if (child.type === 'mdxTextExpression') {
+ return { type: 'text', value: `{${(child as any).value}}` };
+ }
+ return child;
+ });
+ };
}
diff --git a/packages/markdown/remark/src/rehype-islands.ts b/packages/markdown/remark/src/rehype-islands.ts
index e3fa09860..868f9096e 100644
--- a/packages/markdown/remark/src/rehype-islands.ts
+++ b/packages/markdown/remark/src/rehype-islands.ts
@@ -9,27 +9,27 @@ const visit = _visit as (node: any, type: string, callback?: (node: any, index:
// For hydration to work properly, frameworks need the DOM to be the exact same on server/client.
// This reverts some "helpful corrections" that are applied to our perfectly valid HTML!
export default function rehypeIslands(): any {
- return function (node: any): any {
- return visit(node, 'element', (el) => {
- // Bugs only happen inside of <astro-root> islands
- if (el.tagName == 'astro-root') {
- visit(el, 'text', (child, index, parent) => {
- if (child.type === 'text') {
- // Sometimes comments can be trapped as text, which causes them to be escaped
- // This casts them back to real HTML comments
- if (parent && child.value.indexOf('<!--') > -1 && index != null) {
- parent.children.splice(index, 1, { ...child, type: 'comment', value: child.value.replace('<!--', '').replace('-->', '').trim() });
- return [SKIP, index];
- }
- // For some reason `rehype` likes to inject extra linebreaks,
- // but React and Vue throw hydration errors when they see these!
- // This removes any extra linebreaks, which is fine because
- // framework compilers don't preserve them anyway
- child.value = child.value.replace(/\n+/g, '');
- return child;
- }
- });
- }
- });
- };
+ return function (node: any): any {
+ return visit(node, 'element', (el) => {
+ // Bugs only happen inside of <astro-root> islands
+ if (el.tagName == 'astro-root') {
+ visit(el, 'text', (child, index, parent) => {
+ if (child.type === 'text') {
+ // Sometimes comments can be trapped as text, which causes them to be escaped
+ // This casts them back to real HTML comments
+ if (parent && child.value.indexOf('<!--') > -1 && index != null) {
+ parent.children.splice(index, 1, { ...child, type: 'comment', value: child.value.replace('<!--', '').replace('-->', '').trim() });
+ return [SKIP, index];
+ }
+ // For some reason `rehype` likes to inject extra linebreaks,
+ // but React and Vue throw hydration errors when they see these!
+ // This removes any extra linebreaks, which is fine because
+ // framework compilers don't preserve them anyway
+ child.value = child.value.replace(/\n+/g, '');
+ return child;
+ }
+ });
+ }
+ });
+ };
}
diff --git a/packages/markdown/remark/src/rehype-jsx.ts b/packages/markdown/remark/src/rehype-jsx.ts
index 94632efed..cccbd5548 100644
--- a/packages/markdown/remark/src/rehype-jsx.ts
+++ b/packages/markdown/remark/src/rehype-jsx.ts
@@ -2,28 +2,28 @@ import { map } from 'unist-util-map';
const MDX_ELEMENTS = new Set(['mdxJsxFlowElement', 'mdxJsxTextElement']);
export default function rehypeJsx(): any {
- return function (node: any): any {
- return map(node, (child: any) => {
- if (child.type === 'element') {
- return { ...child, tagName: `${child.tagName}` };
- }
- if (MDX_ELEMENTS.has(child.type)) {
- return {
- ...child,
- type: 'element',
- tagName: `${child.name}`,
- properties: child.attributes.reduce((acc: any[], entry: any) => {
- let attr = entry.value;
- if (attr && typeof attr === 'object') {
- attr = `{${attr.value}}`;
- } else if (attr === null) {
- attr = `{true}`;
- }
- return Object.assign(acc, { [entry.name]: attr });
- }, {}),
- };
- }
- return child;
- });
- };
+ return function (node: any): any {
+ return map(node, (child: any) => {
+ if (child.type === 'element') {
+ return { ...child, tagName: `${child.tagName}` };
+ }
+ if (MDX_ELEMENTS.has(child.type)) {
+ return {
+ ...child,
+ type: 'element',
+ tagName: `${child.name}`,
+ properties: child.attributes.reduce((acc: any[], entry: any) => {
+ let attr = entry.value;
+ if (attr && typeof attr === 'object') {
+ attr = `{${attr.value}}`;
+ } else if (attr === null) {
+ attr = `{true}`;
+ }
+ return Object.assign(acc, { [entry.name]: attr });
+ }, {}),
+ };
+ }
+ return child;
+ });
+ };
}
diff --git a/packages/markdown/remark/src/remark-expressions.ts b/packages/markdown/remark/src/remark-expressions.ts
index 0966cc0be..be6bc09ed 100644
--- a/packages/markdown/remark/src/remark-expressions.ts
+++ b/packages/markdown/remark/src/remark-expressions.ts
@@ -4,28 +4,28 @@ let mdxExpressionFromMarkdown: any;
let mdxExpressionToMarkdown: any;
export function remarkExpressions(this: any, options: any) {
- let settings = options || {};
- let data = this.data();
+ let settings = options || {};
+ let data = this.data();
- add('micromarkExtensions', mdxExpression({}));
- add('fromMarkdownExtensions', mdxExpressionFromMarkdown);
- add('toMarkdownExtensions', mdxExpressionToMarkdown);
+ add('micromarkExtensions', mdxExpression({}));
+ add('fromMarkdownExtensions', mdxExpressionFromMarkdown);
+ add('toMarkdownExtensions', mdxExpressionToMarkdown);
- function add(field: any, value: any) {
- /* istanbul ignore if - other extensions. */
- if (data[field]) data[field].push(value);
- else data[field] = [value];
- }
+ function add(field: any, value: any) {
+ /* istanbul ignore if - other extensions. */
+ if (data[field]) data[field].push(value);
+ else data[field] = [value];
+ }
}
export async function loadRemarkExpressions() {
- if (!mdxExpression) {
- const micromarkMdxExpression = await import('micromark-extension-mdx-expression');
- mdxExpression = micromarkMdxExpression.mdxExpression;
- }
- if (!mdxExpressionFromMarkdown || !mdxExpressionToMarkdown) {
- const mdastUtilMdxExpression = await import('mdast-util-mdx-expression');
- mdxExpressionFromMarkdown = mdastUtilMdxExpression.mdxExpressionFromMarkdown;
- mdxExpressionToMarkdown = mdastUtilMdxExpression.mdxExpressionToMarkdown;
- }
+ if (!mdxExpression) {
+ const micromarkMdxExpression = await import('micromark-extension-mdx-expression');
+ mdxExpression = micromarkMdxExpression.mdxExpression;
+ }
+ if (!mdxExpressionFromMarkdown || !mdxExpressionToMarkdown) {
+ const mdastUtilMdxExpression = await import('mdast-util-mdx-expression');
+ mdxExpressionFromMarkdown = mdastUtilMdxExpression.mdxExpressionFromMarkdown;
+ mdxExpressionToMarkdown = mdastUtilMdxExpression.mdxExpressionToMarkdown;
+ }
}
diff --git a/packages/markdown/remark/src/remark-jsx.ts b/packages/markdown/remark/src/remark-jsx.ts
index 7c7333781..637bac9ee 100644
--- a/packages/markdown/remark/src/remark-jsx.ts
+++ b/packages/markdown/remark/src/remark-jsx.ts
@@ -4,28 +4,28 @@ let mdxJsxFromMarkdown: any;
let mdxJsxToMarkdown: any;
export function remarkJsx(this: any, options: any) {
- let settings = options || {};
- let data = this.data();
+ let settings = options || {};
+ let data = this.data();
- // TODO this seems to break adding slugs, no idea why add('micromarkExtensions', mdxJsx({}));
- add('fromMarkdownExtensions', mdxJsxFromMarkdown);
- add('toMarkdownExtensions', mdxJsxToMarkdown);
+ // TODO this seems to break adding slugs, no idea why add('micromarkExtensions', mdxJsx({}));
+ add('fromMarkdownExtensions', mdxJsxFromMarkdown);
+ add('toMarkdownExtensions', mdxJsxToMarkdown);
- function add(field: any, value: any) {
- /* istanbul ignore if - other extensions. */
- if (data[field]) data[field].push(value);
- else data[field] = [value];
- }
+ function add(field: any, value: any) {
+ /* istanbul ignore if - other extensions. */
+ if (data[field]) data[field].push(value);
+ else data[field] = [value];
+ }
}
export async function loadRemarkJsx() {
- if (!mdxJsx) {
- const micromarkMdxJsx = await import('micromark-extension-mdx-jsx');
- mdxJsx = micromarkMdxJsx.mdxJsx;
- }
- if (!mdxJsxFromMarkdown || !mdxJsxToMarkdown) {
- const mdastUtilMdxJsx = await import('mdast-util-mdx-jsx');
- mdxJsxFromMarkdown = mdastUtilMdxJsx.mdxJsxFromMarkdown;
- mdxJsxToMarkdown = mdastUtilMdxJsx.mdxJsxToMarkdown;
- }
+ if (!mdxJsx) {
+ const micromarkMdxJsx = await import('micromark-extension-mdx-jsx');
+ mdxJsx = micromarkMdxJsx.mdxJsx;
+ }
+ if (!mdxJsxFromMarkdown || !mdxJsxToMarkdown) {
+ const mdastUtilMdxJsx = await import('mdast-util-mdx-jsx');
+ mdxJsxFromMarkdown = mdastUtilMdxJsx.mdxJsxFromMarkdown;
+ mdxJsxToMarkdown = mdastUtilMdxJsx.mdxJsxToMarkdown;
+ }
}
diff --git a/packages/markdown/remark/src/remark-prism.ts b/packages/markdown/remark/src/remark-prism.ts
index d7a4ff996..cb6d1cd8e 100644
--- a/packages/markdown/remark/src/remark-prism.ts
+++ b/packages/markdown/remark/src/remark-prism.ts
@@ -7,64 +7,64 @@ const noVisit = new Set(['root', 'html', 'text']);
const languageMap = new Map([['ts', 'typescript']]);
function runHighlighter(lang: string, code: string) {
- let classLanguage = `language-${lang}`;
+ let classLanguage = `language-${lang}`;
- if (lang == null) {
- lang = 'plaintext';
- }
+ if (lang == null) {
+ lang = 'plaintext';
+ }
- const ensureLoaded = (lang: string) => {
- if (lang && !Prism.languages[lang]) {
- loadLanguages([lang]);
- }
- };
+ const ensureLoaded = (lang: string) => {
+ if (lang && !Prism.languages[lang]) {
+ loadLanguages([lang]);
+ }
+ };
- if (languageMap.has(lang)) {
- ensureLoaded(languageMap.get(lang)!);
- } else if (lang === 'astro') {
- ensureLoaded('typescript');
- addAstro(Prism);
- } else {
- ensureLoaded('markup-templating'); // Prism expects this to exist for a number of other langs
- ensureLoaded(lang);
- }
+ if (languageMap.has(lang)) {
+ ensureLoaded(languageMap.get(lang)!);
+ } else if (lang === 'astro') {
+ ensureLoaded('typescript');
+ addAstro(Prism);
+ } else {
+ ensureLoaded('markup-templating'); // Prism expects this to exist for a number of other langs
+ ensureLoaded(lang);
+ }
- if (lang && !Prism.languages[lang]) {
- console.warn(`Unable to load the language: ${lang}`);
- }
+ if (lang && !Prism.languages[lang]) {
+ console.warn(`Unable to load the language: ${lang}`);
+ }
- const grammar = Prism.languages[lang];
- let html = code;
- if (grammar) {
- html = Prism.highlight(code, grammar, lang);
- }
+ const grammar = Prism.languages[lang];
+ let html = code;
+ if (grammar) {
+ html = Prism.highlight(code, grammar, lang);
+ }
- return { classLanguage, html };
+ return { classLanguage, html };
}
type MaybeString = string | null | undefined;
/** */
function transformer(className: MaybeString) {
- return function (tree: any) {
- const visitor = (node: any) => {
- let { lang, value } = node;
- node.type = 'html';
+ return function (tree: any) {
+ const visitor = (node: any) => {
+ let { lang, value } = node;
+ node.type = 'html';
- let { html, classLanguage } = runHighlighter(lang, value);
- let classes = [classLanguage];
- if (className) {
- classes.push(className);
- }
- node.value = `<pre class="${classes.join(' ')}"><code data-astro-raw class="${classLanguage}">${html}</code></pre>`;
- return node;
- };
- return visit(tree, 'code', visitor);
- };
+ let { html, classLanguage } = runHighlighter(lang, value);
+ let classes = [classLanguage];
+ if (className) {
+ classes.push(className);
+ }
+ node.value = `<pre class="${classes.join(' ')}"><code data-astro-raw class="${classLanguage}">${html}</code></pre>`;
+ return node;
+ };
+ return visit(tree, 'code', visitor);
+ };
}
function plugin(className: MaybeString) {
- return transformer.bind(null, className);
+ return transformer.bind(null, className);
}
export default plugin;
diff --git a/packages/markdown/remark/src/remark-scoped-styles.ts b/packages/markdown/remark/src/remark-scoped-styles.ts
index 9ca70c029..ba8780bb7 100644
--- a/packages/markdown/remark/src/remark-scoped-styles.ts
+++ b/packages/markdown/remark/src/remark-scoped-styles.ts
@@ -3,16 +3,16 @@ const noVisit = new Set(['root', 'html', 'text']);
/** */
export default function scopedStyles(className: string) {
- const visitor = (node: any) => {
- if (noVisit.has(node.type)) return;
+ const visitor = (node: any) => {
+ if (noVisit.has(node.type)) return;
- const { data } = node;
- let currentClassName = data?.hProperties?.class ?? '';
- node.data = node.data || {};
- node.data.hProperties = node.data.hProperties || {};
- node.data.hProperties.class = `${className} ${currentClassName}`.trim();
+ const { data } = node;
+ let currentClassName = data?.hProperties?.class ?? '';
+ node.data = node.data || {};
+ node.data.hProperties = node.data.hProperties || {};
+ node.data.hProperties.class = `${className} ${currentClassName}`.trim();
- return node;
- };
- return () => (tree: any) => visit(tree, visitor);
+ return node;
+ };
+ return () => (tree: any) => visit(tree, visitor);
}
diff --git a/packages/markdown/remark/src/remark-slug.ts b/packages/markdown/remark/src/remark-slug.ts
index 4454d1087..6d8ddcf2d 100644
--- a/packages/markdown/remark/src/remark-slug.ts
+++ b/packages/markdown/remark/src/remark-slug.ts
@@ -15,18 +15,18 @@ const slugs = new BananaSlug();
* @type {import('unified').Plugin<void[], Root>}
*/
export default function remarkSlug() {
- return (tree: any) => {
- slugs.reset();
- visit(tree, (node) => {
- console.log(node);
- });
- visit(tree, 'heading', (node) => {
- const data = node.data || (node.data = {});
- const props = /** @type {Properties} */ data.hProperties || (data.hProperties = {});
- let id = props.id;
- id = id ? slugs.slug(String(id), true) : slugs.slug(toString(node));
- data.id = id;
- props.id = id;
- });
- };
+ return (tree: any) => {
+ slugs.reset();
+ visit(tree, (node) => {
+ console.log(node);
+ });
+ visit(tree, 'heading', (node) => {
+ const data = node.data || (node.data = {});
+ const props = /** @type {Properties} */ data.hProperties || (data.hProperties = {});
+ let id = props.id;
+ id = id ? slugs.slug(String(id), true) : slugs.slug(toString(node));
+ data.id = id;
+ props.id = id;
+ });
+ };
}
diff --git a/packages/markdown/remark/src/remark-unwrap.ts b/packages/markdown/remark/src/remark-unwrap.ts
index 15ddb7779..6da2a496a 100644
--- a/packages/markdown/remark/src/remark-unwrap.ts
+++ b/packages/markdown/remark/src/remark-unwrap.ts
@@ -6,33 +6,33 @@ const visit = _visit as (node: any, type: string, callback?: (node: any, index:
// Remove the wrapping paragraph for <astro-root> islands
export default function remarkUnwrap() {
- const astroRootNodes = new Set();
- let insideAstroRoot = false;
+ const astroRootNodes = new Set();
+ let insideAstroRoot = false;
- return (tree: any) => {
- // reset state
- insideAstroRoot = false;
- astroRootNodes.clear();
+ return (tree: any) => {
+ // reset state
+ insideAstroRoot = false;
+ astroRootNodes.clear();
- visit(tree, 'html', (node) => {
- if (node.value.indexOf('<astro-root') > -1 && !insideAstroRoot) {
- insideAstroRoot = true;
- }
- if (node.value.indexOf('</astro-root') > -1 && insideAstroRoot) {
- insideAstroRoot = false;
- }
- astroRootNodes.add(node);
- });
+ visit(tree, 'html', (node) => {
+ if (node.value.indexOf('<astro-root') > -1 && !insideAstroRoot) {
+ insideAstroRoot = true;
+ }
+ if (node.value.indexOf('</astro-root') > -1 && insideAstroRoot) {
+ insideAstroRoot = false;
+ }
+ astroRootNodes.add(node);
+ });
- visit(tree, 'paragraph', (node, index, parent) => {
- if (parent && typeof index === 'number' && containsAstroRootNode(node)) {
- parent.children.splice(index, 1, ...node.children);
- return [SKIP, index];
- }
- });
- };
+ visit(tree, 'paragraph', (node, index, parent) => {
+ if (parent && typeof index === 'number' && containsAstroRootNode(node)) {
+ parent.children.splice(index, 1, ...node.children);
+ return [SKIP, index];
+ }
+ });
+ };
- function containsAstroRootNode(node: any) {
- return node.children.map((child: any) => astroRootNodes.has(child)).reduce((all: boolean, v: boolean) => (all ? all : v), false);
- }
+ function containsAstroRootNode(node: any) {
+ return node.children.map((child: any) => astroRootNodes.has(child)).reduce((all: boolean, v: boolean) => (all ? all : v), false);
+ }
}
diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts
index 8ae7795d0..541d3ff27 100644
--- a/packages/markdown/remark/src/types.ts
+++ b/packages/markdown/remark/src/types.ts
@@ -4,14 +4,14 @@ export type UnifiedPluginImport = Promise<{ default: unified.Plugin }>;
export type Plugin = string | [string, any] | UnifiedPluginImport | [UnifiedPluginImport, any];
export interface AstroMarkdownOptions {
- mode?: 'md' | 'mdx';
- remarkPlugins?: Plugin[];
- rehypePlugins?: Plugin[];
+ mode?: 'md' | 'mdx';
+ remarkPlugins?: Plugin[];
+ rehypePlugins?: Plugin[];
}
export interface MarkdownRenderingOptions extends Partial<AstroMarkdownOptions> {
- /** @internal */
- $?: {
- scopedClassName: string | null;
- };
+ /** @internal */
+ $?: {
+ scopedClassName: string | null;
+ };
}
diff --git a/packages/renderers/renderer-lit/client-shim.js b/packages/renderers/renderer-lit/client-shim.js
index 413d2cbd9..cab3fe4d9 100644
--- a/packages/renderers/renderer-lit/client-shim.js
+++ b/packages/renderers/renderer-lit/client-shim.js
@@ -1,10 +1,10 @@
async function polyfill() {
- const { hydrateShadowRoots } = await import('@webcomponents/template-shadowroot/template-shadowroot.js');
- hydrateShadowRoots(document.body);
+ const { hydrateShadowRoots } = await import('@webcomponents/template-shadowroot/template-shadowroot.js');
+ hydrateShadowRoots(document.body);
}
const polyfillCheckEl = new DOMParser().parseFromString(`<p><template shadowroot="open"></template></p>`, 'text/html', { includeShadowRoots: true }).querySelector('p');
if (!polyfillCheckEl || !polyfillCheckEl.shadowRoot) {
- polyfill();
+ polyfill();
}
diff --git a/packages/renderers/renderer-lit/index.js b/packages/renderers/renderer-lit/index.js
index f08c2fef8..e6abec137 100644
--- a/packages/renderers/renderer-lit/index.js
+++ b/packages/renderers/renderer-lit/index.js
@@ -3,33 +3,33 @@
const NODE_VERSION = parseFloat(process.versions.node);
if (NODE_VERSION < 13.9) {
- throw new Error(`Package @lit-labs/ssr requires Node version v13.9 or higher. Please update Node to use @astrojs/renderer-lit`);
+ throw new Error(`Package @lit-labs/ssr requires Node version v13.9 or higher. Please update Node to use @astrojs/renderer-lit`);
}
export default {
- name: '@astrojs/renderer-lit',
- server: './server.js',
- polyfills: ['./client-shim.js'],
- hydrationPolyfills: ['./hydration-support.js'],
- viteConfig() {
- return {
- optimizeDeps: {
- include: [
- '@astrojs/renderer-lit/client-shim.js',
- '@astrojs/renderer-lit/hydration-support.js',
- '@webcomponents/template-shadowroot/template-shadowroot.js',
- 'lit/experimental-hydrate-support.js',
- ],
- exclude: ['@astrojs/renderer-lit/server.js'],
- },
- ssr: {
- external: [
- 'lit-element/lit-element.js',
- '@lit-labs/ssr/lib/install-global-dom-shim.js',
- '@lit-labs/ssr/lib/render-lit-html.js',
- '@lit-labs/ssr/lib/lit-element-renderer.js',
- ],
- },
- };
- },
+ name: '@astrojs/renderer-lit',
+ server: './server.js',
+ polyfills: ['./client-shim.js'],
+ hydrationPolyfills: ['./hydration-support.js'],
+ viteConfig() {
+ return {
+ optimizeDeps: {
+ include: [
+ '@astrojs/renderer-lit/client-shim.js',
+ '@astrojs/renderer-lit/hydration-support.js',
+ '@webcomponents/template-shadowroot/template-shadowroot.js',
+ 'lit/experimental-hydrate-support.js',
+ ],
+ exclude: ['@astrojs/renderer-lit/server.js'],
+ },
+ ssr: {
+ external: [
+ 'lit-element/lit-element.js',
+ '@lit-labs/ssr/lib/install-global-dom-shim.js',
+ '@lit-labs/ssr/lib/render-lit-html.js',
+ '@lit-labs/ssr/lib/lit-element-renderer.js',
+ ],
+ },
+ };
+ },
};
diff --git a/packages/renderers/renderer-lit/server.js b/packages/renderers/renderer-lit/server.js
index e08b2bf9a..1622ef619 100644
--- a/packages/renderers/renderer-lit/server.js
+++ b/packages/renderers/renderer-lit/server.js
@@ -3,70 +3,70 @@ import '@lit-labs/ssr/lib/render-lit-html.js';
import { LitElementRenderer } from '@lit-labs/ssr/lib/lit-element-renderer.js';
function isCustomElementTag(name) {
- return typeof name === 'string' && /-/.test(name);
+ return typeof name === 'string' && /-/.test(name);
}
function getCustomElementConstructor(name) {
- if (typeof customElements !== 'undefined' && isCustomElementTag(name)) {
- return customElements.get(name) || null;
- }
- return null;
+ if (typeof customElements !== 'undefined' && isCustomElementTag(name)) {
+ return customElements.get(name) || null;
+ }
+ return null;
}
async function isLitElement(Component) {
- const Ctr = getCustomElementConstructor(Component);
- return !!(Ctr && Ctr._$litElement$);
+ const Ctr = getCustomElementConstructor(Component);
+ return !!(Ctr && Ctr._$litElement$);
}
async function check(Component, _props, _children) {
- // Lit doesn't support getting a tagName from a Constructor at this time.
- // So this must be a string at the moment.
- return !!(await isLitElement(Component));
+ // Lit doesn't support getting a tagName from a Constructor at this time.
+ // So this must be a string at the moment.
+ return !!(await isLitElement(Component));
}
function* render(tagName, attrs, children) {
- const instance = new LitElementRenderer(tagName);
+ const instance = new LitElementRenderer(tagName);
- // LitElementRenderer creates a new element instance, so copy over.
- const Ctr = getCustomElementConstructor(tagName);
- for (let [name, value] of Object.entries(attrs)) {
- // check if this is a reactive property
- if (name in Ctr.prototype) {
- instance.setProperty(name, value);
- } else {
- instance.setAttribute(name, value);
- }
- }
+ // LitElementRenderer creates a new element instance, so copy over.
+ const Ctr = getCustomElementConstructor(tagName);
+ for (let [name, value] of Object.entries(attrs)) {
+ // check if this is a reactive property
+ if (name in Ctr.prototype) {
+ instance.setProperty(name, value);
+ } else {
+ instance.setAttribute(name, value);
+ }
+ }
- instance.connectedCallback();
+ instance.connectedCallback();
- yield `<${tagName}`;
- yield* instance.renderAttributes();
- yield `>`;
- const shadowContents = instance.renderShadow({});
- if (shadowContents !== undefined) {
- yield '<template shadowroot="open">';
- yield* shadowContents;
- yield '</template>';
- }
- yield children || ''; // don’t print “undefined” as string
- yield `</${tagName}>`;
+ yield `<${tagName}`;
+ yield* instance.renderAttributes();
+ yield `>`;
+ const shadowContents = instance.renderShadow({});
+ if (shadowContents !== undefined) {
+ yield '<template shadowroot="open">';
+ yield* shadowContents;
+ yield '</template>';
+ }
+ yield children || ''; // don’t print “undefined” as string
+ yield `</${tagName}>`;
}
async function renderToStaticMarkup(Component, props, children) {
- let tagName = Component;
+ let tagName = Component;
- let out = '';
- for (let chunk of render(tagName, props, children)) {
- out += chunk;
- }
+ let out = '';
+ for (let chunk of render(tagName, props, children)) {
+ out += chunk;
+ }
- return {
- html: out,
- };
+ return {
+ html: out,
+ };
}
export default {
- check,
- renderToStaticMarkup,
+ check,
+ renderToStaticMarkup,
};
diff --git a/packages/renderers/renderer-preact/compat/index.js b/packages/renderers/renderer-preact/compat/index.js
index d88b598a7..3f993ec2f 100644
--- a/packages/renderers/renderer-preact/compat/index.js
+++ b/packages/renderers/renderer-preact/compat/index.js
@@ -1,31 +1,31 @@
export default {
- name: '@astrojs/renderer-preact/compat',
- client: '../client.js',
- server: '../server.js',
- jsxImportSource: 'react',
- jsxTransformOptions: async () => {
- const {
- default: { default: jsx },
- } = await import('@babel/plugin-transform-react-jsx');
- return {
- plugins: [jsx({}, { runtime: 'automatic', importSource: 'preact/compat' })],
- };
- },
- viteConfig() {
- return {
- alias: {
- react: 'preact/compat',
- 'react-dom': 'preact/compat',
- },
- resolve: {
- dedupe: ['react', 'react-dom'],
- },
- optimizeDeps: {
- include: ['@astrojs/renderer-preact/client.js', 'preact/compat', 'preact/compat/jsx-runtime', 'preact-render-to-string'],
- },
- ssr: {
- external: ['preact-render-to-string'],
- },
- };
- },
+ name: '@astrojs/renderer-preact/compat',
+ client: '../client.js',
+ server: '../server.js',
+ jsxImportSource: 'react',
+ jsxTransformOptions: async () => {
+ const {
+ default: { default: jsx },
+ } = await import('@babel/plugin-transform-react-jsx');
+ return {
+ plugins: [jsx({}, { runtime: 'automatic', importSource: 'preact/compat' })],
+ };
+ },
+ viteConfig() {
+ return {
+ alias: {
+ react: 'preact/compat',
+ 'react-dom': 'preact/compat',
+ },
+ resolve: {
+ dedupe: ['react', 'react-dom'],
+ },
+ optimizeDeps: {
+ include: ['@astrojs/renderer-preact/client.js', 'preact/compat', 'preact/compat/jsx-runtime', 'preact-render-to-string'],
+ },
+ ssr: {
+ external: ['preact-render-to-string'],
+ },
+ };
+ },
};
diff --git a/packages/renderers/renderer-preact/index.js b/packages/renderers/renderer-preact/index.js
index a1a21a561..59e900ebe 100644
--- a/packages/renderers/renderer-preact/index.js
+++ b/packages/renderers/renderer-preact/index.js
@@ -1,25 +1,25 @@
export default {
- name: '@astrojs/renderer-preact',
- client: './client.js',
- server: './server.js',
- jsxImportSource: 'preact',
- jsxTransformOptions: async () => {
- const {
- default: { default: jsx },
- } = await import('@babel/plugin-transform-react-jsx');
- return {
- plugins: [jsx({}, { runtime: 'automatic', importSource: 'preact' })],
- };
- },
- viteConfig() {
- return {
- optimizeDeps: {
- include: ['@astrojs/renderer-preact/client.js', 'preact', 'preact/jsx-runtime', 'preact-render-to-string'],
- exclude: ['@astrojs/renderer-preact/server.js'],
- },
- ssr: {
- external: ['preact-render-to-string'],
- },
- };
- },
+ name: '@astrojs/renderer-preact',
+ client: './client.js',
+ server: './server.js',
+ jsxImportSource: 'preact',
+ jsxTransformOptions: async () => {
+ const {
+ default: { default: jsx },
+ } = await import('@babel/plugin-transform-react-jsx');
+ return {
+ plugins: [jsx({}, { runtime: 'automatic', importSource: 'preact' })],
+ };
+ },
+ viteConfig() {
+ return {
+ optimizeDeps: {
+ include: ['@astrojs/renderer-preact/client.js', 'preact', 'preact/jsx-runtime', 'preact-render-to-string'],
+ exclude: ['@astrojs/renderer-preact/server.js'],
+ },
+ ssr: {
+ external: ['preact-render-to-string'],
+ },
+ };
+ },
};
diff --git a/packages/renderers/renderer-preact/server.js b/packages/renderers/renderer-preact/server.js
index 696217abb..25b1a1530 100644
--- a/packages/renderers/renderer-preact/server.js
+++ b/packages/renderers/renderer-preact/server.js
@@ -3,33 +3,33 @@ import render from 'preact-render-to-string';
import StaticHtml from './static-html.js';
function check(Component, props, children) {
- if (typeof Component !== 'function') return false;
+ if (typeof Component !== 'function') return false;
- if (Component.prototype != null && typeof Component.prototype.render === 'function') {
- return BaseComponent.isPrototypeOf(Component);
- }
+ if (Component.prototype != null && typeof Component.prototype.render === 'function') {
+ return BaseComponent.isPrototypeOf(Component);
+ }
- try {
- const { html } = renderToStaticMarkup(Component, props, children);
- if (typeof html !== 'string') {
- return false;
- }
+ try {
+ const { html } = renderToStaticMarkup(Component, props, children);
+ if (typeof html !== 'string') {
+ return false;
+ }
- // There are edge cases (SolidJS) where Preact *might* render a string,
- // but components would be <undefined></undefined>
+ // There are edge cases (SolidJS) where Preact *might* render a string,
+ // but components would be <undefined></undefined>
- return !/\<undefined\>/.test(html);
- } catch (err) {
- return false;
- }
+ return !/\<undefined\>/.test(html);
+ } catch (err) {
+ return false;
+ }
}
function renderToStaticMarkup(Component, props, children) {
- const html = render(h(Component, props, children != null ? h(StaticHtml, { value: children }) : children));
- return { html };
+ const html = render(h(Component, props, children != null ? h(StaticHtml, { value: children }) : children));
+ return { html };
}
export default {
- check,
- renderToStaticMarkup,
+ check,
+ renderToStaticMarkup,
};
diff --git a/packages/renderers/renderer-preact/static-html.js b/packages/renderers/renderer-preact/static-html.js
index 5a31a68af..9af8002a7 100644
--- a/packages/renderers/renderer-preact/static-html.js
+++ b/packages/renderers/renderer-preact/static-html.js
@@ -8,8 +8,8 @@ import { h } from 'preact';
* entirely static and will never change via `shouldComponentUpdate`.
*/
const StaticHtml = ({ value }) => {
- if (!value) return null;
- return h('astro-fragment', { dangerouslySetInnerHTML: { __html: value } });
+ if (!value) return null;
+ return h('astro-fragment', { dangerouslySetInnerHTML: { __html: value } });
};
/**
diff --git a/packages/renderers/renderer-react/client.js b/packages/renderers/renderer-react/client.js
index aa29d1ba9..a6bc7d3bc 100644
--- a/packages/renderers/renderer-react/client.js
+++ b/packages/renderers/renderer-react/client.js
@@ -3,11 +3,11 @@ import { hydrate } from 'react-dom';
import StaticHtml from './static-html.js';
export default (element) => (Component, props, children) =>
- hydrate(
- createElement(
- Component,
- { ...props, suppressHydrationWarning: true },
- children != null ? createElement(StaticHtml, { value: children, suppressHydrationWarning: true }) : children
- ),
- element
- );
+ hydrate(
+ createElement(
+ Component,
+ { ...props, suppressHydrationWarning: true },
+ children != null ? createElement(StaticHtml, { value: children, suppressHydrationWarning: true }) : children
+ ),
+ element
+ );
diff --git a/packages/renderers/renderer-react/index.js b/packages/renderers/renderer-react/index.js
index fdebd3fcb..e73dd622a 100644
--- a/packages/renderers/renderer-react/index.js
+++ b/packages/renderers/renderer-react/index.js
@@ -1,28 +1,28 @@
export default {
- name: '@astrojs/renderer-react',
- client: './client.js',
- server: './server.js',
- jsxImportSource: 'react',
- jsxTransformOptions: async () => {
- const {
- default: { default: jsx },
- } = await import('@babel/plugin-transform-react-jsx');
- return {
- plugins: [jsx({}, { runtime: 'automatic', importSource: 'react' })],
- };
- },
- viteConfig() {
- return {
- optimizeDeps: {
- include: ['@astrojs/renderer-react/client.js', 'react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom'],
- exclude: ['@astrojs/renderer-react/server.js'],
- },
- resolve: {
- dedupe: ['react', 'react-dom'],
- },
- ssr: {
- external: ['react-dom/server.js'],
- },
- };
- },
+ name: '@astrojs/renderer-react',
+ client: './client.js',
+ server: './server.js',
+ jsxImportSource: 'react',
+ jsxTransformOptions: async () => {
+ const {
+ default: { default: jsx },
+ } = await import('@babel/plugin-transform-react-jsx');
+ return {
+ plugins: [jsx({}, { runtime: 'automatic', importSource: 'react' })],
+ };
+ },
+ viteConfig() {
+ return {
+ optimizeDeps: {
+ include: ['@astrojs/renderer-react/client.js', 'react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom'],
+ exclude: ['@astrojs/renderer-react/server.js'],
+ },
+ resolve: {
+ dedupe: ['react', 'react-dom'],
+ },
+ ssr: {
+ external: ['react-dom/server.js'],
+ },
+ };
+ },
};
diff --git a/packages/renderers/renderer-react/server.js b/packages/renderers/renderer-react/server.js
index 2660c8762..1c0c41286 100644
--- a/packages/renderers/renderer-react/server.js
+++ b/packages/renderers/renderer-react/server.js
@@ -5,63 +5,63 @@ import StaticHtml from './static-html.js';
const reactTypeof = Symbol.for('react.element');
function errorIsComingFromPreactComponent(err) {
- return err.message && (err.message.startsWith("Cannot read property '__H'") || err.message.includes("(reading '__H')"));
+ return err.message && (err.message.startsWith("Cannot read property '__H'") || err.message.includes("(reading '__H')"));
}
function check(Component, props, children) {
- // Note: there are packages that do some unholy things to create "components".
- // Checking the $$typeof property catches most of these patterns.
- if (typeof Component === 'object') {
- const $$typeof = Component['$$typeof'];
- return $$typeof && $$typeof.toString().slice('Symbol('.length).startsWith('react');
- }
- if (typeof Component !== 'function') return false;
+ // Note: there are packages that do some unholy things to create "components".
+ // Checking the $$typeof property catches most of these patterns.
+ if (typeof Component === 'object') {
+ const $$typeof = Component['$$typeof'];
+ return $$typeof && $$typeof.toString().slice('Symbol('.length).startsWith('react');
+ }
+ if (typeof Component !== 'function') return false;
- if (Component.prototype != null && typeof Component.prototype.render === 'function') {
- return React.Component.isPrototypeOf(Component) || React.PureComponent.isPrototypeOf(Component);
- }
+ if (Component.prototype != null && typeof Component.prototype.render === 'function') {
+ return React.Component.isPrototypeOf(Component) || React.PureComponent.isPrototypeOf(Component);
+ }
- let error = null;
- let isReactComponent = false;
- function Tester(...args) {
- try {
- const vnode = Component(...args);
- if (vnode && vnode['$$typeof'] === reactTypeof) {
- isReactComponent = true;
- }
- } catch (err) {
- if (!errorIsComingFromPreactComponent(err)) {
- error = err;
- }
- }
+ let error = null;
+ let isReactComponent = false;
+ function Tester(...args) {
+ try {
+ const vnode = Component(...args);
+ if (vnode && vnode['$$typeof'] === reactTypeof) {
+ isReactComponent = true;
+ }
+ } catch (err) {
+ if (!errorIsComingFromPreactComponent(err)) {
+ error = err;
+ }
+ }
- return React.createElement('div');
- }
+ return React.createElement('div');
+ }
- renderToStaticMarkup(Tester, props, children, {});
+ renderToStaticMarkup(Tester, props, children, {});
- if (error) {
- throw error;
- }
- return isReactComponent;
+ if (error) {
+ throw error;
+ }
+ return isReactComponent;
}
function renderToStaticMarkup(Component, props, children, metadata) {
- delete props['class'];
- const vnode = React.createElement(Component, {
- ...props,
- children: children != null ? React.createElement(StaticHtml, { value: children }) : undefined,
- });
- let html;
- if (metadata && metadata.hydrate) {
- html = ReactDOM.renderToString(vnode);
- } else {
- html = ReactDOM.renderToStaticMarkup(vnode);
- }
- return { html };
+ delete props['class'];
+ const vnode = React.createElement(Component, {
+ ...props,
+ children: children != null ? React.createElement(StaticHtml, { value: children }) : undefined,
+ });
+ let html;
+ if (metadata && metadata.hydrate) {
+ html = ReactDOM.renderToString(vnode);
+ } else {
+ html = ReactDOM.renderToStaticMarkup(vnode);
+ }
+ return { html };
}
export default {
- check,
- renderToStaticMarkup,
+ check,
+ renderToStaticMarkup,
};
diff --git a/packages/renderers/renderer-react/static-html.js b/packages/renderers/renderer-react/static-html.js
index 1efc625d0..47130d786 100644
--- a/packages/renderers/renderer-react/static-html.js
+++ b/packages/renderers/renderer-react/static-html.js
@@ -8,8 +8,8 @@ import { createElement as h } from 'react';
* entirely static and will never change via `shouldComponentUpdate`.
*/
const StaticHtml = ({ value }) => {
- if (!value) return null;
- return h('astro-fragment', { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: value } });
+ if (!value) return null;
+ return h('astro-fragment', { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: value } });
};
/**
diff --git a/packages/renderers/renderer-solid/client.js b/packages/renderers/renderer-solid/client.js
index 9aa0ecbb8..b67b3acdb 100644
--- a/packages/renderers/renderer-solid/client.js
+++ b/packages/renderers/renderer-solid/client.js
@@ -1,14 +1,14 @@
import { hydrate, createComponent } from 'solid-js/web';
export default (element) => (Component, props, childHTML) => {
- let children;
- if (childHTML != null) {
- children = document.createElement('astro-fragment');
- children.innerHTML = childHTML;
- }
+ let children;
+ if (childHTML != null) {
+ children = document.createElement('astro-fragment');
+ children.innerHTML = childHTML;
+ }
- // Using Solid's `hydrate` method ensures that a `root` is created
- // in order to properly handle reactivity. It also handles
- // components that are not native HTML elements.
- hydrate(() => createComponent(Component, { ...props, children }), element);
+ // Using Solid's `hydrate` method ensures that a `root` is created
+ // in order to properly handle reactivity. It also handles
+ // components that are not native HTML elements.
+ hydrate(() => createComponent(Component, { ...props, children }), element);
};
diff --git a/packages/renderers/renderer-solid/index.js b/packages/renderers/renderer-solid/index.js
index f1e566fd8..a764e5f64 100644
--- a/packages/renderers/renderer-solid/index.js
+++ b/packages/renderers/renderer-solid/index.js
@@ -1,57 +1,57 @@
export default {
- name: '@astrojs/renderer-solid',
- client: './client.js',
- server: './server.js',
- jsxImportSource: 'solid-js',
- jsxTransformOptions: async ({ ssr }) => {
- const [{ default: solid }] = await Promise.all([import('babel-preset-solid')]);
- const options = {
- presets: [solid({}, { generate: ssr ? 'ssr' : 'dom', hydratable: true })],
- plugins: [],
- };
+ name: '@astrojs/renderer-solid',
+ client: './client.js',
+ server: './server.js',
+ jsxImportSource: 'solid-js',
+ jsxTransformOptions: async ({ ssr }) => {
+ const [{ default: solid }] = await Promise.all([import('babel-preset-solid')]);
+ const options = {
+ presets: [solid({}, { generate: ssr ? 'ssr' : 'dom', hydratable: true })],
+ plugins: [],
+ };
- if (ssr) {
- options.plugins.push([
- 'babel-plugin-module-resolver',
- {
- cwd: process.cwd(),
- alias: {
- 'solid-js/store': 'solid-js/store/dist/server.js',
- 'solid-js/web': 'solid-js/web/dist/server.js',
- 'solid-js': 'solid-js/dist/server.js',
- },
- },
- ]);
- }
+ if (ssr) {
+ options.plugins.push([
+ 'babel-plugin-module-resolver',
+ {
+ cwd: process.cwd(),
+ alias: {
+ 'solid-js/store': 'solid-js/store/dist/server.js',
+ 'solid-js/web': 'solid-js/web/dist/server.js',
+ 'solid-js': 'solid-js/dist/server.js',
+ },
+ },
+ ]);
+ }
- return options;
- },
- viteConfig(options) {
- // https://github.com/solidjs/vite-plugin-solid
+ return options;
+ },
+ viteConfig(options) {
+ // https://github.com/solidjs/vite-plugin-solid
- // We inject the dev mode only if the user explicitely wants it or if we are in dev (serve) mode
- const replaceDev = options.mode === 'development' || options.command === 'serve';
+ // We inject the dev mode only if the user explicitely wants it or if we are in dev (serve) mode
+ const replaceDev = options.mode === 'development' || options.command === 'serve';
- const nestedDeps = ['solid-js', 'solid-js/web', 'solid-js/store', 'solid-js/html', 'solid-js/h'];
+ const nestedDeps = ['solid-js', 'solid-js/web', 'solid-js/store', 'solid-js/html', 'solid-js/h'];
- return {
- /**
- * We only need esbuild on .ts or .js files.
- * .tsx & .jsx files are handled by us
- */
- esbuild: { include: /\.ts$/ },
- resolve: {
- conditions: ['solid', ...(replaceDev ? ['development'] : [])],
- dedupe: nestedDeps,
- alias: [{ find: /^solid-refresh$/, replacement: '/@solid-refresh' }],
- },
- optimizeDeps: {
- include: nestedDeps,
- exclude: ['@astrojs/renderer-solid/server.js'],
- },
- ssr: {
- external: ['solid-js/web/dist/server.js', 'solid-js/store/dist/server.js', 'solid-js/dist/server.js', 'babel-preset-solid'],
- },
- };
- },
+ return {
+ /**
+ * We only need esbuild on .ts or .js files.
+ * .tsx & .jsx files are handled by us
+ */
+ esbuild: { include: /\.ts$/ },
+ resolve: {
+ conditions: ['solid', ...(replaceDev ? ['development'] : [])],
+ dedupe: nestedDeps,
+ alias: [{ find: /^solid-refresh$/, replacement: '/@solid-refresh' }],
+ },
+ optimizeDeps: {
+ include: nestedDeps,
+ exclude: ['@astrojs/renderer-solid/server.js'],
+ },
+ ssr: {
+ external: ['solid-js/web/dist/server.js', 'solid-js/store/dist/server.js', 'solid-js/dist/server.js', 'babel-preset-solid'],
+ },
+ };
+ },
};
diff --git a/packages/renderers/renderer-solid/server.js b/packages/renderers/renderer-solid/server.js
index 238d7531a..c8bb33adf 100644
--- a/packages/renderers/renderer-solid/server.js
+++ b/packages/renderers/renderer-solid/server.js
@@ -1,28 +1,28 @@
import { renderToString, ssr, createComponent } from 'solid-js/web/dist/server.js';
function check(Component, props, children) {
- if (typeof Component !== 'function') return false;
- try {
- const { html } = renderToStaticMarkup(Component, props, children);
- return typeof html === 'string';
- } catch (err) {
- return false;
- }
+ if (typeof Component !== 'function') return false;
+ try {
+ const { html } = renderToStaticMarkup(Component, props, children);
+ return typeof html === 'string';
+ } catch (err) {
+ return false;
+ }
}
function renderToStaticMarkup(Component, props, children) {
- const html = renderToString(() =>
- createComponent(Component, {
- ...props,
- // In Solid SSR mode, `ssr` creates the expected structure for `children`.
- // In Solid client mode, `ssr` is just a stub.
- children: children != null ? ssr(`<astro-fragment>${children}</astro-fragment>`) : children,
- })
- );
- return { html: html + `<script>window._$HYDRATION||(window._$HYDRATION={events:[],completed:new WeakSet})</script>` };
+ const html = renderToString(() =>
+ createComponent(Component, {
+ ...props,
+ // In Solid SSR mode, `ssr` creates the expected structure for `children`.
+ // In Solid client mode, `ssr` is just a stub.
+ children: children != null ? ssr(`<astro-fragment>${children}</astro-fragment>`) : children,
+ })
+ );
+ return { html: html + `<script>window._$HYDRATION||(window._$HYDRATION={events:[],completed:new WeakSet})</script>` };
}
export default {
- check,
- renderToStaticMarkup,
+ check,
+ renderToStaticMarkup,
};
diff --git a/packages/renderers/renderer-solid/static-html.js b/packages/renderers/renderer-solid/static-html.js
index 01bd8716e..9f1a394b5 100644
--- a/packages/renderers/renderer-solid/static-html.js
+++ b/packages/renderers/renderer-solid/static-html.js
@@ -5,8 +5,8 @@ import { ssr } from 'solid-js/web/dist/server.js';
* a wrapper `astro-fragment` to render that content as VNodes.
*/
const StaticHtml = ({ innerHTML }) => {
- if (!innerHTML) return null;
- return ssr(`<astro-fragment>${innerHTML}</astro-fragment>`);
+ if (!innerHTML) return null;
+ return ssr(`<astro-fragment>${innerHTML}</astro-fragment>`);
};
export default StaticHtml;
diff --git a/packages/renderers/renderer-svelte/Wrapper.svelte.ssr.js b/packages/renderers/renderer-svelte/Wrapper.svelte.ssr.js
index b866f0894..9bca437b5 100644
--- a/packages/renderers/renderer-svelte/Wrapper.svelte.ssr.js
+++ b/packages/renderers/renderer-svelte/Wrapper.svelte.ssr.js
@@ -2,13 +2,13 @@
import { create_ssr_component, missing_component, validate_component } from 'svelte/internal';
const App = create_ssr_component(($$result, $$props, $$bindings, slots) => {
- const { __astro_component: Component, __astro_children, ...props } = $$props;
- const children = {};
- if (__astro_children != null) {
- children.default = () => `<astro-fragment>${__astro_children}</astro-fragment>`;
- }
+ const { __astro_component: Component, __astro_children, ...props } = $$props;
+ const children = {};
+ if (__astro_children != null) {
+ children.default = () => `<astro-fragment>${__astro_children}</astro-fragment>`;
+ }
- return `${validate_component(Component || missing_component, 'svelte:component').$$render($$result, Object.assign(props), {}, children)}`;
+ return `${validate_component(Component || missing_component, 'svelte:component').$$render($$result, Object.assign(props), {}, children)}`;
});
export default App;
diff --git a/packages/renderers/renderer-svelte/client.js b/packages/renderers/renderer-svelte/client.js
index 6facfa0b4..c10c7afa0 100644
--- a/packages/renderers/renderer-svelte/client.js
+++ b/packages/renderers/renderer-svelte/client.js
@@ -1,14 +1,14 @@
import SvelteWrapper from './Wrapper.svelte';
export default (target) => {
- return (component, props, children) => {
- delete props['class'];
- try {
- new SvelteWrapper({
- target,
- props: { __astro_component: component, __astro_children: children, ...props },
- hydrate: true,
- });
- } catch (e) {}
- };
+ return (component, props, children) => {
+ delete props['class'];
+ try {
+ new SvelteWrapper({
+ target,
+ props: { __astro_component: component, __astro_children: children, ...props },
+ hydrate: true,
+ });
+ } catch (e) {}
+ };
};
diff --git a/packages/renderers/renderer-svelte/index.js b/packages/renderers/renderer-svelte/index.js
index 68e19ed10..a4dfd23a9 100644
--- a/packages/renderers/renderer-svelte/index.js
+++ b/packages/renderers/renderer-svelte/index.js
@@ -2,31 +2,31 @@ import { svelte } from '@sveltejs/vite-plugin-svelte';
import preprocess from 'svelte-preprocess';
export default {
- name: '@astrojs/renderer-svelte',
- client: './client.js',
- server: './server.js',
- viteConfig({ mode }) {
- return {
- optimizeDeps: {
- include: ['@astrojs/renderer-svelte/client.js', 'svelte', 'svelte/internal'],
- exclude: ['@astrojs/renderer-svelte/server.js'],
- },
- plugins: [
- svelte({
- emitCss: true,
- compilerOptions: { dev: mode === 'development', hydratable: true },
- preprocess: [
- preprocess({
- less: true,
- sass: { renderSync: true },
- scss: { renderSync: true },
- postcss: true,
- stylus: true,
- typescript: true,
- }),
- ],
- }),
- ],
- };
- },
+ name: '@astrojs/renderer-svelte',
+ client: './client.js',
+ server: './server.js',
+ viteConfig({ mode }) {
+ return {
+ optimizeDeps: {
+ include: ['@astrojs/renderer-svelte/client.js', 'svelte', 'svelte/internal'],
+ exclude: ['@astrojs/renderer-svelte/server.js'],
+ },
+ plugins: [
+ svelte({
+ emitCss: true,
+ compilerOptions: { dev: mode === 'development', hydratable: true },
+ preprocess: [
+ preprocess({
+ less: true,
+ sass: { renderSync: true },
+ scss: { renderSync: true },
+ postcss: true,
+ stylus: true,
+ typescript: true,
+ }),
+ ],
+ }),
+ ],
+ };
+ },
};
diff --git a/packages/renderers/renderer-svelte/server.js b/packages/renderers/renderer-svelte/server.js
index a2eda85a3..c51b2f4b4 100644
--- a/packages/renderers/renderer-svelte/server.js
+++ b/packages/renderers/renderer-svelte/server.js
@@ -1,15 +1,15 @@
import SvelteWrapper from './Wrapper.svelte.ssr.js';
function check(Component) {
- return Component['render'] && Component['$$render'];
+ return Component['render'] && Component['$$render'];
}
async function renderToStaticMarkup(Component, props, children) {
- const { html } = SvelteWrapper.render({ __astro_component: Component, __astro_children: children, ...props });
- return { html };
+ const { html } = SvelteWrapper.render({ __astro_component: Component, __astro_children: children, ...props });
+ return { html };
}
export default {
- check,
- renderToStaticMarkup,
+ check,
+ renderToStaticMarkup,
};
diff --git a/packages/renderers/renderer-vue/client.js b/packages/renderers/renderer-vue/client.js
index edda137cd..0ba4e8106 100644
--- a/packages/renderers/renderer-vue/client.js
+++ b/packages/renderers/renderer-vue/client.js
@@ -2,13 +2,13 @@ import { h, createSSRApp } from 'vue';
import StaticHtml from './static-html.js';
export default (element) => (Component, props, children) => {
- delete props['class'];
- // Expose name on host component for Vue devtools
- const name = Component.name ? `${Component.name} Host` : undefined;
- const slots = {};
- if (children != null) {
- slots.default = () => h(StaticHtml, { value: children });
- }
- const app = createSSRApp({ name, render: () => h(Component, props, slots) });
- app.mount(element, true);
+ delete props['class'];
+ // Expose name on host component for Vue devtools
+ const name = Component.name ? `${Component.name} Host` : undefined;
+ const slots = {};
+ if (children != null) {
+ slots.default = () => h(StaticHtml, { value: children });
+ }
+ const app = createSSRApp({ name, render: () => h(Component, props, slots) });
+ app.mount(element, true);
};
diff --git a/packages/renderers/renderer-vue/index.js b/packages/renderers/renderer-vue/index.js
index a3faf2bf9..51c880c4a 100644
--- a/packages/renderers/renderer-vue/index.js
+++ b/packages/renderers/renderer-vue/index.js
@@ -1,19 +1,19 @@
import vue from '@vitejs/plugin-vue';
export default {
- name: '@astrojs/renderer-vue',
- client: './client.js',
- server: './server.js',
- viteConfig() {
- return {
- optimizeDeps: {
- include: ['@astrojs/renderer-vue/client.js', 'vue'],
- exclude: ['@astrojs/renderer-vue/server.js'],
- },
- plugins: [vue()],
- ssr: {
- external: ['@vue/server-renderer'],
- },
- };
- },
+ name: '@astrojs/renderer-vue',
+ client: './client.js',
+ server: './server.js',
+ viteConfig() {
+ return {
+ optimizeDeps: {
+ include: ['@astrojs/renderer-vue/client.js', 'vue'],
+ exclude: ['@astrojs/renderer-vue/server.js'],
+ },
+ plugins: [vue()],
+ ssr: {
+ external: ['@vue/server-renderer'],
+ },
+ };
+ },
};
diff --git a/packages/renderers/renderer-vue/server.js b/packages/renderers/renderer-vue/server.js
index 83e389b5d..1ae2b757b 100644
--- a/packages/renderers/renderer-vue/server.js
+++ b/packages/renderers/renderer-vue/server.js
@@ -3,20 +3,20 @@ import { renderToString } from 'vue/server-renderer';
import StaticHtml from './static-html.js';
function check(Component) {
- return !!Component['ssrRender'];
+ return !!Component['ssrRender'];
}
async function renderToStaticMarkup(Component, props, children) {
- const slots = {};
- if (children != null) {
- slots.default = () => h(StaticHtml, { value: children });
- }
- const app = createSSRApp({ render: () => h(Component, props, slots) });
- const html = await renderToString(app);
- return { html };
+ const slots = {};
+ if (children != null) {
+ slots.default = () => h(StaticHtml, { value: children });
+ }
+ const app = createSSRApp({ render: () => h(Component, props, slots) });
+ const html = await renderToString(app);
+ return { html };
}
export default {
- check,
- renderToStaticMarkup,
+ check,
+ renderToStaticMarkup,
};
diff --git a/packages/renderers/renderer-vue/static-html.js b/packages/renderers/renderer-vue/static-html.js
index e623f1049..ff1459b6f 100644
--- a/packages/renderers/renderer-vue/static-html.js
+++ b/packages/renderers/renderer-vue/static-html.js
@@ -7,13 +7,13 @@ import { h, defineComponent } from 'vue';
* This is the Vue + JSX equivalent of using `<div v-html="value" />`
*/
const StaticHtml = defineComponent({
- props: {
- value: String,
- },
- setup({ value }) {
- if (!value) return () => null;
- return () => h('astro-fragment', { innerHTML: value });
- },
+ props: {
+ value: String,
+ },
+ setup({ value }) {
+ if (!value) return () => null;
+ return () => h('astro-fragment', { innerHTML: value });
+ },
});
/**
diff --git a/scripts/cmd/build.js b/scripts/cmd/build.js
index 469f5d0c3..b5eb91195 100644
--- a/scripts/cmd/build.js
+++ b/scripts/cmd/build.js
@@ -7,70 +7,70 @@ import glob from 'tiny-glob';
/** @type {import('esbuild').BuildOptions} */
const defaultConfig = {
- minify: false,
- format: 'esm',
- platform: 'node',
- target: 'node14',
- sourcemap: 'inline',
- sourcesContent: false,
+ minify: false,
+ format: 'esm',
+ platform: 'node',
+ target: 'node14',
+ sourcemap: 'inline',
+ sourcesContent: false,
};
const dt = new Intl.DateTimeFormat('en-us', {
- hour: '2-digit',
- minute: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
});
export default async function build(...args) {
- const config = Object.assign({}, defaultConfig);
- const isDev = args.slice(-1)[0] === 'IS_DEV';
- const patterns = args
- .filter((f) => !!f) // remove empty args
- .map((f) => f.replace(/^'/, '').replace(/'$/, '')); // Needed for Windows: glob strings contain surrounding string chars??? remove these
- let entryPoints = [].concat(...(await Promise.all(patterns.map((pattern) => glob(pattern, { filesOnly: true, absolute: true })))));
+ const config = Object.assign({}, defaultConfig);
+ const isDev = args.slice(-1)[0] === 'IS_DEV';
+ const patterns = args
+ .filter((f) => !!f) // remove empty args
+ .map((f) => f.replace(/^'/, '').replace(/'$/, '')); // Needed for Windows: glob strings contain surrounding string chars??? remove these
+ let entryPoints = [].concat(...(await Promise.all(patterns.map((pattern) => glob(pattern, { filesOnly: true, absolute: true })))));
- const { type = 'module', dependencies = {} } = await fs.readFile('./package.json').then((res) => JSON.parse(res.toString()));
- const format = type === 'module' ? 'esm' : 'cjs';
- const outdir = 'dist';
- await clean(outdir);
+ const { type = 'module', dependencies = {} } = await fs.readFile('./package.json').then((res) => JSON.parse(res.toString()));
+ const format = type === 'module' ? 'esm' : 'cjs';
+ const outdir = 'dist';
+ await clean(outdir);
- if (!isDev) {
- await esbuild.build({
- ...config,
- bundle: entryPoints.length === 1, // Note: only use `bundle` with a single entrypoint!
- entryPoints,
- outdir,
- format,
- plugins: [svelte({ isDev })],
- });
- return;
- }
+ if (!isDev) {
+ await esbuild.build({
+ ...config,
+ bundle: entryPoints.length === 1, // Note: only use `bundle` with a single entrypoint!
+ entryPoints,
+ outdir,
+ format,
+ plugins: [svelte({ isDev })],
+ });
+ return;
+ }
- const builder = await esbuild.build({
- ...config,
- watch: {
- onRebuild(error, result) {
- const date = dt.format(new Date());
- if (error || (result && result.errors.length)) {
- console.error(dim(`[${date}] `) + red(error || result.errors.join('\n')));
- } else {
- if (result.warnings.length) {
- console.log(dim(`[${date}] `) + yellow('⚠ updated with warnings:\n' + result.warnings.join('\n')));
- }
- console.log(dim(`[${date}] `) + green('✔ updated'));
- }
- },
- },
- entryPoints,
- outdir,
- format,
- plugins: [svelte({ isDev })],
- });
+ const builder = await esbuild.build({
+ ...config,
+ watch: {
+ onRebuild(error, result) {
+ const date = dt.format(new Date());
+ if (error || (result && result.errors.length)) {
+ console.error(dim(`[${date}] `) + red(error || result.errors.join('\n')));
+ } else {
+ if (result.warnings.length) {
+ console.log(dim(`[${date}] `) + yellow('⚠ updated with warnings:\n' + result.warnings.join('\n')));
+ }
+ console.log(dim(`[${date}] `) + green('✔ updated'));
+ }
+ },
+ },
+ entryPoints,
+ outdir,
+ format,
+ plugins: [svelte({ isDev })],
+ });
- process.on('beforeExit', () => {
- builder.stop && builder.stop();
- });
+ process.on('beforeExit', () => {
+ builder.stop && builder.stop();
+ });
}
async function clean(outdir) {
- return del([`${outdir}/**`, `!${outdir}/**/*.d.ts`]);
+ return del([`${outdir}/**`, `!${outdir}/**/*.d.ts`]);
}
diff --git a/scripts/cmd/copy.js b/scripts/cmd/copy.js
index f31fb3739..925990fe4 100644
--- a/scripts/cmd/copy.js
+++ b/scripts/cmd/copy.js
@@ -8,76 +8,76 @@ const { resolve, dirname, sep, join } = posix;
/** @type {import('arg').Spec} */
const spec = {
- '--tgz': Boolean,
+ '--tgz': Boolean,
};
export default async function copy() {
- let { _: patterns, ['--tgz']: isCompress } = arg(spec);
- patterns = patterns.slice(1);
+ let { _: patterns, ['--tgz']: isCompress } = arg(spec);
+ patterns = patterns.slice(1);
- if (isCompress) {
- const files = await glob(patterns, { gitignore: true });
- const rootDir = resolveRootDir(files);
- const destDir = rootDir.replace(/^[^/]+/, 'dist');
+ if (isCompress) {
+ const files = await glob(patterns, { gitignore: true });
+ const rootDir = resolveRootDir(files);
+ const destDir = rootDir.replace(/^[^/]+/, 'dist');
- const templates = files.reduce((acc, curr) => {
- const name = curr.replace(rootDir, '').slice(1).split(sep)[0];
- if (acc[name]) {
- acc[name].push(resolve(curr));
- } else {
- acc[name] = [resolve(curr)];
- }
- return acc;
- }, {});
+ const templates = files.reduce((acc, curr) => {
+ const name = curr.replace(rootDir, '').slice(1).split(sep)[0];
+ if (acc[name]) {
+ acc[name].push(resolve(curr));
+ } else {
+ acc[name] = [resolve(curr)];
+ }
+ return acc;
+ }, {});
- let meta = {};
- return Promise.all(
- Object.entries(templates).map(([template, files]) => {
- const cwd = resolve(join(rootDir, template));
- const dest = join(destDir, `${template}.tgz`);
- const metafile = files.find((f) => f.endsWith('meta.json'));
- if (metafile) {
- files = files.filter((f) => f !== metafile);
- meta[template] = JSON.parse(readFileSync(metafile).toString());
- }
- return fs.mkdir(dirname(dest), { recursive: true }).then(() =>
- tar.create(
- {
- gzip: true,
- portable: true,
- file: dest,
- cwd,
- },
- files.map((f) => f.replace(cwd, '').slice(1))
- )
- );
- })
- ).then(() => {
- if (Object.keys(meta).length > 0) {
- return fs.writeFile(resolve(destDir, 'meta.json'), JSON.stringify(meta, null, 2));
- }
- });
- }
+ let meta = {};
+ return Promise.all(
+ Object.entries(templates).map(([template, files]) => {
+ const cwd = resolve(join(rootDir, template));
+ const dest = join(destDir, `${template}.tgz`);
+ const metafile = files.find((f) => f.endsWith('meta.json'));
+ if (metafile) {
+ files = files.filter((f) => f !== metafile);
+ meta[template] = JSON.parse(readFileSync(metafile).toString());
+ }
+ return fs.mkdir(dirname(dest), { recursive: true }).then(() =>
+ tar.create(
+ {
+ gzip: true,
+ portable: true,
+ file: dest,
+ cwd,
+ },
+ files.map((f) => f.replace(cwd, '').slice(1))
+ )
+ );
+ })
+ ).then(() => {
+ if (Object.keys(meta).length > 0) {
+ return fs.writeFile(resolve(destDir, 'meta.json'), JSON.stringify(meta, null, 2));
+ }
+ });
+ }
- const files = await glob(patterns);
- await Promise.all(
- files.map((file) => {
- const dest = resolve(file.replace(/^[^/]+/, 'dist'));
- return fs.mkdir(dirname(dest), { recursive: true }).then(() => fs.copyFile(resolve(file), dest));
- })
- );
+ const files = await glob(patterns);
+ await Promise.all(
+ files.map((file) => {
+ const dest = resolve(file.replace(/^[^/]+/, 'dist'));
+ return fs.mkdir(dirname(dest), { recursive: true }).then(() => fs.copyFile(resolve(file), dest));
+ })
+ );
}
function resolveRootDir(files) {
- return files
- .reduce((acc, curr) => {
- const currParts = curr.split(sep);
- if (acc.length === 0) return currParts;
- const result = [];
- currParts.forEach((part, i) => {
- if (acc[i] === part) result.push(part);
- });
- return result;
- }, [])
- .join(sep);
+ return files
+ .reduce((acc, curr) => {
+ const currParts = curr.split(sep);
+ if (acc.length === 0) return currParts;
+ const result = [];
+ currParts.forEach((part, i) => {
+ if (acc[i] === part) result.push(part);
+ });
+ return result;
+ }, [])
+ .join(sep);
}
diff --git a/scripts/index.js b/scripts/index.js
index 7908b112c..dd789f032 100755
--- a/scripts/index.js
+++ b/scripts/index.js
@@ -1,19 +1,19 @@
#!/usr/bin/env node
export default async function run() {
- const [cmd, ...args] = process.argv.slice(2);
- switch (cmd) {
- case 'dev':
- case 'build': {
- const { default: build } = await import('./cmd/build.js');
- await build(...args, cmd === 'dev' ? 'IS_DEV' : undefined);
- break;
- }
- case 'copy': {
- const { default: copy } = await import('./cmd/copy.js');
- await copy(...args);
- break;
- }
- }
+ const [cmd, ...args] = process.argv.slice(2);
+ switch (cmd) {
+ case 'dev':
+ case 'build': {
+ const { default: build } = await import('./cmd/build.js');
+ await build(...args, cmd === 'dev' ? 'IS_DEV' : undefined);
+ break;
+ }
+ case 'copy': {
+ const { default: copy } = await import('./cmd/copy.js');
+ await copy(...args);
+ break;
+ }
+ }
}
run();
diff --git a/scripts/notify/index.js b/scripts/notify/index.js
index 6427e91aa..950fef1de 100755
--- a/scripts/notify/index.js
+++ b/scripts/notify/index.js
@@ -6,76 +6,76 @@ const baseUrl = new URL('https://github.com/withastro/astro/blob/main/');
const emojis = ['🎉', '🥳', '🚀', '🧑‍🚀', '🎊', '🏆', '✅', '🤩', '🤖', '🙌'];
const descriptors = ['new releases', 'hot and fresh updates', 'shiny updates', 'exciting changes', 'package updates', 'awesome updates', 'bug fixes and features', 'updates'];
const verbs = [
- 'just went out!',
- 'just launched!',
- 'now available!',
- 'in the wild!',
- 'now live!',
- 'hit the registry!',
- 'to share!',
- 'for you!',
- 'for y’all! 🤠',
- 'comin’ your way!',
- 'comin’ atcha!',
- 'comin’ in hot!',
- 'freshly minted on the blockchain! (jk)',
- '[is] out (now with 100% more reticulated splines!)',
- '(as seen on TV!)',
- 'just dropped!',
- '– artisanally hand-crafted just for you.',
- '– oh happy day!',
- '– enjoy!',
- 'now out. Be the first on your block to download!',
- 'made with love 💕',
- '[is] out! Our best [version] yet!',
- '[is] here. DOWNLOAD! DOWNLOAD! DOWNLOAD!',
- '... HUZZAH!',
- '[has] landed!',
- 'landed! The internet just got a little more fun.',
- '– from our family to yours.',
- '– go forth and build!',
+ 'just went out!',
+ 'just launched!',
+ 'now available!',
+ 'in the wild!',
+ 'now live!',
+ 'hit the registry!',
+ 'to share!',
+ 'for you!',
+ 'for y’all! 🤠',
+ 'comin’ your way!',
+ 'comin’ atcha!',
+ 'comin’ in hot!',
+ 'freshly minted on the blockchain! (jk)',
+ '[is] out (now with 100% more reticulated splines!)',
+ '(as seen on TV!)',
+ 'just dropped!',
+ '– artisanally hand-crafted just for you.',
+ '– oh happy day!',
+ '– enjoy!',
+ 'now out. Be the first on your block to download!',
+ 'made with love 💕',
+ '[is] out! Our best [version] yet!',
+ '[is] here. DOWNLOAD! DOWNLOAD! DOWNLOAD!',
+ '... HUZZAH!',
+ '[has] landed!',
+ 'landed! The internet just got a little more fun.',
+ '– from our family to yours.',
+ '– go forth and build!',
];
function item(items) {
- return items[Math.floor(Math.random() * items.length)];
+ return items[Math.floor(Math.random() * items.length)];
}
const plurals = new Map([
- ['is', 'are'],
- ['has', 'have'],
+ ['is', 'are'],
+ ['has', 'have'],
]);
function pluralize(text) {
- return text.replace(/(\[([^\]]+)\])/gm, (_, _full, match) => (plurals.has(match) ? plurals.get(match) : `${match}s`));
+ return text.replace(/(\[([^\]]+)\])/gm, (_, _full, match) => (plurals.has(match) ? plurals.get(match) : `${match}s`));
}
function singularlize(text) {
- return text.replace(/(\[([^\]]+)\])/gm, (_, _full, match) => `${match}`);
+ return text.replace(/(\[([^\]]+)\])/gm, (_, _full, match) => `${match}`);
}
async function run() {
- const releases = process.argv.slice(2)[0];
- const data = JSON.parse(releases);
- const packages = await Promise.all(
- data.map(({ name, version }) => {
- const p = path.relative('./', path.dirname(require.resolve(name))).replace(path.sep, '/');
- return { name, version, url: new URL(`${p}/CHANGELOG.md#${version.replace(/\./g, '')}`, baseUrl).toString() };
- })
- );
+ const releases = process.argv.slice(2)[0];
+ const data = JSON.parse(releases);
+ const packages = await Promise.all(
+ data.map(({ name, version }) => {
+ const p = path.relative('./', path.dirname(require.resolve(name))).replace(path.sep, '/');
+ return { name, version, url: new URL(`${p}/CHANGELOG.md#${version.replace(/\./g, '')}`, baseUrl).toString() };
+ })
+ );
- const emoji = item(emojis);
- const descriptor = item(descriptors);
- const verb = item(verbs);
+ const emoji = item(emojis);
+ const descriptor = item(descriptors);
+ const verb = item(verbs);
- if (packages.length === 1) {
- const { name, version, url } = packages[0];
- console.log(`${emoji} \`${name}@${version}\` ${singularlize(verb)}\nRead the [release notes →](<${url}>)`);
- } else {
- console.log(`${emoji} Some ${descriptor} ${pluralize(verb)}\n`);
- for (const { name, version, url } of packages) {
- console.log(`• \`${name}@${version}\` Read the [release notes →](<${url}>)`);
- }
- }
+ if (packages.length === 1) {
+ const { name, version, url } = packages[0];
+ console.log(`${emoji} \`${name}@${version}\` ${singularlize(verb)}\nRead the [release notes →](<${url}>)`);
+ } else {
+ console.log(`${emoji} Some ${descriptor} ${pluralize(verb)}\n`);
+ for (const { name, version, url } of packages) {
+ console.log(`• \`${name}@${version}\` Read the [release notes →](<${url}>)`);
+ }
+ }
}
run();
diff --git a/scripts/smoke/index.js b/scripts/smoke/index.js
index e1e296a61..d5412adc0 100644
--- a/scripts/smoke/index.js
+++ b/scripts/smoke/index.js
@@ -7,25 +7,25 @@ import { fileURLToPath } from 'url';
// and update our CI to run through Turbo.
export default async function run() {
- const examplesUrl = new URL('../../examples/', import.meta.url);
- const examplesToTest = fs
- .readdirSync(examplesUrl)
- .map((filename) => new URL(filename, examplesUrl))
- .filter((fileUrl) => fs.statSync(fileUrl).isDirectory());
- const allProjectsToTest = [...examplesToTest, new URL('../../www', import.meta.url), new URL('../../docs', import.meta.url)];
+ const examplesUrl = new URL('../../examples/', import.meta.url);
+ const examplesToTest = fs
+ .readdirSync(examplesUrl)
+ .map((filename) => new URL(filename, examplesUrl))
+ .filter((fileUrl) => fs.statSync(fileUrl).isDirectory());
+ const allProjectsToTest = [...examplesToTest, new URL('../../www', import.meta.url), new URL('../../docs', import.meta.url)];
- console.log('');
- for (const projectToTest of allProjectsToTest) {
- const filePath = fileURLToPath(projectToTest);
- console.log(' 🤖 Testing', filePath, '\n');
- try {
- await execa('yarn', ['build'], { cwd: fileURLToPath(projectToTest), stdout: 'inherit', stderr: 'inherit' });
- } catch (err) {
- console.log(err);
- process.exit(1);
- }
- console.log('\n 🤖 Test complete.');
- }
+ console.log('');
+ for (const projectToTest of allProjectsToTest) {
+ const filePath = fileURLToPath(projectToTest);
+ console.log(' 🤖 Testing', filePath, '\n');
+ try {
+ await execa('yarn', ['build'], { cwd: fileURLToPath(projectToTest), stdout: 'inherit', stderr: 'inherit' });
+ } catch (err) {
+ console.log(err);
+ process.exit(1);
+ }
+ console.log('\n 🤖 Test complete.');
+ }
}
run();
diff --git a/scripts/stats/index.js b/scripts/stats/index.js
index 646896966..435bb5c56 100644
--- a/scripts/stats/index.js
+++ b/scripts/stats/index.js
@@ -45,77 +45,77 @@ const COLUMN_ID_BUGS_PRIORITIZED = 14946516;
// const pulls = JSON.parse(readFileSync('pulls.json').toString());
async function countCards(column_id) {
- return octokit.paginate('GET /projects/columns/{column_id}/cards', {
- column_id,
- mediaType: {
- previews: ['inertia'],
- },
- });
+ return octokit.paginate('GET /projects/columns/{column_id}/cards', {
+ column_id,
+ mediaType: {
+ previews: ['inertia'],
+ },
+ });
}
async function countCommits(since) {
- return octokit.paginate('GET /repos/{owner}/{repo}/commits', {
- owner,
- repo,
- since: since.toISOString(),
- });
+ return octokit.paginate('GET /repos/{owner}/{repo}/commits', {
+ owner,
+ repo,
+ since: since.toISOString(),
+ });
}
export async function run() {
- const twentyFourHoursAgo = new Date();
- twentyFourHoursAgo.setDate(twentyFourHoursAgo.getDate() - 1);
+ const twentyFourHoursAgo = new Date();
+ twentyFourHoursAgo.setDate(twentyFourHoursAgo.getDate() - 1);
- const allOpenIssues = await octokit.paginate('GET /repos/{owner}/{repo}/issues', {
- owner,
- repo,
- });
- const openIssues = allOpenIssues.filter((iss) => !iss.pull_request);
- const openPulls = allOpenIssues.filter((iss) => iss.pull_request);
+ const allOpenIssues = await octokit.paginate('GET /repos/{owner}/{repo}/issues', {
+ owner,
+ repo,
+ });
+ const openIssues = allOpenIssues.filter((iss) => !iss.pull_request);
+ const openPulls = allOpenIssues.filter((iss) => iss.pull_request);
- const allIssuesLastTwentyFourHours = await octokit.paginate('GET /repos/{owner}/{repo}/issues', {
- owner,
- repo,
- state: 'all',
- per_page: 100,
- since: twentyFourHoursAgo.toISOString(),
- });
- const issuesLastTwentyFourHours = allIssuesLastTwentyFourHours.filter((iss) => new Date(iss.created_at) > twentyFourHoursAgo && !iss.pull_request);
- const pullsLastTwentyFourHours = allIssuesLastTwentyFourHours.filter((iss) => new Date(iss.created_at) > twentyFourHoursAgo && iss.pull_request);
+ const allIssuesLastTwentyFourHours = await octokit.paginate('GET /repos/{owner}/{repo}/issues', {
+ owner,
+ repo,
+ state: 'all',
+ per_page: 100,
+ since: twentyFourHoursAgo.toISOString(),
+ });
+ const issuesLastTwentyFourHours = allIssuesLastTwentyFourHours.filter((iss) => new Date(iss.created_at) > twentyFourHoursAgo && !iss.pull_request);
+ const pullsLastTwentyFourHours = allIssuesLastTwentyFourHours.filter((iss) => new Date(iss.created_at) > twentyFourHoursAgo && iss.pull_request);
- const entry = [
- // Date (Human Readable)
- `"${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}"`,
- // Commits in last 24 hours
- (await countCommits(twentyFourHoursAgo)).length,
- // New Issues(All) in last 24 hours
- issuesLastTwentyFourHours.length,
- // New Issues(Bugs) in last 24 hours
- issuesLastTwentyFourHours.filter((iss) => iss.title.startsWith('🐛 BUG:')).length,
- // New Issues(RFC) in last 24 hours
- issuesLastTwentyFourHours.filter((iss) => iss.title.startsWith('💡 RFC:')).length,
- // New Issues(Docs) in last 24 hours
- issuesLastTwentyFourHours.filter((iss) => iss.title.startsWith('📘 DOC:')).length,
- // New Pull Requests in last 24 hours
- pullsLastTwentyFourHours.length,
- // Pull requests
- openPulls.length,
- // Open Issues
- openIssues.length,
- // Bugs: Needs Triage
- (await countCards(COLUMN_ID_BUGS_NEEDS_TRIAGE)).length,
- // Bugs: Accepted
- (await countCards(COLUMN_ID_BUGS_ACCEPTED)).length + (await countCards(COLUMN_ID_BUGS_PRIORITIZED)).length,
- // RFC: In Progress
- 0, // (await countCards(COLUMN_ID_RFCS_IN_PROGRESS)).length,
- // RFC: Accepted
- 0, // (await countCards(COLUMN_ID_RFCS_ACCEPTED)).length + (await countCards(COLUMN_ID_RFCS_PRIORITIZED)).length,
- // Date (ISO)
- `"${new Date().toISOString()}"`,
- ].join(',');
+ const entry = [
+ // Date (Human Readable)
+ `"${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}"`,
+ // Commits in last 24 hours
+ (await countCommits(twentyFourHoursAgo)).length,
+ // New Issues(All) in last 24 hours
+ issuesLastTwentyFourHours.length,
+ // New Issues(Bugs) in last 24 hours
+ issuesLastTwentyFourHours.filter((iss) => iss.title.startsWith('🐛 BUG:')).length,
+ // New Issues(RFC) in last 24 hours
+ issuesLastTwentyFourHours.filter((iss) => iss.title.startsWith('💡 RFC:')).length,
+ // New Issues(Docs) in last 24 hours
+ issuesLastTwentyFourHours.filter((iss) => iss.title.startsWith('📘 DOC:')).length,
+ // New Pull Requests in last 24 hours
+ pullsLastTwentyFourHours.length,
+ // Pull requests
+ openPulls.length,
+ // Open Issues
+ openIssues.length,
+ // Bugs: Needs Triage
+ (await countCards(COLUMN_ID_BUGS_NEEDS_TRIAGE)).length,
+ // Bugs: Accepted
+ (await countCards(COLUMN_ID_BUGS_ACCEPTED)).length + (await countCards(COLUMN_ID_BUGS_PRIORITIZED)).length,
+ // RFC: In Progress
+ 0, // (await countCards(COLUMN_ID_RFCS_IN_PROGRESS)).length,
+ // RFC: Accepted
+ 0, // (await countCards(COLUMN_ID_RFCS_ACCEPTED)).length + (await countCards(COLUMN_ID_RFCS_PRIORITIZED)).length,
+ // Date (ISO)
+ `"${new Date().toISOString()}"`,
+ ].join(',');
- const statCsv = readFileSync('scripts/stats/stats.csv', { encoding: 'utf-8' });
- const [statHeader, ...statItems] = statCsv.split('\n');
- const updatedStatCsv = [statHeader, entry, ...statItems].join('\n');
- writeFileSync('scripts/stats/stats.csv', updatedStatCsv);
+ const statCsv = readFileSync('scripts/stats/stats.csv', { encoding: 'utf-8' });
+ const [statHeader, ...statItems] = statCsv.split('\n');
+ const updatedStatCsv = [statHeader, entry, ...statItems].join('\n');
+ writeFileSync('scripts/stats/stats.csv', updatedStatCsv);
}
run();
diff --git a/scripts/utils/svelte-plugin.js b/scripts/utils/svelte-plugin.js
index f8a30ff33..29e60c2d5 100644
--- a/scripts/utils/svelte-plugin.js
+++ b/scripts/utils/svelte-plugin.js
@@ -4,58 +4,58 @@ import { relative, isAbsolute, join, dirname } from 'path';
import { promises as fs } from 'fs';
const convertMessage = ({ message, start, end, filename, frame }) => ({
- text: message,
- location: start &&
- end && {
- file: filename,
- line: start.line,
- column: start.column,
- length: start.line === end.line ? end.column - start.column : 0,
- lineText: frame,
- },
+ text: message,
+ location: start &&
+ end && {
+ file: filename,
+ line: start.line,
+ column: start.column,
+ length: start.line === end.line ? end.column - start.column : 0,
+ lineText: frame,
+ },
});
const handleLoad = async (args, generate, { isDev }) => {
- const { path } = args;
- const source = await fs.readFile(path, 'utf8');
- const filename = relative(process.cwd(), path);
+ const { path } = args;
+ const source = await fs.readFile(path, 'utf8');
+ const filename = relative(process.cwd(), path);
- try {
- let compileOptions = { dev: isDev, css: false, generate, hydratable: true };
+ try {
+ let compileOptions = { dev: isDev, css: false, generate, hydratable: true };
- let { js, warnings } = compile(source, { ...compileOptions, filename });
- let contents = js.code + `\n//# sourceMappingURL=` + js.map.toUrl();
+ let { js, warnings } = compile(source, { ...compileOptions, filename });
+ let contents = js.code + `\n//# sourceMappingURL=` + js.map.toUrl();
- return { loader: 'js', contents, resolveDir: dirname(path), warnings: warnings.map((w) => convertMessage(w)) };
- } catch (e) {
- return { errors: [convertMessage(e)] };
- }
+ return { loader: 'js', contents, resolveDir: dirname(path), warnings: warnings.map((w) => convertMessage(w)) };
+ } catch (e) {
+ return { errors: [convertMessage(e)] };
+ }
};
export default function sveltePlugin({ isDev = false }) {
- return {
- name: 'svelte-esbuild',
- setup(build) {
- build.onResolve({ filter: /\.svelte$/ }, (args) => {
- let path = args.path.replace(/\.(?:client|server)/, '');
- path = isAbsolute(path) ? path : join(args.resolveDir, path);
-
- if (/\.client\.svelte$/.test(args.path)) {
- return {
- path,
- namespace: 'svelte:client',
- };
- }
-
- if (/\.server\.svelte$/.test(args.path)) {
- return {
- path,
- namespace: 'svelte:server',
- };
- }
- });
- build.onLoad({ filter: /.*/, namespace: 'svelte:client' }, (args) => handleLoad(args, 'dom', { isDev }));
- build.onLoad({ filter: /.*/, namespace: 'svelte:server' }, (args) => handleLoad(args, 'ssr', { isDev }));
- },
- };
+ return {
+ name: 'svelte-esbuild',
+ setup(build) {
+ build.onResolve({ filter: /\.svelte$/ }, (args) => {
+ let path = args.path.replace(/\.(?:client|server)/, '');
+ path = isAbsolute(path) ? path : join(args.resolveDir, path);
+
+ if (/\.client\.svelte$/.test(args.path)) {
+ return {
+ path,
+ namespace: 'svelte:client',
+ };
+ }
+
+ if (/\.server\.svelte$/.test(args.path)) {
+ return {
+ path,
+ namespace: 'svelte:server',
+ };
+ }
+ });
+ build.onLoad({ filter: /.*/, namespace: 'svelte:client' }, (args) => handleLoad(args, 'dom', { isDev }));
+ build.onLoad({ filter: /.*/, namespace: 'svelte:server' }, (args) => handleLoad(args, 'ssr', { isDev }));
+ },
+ };
}
diff --git a/www/astro.config.mjs b/www/astro.config.mjs
index 34fc3cd74..1ee290d12 100644
--- a/www/astro.config.mjs
+++ b/www/astro.config.mjs
@@ -8,8 +8,8 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- buildOptions: {
- sitemap: true,
- site: 'https://astro.build/',
- },
+ buildOptions: {
+ sitemap: true,
+ site: 'https://astro.build/',
+ },
});
diff --git a/www/src/components/Article.astro b/www/src/components/Article.astro
index 952c4af5a..4b2d814ac 100644
--- a/www/src/components/Article.astro
+++ b/www/src/components/Article.astro
@@ -1,69 +1,69 @@
<article>
- <slot></slot>
+ <slot></slot>
</article>
<style lang="scss">
- article {
- text-align: left;
- line-height: 1.5;
- display: flex;
- flex-direction: column;
- }
- :global(article > * + *) {
- margin-top: 1rem;
- }
- h1 {
- font-family: var(--font-sans);
- font-weight: 400;
- font-size: 16px;
- overflow: hidden;
- width: 100%;
- }
- em {
- font-style: normal;
- color: var(--color-green);
- }
- a {
- color: rgba(255, 255, 255, 0.8);
- position: relative;
- display: flex;
- font-family: var(--font-mono);
- font-size: 1rem;
- font-weight: 700;
- text-decoration: none;
- height: 40px;
- align-items: center;
- justify-content: center;
- text-align: center;
- padding-left: 24px;
- padding-right: 24px;
- width: max-content;
- margin: 2.5rem auto;
- }
- a:hover,
- a:focus {
- background: white;
- color: black;
- }
+ article {
+ text-align: left;
+ line-height: 1.5;
+ display: flex;
+ flex-direction: column;
+ }
+ :global(article > * + *) {
+ margin-top: 1rem;
+ }
+ h1 {
+ font-family: var(--font-sans);
+ font-weight: 400;
+ font-size: 16px;
+ overflow: hidden;
+ width: 100%;
+ }
+ em {
+ font-style: normal;
+ color: var(--color-green);
+ }
+ a {
+ color: rgba(255, 255, 255, 0.8);
+ position: relative;
+ display: flex;
+ font-family: var(--font-mono);
+ font-size: 1rem;
+ font-weight: 700;
+ text-decoration: none;
+ height: 40px;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ padding-left: 24px;
+ padding-right: 24px;
+ width: max-content;
+ margin: 2.5rem auto;
+ }
+ a:hover,
+ a:focus {
+ background: white;
+ color: black;
+ }
- a::before,
- a::after {
- content: "";
- display: block;
- position: absolute;
- top: 0;
- bottom: 0;
- width: 8px;
- border: 2px solid white;
- border-left-style: dotted;
- border-right-style: dotted;
- }
- a::before {
- border-right: 0;
- left: 0;
- }
- a::after {
- border-left: 0;
- right: 0;
- }
+ a::before,
+ a::after {
+ content: '';
+ display: block;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 8px;
+ border: 2px solid white;
+ border-left-style: dotted;
+ border-right-style: dotted;
+ }
+ a::before {
+ border-right: 0;
+ left: 0;
+ }
+ a::after {
+ border-left: 0;
+ right: 0;
+ }
</style>
diff --git a/www/src/components/ArticleFooter.astro b/www/src/components/ArticleFooter.astro
index 8078e2cc3..809556020 100644
--- a/www/src/components/ArticleFooter.astro
+++ b/www/src/components/ArticleFooter.astro
@@ -3,13 +3,13 @@ import AvatarList from './AvatarList.astro';
---
<footer>
- <AvatarList />
+ <AvatarList />
</footer>
<style>
-footer {
- margin-top: auto;
- padding: 2rem 0;
- border-top: 3px solid var(--theme-divider);
-}
+ footer {
+ margin-top: auto;
+ padding: 2rem 0;
+ border-top: 3px solid var(--theme-divider);
+ }
</style>
diff --git a/www/src/components/Author.astro b/www/src/components/Author.astro
index 0743f8f82..c042c7ab0 100644
--- a/www/src/components/Author.astro
+++ b/www/src/components/Author.astro
@@ -2,12 +2,13 @@
import authorData from '../data/authors.json';
export interface Props {
- authorId: string;
+ authorId: string;
}
const { authorId } = Astro.props;
const author = authorData[authorId];
---
+
<div class="author">
- <p>by {author.name}{' '}<a href={`https://twitter.com/${author.twitter}`}>@{author.twitter}</a></p>
+ <p>by {author.name}{' '}<a href={`https://twitter.com/${author.twitter}`}>@{author.twitter}</a></p>
</div>
diff --git a/www/src/components/AvatarList.astro b/www/src/components/AvatarList.astro
index aafcb371b..1ee68a222 100644
--- a/www/src/components/AvatarList.astro
+++ b/www/src/components/AvatarList.astro
@@ -1,74 +1,92 @@
<!-- Thanks to @5t3ph for https://smolcss.dev/#smol-avatar-list! -->
<ul class="avatar-list">
- <li><a href="https://smolcss.dev/#smol-avatar-list"><img alt="Avatar 1" width="64" height="64" src='https://avataaars.io/?avatarStyle=Transparent&topType=LongHairBun&accessoriesType=Blank&hairColor=Auburn&facialHairType=BeardMedium&facialHairColor=Auburn&clotheType=ShirtCrewNeck&clotheColor=Blue01&eyeType=Side&eyebrowType=RaisedExcitedNatural&mouthType=Serious&skinColor=Tanned' /></a></li>
- <li><a href="https://smolcss.dev/#smol-avatar-list"><img alt="Avatar 2" width="64" height="64" src='https://avataaars.io/?avatarStyle=Transparent&topType=LongHairDreads&accessoriesType=Blank&hairColor=Brown&facialHairType=Blank&clotheType=ShirtScoopNeck&clotheColor=PastelGreen&eyeType=Default&eyebrowType=DefaultNatural&mouthType=Smile&skinColor=Tanned' /></a></li>
- <li><a href="https://smolcss.dev/#smol-avatar-list"><img alt="Avatar 3" width="64" height="64" src='https://avataaars.io/?avatarStyle=Transparent&topType=LongHairCurly&hairColor=BrownDark&facialHairType=Blank&clotheType=GraphicShirt&clotheColor=Pink&graphicType=Diamond&eyeType=Side&eyebrowType=Default&mouthType=Default&skinColor=Brown'/></a></li>
+ <li>
+ <a href="https://smolcss.dev/#smol-avatar-list"
+ ><img
+ alt="Avatar 1"
+ width="64"
+ height="64"
+ src="https://avataaars.io/?avatarStyle=Transparent&topType=LongHairBun&accessoriesType=Blank&hairColor=Auburn&facialHairType=BeardMedium&facialHairColor=Auburn&clotheType=ShirtCrewNeck&clotheColor=Blue01&eyeType=Side&eyebrowType=RaisedExcitedNatural&mouthType=Serious&skinColor=Tanned"
+ /></a>
+ </li>
+ <li>
+ <a href="https://smolcss.dev/#smol-avatar-list"
+ ><img
+ alt="Avatar 2"
+ width="64"
+ height="64"
+ src="https://avataaars.io/?avatarStyle=Transparent&topType=LongHairDreads&accessoriesType=Blank&hairColor=Brown&facialHairType=Blank&clotheType=ShirtScoopNeck&clotheColor=PastelGreen&eyeType=Default&eyebrowType=DefaultNatural&mouthType=Smile&skinColor=Tanned"
+ /></a>
+ </li>
+ <li>
+ <a href="https://smolcss.dev/#smol-avatar-list"
+ ><img
+ alt="Avatar 3"
+ width="64"
+ height="64"
+ src="https://avataaars.io/?avatarStyle=Transparent&topType=LongHairCurly&hairColor=BrownDark&facialHairType=Blank&clotheType=GraphicShirt&clotheColor=Pink&graphicType=Diamond&eyeType=Side&eyebrowType=Default&mouthType=Default&skinColor=Brown"
+ /></a>
+ </li>
</ul>
<style>
-.avatar-list {
- --avatar-size: 2.5rem;
- --avatar-count: 3;
+ .avatar-list {
+ --avatar-size: 2.5rem;
+ --avatar-count: 3;
- display: grid;
- list-style: none;
- /* Default to displaying most of the avatar to
+ display: grid;
+ list-style: none;
+ /* Default to displaying most of the avatar to
enable easier access on touch devices, ensuring
the WCAG touch target size is met or exceeded */
- grid-template-columns: repeat(
- var(--avatar-count),
- max(44px, calc(var(--avatar-size) / 1.15))
- );
- /* `padding` matches added visual dimensions of
+ grid-template-columns: repeat(var(--avatar-count), max(44px, calc(var(--avatar-size) / 1.15)));
+ /* `padding` matches added visual dimensions of
the `box-shadow` to help create a more accurate
computed component size */
- padding: 0.08em;
- font-size: var(--avatar-size);
-}
+ padding: 0.08em;
+ font-size: var(--avatar-size);
+ }
-@media (any-hover: hover) and (any-pointer: fine) {
- .avatar-list {
- /* We create 1 extra cell to enable the computed
+ @media (any-hover: hover) and (any-pointer: fine) {
+ .avatar-list {
+ /* We create 1 extra cell to enable the computed
width to match the final visual width */
- grid-template-columns: repeat(
- calc(var(--avatar-count) + 1),
- calc(var(--avatar-size) / 1.75)
- );
- }
-}
+ grid-template-columns: repeat(calc(var(--avatar-count) + 1), calc(var(--avatar-size) / 1.75));
+ }
+ }
-.avatar-list li {
- width: var(--avatar-size);
- height: var(--avatar-size);
-}
+ .avatar-list li {
+ width: var(--avatar-size);
+ height: var(--avatar-size);
+ }
-.avatar-list li:hover ~ li a,
-.avatar-list li:focus-within ~ li a {
- transform: translateX(33%);
-}
+ .avatar-list li:hover ~ li a,
+ .avatar-list li:focus-within ~ li a {
+ transform: translateX(33%);
+ }
-.avatar-list img,
-.avatar-list a {
- display: block;
- border-radius: 50%;
-}
+ .avatar-list img,
+ .avatar-list a {
+ display: block;
+ border-radius: 50%;
+ }
-.avatar-list a {
- transition: transform 180ms ease-in-out;
-}
+ .avatar-list a {
+ transition: transform 180ms ease-in-out;
+ }
-.avatar-list img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- background-color: #fff;
- box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
-}
+ .avatar-list img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ background-color: #fff;
+ box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
+ }
-.avatar-list a:focus {
- outline: 2px solid transparent;
- /* Double-layer trick to work for dark and light backgrounds */
- box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
-}
+ .avatar-list a:focus {
+ outline: 2px solid transparent;
+ /* Double-layer trick to work for dark and light backgrounds */
+ box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
+ }
</style>
diff --git a/www/src/components/BackArrow.astro b/www/src/components/BackArrow.astro
index 2d9d3332f..5ee1a331c 100644
--- a/www/src/components/BackArrow.astro
+++ b/www/src/components/BackArrow.astro
@@ -1,16 +1,18 @@
---
interface Props {
- url: string
- title: string
+ url: string;
+ title: string;
}
-const { url, title } = Astro.props
+const { url, title } = Astro.props;
---
+
<a href={url}>
- &leftarrow; {title}
+ &leftarrow; {title}
</a>
+
<style>
-a {
- display: block;
- margin-top: 2rem;
-}
-</style> \ No newline at end of file
+ a {
+ display: block;
+ margin-top: 2rem;
+ }
+</style>
diff --git a/www/src/components/BaseHead.astro b/www/src/components/BaseHead.astro
index 552c5ad12..dbebff48c 100644
--- a/www/src/components/BaseHead.astro
+++ b/www/src/components/BaseHead.astro
@@ -1,9 +1,9 @@
---
export interface Props {
- title: string;
- description: string;
- canonicalURL: URL | string,
- image?: string;
+ title: string;
+ description: string;
+ canonicalURL: URL | string;
+ image?: string;
}
const { title, description, image = 'https://astro.build/social.jpg?v=1', canonicalURL } = Astro.props;
---
@@ -11,16 +11,16 @@ const { title, description, image = 'https://astro.build/social.jpg?v=1', canoni
<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
-<meta name="theme-color" content="#ff5e00"/>
+<meta name="theme-color" content="#ff5e00" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="alternate icon" type="image/x-icon" href="/favicon.ico" />
-<link rel="sitemap" href="/sitemap.xml"/>
+<link rel="sitemap" href="/sitemap.xml" />
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
-<link rel="canonical" href={canonicalURL}/>
+<link rel="canonical" href={canonicalURL} />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
diff --git a/www/src/components/BlockQuote.astro b/www/src/components/BlockQuote.astro
index f1f594461..94a081ee5 100644
--- a/www/src/components/BlockQuote.astro
+++ b/www/src/components/BlockQuote.astro
@@ -1,42 +1,42 @@
---
export interface Props {
- author: string;
- source: string;
- sourceHref: string;
+ author: string;
+ source: string;
+ sourceHref: string;
}
const { author, source, sourceHref } = Astro.props;
---
<blockquote>
- <slot />
- <div class="source">
- <p>{author}</p>
- <a href={sourceHref}>{source}</a>
- </div>
+ <slot />
+ <div class="source">
+ <p>{author}</p>
+ <a href={sourceHref}>{source}</a>
+ </div>
</blockquote>
<style>
- blockquote {
- font-size: 1.5rem;
- --padding-block: 1rem;
- --padding-inline: 1.25rem;
- --color: var(--theme-divider);
+ blockquote {
+ font-size: 1.5rem;
+ --padding-block: 1rem;
+ --padding-inline: 1.25rem;
+ --color: var(--theme-divider);
- display: flex;
- flex-direction: column;
+ display: flex;
+ flex-direction: column;
- padding: var(--padding-block) var(--padding-inline);
- margin-left: calc(var(--padding-inline) * -1);
- margin-right: calc(var(--padding-inline) * -1);
-
- background: transparent;
- border-left: calc(var(--padding-inline) / 2) solid var(--color);
- border-radius: 0;
- }
+ padding: var(--padding-block) var(--padding-inline);
+ margin-left: calc(var(--padding-inline) * -1);
+ margin-right: calc(var(--padding-inline) * -1);
- blockquote .source {
- font-weight: 500;
- color: var(--color);
- font-size: 1rem;
- }
+ background: transparent;
+ border-left: calc(var(--padding-inline) / 2) solid var(--color);
+ border-radius: 0;
+ }
+
+ blockquote .source {
+ font-weight: 500;
+ color: var(--color);
+ font-size: 1rem;
+ }
</style>
diff --git a/www/src/components/BlogHead.astro b/www/src/components/BlogHead.astro
index 5ce0505f9..50e9ed69e 100644
--- a/www/src/components/BlogHead.astro
+++ b/www/src/components/BlogHead.astro
@@ -3,5 +3,6 @@ import BaseHead from './BaseHead.astro';
const { title, description, image, canonicalURL } = Astro.props;
---
-<BaseHead title={title} description={description} image={image} canonicalURL={canonicalURL} />
-<link rel="stylesheet" href={Astro.resolve('../scss/blog.scss')} /> \ No newline at end of file
+
+<BaseHead {title} {description} {image} {canonicalURL} />
+<link rel="stylesheet" href={Astro.resolve('../scss/blog.scss')} />
diff --git a/www/src/components/BlogHeader.astro b/www/src/components/BlogHeader.astro
index 9e80cc1c0..0899d15ec 100644
--- a/www/src/components/BlogHeader.astro
+++ b/www/src/components/BlogHeader.astro
@@ -1,110 +1,129 @@
<header class="layout">
- <article>
- <h1>
- <a href="/">
- <svg class="logo" width="32" height="32" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
- <style>
- #flame {
- fill: #ff5d01;
- }
- #a {
- fill: #000014;
- }
- </style>
- <title>Astro</title>
- <path
- id="a"
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
- />
- <path
- id="flame"
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
- />
- </svg>
- <span>Astro</span>
- </a>
- </h1>
- <a class="header-subitem header-subitem-secondary" href="https://docs.astro.build/" target="_blank">
- <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="book" class="svg-inline--fa fa-book fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M448 360V24c0-13.3-10.7-24-24-24H96C43 0 0 43 0 96v320c0 53 43 96 96 96h328c13.3 0 24-10.7 24-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3 0-74.7 5.4-4.3 8.9-11.1 8.9-18.6zM128 134c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm0 64c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm253.4 250H96c-17.7 0-32-14.3-32-32 0-17.6 14.4-32 32-32h285.4c-1.9 17.1-1.9 46.9 0 64z"></path></svg>
- Documentation
- </a>
- <a class="header-subitem" href="https://twitter.com/astrodotbuild" target="_blank">
- <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"><path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/></svg>
- Twitter
- </a>
- <a class="header-subitem header-subitem-secondary" href="https://github.com/withastro/astro" target="_blank">
- <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
- </svg>
- GitHub
- </a>
- </article>
+ <article>
+ <h1>
+ <a href="/">
+ <svg class="logo" width="32" height="32" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <style>
+ #flame {
+ fill: #ff5d01;
+ }
+ #a {
+ fill: #000014;
+ }
+ </style>
+ <title>Astro</title>
+ <path
+ id="a"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
+ ></path>
+ <path
+ id="flame"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
+ ></path>
+ </svg>
+ <span>Astro</span>
+ </a>
+ </h1>
+ <a class="header-subitem header-subitem-secondary" href="https://docs.astro.build/" target="_blank">
+ <svg
+ aria-hidden="true"
+ focusable="false"
+ data-prefix="fas"
+ data-icon="book"
+ class="svg-inline--fa fa-book fa-w-14"
+ role="img"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 448 512"
+ ><path
+ fill="currentColor"
+ d="M448 360V24c0-13.3-10.7-24-24-24H96C43 0 0 43 0 96v320c0 53 43 96 96 96h328c13.3 0 24-10.7 24-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3 0-74.7 5.4-4.3 8.9-11.1 8.9-18.6zM128 134c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm0 64c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm253.4 250H96c-17.7 0-32-14.3-32-32 0-17.6 14.4-32 32-32h285.4c-1.9 17.1-1.9 46.9 0 64z"
+ ></path></svg
+ >
+ Documentation
+ </a>
+ <a class="header-subitem" href="https://twitter.com/astrodotbuild" target="_blank">
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"
+ ><path
+ d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"
+ ></path></svg
+ >
+ Twitter
+ </a>
+ <a class="header-subitem header-subitem-secondary" href="https://github.com/withastro/astro" target="_blank">
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
+ <path
+ d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
+ ></path>
+ </svg>
+ GitHub
+ </a>
+ </article>
</header>
<style>
-header {
- padding-top: 1rem;
- padding-bottom: 1rem;
- height: 5rem;
-}
-article {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-.header-subitem {
- display: flex;
- flex-grow: 0;
- gap: 0.5em;
- align-items: center;
- justify-content: center;
- color: var(--theme-text-lighter);
- font-size: initial;
- padding: 0.5rem;
-}
-.header-subitem:hover {
- color: var(--theme-accent);
-}
-.header-subitem svg {
- width: 1.5rem;
- height: 1.5rem;
-}
+ header {
+ padding-top: 1rem;
+ padding-bottom: 1rem;
+ height: 5rem;
+ }
+ article {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+ .header-subitem {
+ display: flex;
+ flex-grow: 0;
+ gap: 0.5em;
+ align-items: center;
+ justify-content: center;
+ color: var(--theme-text-lighter);
+ font-size: initial;
+ padding: 0.5rem;
+ }
+ .header-subitem:hover {
+ color: var(--theme-accent);
+ }
+ .header-subitem svg {
+ width: 1.5rem;
+ height: 1.5rem;
+ }
-@media (max-width: 40em) {
- .header-subitem-secondary {
- display: none;
- }
-}
+ @media (max-width: 40em) {
+ .header-subitem-secondary {
+ display: none;
+ }
+ }
-@media (max-width: 20em) {
- .header-subitem {
- display: none;
- }
-}
+ @media (max-width: 20em) {
+ .header-subitem {
+ display: none;
+ }
+ }
-h1 {
- margin: 0;
- font-size: 1.5rem;
- max-width: 100%;
- display: flex;
- flex-grow: 1;
-}
+ h1 {
+ margin: 0;
+ font-size: 1.5rem;
+ max-width: 100%;
+ display: flex;
+ flex-grow: 1;
+ }
-.logo {
- transform: translateY(0.25rem);
-}
+ .logo {
+ transform: translateY(0.25rem);
+ }
-svg {
- width: 2.5rem;
- height: 2.5rem;
-}
+ svg {
+ width: 2.5rem;
+ height: 2.5rem;
+ }
-h1 a {
- text-decoration: none;
- display: inline-flex;
-}
+ h1 a {
+ text-decoration: none;
+ display: inline-flex;
+ }
</style>
diff --git a/www/src/components/BlogPost.astro b/www/src/components/BlogPost.astro
index 72396b9cd..580179c42 100644
--- a/www/src/components/BlogPost.astro
+++ b/www/src/components/BlogPost.astro
@@ -5,106 +5,106 @@ import GoogleAnalytics from './GoogleAnalytics.astro';
import BackArrow from './BackArrow.astro';
export interface Props {
- title: string;
- author?: string;
- publishDate: string;
- heroImage?: string;
- heroImageAlt?: string;
+ title: string;
+ author?: string;
+ publishDate: string;
+ heroImage?: string;
+ heroImageAlt?: string;
}
const { title, author, publishDate, heroImage, heroImageAlt } = Astro.props;
---
<div class="layout">
- <article>
- <header>
- <img width="720" height="420" class="hero-image" loading="lazy" src={heroImage || '/social.jpg'} alt={heroImageAlt || ""} />
- <p class="publish-date">{publishDate}</p>
- <h1 class="title">{title}</h1>
- {author && <Author authorId={author} />}
- <GithubStarButton />
- </header>
- <main class="content">
- <slot />
- <BackArrow url={'/blog/'} title={'Back to all blog posts'}/>
- </main>
- </article>
- <GoogleAnalytics />
+ <article>
+ <header>
+ <img width="720" height="420" class="hero-image" loading="lazy" src={heroImage || '/social.jpg'} alt={heroImageAlt || ''} />
+ <p class="publish-date">{publishDate}</p>
+ <h1 class="title">{title}</h1>
+ {author && <Author authorId={author} />}
+ <GithubStarButton />
+ </header>
+ <main class="content">
+ <slot />
+ <BackArrow url={'/blog/'} title={'Back to all blog posts'} />
+ </main>
+ </article>
+ <GoogleAnalytics />
</div>
<style lang="scss">
-.hero-image {
- width: 100vw;
- object-fit: cover;
- object-position: center;
- margin-top: 2rem;
- margin-bottom: 4rem;
- max-width: 1280px;
-
- @media (max-width: 50em) {
- height: 260px;
- margin-top: 0;
- margin-bottom: 2rem;
- }
-}
-
-.content {
- margin-bottom: 8rem;
-
- :global(> * + *) {
- margin-top: 1rem;
- }
-
- :global(h2) {
- margin-top: 4rem;
- }
-
- :global(img) {
- max-width: 100%;
- height: auto;
- }
-
- :global(figure) {
- background-color: var(--theme-bg-accent);
- margin: 2.5rem 0;
- padding: 1.5rem;
- }
-
- // hack until <figcaption> parsing fixed
- :global(figcaption *) {
- display: inline;
-
- & + :global(*) {
- margin-left: 0.3em;
- }
- }
-}
-
-header {
- display: flex;
- flex-direction: column;
- text-align: center;
- align-items: center;
- justify-content: center;
-
- padding-bottom: 2rem;
- margin-bottom: 2rem;
- border-bottom: 4px solid var(--theme-divider);
-}
-
-.title,
-.author,
-.publish-date {
- margin: 0;
-}
-
-.publish-date,
-.author {
- color: var(--theme-text-lighter);
-}
-
-.title {
- font-size: 2.25rem;
- font-weight: 700;
-}
+ .hero-image {
+ width: 100vw;
+ object-fit: cover;
+ object-position: center;
+ margin-top: 2rem;
+ margin-bottom: 4rem;
+ max-width: 1280px;
+
+ @media (max-width: 50em) {
+ height: 260px;
+ margin-top: 0;
+ margin-bottom: 2rem;
+ }
+ }
+
+ .content {
+ margin-bottom: 8rem;
+
+ :global(> * + *) {
+ margin-top: 1rem;
+ }
+
+ :global(h2) {
+ margin-top: 4rem;
+ }
+
+ :global(img) {
+ max-width: 100%;
+ height: auto;
+ }
+
+ :global(figure) {
+ background-color: var(--theme-bg-accent);
+ margin: 2.5rem 0;
+ padding: 1.5rem;
+ }
+
+ // hack until <figcaption> parsing fixed
+ :global(figcaption *) {
+ display: inline;
+
+ & + :global(*) {
+ margin-left: 0.3em;
+ }
+ }
+ }
+
+ header {
+ display: flex;
+ flex-direction: column;
+ text-align: center;
+ align-items: center;
+ justify-content: center;
+
+ padding-bottom: 2rem;
+ margin-bottom: 2rem;
+ border-bottom: 4px solid var(--theme-divider);
+ }
+
+ .title,
+ .author,
+ .publish-date {
+ margin: 0;
+ }
+
+ .publish-date,
+ .author {
+ color: var(--theme-text-lighter);
+ }
+
+ .title {
+ font-size: 2.25rem;
+ font-weight: 700;
+ }
</style>
diff --git a/www/src/components/BlogPostPreview.astro b/www/src/components/BlogPostPreview.astro
index bb1459dbb..aa1e8731b 100644
--- a/www/src/components/BlogPostPreview.astro
+++ b/www/src/components/BlogPostPreview.astro
@@ -2,55 +2,56 @@
import Author from './Author.astro';
export interface Props {
- title: string;
- publishDate: string;
- href: string;
+ title: string;
+ publishDate: string;
+ href: string;
}
const { title, publishDate, href } = Astro.props;
---
+
<article class="post-preview">
- <header>
- <p class="publish-date">{publishDate}</p>
- <a href={href}><h1 class="title">{title}</h1></a>
- </header>
- <slot />{' '}<a href={href}>Read more →</a>
+ <header>
+ <p class="publish-date">{publishDate}</p>
+ <a {href}><h1 class="title">{title}</h1></a>
+ </header>
+ <slot />{' '}<a {href}>Read more →</a>
</article>
<style>
-.content :global(main > * + *) {
- margin-top: 1rem;
-}
-
-.post-preview {
- padding-bottom: 2rem;
- margin-bottom: 2rem;
- border-bottom: 4px solid var(--theme-divider);
-}
-
-header {
- display: flex;
- flex-direction: column;
- text-align: left;
- align-items: flex-start;
- justify-content: center;
-}
-
-.title,
-.author,
-.publish-date {
- margin: 0;
-}
-
-.publish-date,
-.author {
- font-size: 1.25rem;
- color: var(--theme-text-lighter);
-}
-
-.title {
- font-size: 2.25rem;
- font-weight: 700;
- color: var(--theme-text);
-}
+ .content :global(main > * + *) {
+ margin-top: 1rem;
+ }
+
+ .post-preview {
+ padding-bottom: 2rem;
+ margin-bottom: 2rem;
+ border-bottom: 4px solid var(--theme-divider);
+ }
+
+ header {
+ display: flex;
+ flex-direction: column;
+ text-align: left;
+ align-items: flex-start;
+ justify-content: center;
+ }
+
+ .title,
+ .author,
+ .publish-date {
+ margin: 0;
+ }
+
+ .publish-date,
+ .author {
+ font-size: 1.25rem;
+ color: var(--theme-text-lighter);
+ }
+
+ .title {
+ font-size: 2.25rem;
+ font-weight: 700;
+ color: var(--theme-text);
+ }
</style>
diff --git a/www/src/components/GithubStarButton.astro b/www/src/components/GithubStarButton.astro
index 103561eb2..59d50d5e3 100644
--- a/www/src/components/GithubStarButton.astro
+++ b/www/src/components/GithubStarButton.astro
@@ -1,7 +1,8 @@
<!-- Place this tag where you want the button to render. -->
<div class="hidden-mobile" style="text-align: center; height: 36px; margin-top: 0.75rem;">
- <a class="github-button" href="https://github.com/withastro/astro" data-icon="octicon-star"
- data-size="large" data-show-count="true" aria-label="Star withastro/astro on GitHub">Star</a>
+ <a class="github-button" href="https://github.com/withastro/astro" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star withastro/astro on GitHub"
+ >Star</a
+ >
</div>
<!-- Place this tag in your head or just before your close body tag. -->
-<script async="async" defer="defer" src="https://buttons.github.io/buttons.js"></script> \ No newline at end of file
+<script async="async" defer="defer" src="https://buttons.github.io/buttons.js"></script>
diff --git a/www/src/components/GoogleAnalytics.astro b/www/src/components/GoogleAnalytics.astro
index 8790b2b18..7b98ab43a 100644
--- a/www/src/components/GoogleAnalytics.astro
+++ b/www/src/components/GoogleAnalytics.astro
@@ -1,8 +1,10 @@
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-130280175-15"></script>
<script>
- window.dataLayer = window.dataLayer || [];
- function gtag(){dataLayer.push(arguments);}
- gtag('js', new Date());
- gtag('config', 'UA-130280175-15');
+ window.dataLayer = window.dataLayer || [];
+ function gtag() {
+ dataLayer.push(arguments);
+ }
+ gtag('js', new Date());
+ gtag('config', 'UA-130280175-15');
</script>
diff --git a/www/src/components/Logo.astro b/www/src/components/Logo.astro
index 7926dab4d..a92927536 100644
--- a/www/src/components/Logo.astro
+++ b/www/src/components/Logo.astro
@@ -1,56 +1,86 @@
<svg class="logo" width="158" height="170" viewBox="0 0 158 170" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path fill-rule="evenodd" clip-rule="evenodd" d="M96.5039 9.46441C97.4758 10.671 97.9714 12.2991 98.9626 15.5553L120.617 86.6902C112.611 82.5368 103.907 79.5413 94.7281 77.9252L80.6289 30.2798C80.3982 29.5002 79.6822 28.9654 78.8692 28.9654C78.0541 28.9654 77.3367 29.503 77.1079 30.2853L63.1795 77.9011C53.9579 79.51 45.2146 82.5109 37.1741 86.6793L58.9347 15.5388C59.929 12.2882 60.4262 10.6629 61.3981 9.45854C62.2562 8.39532 63.3723 7.56959 64.64 7.06003C66.076 6.48285 67.7756 6.48285 71.1749 6.48285H86.7174C90.1211 6.48285 91.823 6.48285 93.2603 7.06124C94.5291 7.575 95.6459 8.39925 96.5039 9.46441Z" fill="white" />
- <path fill-rule="evenodd" clip-rule="evenodd" d="M99.0951 90.0755C95.5253 93.1279 88.4002 95.2097 80.1929 95.2097C70.1197 95.2097 61.6767 92.0737 59.4363 87.8561C58.6354 90.2733 58.4558 93.0397 58.4558 94.8069C58.4558 94.8069 57.9281 103.485 63.9636 109.52C63.9636 106.386 66.5042 103.846 69.6381 103.846C75.0097 103.846 75.0036 108.532 74.9987 112.334C74.9986 112.448 74.9984 112.561 74.9984 112.673C74.9984 118.444 78.5255 123.391 83.5416 125.477C82.7924 123.936 82.3721 122.205 82.3721 120.377C82.3721 114.873 85.6034 112.823 89.3588 110.441C92.3469 108.546 95.6668 106.441 97.9548 102.217C99.1486 100.013 99.8265 97.4893 99.8265 94.8069C99.8265 93.1573 99.5702 91.5676 99.0951 90.0755Z" fill="#FF5D01" />
- <path fill-rule="evenodd" clip-rule="evenodd" d="M99.0951 90.0755C95.5253 93.1279 88.4002 95.2097 80.1929 95.2097C70.1197 95.2097 61.6767 92.0737 59.4363 87.8561C58.6354 90.2733 58.4558 93.0397 58.4558 94.8069C58.4558 94.8069 57.9281 103.485 63.9636 109.52C63.9636 106.386 66.5042 103.846 69.6381 103.846C75.0097 103.846 75.0036 108.532 74.9987 112.334C74.9986 112.448 74.9984 112.561 74.9984 112.673C74.9984 118.444 78.5255 123.391 83.5416 125.477C82.7924 123.936 82.3721 122.205 82.3721 120.377C82.3721 114.873 85.6034 112.823 89.3588 110.441C92.3469 108.546 95.6668 106.441 97.9548 102.217C99.1486 100.013 99.8265 97.4893 99.8265 94.8069C99.8265 93.1573 99.5702 91.5676 99.0951 90.0755Z" fill="url(#paint1_linear)" />
- <path d="M11.9957 169.024C20.0117 169.024 24.8597 167.104 27.6917 163.12C27.6917 164.896 27.7877 166.576 28.0277 168.112H32.7797C32.3477 165.616 32.2517 163.984 32.2517 159.472V153.328C32.2517 146.704 27.1157 143.2 17.3237 143.2C7.8677 143.2 1.7237 146.848 0.955701 152.128H5.9957C6.7637 148.576 10.7477 146.704 17.3237 146.704C23.8037 146.704 27.6437 148.96 27.6437 152.8V153.28L12.6677 154.144C6.5717 154.48 4.3157 155.344 2.5877 156.592C0.955701 157.792 0.0437012 159.664 0.0437012 161.824C0.0437012 166.384 4.7477 169.024 11.9957 169.024ZM13.5317 165.616C7.9637 165.616 4.8917 164.32 4.8917 161.728C4.8917 158.944 6.8117 157.696 13.5797 157.264L27.6437 156.4V157.504C27.6437 162.544 21.7397 165.616 13.5317 165.616Z" fill="white" />
- <path d="M55.9352 169.024C65.8712 169.024 69.8552 165.76 69.8552 161.008C69.8552 157.072 67.4072 155.056 61.1672 154.528L49.5032 153.616C46.3352 153.376 44.5592 152.464 44.5592 150.496C44.5592 148 47.2952 146.704 53.1992 146.704C59.9192 146.704 63.4232 148.048 65.7272 151.024L69.6152 149.152C67.2152 145.408 61.8872 143.2 53.6312 143.2C45.1352 143.2 40.0472 146.032 40.0472 150.688C40.0472 154.864 43.0712 156.88 48.7832 157.36L60.3512 158.272C64.1432 158.56 65.2952 159.328 65.2952 161.296C65.2952 164.128 62.3672 165.472 56.5592 165.472C49.5032 165.472 45.0392 163.552 42.8792 160.048L39.0872 162.112C42.0152 166.528 47.1512 169.024 55.9352 169.024Z" fill="white" />
- <path d="M79.6765 147.712V159.28C79.6765 164.032 81.3085 168.784 90.1885 168.784C92.4445 168.784 95.1805 168.352 96.3805 167.824V163.936C94.7005 164.32 92.6845 164.608 90.7165 164.608C86.5405 164.608 84.2845 162.976 84.2845 158.848V147.712H96.2845V144.112H84.2845V136L79.6765 137.872V144.112H72.1404V147.712H79.6765Z" fill="white" />
- <path d="M107.728 144.112H103.504V168.112H108.064V159.136C108.064 155.68 108.736 152.752 110.656 150.736C112.336 148.864 114.496 147.808 118.288 147.808C119.584 147.808 120.4 147.904 121.504 148.096V143.68C120.496 143.44 119.632 143.392 118.336 143.392C113.2 143.392 109.12 146.416 107.728 151.072V144.112Z" fill="white" />
- <path d="M140.724 169.024C150.948 169.024 157.956 163.84 157.956 156.112C157.956 148.384 150.948 143.2 140.724 143.2C130.5 143.2 123.492 148.384 123.492 156.112C123.492 163.84 130.5 169.024 140.724 169.024ZM140.724 165.232C133.188 165.232 128.34 161.68 128.34 156.112C128.34 150.544 133.188 146.992 140.724 146.992C148.212 146.992 153.108 150.544 153.108 156.112C153.108 161.68 148.212 165.232 140.724 165.232Z" fill="white" />
- <defs>
- <linearGradient id="paint1_linear" x1="115.168" y1="65.245" x2="94.0326" y2="109.491" gradientUnits="userSpaceOnUse">
- <stop stop-color="#FF1639" />
- <stop offset="1" stop-color="#FF1639" stop-opacity="0" />
- </linearGradient>
- </defs>
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M96.5039 9.46441C97.4758 10.671 97.9714 12.2991 98.9626 15.5553L120.617 86.6902C112.611 82.5368 103.907 79.5413 94.7281 77.9252L80.6289 30.2798C80.3982 29.5002 79.6822 28.9654 78.8692 28.9654C78.0541 28.9654 77.3367 29.503 77.1079 30.2853L63.1795 77.9011C53.9579 79.51 45.2146 82.5109 37.1741 86.6793L58.9347 15.5388C59.929 12.2882 60.4262 10.6629 61.3981 9.45854C62.2562 8.39532 63.3723 7.56959 64.64 7.06003C66.076 6.48285 67.7756 6.48285 71.1749 6.48285H86.7174C90.1211 6.48285 91.823 6.48285 93.2603 7.06124C94.5291 7.575 95.6459 8.39925 96.5039 9.46441Z"
+ fill="white"
+ />
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M99.0951 90.0755C95.5253 93.1279 88.4002 95.2097 80.1929 95.2097C70.1197 95.2097 61.6767 92.0737 59.4363 87.8561C58.6354 90.2733 58.4558 93.0397 58.4558 94.8069C58.4558 94.8069 57.9281 103.485 63.9636 109.52C63.9636 106.386 66.5042 103.846 69.6381 103.846C75.0097 103.846 75.0036 108.532 74.9987 112.334C74.9986 112.448 74.9984 112.561 74.9984 112.673C74.9984 118.444 78.5255 123.391 83.5416 125.477C82.7924 123.936 82.3721 122.205 82.3721 120.377C82.3721 114.873 85.6034 112.823 89.3588 110.441C92.3469 108.546 95.6668 106.441 97.9548 102.217C99.1486 100.013 99.8265 97.4893 99.8265 94.8069C99.8265 93.1573 99.5702 91.5676 99.0951 90.0755Z"
+ fill="#FF5D01"
+ />
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M99.0951 90.0755C95.5253 93.1279 88.4002 95.2097 80.1929 95.2097C70.1197 95.2097 61.6767 92.0737 59.4363 87.8561C58.6354 90.2733 58.4558 93.0397 58.4558 94.8069C58.4558 94.8069 57.9281 103.485 63.9636 109.52C63.9636 106.386 66.5042 103.846 69.6381 103.846C75.0097 103.846 75.0036 108.532 74.9987 112.334C74.9986 112.448 74.9984 112.561 74.9984 112.673C74.9984 118.444 78.5255 123.391 83.5416 125.477C82.7924 123.936 82.3721 122.205 82.3721 120.377C82.3721 114.873 85.6034 112.823 89.3588 110.441C92.3469 108.546 95.6668 106.441 97.9548 102.217C99.1486 100.013 99.8265 97.4893 99.8265 94.8069C99.8265 93.1573 99.5702 91.5676 99.0951 90.0755Z"
+ fill="url(#paint1_linear)"
+ />
+ <path
+ d="M11.9957 169.024C20.0117 169.024 24.8597 167.104 27.6917 163.12C27.6917 164.896 27.7877 166.576 28.0277 168.112H32.7797C32.3477 165.616 32.2517 163.984 32.2517 159.472V153.328C32.2517 146.704 27.1157 143.2 17.3237 143.2C7.8677 143.2 1.7237 146.848 0.955701 152.128H5.9957C6.7637 148.576 10.7477 146.704 17.3237 146.704C23.8037 146.704 27.6437 148.96 27.6437 152.8V153.28L12.6677 154.144C6.5717 154.48 4.3157 155.344 2.5877 156.592C0.955701 157.792 0.0437012 159.664 0.0437012 161.824C0.0437012 166.384 4.7477 169.024 11.9957 169.024ZM13.5317 165.616C7.9637 165.616 4.8917 164.32 4.8917 161.728C4.8917 158.944 6.8117 157.696 13.5797 157.264L27.6437 156.4V157.504C27.6437 162.544 21.7397 165.616 13.5317 165.616Z"
+ fill="white"
+ />
+ <path
+ d="M55.9352 169.024C65.8712 169.024 69.8552 165.76 69.8552 161.008C69.8552 157.072 67.4072 155.056 61.1672 154.528L49.5032 153.616C46.3352 153.376 44.5592 152.464 44.5592 150.496C44.5592 148 47.2952 146.704 53.1992 146.704C59.9192 146.704 63.4232 148.048 65.7272 151.024L69.6152 149.152C67.2152 145.408 61.8872 143.2 53.6312 143.2C45.1352 143.2 40.0472 146.032 40.0472 150.688C40.0472 154.864 43.0712 156.88 48.7832 157.36L60.3512 158.272C64.1432 158.56 65.2952 159.328 65.2952 161.296C65.2952 164.128 62.3672 165.472 56.5592 165.472C49.5032 165.472 45.0392 163.552 42.8792 160.048L39.0872 162.112C42.0152 166.528 47.1512 169.024 55.9352 169.024Z"
+ fill="white"
+ />
+ <path
+ d="M79.6765 147.712V159.28C79.6765 164.032 81.3085 168.784 90.1885 168.784C92.4445 168.784 95.1805 168.352 96.3805 167.824V163.936C94.7005 164.32 92.6845 164.608 90.7165 164.608C86.5405 164.608 84.2845 162.976 84.2845 158.848V147.712H96.2845V144.112H84.2845V136L79.6765 137.872V144.112H72.1404V147.712H79.6765Z"
+ fill="white"
+ />
+ <path
+ d="M107.728 144.112H103.504V168.112H108.064V159.136C108.064 155.68 108.736 152.752 110.656 150.736C112.336 148.864 114.496 147.808 118.288 147.808C119.584 147.808 120.4 147.904 121.504 148.096V143.68C120.496 143.44 119.632 143.392 118.336 143.392C113.2 143.392 109.12 146.416 107.728 151.072V144.112Z"
+ fill="white"
+ />
+ <path
+ d="M140.724 169.024C150.948 169.024 157.956 163.84 157.956 156.112C157.956 148.384 150.948 143.2 140.724 143.2C130.5 143.2 123.492 148.384 123.492 156.112C123.492 163.84 130.5 169.024 140.724 169.024ZM140.724 165.232C133.188 165.232 128.34 161.68 128.34 156.112C128.34 150.544 133.188 146.992 140.724 146.992C148.212 146.992 153.108 150.544 153.108 156.112C153.108 161.68 148.212 165.232 140.724 165.232Z"
+ fill="white"
+ />
+ <defs>
+ <linearGradient id="paint1_linear" x1="115.168" y1="65.245" x2="94.0326" y2="109.491" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#FF1639" />
+ <stop offset="1" stop-color="#FF1639" stop-opacity="0" />
+ </linearGradient>
+ </defs>
</svg>
<style lang="scss">
- .logo {
- margin: 2rem auto;
- }
+ .logo {
+ margin: 2rem auto;
+ }
- .title {
- font-family: var(--font-sans);
- font-size: 1rem;
- }
- .title svg {
- margin-right: -100%;
- }
- .title svg text {
- font-size: 16px;
- font-family: var(--font-sans);
- }
- .title svg text.span {
- fill: white;
- font-size: 16.2px;
- transform: translate(0, 18px);
- }
- .title svg text.em {
- fill: var(--color-green);
- transform: translate(0, 36px);
- }
+ .title {
+ font-family: var(--font-sans);
+ font-size: 1rem;
+ }
+ .title svg {
+ margin-right: -100%;
+ }
+ .title svg text {
+ font-size: 16px;
+ font-family: var(--font-sans);
+ }
+ .title svg text.span {
+ fill: white;
+ font-size: 16.2px;
+ transform: translate(0, 18px);
+ }
+ .title svg text.em {
+ fill: var(--color-green);
+ transform: translate(0, 36px);
+ }
- @media (min-width: 40em) {
- .title svg {
- margin-right: 0;
- margin-bottom: -40px;
- }
- .title svg text.span {
- font-size: 16px;
- }
- .title svg text.em {
- transform: translate(190px, 18px);
- }
- }
+ @media (min-width: 40em) {
+ .title svg {
+ margin-right: 0;
+ margin-bottom: -40px;
+ }
+ .title svg text.span {
+ font-size: 16px;
+ }
+ .title svg text.em {
+ transform: translate(190px, 18px);
+ }
+ }
</style>
diff --git a/www/src/components/Main.astro b/www/src/components/Main.astro
index 2b4855cde..6b18024f9 100644
--- a/www/src/components/Main.astro
+++ b/www/src/components/Main.astro
@@ -1,18 +1,18 @@
<main>
- <slot></slot>
+ <slot></slot>
</main>
<style lang="scss">
- main {
- z-index: 1;
- width: 100%;
- max-width: 760px;
- margin: auto;
- padding: 0 0.75rem;
- }
+ main {
+ z-index: 1;
+ width: 100%;
+ max-width: 760px;
+ margin: auto;
+ padding: 0 0.75rem;
+ }
- :global(main > *) {
- grid-column: 1;
- grid-column: 2;
- }
+ :global(main > *) {
+ grid-column: 1;
+ grid-column: 2;
+ }
</style>
diff --git a/www/src/components/MainHeader.astro b/www/src/components/MainHeader.astro
index a54759172..cbeb3547f 100644
--- a/www/src/components/MainHeader.astro
+++ b/www/src/components/MainHeader.astro
@@ -1,127 +1,153 @@
<header class={`layout is-centered`}>
- <nav>
- <div class="logoWrapper" itemscope itemtype="https://schema.org/Organization">
- <a href="/" itemprop="url">
- <svg class="logo" width="32" height="32" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="astroSvgTitle astroSvgDesc" role="img" itemprop="logo">
- <title id="astroSvgTitle">Astro logo</title>
- <desc id="astroSvgDesc">A white uppercase lambda-like icon with a flame emanating from the bottom.</desc>
- <style>
- #flame {
- fill: #ff5d01;
- }
- #a {
- fill: white;
- }
- </style>
- <path
- id="a"
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
- />
- <path
- id="flame"
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
- />
- </svg>
- <span itemprop="name">Astro</span>
- </a>
- </div>
- <ul role="list">
- <li>
- <a class="header-subitem" href="https://docs.astro.build/">
- <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="book" class="svg-inline--fa fa-book fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
- <path fill="currentColor" d="M448 360V24c0-13.3-10.7-24-24-24H96C43 0 0 43 0 96v320c0 53 43 96 96 96h328c13.3 0 24-10.7 24-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3 0-74.7 5.4-4.3 8.9-11.1 8.9-18.6zM128 134c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm0 64c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm253.4 250H96c-17.7 0-32-14.3-32-32 0-17.6 14.4-32 32-32h285.4c-1.9 17.1-1.9 46.9 0 64z"></path>
- </svg>
- Documentation
- </a>
- </li>
- <li>
- <a class="header-subitem header-subitem-secondary" href="https://twitter.com/astrodotbuild">
- <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
- <path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
- </svg>
- Twitter
- </a>
- </li>
- <li>
- <a class="header-subitem header-subitem-secondary" href="https://github.com/withastro/astro">
- <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
- </svg>
- GitHub
- </a>
- </li>
- </ul>
- </nav>
+ <nav>
+ <div class="logoWrapper" itemscope itemtype="https://schema.org/Organization">
+ <a href="/" itemprop="url">
+ <svg
+ class="logo"
+ width="32"
+ height="32"
+ viewBox="0 0 256 256"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ aria-labelledby="astroSvgTitle astroSvgDesc"
+ role="img"
+ itemprop="logo"
+ >
+ <title id="astroSvgTitle">Astro logo</title>
+ <desc id="astroSvgDesc">A white uppercase lambda-like icon with a flame emanating from the bottom.</desc>
+ <style>
+ #flame {
+ fill: #ff5d01;
+ }
+ #a {
+ fill: white;
+ }
+ </style>
+ <path
+ id="a"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
+ ></path>
+ <path
+ id="flame"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
+ ></path>
+ </svg>
+ <span itemprop="name">Astro</span>
+ </a>
+ </div>
+ <ul role="list">
+ <li>
+ <a class="header-subitem" href="https://docs.astro.build/">
+ <svg
+ aria-hidden="true"
+ focusable="false"
+ data-prefix="fas"
+ data-icon="book"
+ class="svg-inline--fa fa-book fa-w-14"
+ role="img"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 448 512"
+ >
+ <path
+ fill="currentColor"
+ d="M448 360V24c0-13.3-10.7-24-24-24H96C43 0 0 43 0 96v320c0 53 43 96 96 96h328c13.3 0 24-10.7 24-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3 0-74.7 5.4-4.3 8.9-11.1 8.9-18.6zM128 134c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm0 64c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm253.4 250H96c-17.7 0-32-14.3-32-32 0-17.6 14.4-32 32-32h285.4c-1.9 17.1-1.9 46.9 0 64z"
+ ></path>
+ </svg>
+ Documentation
+ </a>
+ </li>
+ <li>
+ <a class="header-subitem header-subitem-secondary" href="https://twitter.com/astrodotbuild">
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
+ <path
+ d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"
+ ></path>
+ </svg>
+ Twitter
+ </a>
+ </li>
+ <li>
+ <a class="header-subitem header-subitem-secondary" href="https://github.com/withastro/astro">
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
+ <path
+ d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
+ ></path>
+ </svg>
+ GitHub
+ </a>
+ </li>
+ </ul>
+ </nav>
</header>
<style>
-header {
- padding-top: 1rem;
- padding-bottom: 1rem;
- height: 5rem;
-}
-header.is-centered article {
- justify-content: center;
-}
-nav,
-nav ul {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-nav ul {
- list-style: none;
-}
-.header-subitem {
- display: flex;
- flex-grow: 0;
- gap: 0.5em;
- align-items: center;
- justify-content: center;
- color: var(--theme-text-lighter);
- font-size: initial;
- padding: 0.5rem;
- margin-top: 0;
-}
-.header-subitem:hover {
- color: var(--theme-accent);
-}
-.header-subitem svg {
- width: 1.5rem;
- height: 1.5rem;
-}
-@media (max-width: 40em) {
- .header-subitem-secondary {
- display: none;
- }
-}
-@media (max-width: 20em) {
- .header-subitem {
- display: none;
- }
-}
-.logoWrapper {
- margin: 0;
- font-size: 1.5rem;
- max-width: 100%;
- display: flex;
- flex-grow: 1;
-}
-.logoWrapper a {
- text-decoration: none;
- display: inline-flex;
- align-items: center;
- padding: 0 0.5rem 0 0;
-}
-.logo {
- transform: translateY(0.25rem);
-}
-svg {
- width: 2.5rem;
- height: 2.5rem;
-}
+ header {
+ padding-top: 1rem;
+ padding-bottom: 1rem;
+ height: 5rem;
+ }
+ header.is-centered article {
+ justify-content: center;
+ }
+ nav,
+ nav ul {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+ nav ul {
+ list-style: none;
+ }
+ .header-subitem {
+ display: flex;
+ flex-grow: 0;
+ gap: 0.5em;
+ align-items: center;
+ justify-content: center;
+ color: var(--theme-text-lighter);
+ font-size: initial;
+ padding: 0.5rem;
+ margin-top: 0;
+ }
+ .header-subitem:hover {
+ color: var(--theme-accent);
+ }
+ .header-subitem svg {
+ width: 1.5rem;
+ height: 1.5rem;
+ }
+ @media (max-width: 40em) {
+ .header-subitem-secondary {
+ display: none;
+ }
+ }
+ @media (max-width: 20em) {
+ .header-subitem {
+ display: none;
+ }
+ }
+ .logoWrapper {
+ margin: 0;
+ font-size: 1.5rem;
+ max-width: 100%;
+ display: flex;
+ flex-grow: 1;
+ }
+ .logoWrapper a {
+ text-decoration: none;
+ display: inline-flex;
+ align-items: center;
+ padding: 0 0.5rem 0 0;
+ }
+ .logo {
+ transform: translateY(0.25rem);
+ }
+ svg {
+ width: 2.5rem;
+ height: 2.5rem;
+ }
</style>
diff --git a/www/src/components/Note.astro b/www/src/components/Note.astro
index abbdb987b..657da5708 100644
--- a/www/src/components/Note.astro
+++ b/www/src/components/Note.astro
@@ -1,48 +1,48 @@
---
export interface Props {
- title?: string;
- type?: 'tip' | 'warning' | 'error'
+ title?: string;
+ type?: 'tip' | 'warning' | 'error';
}
const { type = 'tip', title } = Astro.props;
---
<aside class={`note type-${type}`}>
- {title && <label>{title}</label>}
- <slot />
+ {title && <label>{title}</label>}
+ <slot />
</aside>
<style>
- .note {
- --padding-block: 1rem;
- --padding-inline: 1.25rem;
+ .note {
+ --padding-block: 1rem;
+ --padding-inline: 1.25rem;
- display: flex;
- flex-direction: column;
+ display: flex;
+ flex-direction: column;
- padding: var(--padding-block) var(--padding-inline);
- margin-left: calc(var(--padding-inline) * -1);
- margin-right: calc(var(--padding-inline) * -1);
-
- background: var(--theme-bg-offset);
- border-left: calc(var(--padding-inline) / 2) solid var(--color);
- border-radius: 0;
- }
+ padding: var(--padding-block) var(--padding-inline);
+ margin-left: calc(var(--padding-inline) * -1);
+ margin-right: calc(var(--padding-inline) * -1);
- .note label {
- font-weight: 500;
- color: var(--color);
- }
+ background: var(--theme-bg-offset);
+ border-left: calc(var(--padding-inline) / 2) solid var(--color);
+ border-radius: 0;
+ }
- .note.type-tip {
- --color: var(--color-green);
- --color-rgb: var(--color-green-rgb);
- }
- .note.type-warning {
- --color: var(--color-yellow);
- --color-rgb: var(--color-yellow-rgb);
- }
- .note.type-error {
- --color: var(--color-red);
- --color-rgb: var(--color-red-rgb);
- }
+ .note label {
+ font-weight: 500;
+ color: var(--color);
+ }
+
+ .note.type-tip {
+ --color: var(--color-green);
+ --color-rgb: var(--color-green-rgb);
+ }
+ .note.type-warning {
+ --color: var(--color-yellow);
+ --color-rgb: var(--color-yellow-rgb);
+ }
+ .note.type-error {
+ --color: var(--color-red);
+ --color-rgb: var(--color-red-rgb);
+ }
</style>
diff --git a/www/src/components/Planets.astro b/www/src/components/Planets.astro
index c39e0d2d9..a22565ead 100644
--- a/www/src/components/Planets.astro
+++ b/www/src/components/Planets.astro
@@ -1,89 +1,97 @@
<div class="planets">
- <svg class="planet planet-a" width="480" height="480" viewBox="0 0 480 480" fill="none"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink">
- <mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="60" y="60" width="360" height="360">
- <circle cx="240" cy="240" r="180" fill="#111827" />
- <circle cx="240" cy="240" r="180" fill="url(#paint0_radial)" />
- <circle cx="240" cy="240" r="180" fill="url(#paint1_radial)" />
- </mask>
- <g mask="url(#mask0)">
- <rect x="60" y="60" width="360" height="360" fill="#000014" />
- <rect x="60" y="60" width="360" height="360" fill="url(#paint2_radial)" />
- <rect x="60" y="60" width="360" height="360" fill="url(#paint3_radial)" />
- <rect x="60" y="60" width="360" height="360" fill="url(#pattern0)" style="mix-blend-mode: soft-light;" />
- </g>
- <g clip-path="url(#clip0)">
- <mask id="mask1" mask-type="alpha" maskUnits="userSpaceOnUse" x="7" y="74" width="447" height="299">
- <path fill-rule="evenodd" clip-rule="evenodd" d="M427.895 109.444C424.688 103.888 414.743 97.9595 391.557 98.4078L391.093 74.4123C416.015 73.9304 438.465 79.7514 448.68 97.4443C458.895 115.137 452.711 137.49 439.833 158.832C426.476 180.968 404.009 205.44 375.257 229.93C346.393 254.515 311.707 278.679 274.675 300.059C237.643 321.44 199.374 339.397 163.651 352.101C128.066 364.756 95.6385 371.977 69.7904 372.476C44.8679 372.958 22.4182 367.137 12.2032 349.444C1.98817 331.751 8.17188 309.399 21.0504 288.056L41.5992 300.456C29.6176 320.312 29.78 331.888 32.9878 337.444C36.1956 343 46.1398 348.929 69.3264 348.481C91.5874 348.05 121.289 341.694 155.609 329.488C189.79 317.333 226.738 300.023 262.675 279.275C298.612 258.526 332.077 235.183 359.695 211.659C387.425 188.04 407.781 165.496 419.284 146.433C431.266 126.577 431.103 115 427.895 109.444Z" fill="white" />
- </mask>
- <g mask="url(#mask1)">
- <rect x="-6.10352e-05" width="480" height="480" fill="#882DE7" />
- <rect x="-6.10352e-05" width="480" height="480" fill="url(#pattern1)" style="mix-blend-mode: overlay;" />
- <rect x="-6.10352e-05" width="480" height="480" fill="url(#paint4_linear)" />
- </g>
- </g>
- <defs>
- <pattern id="pattern0" patternContentUnits="objectBoundingBox" width="0.611111" height="0.611111">
- <use xlink:href="#image0" transform="scale(0.00277778)" />
- </pattern>
- <pattern id="pattern1" patternContentUnits="objectBoundingBox" width="0.458333" height="0.458333">
- <use xlink:href="#image0" transform="scale(0.00208333)" />
- </pattern>
- <radialGradient id="paint0_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(178.092 420) rotate(-135) scale(335.015)">
- <stop offset="0.494792" stop-color="#330069" stop-opacity="0" />
- <stop offset="1" stop-color="#330069" />
- </radialGradient>
- <radialGradient id="paint1_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(178.093 420) rotate(-135) scale(536.16)">
- <stop offset="0.494792" stop-color="#3894FF" stop-opacity="0" />
- <stop offset="1" stop-color="#3894FF" />
- </radialGradient>
- <radialGradient id="paint2_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(178.092 420) rotate(-135) scale(335.015)">
- <stop offset="0.494792" stop-color="#330069" stop-opacity="0" />
- <stop offset="1" stop-color="#330069" />
- </radialGradient>
- <radialGradient id="paint3_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(178.093 420) rotate(-135) scale(536.16)">
- <stop offset="0.494792" stop-color="#3894FF" stop-opacity="0" />
- <stop offset="1" stop-color="#3894FF" />
- </radialGradient>
- <linearGradient id="paint4_linear" x1="212.279" y1="188.465" x2="375.48" y2="478.113" gradientUnits="userSpaceOnUse">
- <stop stop-color="#111827" />
- <stop offset="1" stop-color="#882DE7" stop-opacity="0" />
- </linearGradient>
- <clipPath id="clip0">
- <rect width="473.043" height="339.334" fill="white" transform="translate(3.4787 70.3331)" />
- </clipPath>
- <image id="image0" width="220" height="220" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAADcCAYAAAAbWs+BAAAgAElEQVR4XnTdBbS2y1El4G5gILjr4O4S3IO7u0tIcIIO7u4Ed3cJzuCuIbi7y+A6+NSsp1L7XX2+e/nXuuv+/znf90p31a5du6q7d1U9x1rryfbe37XWWlX1eWutT15rvd/e+03nZ4+71vqovffbV9WTrbUeZe/9e/O7H15rffPe+xOr6iX33j80P3+GvfdvVdXz7b0fNj972b3391bVm621Xmat9bN+vtZ6wbXWj+29P3M+98C11jeutT53rfUPe++3mJ8/8Vrrf+69f7aqXmOt9Yueo6oe4OdrrYeste679/7i+fxL772/v6q+d+/9slX1PGutf9l7/3pVfdTc45XXWl+29/7dueZT7L0/vaqecK31v9Zaj7T3fre53iPsvf9fVT3CWutl11r32Xt/S1W90d77q+Yzn7PW+vS11v9da/313vvvjMta62/WWp+91nqltdbfr7X+dK31NHvvf6+qd/X5vbf3NQfexc9+f631T3vvL6mq91pr/cFa62P33k9TVb7//GutF917f+2MY/+vqh5r7/0PVfW/11pvvNb6IO9QVZ814/zlVfU0a60PW2u9yN776eZ7fvaU5rCq/myt9XV773eZ3/3XWut59t6/WFXPtNZ6nL33T1XVV++933Dm4IX23vevqkdZa7mmMTYu37PW+ve99z/NtZ5u7/07VfUUay3/ud6jr7VeYK31/9ZaX7HWevG11g/69977B6vqhdda5sRn/nbs7bnWWv+81nrg3ttcXX+q6pv23q859/uRtdbX770fXFWfsfd+x7nej6+1Po79rrXY65fM539jrfW6e+9fMoZ771eqqk8dP3ld77/3/o2q+rS11v/Ze3/EfO9b11quwS5/c372XHvvX5i/f+quqlcZY+B4z7jWeo+11nvvvT+uqt5+rfXLa62n33t/0Xzp0ebfBv5PZjA/ZO/9+1X1jmutV9h7v3pVvfve+5PGcJ9z7/19VfXWM0DfsNb6H3vvf6mqj997v1dVfdBa66fXWt+2937EuddPrrUYu5//yN7boMeogMLX7r1/oqq+de/9alX1UzMhXpaRM6jP2nv/3ADLO++9H8h4994fP4P+q4xy7/1ZVeX9OeQfzf057testRjSB1fVW8aZ5/dvZVyq6pHHcT577/12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" />
- </defs>
- </svg>
+ <svg class="planet planet-a" width="480" height="480" viewBox="0 0 480 480" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="60" y="60" width="360" height="360">
+ <circle cx="240" cy="240" r="180" fill="#111827" />
+ <circle cx="240" cy="240" r="180" fill="url(#paint0_radial)" />
+ <circle cx="240" cy="240" r="180" fill="url(#paint1_radial)" />
+ </mask>
+ <g mask="url(#mask0)">
+ <rect x="60" y="60" width="360" height="360" fill="#000014" />
+ <rect x="60" y="60" width="360" height="360" fill="url(#paint2_radial)" />
+ <rect x="60" y="60" width="360" height="360" fill="url(#paint3_radial)" />
+ <rect x="60" y="60" width="360" height="360" fill="url(#pattern0)" style="mix-blend-mode: soft-light;" />
+ </g>
+ <g clip-path="url(#clip0)">
+ <mask id="mask1" mask-type="alpha" maskUnits="userSpaceOnUse" x="7" y="74" width="447" height="299">
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M427.895 109.444C424.688 103.888 414.743 97.9595 391.557 98.4078L391.093 74.4123C416.015 73.9304 438.465 79.7514 448.68 97.4443C458.895 115.137 452.711 137.49 439.833 158.832C426.476 180.968 404.009 205.44 375.257 229.93C346.393 254.515 311.707 278.679 274.675 300.059C237.643 321.44 199.374 339.397 163.651 352.101C128.066 364.756 95.6385 371.977 69.7904 372.476C44.8679 372.958 22.4182 367.137 12.2032 349.444C1.98817 331.751 8.17188 309.399 21.0504 288.056L41.5992 300.456C29.6176 320.312 29.78 331.888 32.9878 337.444C36.1956 343 46.1398 348.929 69.3264 348.481C91.5874 348.05 121.289 341.694 155.609 329.488C189.79 317.333 226.738 300.023 262.675 279.275C298.612 258.526 332.077 235.183 359.695 211.659C387.425 188.04 407.781 165.496 419.284 146.433C431.266 126.577 431.103 115 427.895 109.444Z"
+ fill="white"
+ />
+ </mask>
+ <g mask="url(#mask1)">
+ <rect x="-6.10352e-05" width="480" height="480" fill="#882DE7" />
+ <rect x="-6.10352e-05" width="480" height="480" fill="url(#pattern1)" style="mix-blend-mode: overlay;" />
+ <rect x="-6.10352e-05" width="480" height="480" fill="url(#paint4_linear)" />
+ </g>
+ </g>
+ <defs>
+ <pattern id="pattern0" patternContentUnits="objectBoundingBox" width="0.611111" height="0.611111">
+ <use xlink:href="#image0" transform="scale(0.00277778)" />
+ </pattern>
+ <pattern id="pattern1" patternContentUnits="objectBoundingBox" width="0.458333" height="0.458333">
+ <use xlink:href="#image0" transform="scale(0.00208333)" />
+ </pattern>
+ <radialGradient id="paint0_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(178.092 420) rotate(-135) scale(335.015)">
+ <stop offset="0.494792" stop-color="#330069" stop-opacity="0" />
+ <stop offset="1" stop-color="#330069" />
+ </radialGradient>
+ <radialGradient id="paint1_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(178.093 420) rotate(-135) scale(536.16)">
+ <stop offset="0.494792" stop-color="#3894FF" stop-opacity="0" />
+ <stop offset="1" stop-color="#3894FF" />
+ </radialGradient>
+ <radialGradient id="paint2_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(178.092 420) rotate(-135) scale(335.015)">
+ <stop offset="0.494792" stop-color="#330069" stop-opacity="0" />
+ <stop offset="1" stop-color="#330069" />
+ </radialGradient>
+ <radialGradient id="paint3_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(178.093 420) rotate(-135) scale(536.16)">
+ <stop offset="0.494792" stop-color="#3894FF" stop-opacity="0" />
+ <stop offset="1" stop-color="#3894FF" />
+ </radialGradient>
+ <linearGradient id="paint4_linear" x1="212.279" y1="188.465" x2="375.48" y2="478.113" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#111827" />
+ <stop offset="1" stop-color="#882DE7" stop-opacity="0" />
+ </linearGradient>
+ <clipPath id="clip0">
+ <rect width="473.043" height="339.334" fill="white" transform="translate(3.4787 70.3331)" />
+ </clipPath>
+ <image
+ id="image0"
+ width="220"
+ height="220"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAADcCAYAAAAbWs+BAAAgAElEQVR4XnTdBbS2y1El4G5gILjr4O4S3IO7u0tIcIIO7u4Ed3cJzuCuIbi7y+A6+NSsp1L7XX2+e/nXuuv+/znf90p31a5du6q7d1U9x1rryfbe37XWWlX1eWutT15rvd/e+03nZ4+71vqovffbV9WTrbUeZe/9e/O7H15rffPe+xOr6iX33j80P3+GvfdvVdXz7b0fNj972b3391bVm621Xmat9bN+vtZ6wbXWj+29P3M+98C11jeutT53rfUPe++3mJ8/8Vrrf+69f7aqXmOt9Yueo6oe4OdrrYeste679/7i+fxL772/v6q+d+/9slX1PGutf9l7/3pVfdTc45XXWl+29/7dueZT7L0/vaqecK31v9Zaj7T3fre53iPsvf9fVT3CWutl11r32Xt/S1W90d77q+Yzn7PW+vS11v9da/313vvvjMta62/WWp+91nqltdbfr7X+dK31NHvvf6+qd/X5vbf3NQfexc9+f631T3vvL6mq91pr/cFa62P33k9TVb7//GutF917f+2MY/+vqh5r7/0PVfW/11pvvNb6IO9QVZ814/zlVfU0a60PW2u9yN776eZ7fvaU5rCq/myt9XV773eZ3/3XWut59t6/WFXPtNZ6nL33T1XVV++933Dm4IX23vevqkdZa7mmMTYu37PW+ve99z/NtZ5u7/07VfUUay3/ud6jr7VeYK31/9ZaX7HWevG11g/69977B6vqhdda5sRn/nbs7bnWWv+81nrg3ttcXX+q6pv23q859/uRtdbX770fXFWfsfd+x7nej6+1Po79rrXY65fM539jrfW6e+9fMoZ771eqqk8dP3ld77/3/o2q+rS11v/Ze3/EfO9b11quwS5/c372XHvvX5i/f+quqlcZY+B4z7jWeo+11nvvvT+uqt5+rfXLa62n33t/0Xzp0ebfBv5PZjA/ZO/9+1X1jmutV9h7v3pVvfve+5PGcJ9z7/19VfXWM0DfsNb6H3vvf6mqj997v1dVfdBa66fXWt+2937EuddPrrUYu5//yN7boMeogMLX7r1/oqq+de/9alX1UzMhXpaRM6jP2nv/3ADLO++9H8h4994fP4P+q4xy7/1ZVeX9OeQfzf057testRjSB1fVW8aZ5/dvZVyq6pHHcT577/12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"
+ />
+ </defs>
+ </svg>
</div>
<style>
- .planets {
- position: absolute;
- pointer-events: none;
- z-index: 0;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- inset: 0;
- width: 100%;
- height: 100%;
- }
- .planet-a {
- position: absolute;
- top: -128px;
- left: -128px;
- opacity: 0.5;
- transform: scale(0.5);
- }
- @media (min-width: 64em) {
- .planet-a {
- opacity: 1;
- left: unset;
- right: -32px;
- transform: scale(1);
- }
- }
+ .planets {
+ position: absolute;
+ pointer-events: none;
+ z-index: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ }
+ .planet-a {
+ position: absolute;
+ top: -128px;
+ left: -128px;
+ opacity: 0.5;
+ transform: scale(0.5);
+ }
+ @media (min-width: 64em) {
+ .planet-a {
+ opacity: 1;
+ left: unset;
+ right: -32px;
+ transform: scale(1);
+ }
+ }
</style>
diff --git a/www/src/components/Shell.astro b/www/src/components/Shell.astro
index 3147ba886..09d4e0aeb 100644
--- a/www/src/components/Shell.astro
+++ b/www/src/components/Shell.astro
@@ -1,9 +1,10 @@
---
export interface Props {
- code: string;
+ code: string;
}
const { code } = Astro.props;
---
+
<pre><code>{String(code).trim().split('\n').map(
line => <span class="line">{
line.startsWith('#') ? <span class="comment">{line}</span> : line
@@ -11,16 +12,17 @@ const { code } = Astro.props;
}</code></pre>
<style>
- pre, code {
- white-space: pre;
- }
- .comment {
- color: var(--color-gray-400);
- }
- .line {
- display: block;
- }
- .line:empty::after {
- content: " ";
- }
+ pre,
+ code {
+ white-space: pre;
+ }
+ .comment {
+ color: var(--color-gray-400);
+ }
+ .line {
+ display: block;
+ }
+ .line:empty::after {
+ content: ' ';
+ }
</style>
diff --git a/www/src/components/Space.astro b/www/src/components/Space.astro
index bcdf18ea6..66e9e2702 100644
--- a/www/src/components/Space.astro
+++ b/www/src/components/Space.astro
@@ -4,23 +4,23 @@ import Stars from './Stars.astro';
---
<div class="space">
- <Planets />
- <Stars />
+ <Planets />
+ <Stars />
</div>
<style lang="scss">
- .space {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- inset: 0;
- width: 100vw;
- min-height: 100vh;
- height: 100%;
- overflow: hidden;
- opacity: 0.6;
- z-index: -1;
- }
+ .space {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ inset: 0;
+ width: 100vw;
+ min-height: 100vh;
+ height: 100%;
+ overflow: hidden;
+ opacity: 0.6;
+ z-index: -1;
+ }
</style>
diff --git a/www/src/components/Sponsors.astro b/www/src/components/Sponsors.astro
index 2d0a1e05c..81d9edbcf 100644
--- a/www/src/components/Sponsors.astro
+++ b/www/src/components/Sponsors.astro
@@ -1,43 +1,43 @@
+<div class="wrapper">
+ <h2 class="title">Sponsored by</h2>
+ <div class="list">
+ <a href="https://www.netlify.com/" target="_blank"><img src="/assets/netlify.svg" width="147" height="40" alt="Netlify" /></a>
+ <a href="https://www.vercel.com/" target="_blank"><img src="/assets/vercel.svg" width="150" height="34" alt="Vercel" /></a>
+ </div>
+ <div class="oc"><a href="https://opencollective.com/astrodotbuild" target="_blank">Astro on Open Collective</a></div>
+</div>
+
<style lang="scss">
-.wrapper {
- margin-bottom: 3rem;
- margin-top: 3rem;
-}
+ .wrapper {
+ margin-bottom: 3rem;
+ margin-top: 3rem;
+ }
-.list {
- text-align: center;
+ .list {
+ text-align: center;
- a {
- vertical-align: middle;
- margin-left: 0.75em;
- margin-right: 0.75em;
+ a {
+ vertical-align: middle;
+ margin-left: 0.75em;
+ margin-right: 0.75em;
- &::before,
- &::after {
- display: none;
- }
- }
-}
+ &::before,
+ &::after {
+ display: none;
+ }
+ }
+ }
-.title {
- font-size: 1em;
- font-weight: 700;
- text-align: center;
- margin-bottom: 1.5rem;
-}
+ .title {
+ font-size: 1em;
+ font-weight: 700;
+ text-align: center;
+ margin-bottom: 1.5rem;
+ }
-.oc {
- margin-top: 1.5rem;
- font-size: 0.75em;
- text-align: center;
-}
+ .oc {
+ margin-top: 1.5rem;
+ font-size: 0.75em;
+ text-align: center;
+ }
</style>
-
-<div class="wrapper">
- <h2 class="title">Sponsored by</h2>
- <div class="list">
- <a href="https://www.netlify.com/" target="_blank"><img src="/assets/netlify.svg" width="147" height="40" alt="Netlify" /></a>
- <a href="https://www.vercel.com/" target="_blank"><img src="/assets/vercel.svg" width="150" height="34" alt="Vercel" /></a>
- </div>
- <div class="oc"><a href="https://opencollective.com/astrodotbuild" target="_blank">Astro on Open Collective</a></div>
-</div>
diff --git a/www/src/components/Stars.astro b/www/src/components/Stars.astro
index 952bc0340..bb6c91633 100644
--- a/www/src/components/Stars.astro
+++ b/www/src/components/Stars.astro
@@ -1,123 +1,140 @@
<div class="stars">
- <svg class="meteor meteor-a" width="245" height="8" viewBox="0 0 245 8" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path fill-rule="evenodd" clip-rule="evenodd" d="M244.056 4.01L4.16456 7.94042C1.96643 7.97644 0.165161 6.20444 0.165161 4.00602V4.00602C0.165161 1.80754 1.9665 0.0355242 4.16468 0.0716125L244.056 4.01Z" fill="url(#paint0_linear)"/>
- <defs>
- <linearGradient id="paint0_linear" x1="11.8939" y1="8.00591" x2="256.078" y2="8.00591" gradientUnits="userSpaceOnUse">
- <stop stop-color="white"/>
- <stop offset="1" stop-color="white" stop-opacity="0"/>
- </linearGradient>
- </defs>
- </svg>
+ <svg class="meteor meteor-a" width="245" height="8" viewBox="0 0 245 8" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M244.056 4.01L4.16456 7.94042C1.96643 7.97644 0.165161 6.20444 0.165161 4.00602V4.00602C0.165161 1.80754 1.9665 0.0355242 4.16468 0.0716125L244.056 4.01Z"
+ fill="url(#paint0_linear)"
+ />
+ <defs>
+ <linearGradient id="paint0_linear" x1="11.8939" y1="8.00591" x2="256.078" y2="8.00591" gradientUnits="userSpaceOnUse">
+ <stop stop-color="white" />
+ <stop offset="1" stop-color="white" stop-opacity="0" />
+ </linearGradient>
+ </defs>
+ </svg>
- <svg class="meteor meteor-b" width="245" height="8" viewBox="0 0 245 8" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path fill-rule="evenodd" clip-rule="evenodd" d="M244.056 4.01L4.16456 7.94042C1.96643 7.97644 0.165161 6.20444 0.165161 4.00602V4.00602C0.165161 1.80754 1.9665 0.0355242 4.16468 0.0716125L244.056 4.01Z" fill="url(#paint0_linear)"/>
- <defs>
- <linearGradient id="paint0_linear" x1="11.8939" y1="8.00591" x2="256.078" y2="8.00591" gradientUnits="userSpaceOnUse">
- <stop stop-color="white"/>
- <stop offset="1" stop-color="white" stop-opacity="0"/>
- </linearGradient>
- </defs>
- </svg>
+ <svg class="meteor meteor-b" width="245" height="8" viewBox="0 0 245 8" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M244.056 4.01L4.16456 7.94042C1.96643 7.97644 0.165161 6.20444 0.165161 4.00602V4.00602C0.165161 1.80754 1.9665 0.0355242 4.16468 0.0716125L244.056 4.01Z"
+ fill="url(#paint0_linear)"
+ />
+ <defs>
+ <linearGradient id="paint0_linear" x1="11.8939" y1="8.00591" x2="256.078" y2="8.00591" gradientUnits="userSpaceOnUse">
+ <stop stop-color="white" />
+ <stop offset="1" stop-color="white" stop-opacity="0" />
+ </linearGradient>
+ </defs>
+ </svg>
- <svg class="meteor meteor-c" width="245" height="8" viewBox="0 0 245 8" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path fill-rule="evenodd" clip-rule="evenodd" d="M244.056 4.01L4.16456 7.94042C1.96643 7.97644 0.165161 6.20444 0.165161 4.00602V4.00602C0.165161 1.80754 1.9665 0.0355242 4.16468 0.0716125L244.056 4.01Z" fill="url(#paint0_linear)"/>
- <defs>
- <linearGradient id="paint0_linear" x1="11.8939" y1="8.00591" x2="256.078" y2="8.00591" gradientUnits="userSpaceOnUse">
- <stop stop-color="white"/>
- <stop offset="1" stop-color="white" stop-opacity="0"/>
- </linearGradient>
- </defs>
- </svg>
+ <svg class="meteor meteor-c" width="245" height="8" viewBox="0 0 245 8" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M244.056 4.01L4.16456 7.94042C1.96643 7.97644 0.165161 6.20444 0.165161 4.00602V4.00602C0.165161 1.80754 1.9665 0.0355242 4.16468 0.0716125L244.056 4.01Z"
+ fill="url(#paint0_linear)"
+ />
+ <defs>
+ <linearGradient id="paint0_linear" x1="11.8939" y1="8.00591" x2="256.078" y2="8.00591" gradientUnits="userSpaceOnUse">
+ <stop stop-color="white" />
+ <stop offset="1" stop-color="white" stop-opacity="0" />
+ </linearGradient>
+ </defs>
+ </svg>
- <svg class="bg" width="1440" height="1090" viewBox="0 0 1440 1090" fill="none"
- xmlns="http://www.w3.org/2000/svg">
- <circle cx="58" cy="226.5" r="2" fill="white" fill-opacity="0.4" />
- <circle cx="534.857" cy="127.5" r="2" fill="white" fill-opacity="0.4" />
- <circle cx="90.1501" cy="37.3223" r="2" fill="white" fill-opacity="0.4" />
- <circle cx="1009" cy="100.5" r="2" fill="white" fill-opacity="0.4" />
- <circle cx="166" cy="493.833" r="2" fill="white" fill-opacity="0.4" />
- <circle cx="1386" cy="412.833" r="2" fill="white" fill-opacity="0.4" />
- <circle cx="100" cy="857.167" r="2" fill="white" fill-opacity="0.4" />
- <circle cx="1296.01" cy="748.988" r="2" fill="white" fill-opacity="0.4" />
- <circle cx="1017" cy="767.167" r="2" fill="white" fill-opacity="0.4" />
- <circle cx="84.1501" cy="212.209" r="6" fill="white" fill-opacity="0.4" />
- <circle cx="1351" cy="119.5" r="6" fill="white" fill-opacity="0.4" />
- <circle cx="1179" cy="785" r="6" fill="white" fill-opacity="0.4" />
- <path d="M983.911 60.1743L975.34 55.0487L967.148 60.7596L972.274 52.1889L966.563 43.9965L975.133 49.1221L983.326 43.4111L978.2 51.9819L983.911 60.1743Z" fill="white" fill-opacity="0.4" />
- </svg>
+ <svg class="bg" width="1440" height="1090" viewBox="0 0 1440 1090" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <circle cx="58" cy="226.5" r="2" fill="white" fill-opacity="0.4" />
+ <circle cx="534.857" cy="127.5" r="2" fill="white" fill-opacity="0.4" />
+ <circle cx="90.1501" cy="37.3223" r="2" fill="white" fill-opacity="0.4" />
+ <circle cx="1009" cy="100.5" r="2" fill="white" fill-opacity="0.4" />
+ <circle cx="166" cy="493.833" r="2" fill="white" fill-opacity="0.4" />
+ <circle cx="1386" cy="412.833" r="2" fill="white" fill-opacity="0.4" />
+ <circle cx="100" cy="857.167" r="2" fill="white" fill-opacity="0.4" />
+ <circle cx="1296.01" cy="748.988" r="2" fill="white" fill-opacity="0.4" />
+ <circle cx="1017" cy="767.167" r="2" fill="white" fill-opacity="0.4" />
+ <circle cx="84.1501" cy="212.209" r="6" fill="white" fill-opacity="0.4" />
+ <circle cx="1351" cy="119.5" r="6" fill="white" fill-opacity="0.4" />
+ <circle cx="1179" cy="785" r="6" fill="white" fill-opacity="0.4" />
+ <path
+ d="M983.911 60.1743L975.34 55.0487L967.148 60.7596L972.274 52.1889L966.563 43.9965L975.133 49.1221L983.326 43.4111L978.2 51.9819L983.911 60.1743Z"
+ fill="white"
+ fill-opacity="0.4"
+ />
+ </svg>
</div>
-
<style>
- .stars {
- --duration: 10s;
- --delay: 0s;
- --dist: 480px;
- position: absolute;
- pointer-events: none;
- width: 100%;
- height: 100%;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- inset: 0;
- z-index: 0;
- }
- .bg {
- object-position: top center;
- object-fit: fill;
- min-width: 1200px;
- min-height: 100vh;
- width: 100%;
- height: 100%;
- margin: auto;
- }
- .meteor {
- display: none;
- position: absolute;
- transform: scale(var(--scale)) rotate(-34deg);
- animation: meteor var(--duration) var(--delay) cubic-bezier(0.895, 0.030, 0.685, 0.220) infinite both;
- }
- @media (prefers-reduced-motion: no-preference) {
- .meteor {
- display: block;
- }
- }
- .meteor-a {
- --duration: 10s;
- --scale: 1;
- top: calc(var(--dist) * -0.25);
- left: calc(var(--dist));
- }
- .meteor-b {
- --duration: 9s;
- --delay: 9s;
- --scale: 0.8;
- top: -5%;
- right: 10%;
- }
- .meteor-c {
- --duration: 12s;
- --delay: 6s;
- --scale: 1.5;
- bottom: 10%;
- right: -256px;
- transform-origin: bottom left;
- }
+ .stars {
+ --duration: 10s;
+ --delay: 0s;
+ --dist: 480px;
+ position: absolute;
+ pointer-events: none;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ inset: 0;
+ z-index: 0;
+ }
+ .bg {
+ object-position: top center;
+ object-fit: fill;
+ min-width: 1200px;
+ min-height: 100vh;
+ width: 100%;
+ height: 100%;
+ margin: auto;
+ }
+ .meteor {
+ display: none;
+ position: absolute;
+ transform: scale(var(--scale)) rotate(-34deg);
+ animation: meteor var(--duration) var(--delay) cubic-bezier(0.895, 0.03, 0.685, 0.22) infinite both;
+ }
+ @media (prefers-reduced-motion: no-preference) {
+ .meteor {
+ display: block;
+ }
+ }
+ .meteor-a {
+ --duration: 10s;
+ --scale: 1;
+ top: calc(var(--dist) * -0.25);
+ left: calc(var(--dist));
+ }
+ .meteor-b {
+ --duration: 9s;
+ --delay: 9s;
+ --scale: 0.8;
+ top: -5%;
+ right: 10%;
+ }
+ .meteor-c {
+ --duration: 12s;
+ --delay: 6s;
+ --scale: 1.5;
+ bottom: 10%;
+ right: -256px;
+ transform-origin: bottom left;
+ }
- @keyframes meteor {
- 0% {
- transform: scale(calc(var(--scale) * 0.75)) rotate(-34deg) translateX(0);
- opacity: 0;
- }
- 10% {
- opacity: 1;
- }
- 40%,
- 100% {
- transform: scale(calc(var(--scale) * 1.1)) rotate(-34deg) translateX(calc(var(--dist) * -1));
- opacity: 0;
- }
- }
+ @keyframes meteor {
+ 0% {
+ transform: scale(calc(var(--scale) * 0.75)) rotate(-34deg) translateX(0);
+ opacity: 0;
+ }
+ 10% {
+ opacity: 1;
+ }
+ 40%,
+ 100% {
+ transform: scale(calc(var(--scale) * 1.1)) rotate(-34deg) translateX(calc(var(--dist) * -1));
+ opacity: 0;
+ }
+ }
</style>
diff --git a/www/src/components/Tagline.astro b/www/src/components/Tagline.astro
index 232ee76b9..ad8bce0bd 100644
--- a/www/src/components/Tagline.astro
+++ b/www/src/components/Tagline.astro
@@ -1,21 +1,19 @@
-<h1 class="title">
- Build faster websites with <em>less&nbsp;client-side Javascript</em>
-</h1>
+<h1 class="title">Build faster websites with <em>less&nbsp;client-side Javascript</em></h1>
<style lang="scss">
- .title {
- font-family: var(--font-sans);
- font-weight: 400;
- font-size: 2.5rem;
- line-height: 1.1;
-
- @media (min-width: 50em) {
- font-size: 1.525rem;
- }
+ .title {
+ font-family: var(--font-sans);
+ font-weight: 400;
+ font-size: 2.5rem;
+ line-height: 1.1;
- em {
- font-style: normal;
- color: var(--color-green);
- }
- }
+ @media (min-width: 50em) {
+ font-size: 1.525rem;
+ }
+
+ em {
+ font-style: normal;
+ color: var(--color-green);
+ }
+ }
</style>
diff --git a/www/src/components/YouTube.astro b/www/src/components/YouTube.astro
index f31a5c42d..875f90f60 100644
--- a/www/src/components/YouTube.astro
+++ b/www/src/components/YouTube.astro
@@ -42,10 +42,11 @@ const button = Object.assign(
button.setAttribute('aria-label', host.getAttribute('alt'));
host.replaceChildren(button);
-`.replace(/[\n\r\t]+/g, '')
+`.replace(/[\n\r\t]+/g, '');
---
+
<lite-youtube {...Astro.props}>
<a href={`https://www.youtube.com/watch?v=${Astro.props.v}`}>
<img src={`https://i.ytimg.com/vi/${Astro.props.v}/hqdefault.jpg`} loading="lazy" alt={Astro.props.alt} />
- </a><script type="module" src="data:text/javascript," onload={onload}></script>
+ </a><script type="module" src="data:text/javascript," {onload}></script>
</lite-youtube>
diff --git a/www/src/components/YouTube.css b/www/src/components/YouTube.css
index 76917c9c8..9ee000ea5 100644
--- a/www/src/components/YouTube.css
+++ b/www/src/components/YouTube.css
@@ -1,82 +1,82 @@
lite-youtube {
- background: #000;
- contain: content;
- display: block;
- max-width: 720px;
- position: relative;
+ background: #000;
+ contain: content;
+ display: block;
+ max-width: 720px;
+ position: relative;
}
/* responsive iframe with a 16:9 aspect ratio (thanks https://css-tricks.com/responsive-iframes/) */
lite-youtube::after {
- content: '';
- display: block;
- padding-bottom: calc(100% / (16 / 9));
+ content: '';
+ display: block;
+ padding-bottom: calc(100% / (16 / 9));
}
lite-youtube > iframe {
- border: 0;
- height: 100%;
- inset: 0;
- position: absolute;
- width: 100%;
+ border: 0;
+ height: 100%;
+ inset: 0;
+ position: absolute;
+ width: 100%;
}
/* YT's actual play button svg */
lite-youtube > a,
lite-youtube > button {
- cursor: pointer;
- display: block;
- height: 100%;
- inset: 0;
- position: absolute;
- width: 100%;
+ cursor: pointer;
+ display: block;
+ height: 100%;
+ inset: 0;
+ position: absolute;
+ width: 100%;
}
lite-youtube > a > img {
- display: block;
- height: 100%;
- object-fit: cover;
- width: 100%;
+ display: block;
+ height: 100%;
+ object-fit: cover;
+ width: 100%;
}
lite-youtube > a::after,
lite-youtube > button::after {
- content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 68 48'%3E%3Cpath fill='red' fill-opacity='.8' d='M66.5 7.7c-.8-2.9-2.5-5.4-5.4-6.2C55.8.1 34 0 34 0S12.2.1 6.9 1.6c-3 .7-4.6 3.2-5.4 6.1a89.6 89.6 0 000 32.5c.8 3 2.5 5.5 5.4 6.3C12.2 47.9 34 48 34 48s21.8-.1 27.1-1.6c3-.7 4.6-3.2 5.4-6.1C68 35 68 24 68 24s0-11-1.5-16.3z'/%3E%3Cpath fill='%23fff' d='M45 24L27 14v20'/%3E%3C/svg%3E");
- display: block;
- width: 68px;
- height: 48px;
- position: absolute;
- inset: calc(50% - 68px / 2) calc(50% - 48px / 2) auto auto;
- transition: filter 0.1s cubic-bezier(0, 0, 0.2, 1);
+ content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 68 48'%3E%3Cpath fill='red' fill-opacity='.8' d='M66.5 7.7c-.8-2.9-2.5-5.4-5.4-6.2C55.8.1 34 0 34 0S12.2.1 6.9 1.6c-3 .7-4.6 3.2-5.4 6.1a89.6 89.6 0 000 32.5c.8 3 2.5 5.5 5.4 6.3C12.2 47.9 34 48 34 48s21.8-.1 27.1-1.6c3-.7 4.6-3.2 5.4-6.1C68 35 68 24 68 24s0-11-1.5-16.3z'/%3E%3Cpath fill='%23fff' d='M45 24L27 14v20'/%3E%3C/svg%3E");
+ display: block;
+ width: 68px;
+ height: 48px;
+ position: absolute;
+ inset: calc(50% - 68px / 2) calc(50% - 48px / 2) auto auto;
+ transition: filter 0.1s cubic-bezier(0, 0, 0.2, 1);
}
lite-youtube > button {
- background: #000 50% 50% / cover;
- border: none;
+ background: #000 50% 50% / cover;
+ border: none;
}
/* gradient */
lite-youtube button::before {
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAQAAAC58SIOAAAAaklEQVR42nWOUQrAMAhD1e7+B9kZ1zqYKAk6yMfLa8Ca3CbbxL+cTJgNNeDhTZmdQXb24/KAPNNFjDf2CQSMJGCVViveAKuy1AZYFaCqcrVkA4NgGeTKYlg/Jvhio/w6LuvQNV1U3sS31wvH7lU36biAMgAAAABJRU5ErkJggg==')
- top repeat-x;
- content: '';
- display: block;
- height: 60px;
- padding-bottom: 50px;
- position: absolute;
- top: 0;
- transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
- width: 100%;
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAQAAAC58SIOAAAAaklEQVR42nWOUQrAMAhD1e7+B9kZ1zqYKAk6yMfLa8Ca3CbbxL+cTJgNNeDhTZmdQXb24/KAPNNFjDf2CQSMJGCVViveAKuy1AZYFaCqcrVkA4NgGeTKYlg/Jvhio/w6LuvQNV1U3sS31wvH7lU36biAMgAAAABJRU5ErkJggg==')
+ top repeat-x;
+ content: '';
+ display: block;
+ height: 60px;
+ padding-bottom: 50px;
+ position: absolute;
+ top: 0;
+ transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
+ width: 100%;
}
lite-youtube > button::after {
- filter: grayscale(100%);
+ filter: grayscale(100%);
}
lite-youtube:hover > button::after,
lite-youtube button:focus::after {
- filter: none;
+ filter: none;
}
diff --git a/www/src/config.ts b/www/src/config.ts
index 969264302..c9f57838d 100644
--- a/www/src/config.ts
+++ b/www/src/config.ts
@@ -1,14 +1,14 @@
export const sidebar = [
- {
- text: 'Introduction',
- children: [
- { text: 'Welcome', link: '/' },
- { text: 'Example', link: '/example' },
- ],
- },
+ {
+ text: 'Introduction',
+ children: [
+ { text: 'Welcome', link: '/' },
+ { text: 'Example', link: '/example' },
+ ],
+ },
];
export const mediaQueries = {
- mobile: '(max-width: 600px)',
- desktop: '(min-width: 601px)',
+ mobile: '(max-width: 600px)',
+ desktop: '(min-width: 601px)',
};
diff --git a/www/src/layouts/Blog.astro b/www/src/layouts/Blog.astro
index b45fc7dc4..e4035b66f 100644
--- a/www/src/layouts/Blog.astro
+++ b/www/src/layouts/Blog.astro
@@ -8,204 +8,205 @@ const { content } = Astro.props;
const headers = content?.astro?.headers;
---
-<html lang={ content?.lang ?? 'en' }>
- <head>
- <title>{content?.title}</title>
-
- <link rel="stylesheet" href="/blog/index.css" />
- <style>
- body {
- width: 100%;
- display: grid;
- grid-template-rows: 3.5rem 1fr;
- --gutter: 0.5rem;
- --doc-padding: 2rem;
- }
-
- header {
- width: 100%;
- height: 100%;
- background-color: var(--theme-bg-offset);
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .layout {
- display: grid;
- grid-auto-flow: column;
- grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, var(--max-width)) minmax(var(--gutter), 1fr);
- gap: 1em;
- }
-
- .menu-and-logo {
- gap: 1em;
- }
-
- nav.layout {
- justify-content: center;
- width: 100%;
- }
-
- nav.layout :global(> :nth-child(1)) {
- grid-column: 2;
- }
-
- #site-title {
- display: flex;
- align-items: center;
- gap: 0.25em;
- font-size: 1.5rem;
- font-weight: 700;
- margin: 0;
- line-height: 1;
- color: var(--theme-text);
- text-decoration: none;
- }
-
- #site-title:hover,
- #site-title:focus {
- color: var(--theme-text-light);
- }
-
- #site-title h1 {
- font: inherit;
- color: inherit;
- margin: 0;
- }
-
- .nav-wrapper {
- display: flex;
- align-items: center;
- justify-content: space-between;
- width: 100%;
- max-width: 64ch;
- margin: 0 auto;
- }
-
- .layout :global(> *) {
- width: 100%;
- height: 100%;
- }
-
- .sidebar {
- max-height: 100vh;
- position: sticky;
- top: 0;
- padding: var(--doc-padding) 0;
- }
-
- #sidebar-nav {
- display: none;
- max-height: 100vh;
- padding: var(--doc-padding) 0;
- }
-
- #article {
- padding: var(--doc-padding) var(--gutter);
- grid-column: 2;
- display: flex;
- flex-direction: column;
- align-items: center;
- height: 100%;
- }
-
- .content {
- max-width: 64ch;
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- }
-
- .content > main {
- margin-bottom: 4rem;
- }
-
- #sidebar-content {
- display: none;
- }
-
- @media (min-width: 64em) {
- .menu-and-logo button {
- display: none;
- }
- .layout {
- grid-template-columns: 20rem minmax(0, 1fr);
- padding-left: 1rem;
- padding-right: 1rem;
- }
- #article {
- grid-column: 2;
- }
- #sidebar-nav {
- display: flex;
- }
- #sidebar-content {
- /* display: flex; */
- grid-column: 3;
- }
-
- .nav-wrapper {
- display: contents;
- }
- }
-
- @media (min-width: 88em) {
- .layout {
- grid-template-columns: minmax(var(--gutter), 1fr) 20rem minmax(0, var(--max-width)) 16rem minmax(var(--gutter), 1fr);
- padding-left: 0;
- padding-right: 0;
- }
-
- #sidebar-nav,
- .nav-wrapper :global(:nth-child(1)) {
- grid-column: 2;
- }
- #article,
- .nav-wrapper :global(:nth-child(2)) {
- grid-column: 3;
- }
- #sidebar-content,
- .nav-wrapper :global(:nth-child(3)) {
- display: flex;
- grid-column: 4;
- }
- }
-
- </style>
- </head>
-
- <body>
- <header>
- <nav class="layout">
- <div class="nav-wrapper">
- <div class="menu-and-logo flex">
- <a id="site-title" href="/blog">
- <svg width="1em" height="1em" viewBox="0 0 340 340" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M320 170C320 170 296.88 171.746 267.428 188.75C237.975 205.754 205.754 237.975 188.75 267.428C171.746 296.88 170 320 170 320C170 320 168.254 296.88 151.25 267.428C134.246 237.975 102.025 205.754 72.5721 188.75C43.1197 171.746 20 170 20 170C20 170 43.1197 168.254 72.5721 151.25C102.025 134.246 134.246 102.025 151.25 72.5721C168.254 43.1197 170 20 170 20C170 20 171.746 43.1197 188.75 72.5721C205.754 102.025 237.975 134.246 267.428 151.25C296.88 168.254 320 170 320 170Z" fill="currentColor"/>
- </svg>
- <h1>Astro Log</h1>
- </a>
- </div>
-
- <div />
-
- <div>
- <ThemeToggle:idle />
- </div>
- </div>
- </nav>
- </header>
-
- <main class="layout">
- <div id="article">
- <article class="content">
- <main>
- <slot />
- </main>
- </article>
- </div>
- </main>
- </body>
+<html lang={content?.lang ?? 'en'}>
+ <head>
+ <title>{content?.title}</title>
+
+ <link rel="stylesheet" href="/blog/index.css" />
+ <style>
+ body {
+ width: 100%;
+ display: grid;
+ grid-template-rows: 3.5rem 1fr;
+ --gutter: 0.5rem;
+ --doc-padding: 2rem;
+ }
+
+ header {
+ width: 100%;
+ height: 100%;
+ background-color: var(--theme-bg-offset);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .layout {
+ display: grid;
+ grid-auto-flow: column;
+ grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, var(--max-width)) minmax(var(--gutter), 1fr);
+ gap: 1em;
+ }
+
+ .menu-and-logo {
+ gap: 1em;
+ }
+
+ nav.layout {
+ justify-content: center;
+ width: 100%;
+ }
+
+ nav.layout :global(> :nth-child(1)) {
+ grid-column: 2;
+ }
+
+ #site-title {
+ display: flex;
+ align-items: center;
+ gap: 0.25em;
+ font-size: 1.5rem;
+ font-weight: 700;
+ margin: 0;
+ line-height: 1;
+ color: var(--theme-text);
+ text-decoration: none;
+ }
+
+ #site-title:hover,
+ #site-title:focus {
+ color: var(--theme-text-light);
+ }
+
+ #site-title h1 {
+ font: inherit;
+ color: inherit;
+ margin: 0;
+ }
+
+ .nav-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ max-width: 64ch;
+ margin: 0 auto;
+ }
+
+ .layout :global(> *) {
+ width: 100%;
+ height: 100%;
+ }
+
+ .sidebar {
+ max-height: 100vh;
+ position: sticky;
+ top: 0;
+ padding: var(--doc-padding) 0;
+ }
+
+ #sidebar-nav {
+ display: none;
+ max-height: 100vh;
+ padding: var(--doc-padding) 0;
+ }
+
+ #article {
+ padding: var(--doc-padding) var(--gutter);
+ grid-column: 2;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ height: 100%;
+ }
+
+ .content {
+ max-width: 64ch;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .content > main {
+ margin-bottom: 4rem;
+ }
+
+ #sidebar-content {
+ display: none;
+ }
+
+ @media (min-width: 64em) {
+ .menu-and-logo button {
+ display: none;
+ }
+ .layout {
+ grid-template-columns: 20rem minmax(0, 1fr);
+ padding-left: 1rem;
+ padding-right: 1rem;
+ }
+ #article {
+ grid-column: 2;
+ }
+ #sidebar-nav {
+ display: flex;
+ }
+ #sidebar-content {
+ /* display: flex; */
+ grid-column: 3;
+ }
+
+ .nav-wrapper {
+ display: contents;
+ }
+ }
+
+ @media (min-width: 88em) {
+ .layout {
+ grid-template-columns: minmax(var(--gutter), 1fr) 20rem minmax(0, var(--max-width)) 16rem minmax(var(--gutter), 1fr);
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ #sidebar-nav,
+ .nav-wrapper :global(:nth-child(1)) {
+ grid-column: 2;
+ }
+ #article,
+ .nav-wrapper :global(:nth-child(2)) {
+ grid-column: 3;
+ }
+ #sidebar-content,
+ .nav-wrapper :global(:nth-child(3)) {
+ display: flex;
+ grid-column: 4;
+ }
+ }
+ </style>
+ </head>
+
+ <body>
+ <header>
+ <nav class="layout">
+ <div class="nav-wrapper">
+ <div class="menu-and-logo flex">
+ <a id="site-title" href="/blog">
+ <svg width="1em" height="1em" viewBox="0 0 340 340" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path
+ d="M320 170C320 170 296.88 171.746 267.428 188.75C237.975 205.754 205.754 237.975 188.75 267.428C171.746 296.88 170 320 170 320C170 320 168.254 296.88 151.25 267.428C134.246 237.975 102.025 205.754 72.5721 188.75C43.1197 171.746 20 170 20 170C20 170 43.1197 168.254 72.5721 151.25C102.025 134.246 134.246 102.025 151.25 72.5721C168.254 43.1197 170 20 170 20C170 20 171.746 43.1197 188.75 72.5721C205.754 102.025 237.975 134.246 267.428 151.25C296.88 168.254 320 170 320 170Z"
+ fill="currentColor"></path>
+ </svg>
+ <h1>Astro Log</h1>
+ </a>
+ </div>
+
+ <div></div>
+
+ <div>
+ <ThemeToggle:idle />
+ </div>
+ </div>
+ </nav>
+ </header>
+
+ <main class="layout">
+ <div id="article">
+ <article class="content">
+ <main>
+ <slot />
+ </main>
+ </article>
+ </div>
+ </main>
+ </body>
</html>
diff --git a/www/src/pages/404.astro b/www/src/pages/404.astro
index 3ab384e50..e05c2e28b 100644
--- a/www/src/pages/404.astro
+++ b/www/src/pages/404.astro
@@ -1,14 +1,14 @@
<html lang="en">
<head>
- <title>Astro | Page Not Found</title>
- <link rel="stylesheet" href={Astro.resolve('../scss/error.scss')} />
- </head>
+ <title>Astro | Page Not Found</title>
+ <link rel="stylesheet" href={Astro.resolve('../scss/error.scss')} />
+ </head>
<body>
- <section class="error-content">
- <p class="error-code">404 | error</p>
- <h1>Lost in space there, Astronaut?</h1>
- <p>Let's see if we can guide you back</p>
- <a href="/" title="back to homepage">Back to homepage</a>
- </section>
+ <section class="error-content">
+ <p class="error-code">404 | error</p>
+ <h1>Lost in space there, Astronaut?</h1>
+ <p>Let's see if we can guide you back</p>
+ <a href="/" title="back to homepage">Back to homepage</a>
+ </section>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/www/src/pages/blog/[slug].astro b/www/src/pages/blog/[slug].astro
index 95c1230d0..0390801cc 100644
--- a/www/src/pages/blog/[slug].astro
+++ b/www/src/pages/blog/[slug].astro
@@ -5,26 +5,26 @@ import BlogPost from '../../components/BlogPost.astro';
import GoogleAnalytics from '../../components/GoogleAnalytics.astro';
export function getStaticPaths() {
- const posts = Astro.fetchContent('../../data/blog-posts/*.md');
- return posts.map(p => ({
- params: { slug: p.file.pathname.split('/').pop().split('.').shift() },
- props: { post: p },
- }));
+ const posts = Astro.fetchContent('../../data/blog-posts/*.md');
+ return posts.map((p) => ({
+ params: { slug: p.file.pathname.split('/').pop().split('.').shift() },
+ props: { post: p },
+ }));
}
const { Content, title, author, description, publishDate, heroImage, heroImageAlt, socialImage, permalink, lang } = Astro.props.post;
---
<html lang={lang ?? 'en'}>
- <head>
- <BaseHead title={title} description={description} canonicalURL={permalink} image={socialImage && `https://astro.build${socialImage}`} />
- <link rel="stylesheet" href={Astro.resolve('../../scss/blog.scss')} />
- </head>
- <body>
- <BlogHeader />
- <BlogPost title={title} author={author} publishDate={publishDate} heroImage={heroImage} heroImageAlt={heroImageAlt}>
- <Content />
- </BlogPost>
- <GoogleAnalytics />
- </body>
+ <head>
+ <BaseHead {title} {description} canonicalURL={permalink} image={socialImage && `https://astro.build${socialImage}`} />
+ <link rel="stylesheet" href={Astro.resolve('../../scss/blog.scss')} />
+ </head>
+ <body>
+ <BlogHeader />
+ <BlogPost {title} {author} {publishDate} {heroImage} {heroImageAlt}>
+ <Content />
+ </BlogPost>
+ <GoogleAnalytics />
+ </body>
</html>
diff --git a/www/src/pages/blog/index.astro b/www/src/pages/blog/index.astro
index 651a128ac..be8201cd7 100644
--- a/www/src/pages/blog/index.astro
+++ b/www/src/pages/blog/index.astro
@@ -8,77 +8,77 @@ let description = 'Everything you need to know about Astro, direct from mission
let permalink = 'https://astro.build/blog';
let lang = 'en';
-const posts = Astro.fetchContent('../../data/blog-posts/*.md').sort((a, b) => (new Date(b.publishDate) - new Date(a.publishDate)));
+const posts = Astro.fetchContent('../../data/blog-posts/*.md').sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate));
---
-<html lang={ lang ?? 'en' }>
- <head>
- <BaseHead title={title} description={description} canonicalURL={permalink} />
- <link rel="stylesheet" href={Astro.resolve('../../scss/blog.scss')} />
+<html lang={lang ?? 'en'}>
+ <head>
+ <BaseHead {title} {description} canonicalURL={permalink} />
+ <link rel="stylesheet" href={Astro.resolve('../../scss/blog.scss')} />
- <style>
- body {
- width: 100%;
- display: grid;
- grid-template-rows: 3.5rem 1fr;
- --gutter: 0.5rem;
- --doc-padding: 2rem;
- }
+ <style>
+ body {
+ width: 100%;
+ display: grid;
+ grid-template-rows: 3.5rem 1fr;
+ --gutter: 0.5rem;
+ --doc-padding: 2rem;
+ }
- header {
- width: 100%;
- height: 100%;
- background-color: var(--theme-bg-offset);
- display: flex;
- align-items: center;
- justify-content: center;
- }
+ header {
+ width: 100%;
+ height: 100%;
+ background-color: var(--theme-bg-offset);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
- .content {
- margin-top: 4rem;
- margin-bottom: 8rem;
- }
+ .content {
+ margin-top: 4rem;
+ margin-bottom: 8rem;
+ }
- .content :global(main > * + *) {
- margin-top: 1rem;
- }
+ .content :global(main > * + *) {
+ margin-top: 1rem;
+ }
- .intro {
- padding-bottom: 4rem;
- margin-bottom: 2rem;
- border-bottom: 4px solid var(--theme-divider);
- }
+ .intro {
+ padding-bottom: 4rem;
+ margin-bottom: 2rem;
+ border-bottom: 4px solid var(--theme-divider);
+ }
- .intro > * {
- margin: 0;
- }
+ .intro > * {
+ margin: 0;
+ }
- .latest {
- font-size: 2.5rem;
- font-weight: 700;
- }
- </style>
- </head>
+ .latest {
+ font-size: 2.5rem;
+ font-weight: 700;
+ }
+ </style>
+ </head>
- <body>
- <BlogHeader />
- <div class="layout">
- <main class="content">
- <section class="intro">
- <h1 class="latest">The Astro Blog</h1>
- <p>{description}</p>
- </section>
- <section aria-label="Blog post list">
- {posts.map(p => {
- const href = `/blog/${p.file.pathname.split('/').pop().split('.').shift()}`;
- return (
- <BlogPostPreview title={p.title} publishDate={p.publishDate} href={href}>
- <span>{p.description}</span>
- </BlogPostPreview>
- );
- })}
- </section>
- </main>
- </div>
- </body>
+ <body>
+ <BlogHeader />
+ <div class="layout">
+ <main class="content">
+ <section class="intro">
+ <h1 class="latest">The Astro Blog</h1>
+ <p>{description}</p>
+ </section>
+ <section aria-label="Blog post list">
+ {posts.map((p) => {
+ const href = `/blog/${p.file.pathname.split('/').pop().split('.').shift()}`;
+ return (
+ <BlogPostPreview title={p.title} publishDate={p.publishDate} href={href}>
+ <span>{p.description}</span>
+ </BlogPostPreview>
+ );
+ })}
+ </section>
+ </main>
+ </div>
+ </body>
</html>
diff --git a/www/src/pages/index.astro b/www/src/pages/index.astro
index 7075df098..7a24a5c24 100644
--- a/www/src/pages/index.astro
+++ b/www/src/pages/index.astro
@@ -8,7 +8,7 @@ import MainHeader from '../components/MainHeader.astro';
import Sponsors from '../components/Sponsors.astro';
import GoogleAnalytics from '../components/GoogleAnalytics.astro';
import YouTube from '../components/YouTube.astro';
-import "../components/YouTube.css";
+import '../components/YouTube.css';
let title = 'Astro';
let description = 'Build faster websites with less client-side JavaScript';
@@ -16,94 +16,95 @@ let permalink = 'https://astro.build/';
let lang = 'en';
---
-<html lang={ lang ?? 'en' }>
- <head>
- <BaseHead title={title} description={description} canonicalURL={permalink} />
- <link rel="stylesheet" href={Astro.resolve('../scss/global.scss')} />
- </head>
+<html lang={lang ?? 'en'}>
+ <head>
+ <BaseHead {title} {description} canonicalURL={permalink} />
+ <link rel="stylesheet" href={Astro.resolve('../scss/global.scss')} />
+ </head>
- <body>
- <Space />
+ <body>
+ <Space />
- <Main>
- <MainHeader />
- <Article>
+ <Main>
+ <MainHeader />
+ <Article>
+ <YouTube v="dsTXcSeAZq8" alt="Astro in 100 Seconds" />
- <YouTube v="dsTXcSeAZq8" alt="Astro in 100 Seconds" />
+ <Tagline />
- <Tagline />
+ <p>
+ For a technology built on top of three different languages, the modern web seems to focus an awful lot on JavaScript. We don&rsquo;t think it has to&mdash;and
+ that&rsquo;s certainly <a href="https://css-tricks.com/the-great-divide/">not</a>&nbsp;<a
+ href="https://bradfrost.com/blog/post/front-of-the-front-end-and-back-of-the-front-end-web-development/">a</a
+ >&nbsp;<a href="https://macwright.com/2020/05/10/spa-fatigue.html">revolutionary</a>&nbsp;<a href="https://css-tricks.com/whats-old-is-new/">concept</a>.
+ </p>
- <p>
- For a technology built on top of three different languages, the modern web seems to focus an awful lot on JavaScript. We don&rsquo;t think it has to&mdash;and that&rsquo;s certainly <a href="https://css-tricks.com/the-great-divide/">not</a>&nbsp;<a href="https://bradfrost.com/blog/post/front-of-the-front-end-and-back-of-the-front-end-web-development/">a</a>&nbsp;<a href="https://macwright.com/2020/05/10/spa-fatigue.html">revolutionary</a>&nbsp;<a href="https://css-tricks.com/whats-old-is-new/">concept</a>.
- </p>
+ <p>
+ We&rsquo;ll eagerly jump at the chance to sing JavaScript&rsquo;s praises, but HTML and CSS are pretty great too. There aren&rsquo;t enough modern tools which reflect
+ that, which is why we're building Astro.
+ </p>
- <p>
- We&rsquo;ll eagerly jump at the chance to sing JavaScript&rsquo;s praises, but HTML and CSS are pretty great too. There aren&rsquo;t enough modern tools which reflect that, which is why we're building Astro.
- </p>
+ <p>
+ While we&rsquo;re at it, it&rsquo;s time to accept that the framework wars won&rsquo;t have a winner&mdash;that&rsquo;s why Astro lets you use any framework you want (or
+ none at all). And if most sites only have <a href="https://jasonformat.com/islands-architecture/">islands</a> of interactivity, shouldn&rsquo;t our tools optimize for that?
+ We&rsquo;re not <a href="https://markojs.com/">the first</a> to ask the question, but we might be the first with an answer for every framework.
+ </p>
- <p>
- While we&rsquo;re at it, it&rsquo;s time to accept that the framework wars won&rsquo;t have a winner&mdash;that&rsquo;s why Astro lets you use any framework you want (or none at all).
- And if most sites only have <a href="https://jasonformat.com/islands-architecture/">islands</a> of interactivity, shouldn&rsquo;t our tools optimize for that?
- We&rsquo;re not <a href="https://markojs.com/">the first</a> to ask the question, but we might be the first with an answer for every framework.
- </p>
+ <p>We're hard at work on Astro! Keep your eyes to the skies, astronauts.</p>
- <p>We're hard at work on Astro! Keep your eyes to the skies, astronauts.</p>
+ <br />
- <br/>
+ <a class="action-button" href="/blog/astro-021-release/">
+ New Blog Post - Astro 0.21
+ <span style="float: right;">&#8594;</span>
+ </a>
- <a class="action-button" href="/blog/astro-021-release/">
- New Blog Post - Astro 0.21
- <span style="float: right;">&#8594;</span>
- </a>
+ <div class="hint">
+ <p>Psst... <a href="https://astro.build/chat">get early access to new features</a> by joining our Discord community.</p>
+ </div>
+ </Article>
+ <Sponsors />
+ </Main>
- <div class="hint">
- <p>Psst... <a href="https://astro.build/chat">get early access to new features</a> by joining our Discord community.</p>
- </div>
- </Article>
- <Sponsors />
- </Main>
+ <style lang="scss">
+ .hint {
+ font-size: 13px;
+ opacity: 0.8;
+ margin-top: 2em;
+ padding: 2em 0;
+ }
+ .hint > p:first-child {
+ margin-bottom: 2em;
+ }
+ .action-button {
+ border: 1px solid var(--color-green);
+ padding: 1rem;
+ font-weight: 600;
+ }
+ code {
+ font-size: 11px;
+ font-family: var(--font-mono);
+ border: 1px solid rgba(255, 255, 255, 0.4);
+ border-radius: 2px;
+ padding: 0.1em 0.2em;
+ }
+ .videoWrapper {
+ position: relative;
+ padding-bottom: 56.25%; /* 16:9 */
+ height: 0;
+ background: rgba(255, 255, 255, 0.1);
+ margin-bottom: 1rem;
+ }
+ .videoWrapper > iframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
+ </style>
- <style lang="scss">
- .hint {
- font-size: 13px;
- opacity: 0.8;
- margin-top: 2em;
- padding: 2em 0;
- }
- .hint > p:first-child {
- margin-bottom: 2em;
- }
- .action-button {
- border: 1px solid var(--color-green);
- padding: 1rem;
- font-weight: 600;
- }
- code {
- font-size: 11px;
- font-family: var(--font-mono);
- border: 1px solid rgba(255, 255, 255, 0.4);
- border-radius: 2px;
- padding: 0.1em 0.2em;
- }
- .videoWrapper {
- position: relative;
- padding-bottom: 56.25%; /* 16:9 */
- height: 0;
- background:rgba(255, 255, 255, 0.1);
- margin-bottom: 1rem;
- }
- .videoWrapper > iframe {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- }
- </style>
-
- <script>
- console.log('%cGet early Astro access 👉 https://astro.build/chat', "color:#17c083; font-size: 1.2em; padding: 1em;");
- </script>
- <GoogleAnalytics />
- </body>
+ <script> console.log('%cGet early Astro access 👉 https://astro.build/chat', 'color:#17c083; font-size: 1.2em; padding: 1em;'); </script>
+ <GoogleAnalytics />
+ </body>
</html>
diff --git a/www/src/scss/blog.scss b/www/src/scss/blog.scss
index 2f36a76f7..d58401999 100644
--- a/www/src/scss/blog.scss
+++ b/www/src/scss/blog.scss
@@ -2,273 +2,273 @@
@use './code.scss';
:root {
- --font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
- --font-body: 'IBM Plex Sans', var(--font-fallback);
- --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
- 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
-
- --color-white: #fff;
- --color-black: #000014;
-
- --color-gray-50: #f9fafb;
- --color-gray-100: #f3f4f6;
- --color-gray-200: #e5e7eb;
- --color-gray-300: #d1d5db;
- --color-gray-400: #9ca3af;
- --color-gray-500: #6b7280;
- --color-gray-600: #4b5563;
- --color-gray-700: #374151;
- --color-gray-800: #1f2937;
- --color-gray-900: #111827;
-
- --color-blue: #3894ff;
- --color-blue-rgb: 56, 148, 255;
- --color-green: #17c083;
- --color-green-rgb: 23, 192, 131;
- --color-orange: #ff5d01;
- --color-orange-rgb: 255, 93, 1;
- --color-purple: #882de7;
- --color-purple-rgb: 136, 45, 231;
- --color-red: #ff1639;
- --color-red-rgb: 255, 22, 57;
- --color-yellow: #ffbe2d;
- --color-yellow-rgb: 255, 190, 45;
+ --font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
+ --font-body: 'IBM Plex Sans', var(--font-fallback);
+ --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
+ 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
+
+ --color-white: #fff;
+ --color-black: #000014;
+
+ --color-gray-50: #f9fafb;
+ --color-gray-100: #f3f4f6;
+ --color-gray-200: #e5e7eb;
+ --color-gray-300: #d1d5db;
+ --color-gray-400: #9ca3af;
+ --color-gray-500: #6b7280;
+ --color-gray-600: #4b5563;
+ --color-gray-700: #374151;
+ --color-gray-800: #1f2937;
+ --color-gray-900: #111827;
+
+ --color-blue: #3894ff;
+ --color-blue-rgb: 56, 148, 255;
+ --color-green: #17c083;
+ --color-green-rgb: 23, 192, 131;
+ --color-orange: #ff5d01;
+ --color-orange-rgb: 255, 93, 1;
+ --color-purple: #882de7;
+ --color-purple-rgb: 136, 45, 231;
+ --color-red: #ff1639;
+ --color-red-rgb: 255, 22, 57;
+ --color-yellow: #ffbe2d;
+ --color-yellow-rgb: 255, 190, 45;
}
:root {
- color-scheme: light;
- --theme-accent: var(--color-orange);
- --theme-accent-rgb: var(--color-orange-rgb);
- --theme-accent-opacity: 0.1;
- --theme-divider: var(--color-gray-100);
- --theme-text: var(--color-gray-800);
- --theme-text-light: var(--color-gray-600);
- --theme-text-lighter: var(--color-gray-400);
- --theme-bg: var(--color-white);
- --theme-bg-offset: var(--color-gray-100);
- --theme-bg-accent: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
- --theme-code-inline-bg: var(--color-gray-100);
- --theme-code-text: var(--color-gray-100);
- --theme-code-bg: var(--color-gray-700);
+ color-scheme: light;
+ --theme-accent: var(--color-orange);
+ --theme-accent-rgb: var(--color-orange-rgb);
+ --theme-accent-opacity: 0.1;
+ --theme-divider: var(--color-gray-100);
+ --theme-text: var(--color-gray-800);
+ --theme-text-light: var(--color-gray-600);
+ --theme-text-lighter: var(--color-gray-400);
+ --theme-bg: var(--color-white);
+ --theme-bg-offset: var(--color-gray-100);
+ --theme-bg-accent: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
+ --theme-code-inline-bg: var(--color-gray-100);
+ --theme-code-text: var(--color-gray-100);
+ --theme-code-bg: var(--color-gray-700);
}
body {
- background: var(--theme-bg);
- color: var(--theme-text);
+ background: var(--theme-bg);
+ color: var(--theme-text);
}
:root.theme-dark {
- color-scheme: dark;
- --theme-accent-opacity: 0.3;
- --theme-divider: var(--color-gray-900);
- --theme-text: var(--color-gray-200);
- --theme-text-light: var(--color-gray-400);
- --theme-text-lighter: var(--color-gray-600);
- --theme-bg: var(--color-black);
- --theme-bg-offset: var(--color-gray-900);
- --theme-code-inline-bg: var(--color-gray-800);
- --theme-code-text: var(--color-gray-200);
- --theme-code-bg: var(--color-gray-900);
+ color-scheme: dark;
+ --theme-accent-opacity: 0.3;
+ --theme-divider: var(--color-gray-900);
+ --theme-text: var(--color-gray-200);
+ --theme-text-light: var(--color-gray-400);
+ --theme-text-lighter: var(--color-gray-600);
+ --theme-bg: var(--color-black);
+ --theme-bg-offset: var(--color-gray-900);
+ --theme-code-inline-bg: var(--color-gray-800);
+ --theme-code-text: var(--color-gray-200);
+ --theme-code-bg: var(--color-gray-900);
}
::selection {
- color: var(--theme-accent);
- background-color: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
+ color: var(--theme-accent);
+ background-color: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
}
* {
- box-sizing: border-box;
- margin: 0;
+ box-sizing: border-box;
+ margin: 0;
}
:root {
- --user-font-scale: 1rem - 16px;
- --max-width: calc(100% - 2rem);
+ --user-font-scale: 1rem - 16px;
+ --max-width: calc(100% - 2rem);
}
@media (min-width: 50em) {
- :root {
- --max-width: 40em;
- }
+ :root {
+ --max-width: 40em;
+ }
}
body {
- display: flex;
- flex-direction: column;
- min-height: 100vh;
- font-family: var(--font-body);
- font-size: 1rem;
- font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
- line-height: 1.625;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ font-family: var(--font-body);
+ font-size: 1rem;
+ font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
+ line-height: 1.625;
}
body {
- width: 100%;
- display: grid;
- --gutter: 0.5rem;
- --doc-padding: 2rem;
+ width: 100%;
+ display: grid;
+ --gutter: 0.5rem;
+ --doc-padding: 2rem;
}
.layout {
- display: grid;
- grid-auto-flow: column;
- grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, var(--max-width)) minmax(var(--gutter), 1fr);
- gap: 1em;
+ display: grid;
+ grid-auto-flow: column;
+ grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, var(--max-width)) minmax(var(--gutter), 1fr);
+ gap: 1em;
}
.layout > :is(main, article) {
- grid-column: 2;
+ grid-column: 2;
}
nav ul {
- list-style: none;
- padding: 0;
+ list-style: none;
+ padding: 0;
}
/* Typography */
:is(h1, h2, h3, h4, h5, h6) {
- margin-bottom: 1.38rem;
- font-weight: 400;
- line-height: 1.3;
+ margin-bottom: 1.38rem;
+ font-weight: 400;
+ line-height: 1.3;
}
:is(h1, h2) {
- max-width: 40ch;
+ max-width: 40ch;
}
:is(h2, h3):not(:first-child) {
- margin-top: 3rem;
+ margin-top: 3rem;
}
h1 {
- font-size: clamp(2.488rem, 1.924rem + 1.41vw, 3.052rem);
+ font-size: clamp(2.488rem, 1.924rem + 1.41vw, 3.052rem);
}
h2 {
- font-size: clamp(2.074rem, 1.707rem + 0.9175vw, 2.441rem);
+ font-size: clamp(2.074rem, 1.707rem + 0.9175vw, 2.441rem);
}
h3 {
- font-size: clamp(1.728rem, 1.503rem + 0.5625vw, 1.953rem);
+ font-size: clamp(1.728rem, 1.503rem + 0.5625vw, 1.953rem);
}
h4 {
- font-size: clamp(1.44rem, 1.317rem + 0.3075vw, 1.563rem);
+ font-size: clamp(1.44rem, 1.317rem + 0.3075vw, 1.563rem);
}
h5 {
- font-size: clamp(1.2rem, 1.15rem + 0.125vw, 1.25rem);
+ font-size: clamp(1.2rem, 1.15rem + 0.125vw, 1.25rem);
}
p {
- color: var(--theme-text-light);
+ color: var(--theme-text-light);
}
small,
.text_small {
- font-size: 0.833rem;
+ font-size: 0.833rem;
}
a {
- color: var(--theme-accent);
- text-underline-offset: 0.08em;
- text-decoration: none;
- align-items: center;
- gap: 0.5rem;
+ color: var(--theme-accent);
+ text-underline-offset: 0.08em;
+ text-decoration: none;
+ align-items: center;
+ gap: 0.5rem;
}
a > code:not([class*='language']) {
- position: relative;
- color: var(--theme-accent);
- background: transparent;
- text-underline-offset: var(--padding-block);
+ position: relative;
+ color: var(--theme-accent);
+ background: transparent;
+ text-underline-offset: var(--padding-block);
}
a > code:not([class*='language'])::before {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- display: block;
- background: var(--theme-accent);
- opacity: var(--theme-accent-opacity);
- border-radius: var(--border-radius);
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ background: var(--theme-accent);
+ opacity: var(--theme-accent-opacity);
+ border-radius: var(--border-radius);
}
a:hover,
a:focus {
- text-decoration: underline;
+ text-decoration: underline;
}
a:focus {
- outline: 2px solid currentColor;
- outline-offset: 0.25em;
+ outline: 2px solid currentColor;
+ outline-offset: 0.25em;
}
strong {
- font-weight: 600;
- color: inherit;
+ font-weight: 600;
+ color: inherit;
}
/* Supporting Content */
code:not([class*='language']) {
- --border-radius: 3px;
- --padding-block: 0.2rem;
- --padding-inline: 0.33rem;
+ --border-radius: 3px;
+ --padding-block: 0.2rem;
+ --padding-inline: 0.33rem;
- font-family: var(--font-mono);
- font-size: 0.85em;
- color: inherit;
- background-color: var(--theme-code-inline-bg);
- padding: var(--padding-block) var(--padding-inline);
- margin: calc(var(--padding-block) * -1) -0.125em;
- border-radius: var(--border-radius);
- word-break: break-word;
+ font-family: var(--font-mono);
+ font-size: 0.85em;
+ color: inherit;
+ background-color: var(--theme-code-inline-bg);
+ padding: var(--padding-block) var(--padding-inline);
+ margin: calc(var(--padding-block) * -1) -0.125em;
+ border-radius: var(--border-radius);
+ word-break: break-word;
}
pre > code:not([class*='language']) {
- background-color: transparent;
- padding: 0;
- margin: 0;
- border-radius: 0;
- color: inherit;
+ background-color: transparent;
+ padding: 0;
+ margin: 0;
+ border-radius: 0;
+ color: inherit;
}
pre {
- position: relative;
- background-color: var(--theme-code-bg);
- color: var(--theme-code-text);
- --padding-block: 1rem;
- --padding-inline: 2rem;
- padding: var(--padding-block) var(--padding-inline);
- padding-right: calc(var(--padding-inline) * 2);
- margin-left: calc(50vw - var(--padding-inline));
- transform: translateX(-50vw);
-
- line-height: 1.414;
- width: calc(100vw + (var(--padding-inline) * 2));
- max-width: calc(100% + (var(--padding-inline) * 2));
- overflow-y: hidden;
- overflow-x: auto;
+ position: relative;
+ background-color: var(--theme-code-bg);
+ color: var(--theme-code-text);
+ --padding-block: 1rem;
+ --padding-inline: 2rem;
+ padding: var(--padding-block) var(--padding-inline);
+ padding-right: calc(var(--padding-inline) * 2);
+ margin-left: calc(50vw - var(--padding-inline));
+ transform: translateX(-50vw);
+
+ line-height: 1.414;
+ width: calc(100vw + (var(--padding-inline) * 2));
+ max-width: calc(100% + (var(--padding-inline) * 2));
+ overflow-y: hidden;
+ overflow-x: auto;
}
@media (min-width: 37.75em) {
- pre {
- --padding-inline: 1.25rem;
- border-radius: 8px;
- }
+ pre {
+ --padding-inline: 1.25rem;
+ border-radius: 8px;
+ }
}
.flex {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
}
img.cover {
- width: 100%;
- max-height: 50vh;
- object-fit: cover;
+ width: 100%;
+ max-height: 50vh;
+ object-fit: cover;
}
diff --git a/www/src/scss/code.scss b/www/src/scss/code.scss
index ec0e8dea2..835df74f6 100644
--- a/www/src/scss/code.scss
+++ b/www/src/scss/code.scss
@@ -1,155 +1,155 @@
.language-css > code,
.language-sass > code,
.language-scss > code {
- color: #fd9170;
+ color: #fd9170;
}
[class*='language-'] .namespace {
- opacity: 0.7;
+ opacity: 0.7;
}
.token.atrule {
- color: #c792ea;
+ color: #c792ea;
}
.token.attr-name {
- color: #ffcb6b;
+ color: #ffcb6b;
}
.token.attr-value {
- color: #a5e844;
+ color: #a5e844;
}
.token.attribute {
- color: #a5e844;
+ color: #a5e844;
}
.token.boolean {
- color: #c792ea;
+ color: #c792ea;
}
.token.builtin {
- color: #ffcb6b;
+ color: #ffcb6b;
}
.token.cdata {
- color: #80cbc4;
+ color: #80cbc4;
}
.token.char {
- color: #80cbc4;
+ color: #80cbc4;
}
.token.class {
- color: #ffcb6b;
+ color: #ffcb6b;
}
.token.class-name {
- color: #f2ff00;
+ color: #f2ff00;
}
.token.comment {
- color: #888888;
+ color: #888888;
}
.token.constant {
- color: #f2ff00;
+ color: #f2ff00;
}
.token.deleted {
- color: #ff6666;
+ color: #ff6666;
}
.token.doctype {
- color: #616161;
+ color: #616161;
}
.token.entity {
- color: #ff6666;
+ color: #ff6666;
}
.token.function {
- color: #c792ea;
+ color: #c792ea;
}
.token.hexcode {
- color: #f2ff00;
+ color: #f2ff00;
}
.token.id {
- color: #c792ea;
- font-weight: bold;
+ color: #c792ea;
+ font-weight: bold;
}
.token.important {
- color: #c792ea;
- font-weight: bold;
+ color: #c792ea;
+ font-weight: bold;
}
.token.inserted {
- color: #80cbc4;
+ color: #80cbc4;
}
.token.keyword {
- color: #c792ea;
+ color: #c792ea;
}
.token.number {
- color: #fd9170;
+ color: #fd9170;
}
.token.operator {
- color: #89ddff;
+ color: #89ddff;
}
.token.prolog {
- color: #616161;
+ color: #616161;
}
.token.property {
- color: #80cbc4;
+ color: #80cbc4;
}
.token.pseudo-class {
- color: #a5e844;
+ color: #a5e844;
}
.token.pseudo-element {
- color: #a5e844;
+ color: #a5e844;
}
.token.punctuation {
- color: #89ddff;
+ color: #89ddff;
}
.token.regex {
- color: #f2ff00;
+ color: #f2ff00;
}
.token.selector {
- color: #ff6666;
+ color: #ff6666;
}
.token.string {
- color: #a5e844;
+ color: #a5e844;
}
.token.symbol {
- color: #c792ea;
+ color: #c792ea;
}
.token.tag {
- color: #ff6666;
+ color: #ff6666;
}
.token.unit {
- color: #fd9170;
+ color: #fd9170;
}
.token.url {
- color: #ff6666;
+ color: #ff6666;
}
.token.variable {
- color: #ff6666;
+ color: #ff6666;
}
diff --git a/www/src/scss/error.scss b/www/src/scss/error.scss
index c49c42165..d03ecb876 100644
--- a/www/src/scss/error.scss
+++ b/www/src/scss/error.scss
@@ -1,107 +1,107 @@
@use './fonts.scss';
:root {
- --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
- --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
- 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
- --color-accent: #ff5d01;
+ --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
+ 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
+ --color-accent: #ff5d01;
}
* {
- box-sizing: border-box;
- margin: 0;
+ box-sizing: border-box;
+ margin: 0;
}
html {
- background-color: #000014;
+ background-color: #000014;
}
html,
body {
- padding: 0;
- font-size: clamp(14px, calc(1rem + (3vw - 1.2rem)), 20px);
- font-family: var(--font-sans);
- font-weight: 400;
- background-repeat: no-repeat;
- color: #f3f4f6;
+ padding: 0;
+ font-size: clamp(14px, calc(1rem + (3vw - 1.2rem)), 20px);
+ font-family: var(--font-sans);
+ font-weight: 400;
+ background-repeat: no-repeat;
+ color: #f3f4f6;
}
.visually-hidden {
- clip: rect(0 0 0 0);
- clip-path: inset(50%);
- height: 1px;
- overflow: hidden;
- position: absolute;
- white-space: nowrap;
- width: 1px;
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ height: 1px;
+ overflow: hidden;
+ position: absolute;
+ white-space: nowrap;
+ width: 1px;
}
a {
- position: relative;
- text-decoration: none;
- color: var(--color-accent);
- padding: 0.05em 0.125em;
- margin: -0.05em -0.125em;
- transition: color 120ms cubic-bezier(0.23, 1, 0.32, 1);
- z-index: 0;
- display: inline-block;
+ position: relative;
+ text-decoration: none;
+ color: var(--color-accent);
+ padding: 0.05em 0.125em;
+ margin: -0.05em -0.125em;
+ transition: color 120ms cubic-bezier(0.23, 1, 0.32, 1);
+ z-index: 0;
+ display: inline-block;
- &:hover,
- &:focus {
- color: rgba(0, 0, 0, 1);
+ &:hover,
+ &:focus {
+ color: rgba(0, 0, 0, 1);
- &::before {
- transform: scaleY(1);
- background: var(--color-accent);
- }
- }
+ &::before {
+ transform: scaleY(1);
+ background: var(--color-accent);
+ }
+ }
- &:visited {
- // color: var(--color-accent);
- color: var(--color-accent);
- &:hover,
- &:focus {
- color: rgba(0, 0, 0, 1);
- }
- }
+ &:visited {
+ // color: var(--color-accent);
+ color: var(--color-accent);
+ &:hover,
+ &:focus {
+ color: rgba(0, 0, 0, 1);
+ }
+ }
- &::before {
- transform-origin: bottom center;
- content: '';
- display: block;
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- inset: 0;
- background: var(--color-accent);
- pointer-events: none;
- transform: scaleY(0.05);
- transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), background 120ms cubic-bezier(0.23, 1, 0.32, 1);
- z-index: -1;
- }
+ &::before {
+ transform-origin: bottom center;
+ content: '';
+ display: block;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ inset: 0;
+ background: var(--color-accent);
+ pointer-events: none;
+ transform: scaleY(0.05);
+ transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), background 120ms cubic-bezier(0.23, 1, 0.32, 1);
+ z-index: -1;
+ }
}
a + a {
- margin-left: 2px;
+ margin-left: 2px;
}
section.error-content {
- height: 100vh;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- p.error-code {
- color: #ff5d01;
- text-transform: uppercase;
- }
- h1 {
- margin-top: -0.5rem;
- margin-bottom: 1rem;
- font-size: 2rem;
- }
- p {
- margin: 1rem 0;
- }
- a {
- margin: 1rem 0;
- }
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ p.error-code {
+ color: #ff5d01;
+ text-transform: uppercase;
+ }
+ h1 {
+ margin-top: -0.5rem;
+ margin-bottom: 1rem;
+ font-size: 2rem;
+ }
+ p {
+ margin: 1rem 0;
+ }
+ a {
+ margin: 1rem 0;
+ }
}
diff --git a/www/src/scss/fonts.scss b/www/src/scss/fonts.scss
index 0ebce004a..3909773b1 100644
--- a/www/src/scss/fonts.scss
+++ b/www/src/scss/fonts.scss
@@ -1,39 +1,39 @@
/* ibm-plex-sans-regular - latin */
@font-face {
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 400;
- src: local('IBM Plex Sans'), url('/assets/fonts/ibm-plex-sans/ibm-plex-sans-v9-latin-regular.woff2') format('woff2');
+ font-family: 'IBM Plex Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: local('IBM Plex Sans'), url('/assets/fonts/ibm-plex-sans/ibm-plex-sans-v9-latin-regular.woff2') format('woff2');
}
/* ibm-plex-sans-600 - latin */
@font-face {
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 600;
- src: local('IBM Plex Sans Medium'), url('/assets/fonts/ibm-plex-sans/ibm-plex-sans-v9-latin-600.woff2') format('woff2');
+ font-family: 'IBM Plex Sans';
+ font-style: normal;
+ font-weight: 600;
+ src: local('IBM Plex Sans Medium'), url('/assets/fonts/ibm-plex-sans/ibm-plex-sans-v9-latin-600.woff2') format('woff2');
}
/* ibm-plex-sans-700 - latin */
@font-face {
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 700;
- src: local('IBM Plex Sans Bold'), url('/assets/fonts/ibm-plex-sans/ibm-plex-sans-v9-latin-700.woff2') format('woff2');
+ font-family: 'IBM Plex Sans';
+ font-style: normal;
+ font-weight: 700;
+ src: local('IBM Plex Sans Bold'), url('/assets/fonts/ibm-plex-sans/ibm-plex-sans-v9-latin-700.woff2') format('woff2');
}
/* ibm-plex-mono-regular - latin */
@font-face {
- font-family: 'IBM Plex Mono';
- font-style: normal;
- font-weight: 400;
- src: local('IBM Plex Mono Regular'), url('/assets/fonts/ibm-plex-mono/ibm-plex-mono-v7-latin-regular.woff2') format('woff2');
+ font-family: 'IBM Plex Mono';
+ font-style: normal;
+ font-weight: 400;
+ src: local('IBM Plex Mono Regular'), url('/assets/fonts/ibm-plex-mono/ibm-plex-mono-v7-latin-regular.woff2') format('woff2');
}
/* ibm-plex-mono-700 - latin */
@font-face {
- font-family: 'IBM Plex Mono';
- font-style: normal;
- font-weight: 700;
- src: local('IBM Plex Mono Bold'), url('/assets/fonts/ibm-plex-mono/ibm-plex-mono-v7-latin-700.woff2') format('woff2');
+ font-family: 'IBM Plex Mono';
+ font-style: normal;
+ font-weight: 700;
+ src: local('IBM Plex Mono Bold'), url('/assets/fonts/ibm-plex-mono/ibm-plex-mono-v7-latin-700.woff2') format('woff2');
}
diff --git a/www/src/scss/global.scss b/www/src/scss/global.scss
index e27a31a44..7a1e804c6 100644
--- a/www/src/scss/global.scss
+++ b/www/src/scss/global.scss
@@ -1,103 +1,103 @@
@use './fonts.scss';
:root {
- --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
- --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
- 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
- --color-accent: #ff5d01;
+ --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
+ 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
+ --color-accent: #ff5d01;
}
* {
- box-sizing: border-box;
- margin: 0;
+ box-sizing: border-box;
+ margin: 0;
}
html {
- background-color: #000014;
+ background-color: #000014;
}
html,
body {
- padding: 0;
- font-size: clamp(14px, calc(1rem + (3vw - 1.2rem)), 20px);
- font-family: var(--font-sans);
- font-weight: 400;
- background-repeat: no-repeat;
- color: #f3f4f6;
+ padding: 0;
+ font-size: clamp(14px, calc(1rem + (3vw - 1.2rem)), 20px);
+ font-family: var(--font-sans);
+ font-weight: 400;
+ background-repeat: no-repeat;
+ color: #f3f4f6;
}
.visually-hidden {
- clip: rect(0 0 0 0);
- clip-path: inset(50%);
- height: 1px;
- overflow: hidden;
- position: absolute;
- white-space: nowrap;
- width: 1px;
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ height: 1px;
+ overflow: hidden;
+ position: absolute;
+ white-space: nowrap;
+ width: 1px;
}
a {
- position: relative;
- text-decoration: none;
- color: var(--color-accent);
- padding: 0.05em 0.125em;
- margin: -0.05em -0.125em;
- transition: color 120ms cubic-bezier(0.23, 1, 0.32, 1);
- z-index: 0;
- display: inline-block;
+ position: relative;
+ text-decoration: none;
+ color: var(--color-accent);
+ padding: 0.05em 0.125em;
+ margin: -0.05em -0.125em;
+ transition: color 120ms cubic-bezier(0.23, 1, 0.32, 1);
+ z-index: 0;
+ display: inline-block;
- &:hover,
- &:focus {
- color: rgba(0, 0, 0, 1);
+ &:hover,
+ &:focus {
+ color: rgba(0, 0, 0, 1);
- &::before {
- transform: scaleY(1);
- background: var(--color-accent);
- }
- }
+ &::before {
+ transform: scaleY(1);
+ background: var(--color-accent);
+ }
+ }
- &:visited {
- // color: var(--color-accent);
- color: var(--color-accent);
- &:hover,
- &:focus {
- color: rgba(0, 0, 0, 1);
- }
- }
+ &:visited {
+ // color: var(--color-accent);
+ color: var(--color-accent);
+ &:hover,
+ &:focus {
+ color: rgba(0, 0, 0, 1);
+ }
+ }
- &::before {
- transform-origin: bottom center;
- content: '';
- display: block;
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- inset: 0;
- background: var(--color-accent);
- pointer-events: none;
- transform: scaleY(0.05);
- transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), background 120ms cubic-bezier(0.23, 1, 0.32, 1);
- z-index: -1;
- }
+ &::before {
+ transform-origin: bottom center;
+ content: '';
+ display: block;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ inset: 0;
+ background: var(--color-accent);
+ pointer-events: none;
+ transform: scaleY(0.05);
+ transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), background 120ms cubic-bezier(0.23, 1, 0.32, 1);
+ z-index: -1;
+ }
}
a + a {
- margin-left: 2px;
+ margin-left: 2px;
}
header {
- text-align: center;
+ text-align: center;
}
header a {
- color: var(--theme-text-lighter) !important;
- font-weight: bold;
+ color: var(--theme-text-lighter) !important;
+ font-weight: bold;
}
header a::before,
header a:hover::before {
- background: none;
+ background: none;
}
header a:hover {
- background: rgba(255, 255, 255, 0.1);
- text-decoration: underline;
+ background: rgba(255, 255, 255, 0.1);
+ text-decoration: underline;
}
header h1 a:hover {
}