From c269f7b687a2f0ed1bdae4c59dad4becc4e76dc2 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 14 Oct 2021 14:02:00 -0400 Subject: [PATCH] Implements top-level Astro + Astro.resolve (#1556) * Implements top-level Astro + Astro.resolve * Fix linting --- packages/astro/package.json | 2 +- packages/astro/src/@types/astro-file.ts | 9 ++-- packages/astro/src/@types/ssr.ts | 4 +- packages/astro/src/internal/hydration-map.ts | 1 + packages/astro/src/internal/index.ts | 43 ++++++++++++++++ packages/astro/src/runtime/ssr.ts | 50 ++++--------------- .../astro/src/runtime/vite/plugin-astro.ts | 1 + packages/astro/test/astro-global.test.js | 31 ++++-------- .../custom-elements/src/pages/index.astro | 2 +- .../custom-elements/src/pages/load.astro | 2 +- yarn.lock | 8 +-- 11 files changed, 80 insertions(+), 73 deletions(-) diff --git a/packages/astro/package.json b/packages/astro/package.json index 9665f89d2..20290a461 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -40,7 +40,7 @@ "test": "mocha --parallel --timeout 15000" }, "dependencies": { - "@astrojs/compiler": "^0.1.13", + "@astrojs/compiler": "^0.1.14", "@astrojs/language-server": "^0.7.16", "@astrojs/markdown-remark": "^0.3.1", "@astrojs/markdown-support": "0.3.1", diff --git a/packages/astro/src/@types/astro-file.ts b/packages/astro/src/@types/astro-file.ts index dc5c4e2b8..7a0defaac 100644 --- a/packages/astro/src/@types/astro-file.ts +++ b/packages/astro/src/@types/astro-file.ts @@ -26,12 +26,15 @@ export interface AstroBuiltinProps { 'client:visible'?: boolean; } -export interface Astro { +export interface TopLevelAstro { isPage: boolean; fetchContent(globStr: string): Promise[]>; - props: Record; - request: AstroPageRequest; resolve: (path: string) => string; site: URL; +} + +export interface Astro extends TopLevelAstro { + props: Record; + request: AstroPageRequest; slots: Record; } \ No newline at end of file diff --git a/packages/astro/src/@types/ssr.ts b/packages/astro/src/@types/ssr.ts index b8c41c25c..32b08c2d7 100644 --- a/packages/astro/src/@types/ssr.ts +++ b/packages/astro/src/@types/ssr.ts @@ -1,4 +1,4 @@ -import { Astro as AstroGlobal } from './astro-file'; +import { Astro as AstroGlobal, TopLevelAstro } from './astro-file'; import { Renderer } from './astro'; export interface SSRMetadata { @@ -8,6 +8,6 @@ export interface SSRMetadata { export interface SSRResult { styles: Set; scripts: Set; - createAstro(props: Record, slots: Record | null): AstroGlobal; + createAstro(Astro: TopLevelAstro, props: Record, slots: Record | null): AstroGlobal; _metadata: SSRMetadata; } \ No newline at end of file diff --git a/packages/astro/src/internal/hydration-map.ts b/packages/astro/src/internal/hydration-map.ts index c4325a06a..c46fd7df8 100644 --- a/packages/astro/src/internal/hydration-map.ts +++ b/packages/astro/src/internal/hydration-map.ts @@ -30,6 +30,7 @@ class HydrationMap { private getComponentMetadata(Component: any): ComponentMetadata | null { if(this.metadataCache.has(Component)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.metadataCache.get(Component)!; } const metadata = this.findComponentMetadata(Component); diff --git a/packages/astro/src/internal/index.ts b/packages/astro/src/internal/index.ts index 2e41bf415..05c18c60c 100644 --- a/packages/astro/src/internal/index.ts +++ b/packages/astro/src/internal/index.ts @@ -1,6 +1,8 @@ import type { AstroComponentMetadata } from '../@types/astro'; import type { SSRResult } from '../@types/ssr'; +import type { TopLevelAstro } from '../@types/astro-file'; +import { pathToFileURL } from 'url'; import { valueToEstree } from 'estree-util-value-to-estree'; import * as astring from 'astring'; import shorthash from 'shorthash'; @@ -236,6 +238,47 @@ export async function renderComponent(result: SSRResult, displayName: string, Co return `${html}`; } +/** Create the Astro.fetchContent() runtime function. */ +function createFetchContentFn(url: URL) { + const fetchContent = (importMetaGlobResult: Record) => { + 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; + } + return { + content: mod.metadata, + metadata: mod.frontmatter, + file: new URL(spec, url), + }; + }) + .filter(Boolean); + }; + return fetchContent; +} + +export function createAstro(fileURLStr: string, site: string): TopLevelAstro { + const url = pathToFileURL(fileURLStr); + const fetchContent = createFetchContentFn(url) as unknown as TopLevelAstro['fetchContent']; + return { + // TODO I think this is no longer needed. + isPage: false, + site: new URL(site), + fetchContent, + resolve(...segments) { + return segments.reduce( + (u, segment) => new URL(segment, u), + url + ).pathname + } + }; +} + export function addAttribute(value: any, key: string) { if (value == null || value === false) { return ''; diff --git a/packages/astro/src/runtime/ssr.ts b/packages/astro/src/runtime/ssr.ts index 980cfa671..3e57800b7 100644 --- a/packages/astro/src/runtime/ssr.ts +++ b/packages/astro/src/runtime/ssr.ts @@ -2,7 +2,7 @@ import type { BuildResult } from 'esbuild'; import type { ViteDevServer } from 'vite'; import type { AstroConfig, ComponentInstance, GetStaticPathsResult, Params, Props, Renderer, RouteCache, RouteData, RuntimeMode, SSRError } from '../@types/astro'; import type { SSRResult } from '../@types/ssr'; -import type { Astro } from '../@types/astro-file'; +import type { Astro, TopLevelAstro } from '../@types/astro-file'; import type { LogOptions } from '../logger'; import cheerio from 'cheerio'; @@ -80,30 +80,6 @@ async function resolveRenderers(viteServer: ViteDevServer, ids: string[]): Promi return renderers; } -/** Create the Astro.fetchContent() runtime function. */ -function createFetchContentFn(url: URL) { - const fetchContent = (importMetaGlobResult: Record) => { - 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; - } - return { - content: mod.metadata, - metadata: mod.frontmatter, - file: new URL(spec, url), - }; - }) - .filter(Boolean); - }; - return fetchContent; -} - /** use Vite to SSR */ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathname, route, routeCache, viteServer }: SSROptions): Promise { try { @@ -128,6 +104,7 @@ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathna routeCache[route.component] = routeCache[route.component] || ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await mod.getStaticPaths!({ paginate: generatePaginateFunction(route), rss: () => { @@ -158,35 +135,28 @@ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathna styles: new Set(), scripts: new Set(), /** This function returns the `Astro` faux-global */ - createAstro: (props: Record, slots: Record | null) => { + createAstro(AstroGlobal: TopLevelAstro, props: Record, slots: Record | null) { const site = new URL(origin); const url = new URL('.' + pathname, site); - const canonicalURL = getCanonicalURL(pathname, astroConfig.buildOptions.site || origin); - // Cast this type because the actual fetchContent implementation relies on import.meta.globEager - const fetchContent = createFetchContentFn(filePath) as unknown as Astro['fetchContent']; + const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin); + return { - isPage: true, - site, + __proto__: AstroGlobal, + props, request: { canonicalURL, params: {}, url }, - props, - fetchContent, slots: Object.fromEntries( Object.entries(slots || {}).map(([slotName]) => [slotName, true]) - ), - // Only temporary to get types working. - resolve(_s: string) { - throw new Error('Astro.resolve() is not currently supported in next.'); - } - }; + ) + } as unknown as Astro; }, _metadata: { renderers }, }; - let html = await renderPage(result, Component, {}, null); + let html = await renderPage(result, Component, pageProps, null); // 4. modify response if (mode === 'development') { diff --git a/packages/astro/src/runtime/vite/plugin-astro.ts b/packages/astro/src/runtime/vite/plugin-astro.ts index 61fb586ad..1d374dc33 100644 --- a/packages/astro/src/runtime/vite/plugin-astro.ts +++ b/packages/astro/src/runtime/vite/plugin-astro.ts @@ -29,6 +29,7 @@ export default function astro({ config, devServer }: AstroPluginOptions): Plugin // 1. Transform from `.astro` to valid `.ts` // use `sourcemap: "inline"` so that the sourcemap is included in the "code" result that we pass to esbuild. tsResult = await transform(source, { + site: config.buildOptions.site, sourcefile: id, sourcemap: 'both', internalURL: 'astro/internal' diff --git a/packages/astro/test/astro-global.test.js b/packages/astro/test/astro-global.test.js index 1cb5a27dd..641136fa6 100644 --- a/packages/astro/test/astro-global.test.js +++ b/packages/astro/test/astro-global.test.js @@ -1,6 +1,3 @@ -/** - * UNCOMMENT: add Astro.* global - import { expect } from 'chai'; import cheerio from 'cheerio'; import { loadFixture } from './test-utils.js'; @@ -32,15 +29,16 @@ describe('Astro.*', () => { it('Astro.request.canonicalURL', async () => { // given a URL, expect the following canonical URL const canonicalURLs = { - '/': 'https://mysite.dev/blog/index.html', - '/post/post': 'https://mysite.dev/blog/post/post/index.html', - '/posts/1': 'https://mysite.dev/blog/posts/index.html', - '/posts/2': 'https://mysite.dev/blog/posts/2/index.html', + '/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 result = await fixture.readFile(url); - const $ = cheerio.load(result.contents); + const html = await fixture.readFile(url); + + const $ = cheerio.load(html); expect($('link[rel="canonical"]').attr('href')).to.equal(canonicalURL); } }); @@ -54,16 +52,7 @@ describe('Astro.*', () => { it('Astro.resolve in development', async () => { const html = await fixture.readFile('/resolve/index.html'); const $ = cheerio.load(html); - expect($('img').attr('src')).to.equal('/_astro/src/images/penguin.png'); - expect($('#inner-child img').attr('src')).to.equal('/_astro/src/components/nested/images/penguin.png'); + expect($('img').attr('src')).to.equal('/src/images/penguin.png'); + expect($('#inner-child img').attr('src')).to.equal('/src/components/nested/images/penguin.png'); }); - - it('Astro.resolve in the build', async () => { - const html = await fixture.readFile('/resolve/index.html'); - const $ = cheerio.load(html); - expect($('img').attr('src')).to.equal('/blog/_astro/src/images/penguin.png'); - }); -}); -*/ - -it.skip('is skipped', () => {}); +}); \ No newline at end of file diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/index.astro b/packages/astro/test/fixtures/custom-elements/src/pages/index.astro index 1b8b89efe..b7380624a 100644 --- a/packages/astro/test/fixtures/custom-elements/src/pages/index.astro +++ b/packages/astro/test/fixtures/custom-elements/src/pages/index.astro @@ -1,6 +1,6 @@ --- -const title = 'My App'; import '../components/my-element.js'; +const title = 'My App'; --- diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/load.astro b/packages/astro/test/fixtures/custom-elements/src/pages/load.astro index 9cac84321..0f55e246e 100644 --- a/packages/astro/test/fixtures/custom-elements/src/pages/load.astro +++ b/packages/astro/test/fixtures/custom-elements/src/pages/load.astro @@ -1,6 +1,6 @@ --- -const title = 'My App'; import '../components/my-element.js'; +const title = 'My App'; --- diff --git a/yarn.lock b/yarn.lock index 359776c52..21c799845 100644 --- a/yarn.lock +++ b/yarn.lock @@ -106,10 +106,10 @@ "@algolia/logger-common" "4.10.5" "@algolia/requester-common" "4.10.5" -"@astrojs/compiler@^0.1.13": - version "0.1.13" - resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.1.13.tgz#06c810d71c7bcce1603ea77f9381940523544e69" - integrity sha512-t/soXPJ34AQ67goh8EInCNVWd2HYOpgL5hE2xcZGwG74cIuczT4c+Guiqt50KYF5a7eNTdolPdy/dd7yipW8nQ== +"@astrojs/compiler@^0.1.14": + version "0.1.14" + resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.1.14.tgz#15bb8211312c0ef2cacb9483fdb31c5dff1013ea" + integrity sha512-iqc8upge2o9AAL87lB5ptTDZWmrrWpiFX7jGTU3Cuo4X9+KXmJL4oKSMy8sGt7JoKd2j5yZsFfLq9R5V/nwfbQ== dependencies: typescript "^4.3.5"