diff options
author | 2021-05-11 17:31:52 -0600 | |
---|---|---|
committer | 2021-05-11 17:31:52 -0600 | |
commit | 7184149514260c9c5f8525648af1c0f2e51c998e (patch) | |
tree | 251fad305a2e22ea91adecd1e1f751b67c2bab09 | |
parent | d2eb413a6e4d423880bb3af93b63e3f37a6f1049 (diff) | |
download | astro-7184149514260c9c5f8525648af1c0f2e51c998e.tar.gz astro-7184149514260c9c5f8525648af1c0f2e51c998e.tar.zst astro-7184149514260c9c5f8525648af1c0f2e51c998e.zip |
Add Astro.request.canonicalURL and Astro.site to global (#199)
25 files changed, 234 insertions, 98 deletions
diff --git a/.changeset/cold-windows-exercise.md b/.changeset/cold-windows-exercise.md new file mode 100644 index 000000000..d0e5b37bf --- /dev/null +++ b/.changeset/cold-windows-exercise.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Added canonical URL and site globals for .astro files diff --git a/docs/api.md b/docs/api.md index e45c87bd8..9037b75de 100644 --- a/docs/api.md +++ b/docs/api.md @@ -4,14 +4,6 @@ The `Astro` global is available in all contexts in `.astro` files. It has the following functions: -#### `config` - -`Astro.config` returns an object with the following properties: - -| Name | Type | Description | -| :----- | :------- | :--------------------------------------------------------------------------------------------------------- | -| `site` | `string` | Your website’s public root domain. Set it with `site: "https://mysite.com"` in your [Astro config][config] | - #### `fetchContent()` `Astro.fetchContent()` is a way to load local `*.md` files into your static site setup. You can either use this on its own, or within [Astro Collections][docs-collections]. @@ -47,9 +39,16 @@ const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of po `Astro.request` returns an object with the following properties: -| Name | Type | Description | -| :---- | :---- | :------------------------------------- | -| `url` | `URL` | The URL of the request being rendered. | +| Name | Type | Description | +| :------------- | :---- | :---------------------------------------------- | +| `url` | `URL` | The URL of the request being rendered. | +| `canonicalURL` | `URL` | [Canonical URL][canonical] of the current page. | + +⚠️ Temporary restriction: this is only accessible in top-level pages and not in sub-components. + +#### `site` + +`Astro.site` returns a `URL` made from `buildOptions.site` in your Astro config. If undefined, this will return a URL generated from `localhost`. ### `collection` @@ -147,6 +146,7 @@ Astro will generate an RSS 2.0 feed at `/feed/[collection].xml` (for example, `/ <link rel="alternate" type="application/rss+xml" title="My RSS Feed" href="/feed/podcast.xml" /> ``` +[canonical]: https://en.wikipedia.org/wiki/Canonical_link_element [config]: ../README.md#%EF%B8%8F-configuration [docs-collections]: ./collections.md [rss]: #-rss-feed diff --git a/examples/blog/public/global.css b/examples/blog/public/global.scss index a6007631a..a6007631a 100644 --- a/examples/blog/public/global.css +++ b/examples/blog/public/global.scss diff --git a/examples/blog/src/components/MainHead.astro b/examples/blog/src/components/MainHead.astro index bff812b0c..dfeb9dfb4 100644 --- a/examples/blog/src/components/MainHead.astro +++ b/examples/blog/src/components/MainHead.astro @@ -4,6 +4,9 @@ export let title: string; export let description: string; export let image: string | undefined; export let type: string | undefined; +export let next: string | undefined; +export let prev: string | undefined; +export let canonicalURL: string | undefined; // internal data const OG_TYPES = { @@ -17,6 +20,10 @@ const OG_TYPES = { <title>{title}</title> <meta name="description" content={description} /> <link rel="stylesheet" href="/global.css" /> +<link rel="sitemap" href="/sitemap.xml" /> +<link rel="canonical" href={canonicalURL} /> +{next && <link rel="next" href={next} />} +{prev && <link rel="prev" href={prev} />} <!-- OpenGraph --> <meta property="og:title" content={title} /> diff --git a/examples/blog/src/pages/index.astro b/examples/blog/src/pages/index.astro index 2af3a1a03..19ea4ebf9 100644 --- a/examples/blog/src/pages/index.astro +++ b/examples/blog/src/pages/index.astro @@ -21,7 +21,11 @@ let firstThree = allPosts.slice(0, 3); <html> <head> <title>{title}</title> - <MainHead title={title} description={description} /> + <MainHead + title={title} + description={description} + canonicalURL={Astro.request.canonicalURL.href} + /> </head> <body> diff --git a/packages/astro/src/build.ts b/packages/astro/src/build.ts index 262b4525b..439d4ac9b 100644 --- a/packages/astro/src/build.ts +++ b/packages/astro/src/build.ts @@ -17,7 +17,7 @@ import { buildCollectionPage, buildStaticPage, getPageType } from './build/page. import { generateSitemap } from './build/sitemap.js'; import { logURLStats, collectBundleStats, mapBundleStatsToURLStats } from './build/stats.js'; import { getDistPath, sortSet, stopTimer } from './build/util.js'; -import { debug, defaultLogDestination, error, info, trapWarn } from './logger.js'; +import { debug, defaultLogDestination, error, info, warn, trapWarn } from './logger.js'; import { createRuntime } from './runtime.js'; const logging: LogOptions = { @@ -55,6 +55,9 @@ export async function build(astroConfig: AstroConfig): Promise<0 | 1> { dest: defaultLogDestination, }; + // warn users if missing config item in build that may result in broken SEO (can’t disable, as they should provide this) + warn(logging, 'config', `Set "buildOptions.site" to generate correct canonical URLs and sitemap`); + const mode: RuntimeMode = 'production'; const runtime = await createRuntime(astroConfig, { mode, logging: runtimeLogging }); const { runtimeConfig } = runtime; @@ -170,8 +173,6 @@ export async function build(astroConfig: AstroConfig): Promise<0 | 1> { await fs.promises.writeFile(sitemapPath, sitemap, 'utf8'); info(logging, 'build', green('✔'), 'sitemap built.'); debug(logging, 'build', `built sitemap [${stopTimer(timer.sitemap)}]`); - } else if (astroConfig.buildOptions.sitemap) { - info(logging, 'tip', `Set "buildOptions.site" in astro.config.mjs to generate a sitemap.xml, or set "buildOptions.sitemap: false" to disable this message.`); } // write to disk and free up memory diff --git a/packages/astro/src/build/rss.ts b/packages/astro/src/build/rss.ts index b75ed908b..e7a12da55 100644 --- a/packages/astro/src/build/rss.ts +++ b/packages/astro/src/build/rss.ts @@ -27,7 +27,7 @@ export function generateRSS<T>(input: { data: T[]; site: string } & CollectionRS // title, description, customData xml += `<title><![CDATA[${input.title}]]></title>`; xml += `<description><![CDATA[${input.description}]]></description>`; - xml += `<link>${canonicalURL('/feed/' + filename + '.xml', input.site)}</link>`; + xml += `<link>${canonicalURL('/feed/' + filename + '.xml', input.site).href}</link>`; if (typeof input.customData === 'string') xml += input.customData; // items @@ -40,7 +40,7 @@ export function generateRSS<T>(input: { data: T[]; site: string } & CollectionRS if (!result.title) throw new Error(`[${filename}] rss.item() returned object but required "title" is missing.`); if (!result.link) throw new Error(`[${filename}] rss.item() returned object but required "link" is missing.`); xml += `<title><![CDATA[${result.title}]]></title>`; - xml += `<link>${canonicalURL(result.link, input.site)}</link>`; + xml += `<link>${canonicalURL(result.link, input.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. diff --git a/packages/astro/src/build/sitemap.ts b/packages/astro/src/build/sitemap.ts index 5095019c7..7d6bf62a8 100644 --- a/packages/astro/src/build/sitemap.ts +++ b/packages/astro/src/build/sitemap.ts @@ -4,18 +4,18 @@ import { canonicalURL } from './util'; /** Construct sitemap.xml given a set of URLs */ export function generateSitemap(buildState: BuildOutput, site: string): string { - const pages: string[] = []; + const uniqueURLs = new Set<string>(); // TODO: find way to respect <link rel="canonical"> URLs here // TODO: find way to exclude pages from sitemap // look through built pages, only add HTML for (const id of Object.keys(buildState)) { - if (buildState[id].contentType !== 'text/html' || id.endsWith('/1/index.html')) continue; // note: exclude auto-generated "page 1" pages (duplicates of index) - let url = canonicalURL(id.replace(/index\.html$/, ''), site); - pages.push(url); + if (buildState[id].contentType !== 'text/html') continue; + uniqueURLs.add(canonicalURL(id, site).href); } + const pages = [...uniqueURLs]; pages.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">`; diff --git a/packages/astro/src/build/util.ts b/packages/astro/src/build/util.ts index afd9f5b32..6b8834f43 100644 --- a/packages/astro/src/build/util.ts +++ b/packages/astro/src/build/util.ts @@ -5,11 +5,11 @@ import path from 'path'; import { fileURLToPath, URL } from 'url'; /** Normalize URL to its canonical form */ -export function canonicalURL(url: string, base?: string): string { - return new URL( - path.extname(url) ? url : url.replace(/(\/+)?$/, '/'), // add trailing slash if there’s no extension - base - ).href; +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 + return new URL(pathname, base); } /** Sort a Set */ diff --git a/packages/astro/src/cli.ts b/packages/astro/src/cli.ts index 9588b9282..efad9f418 100644 --- a/packages/astro/src/cli.ts +++ b/packages/astro/src/cli.ts @@ -65,7 +65,7 @@ function printHelp() { ${colors.bold('Flags:')} --config <path> Specify the path to the Astro config file. --project-root <path> Specify the path to the project root folder. - --no-sitemap Disable sitemap generation (build only). + --no-sitemap Disable sitemap generation (build only). --version Show the version number and exit. --help Show this help message. `); diff --git a/packages/astro/src/compiler/codegen/index.ts b/packages/astro/src/compiler/codegen/index.ts index e4cfad1cd..6c7e8df07 100644 --- a/packages/astro/src/compiler/codegen/index.ts +++ b/packages/astro/src/compiler/codegen/index.ts @@ -3,16 +3,18 @@ import type { AstroConfig, ValidExtensionPlugins } from '../../@types/astro'; import type { Ast, Script, Style, TemplateNode } from 'astro-parser'; import type { TransformResult } from '../../@types/astro'; +import 'source-map-support/register.js'; import eslexer from 'es-module-lexer'; import esbuild from 'esbuild'; import path from 'path'; +import { fileURLToPath } from 'url'; import { walk } from 'estree-walker'; import _babelGenerator from '@babel/generator'; import babelParser from '@babel/parser'; import { codeFrameColumns } from '@babel/code-frame'; import * as babelTraverse from '@babel/traverse'; import { ImportDeclaration, ExportNamedDeclaration, VariableDeclarator, Identifier } from '@babel/types'; -import { warn } from '../../logger.js'; +import { error, warn } from '../../logger.js'; import { fetchContent } from './content.js'; import { isFetchContent } from './utils.js'; import { yellow } from 'kleur/colors'; @@ -77,7 +79,12 @@ function getAttributes(attrs: Attribute[]): Record<string, string> { switch (val.type) { case 'MustacheTag': { // FIXME: this won't work when JSX element can appear in attributes (rare but possible). - result[attr.name] = '(' + val.expression.codeChunks[0] + ')'; + const codeChunks = val.expression.codeChunks[0]; + if (codeChunks) { + result[attr.name] = '(' + codeChunks + ')'; + } else { + throw new Error(`Parse error: ${attr.name}={}`); // if bad codeChunk, throw error + } continue; } case 'Text': @@ -388,7 +395,7 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp if (!id || !init || id.type !== 'Identifier') continue; if (init.type === 'AwaitExpression') { init = init.argument; - const shortname = path.relative(compileOptions.astroConfig.projectRoot.pathname, state.filename); + const shortname = path.posix.relative(compileOptions.astroConfig.projectRoot.pathname, state.filename); warn(compileOptions.logging, shortname, yellow('awaiting Astro.fetchContent() not necessary')); } if (init.type !== 'CallExpression') continue; @@ -572,29 +579,36 @@ function compileHtml(enterNode: TemplateNode, state: CodegenState, compileOption if (!name) { throw new Error('AHHHH'); } - const attributes = getAttributes(node.attributes); + try { + const attributes = getAttributes(node.attributes); - outSource += outSource === '' ? '' : ','; - if (node.type === 'Slot') { - outSource += `(children`; - return; - } - const COMPONENT_NAME_SCANNER = /^[A-Z]/; - if (!COMPONENT_NAME_SCANNER.test(name)) { - outSource += `h("${name}", ${attributes ? generateAttributes(attributes) : 'null'}`; - return; - } - const [componentName, componentKind] = name.split(':'); - const componentImportData = components[componentName]; - if (!componentImportData) { - throw new Error(`Unknown Component: ${componentName}`); - } - const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], { astroConfig, dynamicImports, filename }); - if (wrapperImport) { - importExportStatements.add(wrapperImport); - } + outSource += outSource === '' ? '' : ','; + if (node.type === 'Slot') { + outSource += `(children`; + return; + } + const COMPONENT_NAME_SCANNER = /^[A-Z]/; + if (!COMPONENT_NAME_SCANNER.test(name)) { + outSource += `h("${name}", ${attributes ? generateAttributes(attributes) : 'null'}`; + return; + } + const [componentName, componentKind] = name.split(':'); + const componentImportData = components[componentName]; + if (!componentImportData) { + throw new Error(`Unknown Component: ${componentName}`); + } + const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], { astroConfig, dynamicImports, filename }); + if (wrapperImport) { + importExportStatements.add(wrapperImport); + } - outSource += `h(${wrapper}, ${attributes ? generateAttributes(attributes) : 'null'}`; + outSource += `h(${wrapper}, ${attributes ? generateAttributes(attributes) : 'null'}`; + } catch (err) { + // handle errors in scope with filename + const rel = filename.replace(fileURLToPath(astroConfig.projectRoot), ''); + // TODO: return actual codeframe here + error(compileOptions.logging, rel, err.toString()); + } return; } case 'Attribute': { diff --git a/packages/astro/src/compiler/index.ts b/packages/astro/src/compiler/index.ts index a1a9bde36..f4bfbb19d 100644 --- a/packages/astro/src/compiler/index.ts +++ b/packages/astro/src/compiler/index.ts @@ -124,6 +124,7 @@ export async function compileComponent( { compileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string } ): Promise<CompileResult> { const result = await transformFromSource(source, { compileOptions, filename, projectRoot }); + const site = compileOptions.astroConfig.buildOptions.site || `http://localhost:${compileOptions.astroConfig.devOptions.port}`; // return template let modJsx = ` @@ -137,7 +138,8 @@ import { h, Fragment } from '${internalImport('h.js')}'; const __astroRequestSymbol = Symbol('astro.request'); async function __render(props, ...children) { const Astro = { - request: props[__astroRequestSymbol] + request: props[__astroRequestSymbol] || {}, + site: new URL('/', ${JSON.stringify(site)}), }; ${result.script} diff --git a/packages/astro/src/config.ts b/packages/astro/src/config.ts index d774d6b9e..682d39ac9 100644 --- a/packages/astro/src/config.ts +++ b/packages/astro/src/config.ts @@ -1,5 +1,6 @@ -import 'source-map-support/register.js'; import type { AstroConfig } from './@types/astro'; + +import 'source-map-support/register.js'; import { join as pathJoin, resolve as pathResolve } from 'path'; import { existsSync } from 'fs'; @@ -9,25 +10,36 @@ const type = (thing: any): string => (Array.isArray(thing) ? 'Array' : typeof th /** Throws error if a user provided an invalid config. Manually-implemented to avoid a heavy validation library. */ function validateConfig(config: any): void { // basic - if (config === undefined || config === null) throw new Error(`[astro config] Config empty!`); - if (typeof config !== 'object') throw new Error(`[astro config] Expected object, received ${typeof config}`); + if (config === undefined || config === null) throw new Error(`[config] Config empty!`); + if (typeof config !== 'object') throw new Error(`[config] Expected object, received ${typeof config}`); // strings - for (const key of ['projectRoot', 'astroRoot', 'dist', 'public', 'site']) { + for (const key of ['projectRoot', 'astroRoot', 'dist', 'public']) { if (config[key] !== undefined && config[key] !== null && typeof config[key] !== 'string') { - throw new Error(`[astro config] ${key}: ${JSON.stringify(config[key])}\n Expected string, received ${type(config[key])}.`); + throw new Error(`[config] ${key}: ${JSON.stringify(config[key])}\n Expected string, received ${type(config[key])}.`); } } // booleans for (const key of ['sitemap']) { if (config[key] !== undefined && config[key] !== null && typeof config[key] !== 'boolean') { - throw new Error(`[astro config] ${key}: ${JSON.stringify(config[key])}\n Expected boolean, received ${type(config[key])}.`); + throw new Error(`[config] ${key}: ${JSON.stringify(config[key])}\n Expected boolean, received ${type(config[key])}.`); + } + } + + // buildOptions + if (config.buildOptions && config.buildOptions.site !== undefined) { + if (typeof config.buildOptions.site !== 'string') throw new Error(`[config] buildOptions.site is not a string`); + try { + new URL(config.buildOptions.site); + } catch (err) { + throw new Error('[config] buildOptions.site must be a valid URL'); } } + // devOptions if (typeof config.devOptions?.port !== 'number') { - throw new Error(`[astro config] devOptions.port: Expected number, received ${type(config.devOptions?.port)}`); + throw new Error(`[config] devOptions.port: Expected number, received ${type(config.devOptions?.port)}`); } } diff --git a/packages/astro/src/dev.ts b/packages/astro/src/dev.ts index 7aa4ba07d..3a366ef00 100644 --- a/packages/astro/src/dev.ts +++ b/packages/astro/src/dev.ts @@ -3,9 +3,9 @@ import type { AstroConfig } from './@types/astro'; import type { LogOptions } from './logger.js'; import { logger as snowpackLogger } from 'snowpack'; -import { bold, green } from 'kleur/colors'; +import { green } from 'kleur/colors'; import http from 'http'; -import { relative as pathRelative } from 'path'; +import path from 'path'; import { performance } from 'perf_hooks'; import { defaultLogDestination, error, info, parseError } from './logger.js'; import { createRuntime } from './runtime.js'; @@ -63,7 +63,7 @@ export default async function dev(astroConfig: AstroConfig) { switch (result.type) { case 'parse-error': { const err = result.error; - err.filename = pathRelative(projectRoot.pathname, err.filename); + if (err.filename) err.filename = path.posix.relative(projectRoot.pathname, err.filename); parseError(logging, err); break; } diff --git a/packages/astro/src/logger.ts b/packages/astro/src/logger.ts index c1b024c0c..282e8506e 100644 --- a/packages/astro/src/logger.ts +++ b/packages/astro/src/logger.ts @@ -114,6 +114,10 @@ export function table(opts: LogOptions, columns: number[]) { /** 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')} │`) diff --git a/packages/astro/src/runtime.ts b/packages/astro/src/runtime.ts index 76e27516d..965ea641a 100644 --- a/packages/astro/src/runtime.ts +++ b/packages/astro/src/runtime.ts @@ -1,14 +1,15 @@ import 'source-map-support/register.js'; -import { fileURLToPath } from 'url'; import type { SnowpackDevServer, ServerRuntime as SnowpackServerRuntime, SnowpackConfig } from 'snowpack'; -import type { AstroConfig, CollectionResult, CollectionRSS, CreateCollection, Params, RuntimeMode } from './@types/astro'; -import type { LogOptions } from './logger'; import type { CompileError } from 'astro-parser'; -import { debug, info } from './logger.js'; -import { searchForPage } from './search.js'; +import type { LogOptions } from './logger'; +import type { AstroConfig, CollectionResult, CollectionRSS, CreateCollection, Params, RuntimeMode } from './@types/astro'; import { existsSync } from 'fs'; +import { fileURLToPath } from 'url'; import { loadConfiguration, logger as snowpackLogger, startServer as startSnowpackServer } from 'snowpack'; +import { canonicalURL } from './build/util.js'; +import { debug, info } from './logger.js'; +import { searchForPage } from './search.js'; // We need to use require.resolve for snowpack plugins, so create a require function here. import { createRequire } from 'module'; @@ -49,9 +50,10 @@ snowpackLogger.level = 'silent'; /** Pass a URL to Astro to resolve and build */ async function load(config: RuntimeConfig, rawPathname: string | undefined): Promise<LoadResult> { const { logging, backendSnowpackRuntime, frontendSnowpack } = config; - const { astroRoot } = config.astroConfig; + const { astroRoot, buildOptions, devOptions } = config.astroConfig; - const fullurl = new URL(rawPathname || '/', 'https://example.org/'); + let origin = buildOptions.site ? new URL(buildOptions.site).origin : `http://localhost:${devOptions.port}`; + const fullurl = new URL(rawPathname || '/', origin); const reqPath = decodeURI(fullurl.pathname); info(logging, 'access', reqPath); @@ -208,6 +210,7 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro request: { // params should go here when implemented url: requestURL, + canonicalURL: canonicalURL(requestURL.pathname, requestURL.origin), }, children: [], props: { collection }, diff --git a/packages/astro/test/astro-global.test.js b/packages/astro/test/astro-global.test.js new file mode 100644 index 000000000..891e1cfb2 --- /dev/null +++ b/packages/astro/test/astro-global.test.js @@ -0,0 +1,45 @@ +import { suite } from 'uvu'; +import * as assert from 'uvu/assert'; +import { doc } from './test-utils.js'; +import { setup } from './helpers.js'; + +const Global = suite('Astro.*'); + +setup(Global, './fixtures/astro-global'); + +Global('Astro.request.url', async (context) => { + const result = await context.runtime.load('/'); + + assert.equal(result.statusCode, 200); + + const $ = doc(result.contents); + assert.equal($('#pathname').text(), '/'); +}); + +Global('Astro.request.canonicalURL', async (context) => { + // given a URL, expect the following canonical URL + const canonicalURLs = { + '/': 'https://mysite.dev/', + '/post/post': 'https://mysite.dev/post/post/', + '/posts': 'https://mysite.dev/posts/', + '/posts/1': 'https://mysite.dev/posts/', // should be the same as /posts + '/posts/2': 'https://mysite.dev/posts/2/', + }; + + for (const [url, canonicalURL] of Object.entries(canonicalURLs)) { + const result = await context.runtime.load(url); + const $ = doc(result.contents); + assert.equal($('link[rel="canonical"]').attr('href'), canonicalURL); + } +}); + +Global('Astro.site', async (context) => { + const result = await context.runtime.load('/'); + + assert.equal(result.statusCode, 200); + + const $ = doc(result.contents); + assert.equal($('#site').attr('href'), 'https://mysite.dev'); +}); + +Global.run(); diff --git a/packages/astro/test/astro-request.test.js b/packages/astro/test/astro-request.test.js deleted file mode 100644 index 1156714dd..000000000 --- a/packages/astro/test/astro-request.test.js +++ /dev/null @@ -1,19 +0,0 @@ -import { suite } from 'uvu'; -import * as assert from 'uvu/assert'; -import { doc } from './test-utils.js'; -import { setup } from './helpers.js'; - -const Request = suite('Astro.request'); - -setup(Request, './fixtures/astro-request'); - -Request('Astro.request available', async (context) => { - const result = await context.runtime.load('/'); - - assert.equal(result.statusCode, 200); - - const $ = doc(result.contents); - assert.equal($('h1').text(), '/'); -}); - -Request.run(); diff --git a/packages/astro/test/fixtures/astro-global/astro.config.mjs b/packages/astro/test/fixtures/astro-global/astro.config.mjs new file mode 100644 index 000000000..a618e1ba3 --- /dev/null +++ b/packages/astro/test/fixtures/astro-global/astro.config.mjs @@ -0,0 +1,6 @@ +export default { + buildOptions: { + site: 'https://mysite.dev', + sitemap: false, + }, +}; diff --git a/packages/astro/test/fixtures/astro-global/src/layouts/post.astro b/packages/astro/test/fixtures/astro-global/src/layouts/post.astro new file mode 100644 index 000000000..4d75a3c28 --- /dev/null +++ b/packages/astro/test/fixtures/astro-global/src/layouts/post.astro @@ -0,0 +1,12 @@ +--- +export let content; +--- +<html> + <head> + <title>{content.title}</title> + <link rel="canonical" href={Astro.request.canonicalURL.href}> + </head> + <body> + <slot></slot> + </body> +</html> diff --git a/packages/astro/test/fixtures/astro-global/src/pages/$posts.astro b/packages/astro/test/fixtures/astro-global/src/pages/$posts.astro new file mode 100644 index 000000000..2384f6ba9 --- /dev/null +++ b/packages/astro/test/fixtures/astro-global/src/pages/$posts.astro @@ -0,0 +1,28 @@ +--- +export let collection; + +export function createCollection() { + return { + async data() { + const data = Astro.fetchContent('./post/*.md'); + return data; + }, + pageSize: 1, + }; +} +--- + +<html> + <head> + <title>All Posts</title> + <link rel="canonical" href={Astro.request.canonicalURL.href} /> + </head> + <body> + {collection.data.map((data) => ( + <div> + <h1>{data.title}</h1> + <a href={data.url}>Read</a> + </div> + ))} + </body> +</html> diff --git a/packages/astro/test/fixtures/astro-global/src/pages/index.astro b/packages/astro/test/fixtures/astro-global/src/pages/index.astro new file mode 100644 index 000000000..43b0ee9a6 --- /dev/null +++ b/packages/astro/test/fixtures/astro-global/src/pages/index.astro @@ -0,0 +1,10 @@ +<html> +<head> + <title>Test</title> + <link rel="canonical" href={Astro.request.canonicalURL.href}> +</head> +<body> + <div id="pathname">{Astro.request.url.pathname}</div> + <a id="site" href={Astro.site.origin}>Home</a> +</body> +</html> diff --git a/packages/astro/test/fixtures/astro-global/src/pages/post/post-2.md b/packages/astro/test/fixtures/astro-global/src/pages/post/post-2.md new file mode 100644 index 000000000..e98f2ec79 --- /dev/null +++ b/packages/astro/test/fixtures/astro-global/src/pages/post/post-2.md @@ -0,0 +1,6 @@ +--- +title: 'My Post 2' +layout: '../../layouts/post.astro' +--- + +# Post 2 diff --git a/packages/astro/test/fixtures/astro-global/src/pages/post/post.md b/packages/astro/test/fixtures/astro-global/src/pages/post/post.md new file mode 100644 index 000000000..dd65da76f --- /dev/null +++ b/packages/astro/test/fixtures/astro-global/src/pages/post/post.md @@ -0,0 +1,6 @@ +--- +title: 'My Post' +layout: '../../layouts/post.astro' +--- + +# My Post diff --git a/packages/astro/test/fixtures/astro-request/src/pages/index.astro b/packages/astro/test/fixtures/astro-request/src/pages/index.astro deleted file mode 100644 index f809a76e3..000000000 --- a/packages/astro/test/fixtures/astro-request/src/pages/index.astro +++ /dev/null @@ -1,10 +0,0 @@ ---- -let path = Astro.request.url.pathname; ---- - -<html> -<head><title>Test</title></head> -<body> - <h1>{path}</h1> -</body> -</html>
\ No newline at end of file |