diff options
author | 2021-11-15 10:33:51 -0500 | |
---|---|---|
committer | 2021-11-15 10:33:51 -0500 | |
commit | fca1a99dbdcc32aa130de29917629ad2fa397369 (patch) | |
tree | 9fd17a9afaec2a08f622209f1bea8ff158d38f91 | |
parent | a8b428ca27fd596bc331cdbb7a0388db1d052d55 (diff) | |
download | astro-fca1a99dbdcc32aa130de29917629ad2fa397369.tar.gz astro-fca1a99dbdcc32aa130de29917629ad2fa397369.tar.zst astro-fca1a99dbdcc32aa130de29917629ad2fa397369.zip |
Support for using a subpath (#1801)
* Support for using a subpath
* Update the readme
* Fix the package name
* Adds a changeset
* Ensure that public/ must be loaded from the subpath
* Update preview to use the new paths
* Use exact compiler version
* Use the newest version
* Use range again
* Use newer range
-rw-r--r-- | .changeset/twelve-oranges-approve.md | 11 | ||||
-rw-r--r-- | examples/subpath/README.md | 45 | ||||
-rw-r--r-- | examples/subpath/astro.config.mjs | 16 | ||||
-rw-r--r-- | examples/subpath/package.json | 14 | ||||
-rw-r--r-- | examples/subpath/public/favicon.ico | bin | 0 -> 4286 bytes | |||
-rw-r--r-- | examples/subpath/public/images/penguin.png | bin | 0 -> 77977 bytes | |||
-rw-r--r-- | examples/subpath/public/robots.txt | 2 | ||||
-rw-r--r-- | examples/subpath/sandbox.config.json | 11 | ||||
-rw-r--r-- | examples/subpath/src/components/Time.jsx | 7 | ||||
-rw-r--r-- | examples/subpath/src/pages/index.astro | 32 | ||||
-rw-r--r-- | examples/subpath/src/styles/main.scss | 5 | ||||
-rw-r--r-- | packages/astro/package.json | 2 | ||||
-rw-r--r-- | packages/astro/src/core/build/index.ts | 1 | ||||
-rw-r--r-- | packages/astro/src/core/dev/index.ts | 29 | ||||
-rw-r--r-- | packages/astro/src/core/dev/template/4xx.ts | 22 | ||||
-rw-r--r-- | packages/astro/src/core/preview/index.ts | 12 |
16 files changed, 199 insertions, 10 deletions
diff --git a/.changeset/twelve-oranges-approve.md b/.changeset/twelve-oranges-approve.md new file mode 100644 index 000000000..25ee7464c --- /dev/null +++ b/.changeset/twelve-oranges-approve.md @@ -0,0 +1,11 @@ +--- +'astro': patch +--- + +Provides first-class support for a site deployed to a subpath + +Now you can deploy your site to a subpath more easily. Astro will use your `buildOptions.site` URL and host the dev server from there. + +If your site config is `http://example.com/blog` you will need to go to `http://localhost:3000/blog/` in dev and when using `astro preview`. + +Includes a helpful 404 page when encountering this in dev and preview.
\ No newline at end of file diff --git a/examples/subpath/README.md b/examples/subpath/README.md new file mode 100644 index 000000000..d1175d61a --- /dev/null +++ b/examples/subpath/README.md @@ -0,0 +1,45 @@ +# Astro Starter Kit: A site deployed to a subpath + +``` +npm init astro -- --template subpath +``` + +[](https://stackblitz.com/github/snowpackjs/astro/tree/latest/examples/subpath) + +> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun! + +## 🚀 Project Structure + +Inside of your Astro project, you'll see the following folders and files: + +``` +/ +├── public/ +├── src/ +│ └── components/ +│ └── Time.jsx +│ └── pages/ +│ └── index.astro +└── package.json +``` + +Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. + +There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. + +Any static assets, like images, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +|:---------------- |:-------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:3000` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | + +## 👀 Want to learn more? + +Feel free to check [our documentation](https://github.com/snowpackjs/astro) or jump into our [Discord server](https://astro.build/chat). diff --git a/examples/subpath/astro.config.mjs b/examples/subpath/astro.config.mjs new file mode 100644 index 000000000..989f60121 --- /dev/null +++ b/examples/subpath/astro.config.mjs @@ -0,0 +1,16 @@ +// Full Astro Configuration API Documentation: +// https://docs.astro.build/reference/configuration-reference + +// @type-check enabled! +// VSCode and other TypeScript-enabled text editors will provide auto-completion, +// helpful tooltips, and warnings if your exported object is invalid. +// You can disable this by removing "@ts-check" and `@type` comments below. + +// @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'], +}); diff --git a/examples/subpath/package.json b/examples/subpath/package.json new file mode 100644 index 000000000..2aaf016c5 --- /dev/null +++ b/examples/subpath/package.json @@ -0,0 +1,14 @@ +{ + "name": "@example/subpath", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview" + }, + "devDependencies": { + "astro": "^0.21.0-next.3" + } +} diff --git a/examples/subpath/public/favicon.ico b/examples/subpath/public/favicon.ico Binary files differnew file mode 100644 index 000000000..578ad458b --- /dev/null +++ b/examples/subpath/public/favicon.ico diff --git a/examples/subpath/public/images/penguin.png b/examples/subpath/public/images/penguin.png Binary files differnew file mode 100644 index 000000000..bc9523bd4 --- /dev/null +++ b/examples/subpath/public/images/penguin.png diff --git a/examples/subpath/public/robots.txt b/examples/subpath/public/robots.txt new file mode 100644 index 000000000..1f53798bb --- /dev/null +++ b/examples/subpath/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/examples/subpath/sandbox.config.json b/examples/subpath/sandbox.config.json new file mode 100644 index 000000000..9178af77d --- /dev/null +++ b/examples/subpath/sandbox.config.json @@ -0,0 +1,11 @@ +{ + "infiniteLoopProtection": true, + "hardReloadOnChange": false, + "view": "browser", + "template": "node", + "container": { + "port": 3000, + "startScript": "start", + "node": "14" + } +} diff --git a/examples/subpath/src/components/Time.jsx b/examples/subpath/src/components/Time.jsx new file mode 100644 index 000000000..8172b77dd --- /dev/null +++ b/examples/subpath/src/components/Time.jsx @@ -0,0 +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> +}
\ No newline at end of file diff --git a/examples/subpath/src/pages/index.astro b/examples/subpath/src/pages/index.astro new file mode 100644 index 000000000..b08f2e06f --- /dev/null +++ b/examples/subpath/src/pages/index.astro @@ -0,0 +1,32 @@ +--- +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> + +<body> + <h1>Welcome to <a href="https://astro.build/">Astro</a></h1> + + <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> + +</html> diff --git a/examples/subpath/src/styles/main.scss b/examples/subpath/src/styles/main.scss new file mode 100644 index 000000000..573fc4396 --- /dev/null +++ b/examples/subpath/src/styles/main.scss @@ -0,0 +1,5 @@ +body { + #app { + color: tomato; + } +}
\ No newline at end of file diff --git a/packages/astro/package.json b/packages/astro/package.json index 88ccde6f8..71a7f9b4a 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -53,7 +53,7 @@ "test": "mocha --parallel --timeout 15000" }, "dependencies": { - "@astrojs/compiler": "^0.2.26", + "@astrojs/compiler": "^0.2.27", "@astrojs/language-server": "^0.7.16", "@astrojs/markdown-remark": "^0.4.0-next.1", "@astrojs/markdown-support": "0.3.1", diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index ed8529a77..b3a514e88 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -184,6 +184,7 @@ class AstroBuilder { publicDir: viteConfig.publicDir, root: viteConfig.root, server: viteConfig.server, + base: this.config.buildOptions.site ? new URL(this.config.buildOptions.site).pathname : '/' }); debug(logging, 'build', timerMessage('Vite build finished', timer.buildStart)); diff --git a/packages/astro/src/core/dev/index.ts b/packages/astro/src/core/dev/index.ts index 3774a9995..5c81f4101 100644 --- a/packages/astro/src/core/dev/index.ts +++ b/packages/astro/src/core/dev/index.ts @@ -19,7 +19,7 @@ import { collectResources } from '../ssr/html.js'; import { createRouteManifest, matchRoute } from '../ssr/routing.js'; import { createVite } from '../create-vite.js'; import * as msg from './messages.js'; -import notFoundTemplate from './template/4xx.js'; +import notFoundTemplate, { subpathNotUsedTemplate } from './template/4xx.js'; import serverErrorTemplate from './template/5xx.js'; export interface DevOptions { @@ -59,6 +59,9 @@ export class AstroDevServer { private logging: LogOptions; private manifest: ManifestData; private mostRecentRoute?: RouteData; + private site: URL | undefined; + private pathname: string; + private url: URL; private origin: string; private routeCache: RouteCache = {}; private viteServer: vite.ViteDevServer | undefined; @@ -69,6 +72,9 @@ export class AstroDevServer { this.logging = options.logging; this.port = config.devOptions.port; this.origin = `http://localhost:${this.port}`; + this.site = config.buildOptions.site ? new URL(config.buildOptions.site) : undefined; + this.pathname = this.site ? this.site.pathname + '/' : '/'; + this.url = new URL(this.pathname, this.origin); this.manifest = createRouteManifest({ config }); } @@ -195,7 +201,7 @@ export class AstroDevServer { const listen = () => { 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}` })); + info(this.logging, 'astro', msg.devHost({ host: `http://${this.hostname}:${this.port}${this.pathname}` })); resolve(); }); this.httpServer?.on('error', onError); @@ -228,7 +234,7 @@ export class AstroDevServer { server: { middlewareMode: 'ssr', host: this.hostname, - }, + } }, this.config.vite || {} ), @@ -271,10 +277,19 @@ export class AstroDevServer { const reqStart = performance.now(); let filePath: URL | undefined; try { - const route = matchRoute(pathname, this.manifest); + let routePathname = pathname.startsWith(this.pathname) ? pathname.substr(this.pathname.length) || '/' : undefined; + if(!routePathname) { + 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; } @@ -348,7 +363,11 @@ export class AstroDevServer { } // if not found, fall back to default template else { - html = notFoundTemplate({ statusCode, title: 'Not found', tabTitle: '404: Not Found', pathname }); + if(pathname === '/' && !pathname.startsWith(this.pathname)) { + html = subpathNotUsedTemplate(this.pathname, 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, { diff --git a/packages/astro/src/core/dev/template/4xx.ts b/packages/astro/src/core/dev/template/4xx.ts index 99c022963..b24e87098 100644 --- a/packages/astro/src/core/dev/template/4xx.ts +++ b/packages/astro/src/core/dev/template/4xx.ts @@ -1,5 +1,6 @@ import { encode } from 'html-entities'; import { baseCSS } from './css.js'; + interface ErrorTemplateOptions { /** a short description of the error */ pathname: string; @@ -9,10 +10,12 @@ interface ErrorTemplateOptions { 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 }: ErrorTemplateOptions): string { +export default function template({ title, pathname, statusCode = 404, tabTitle, body }: ErrorTemplateOptions): string { return `<!doctype html> <html lang="en"> <head> @@ -44,8 +47,23 @@ export default function template({ title, pathname, statusCode = 404, tabTitle } <main class="center"> <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> - <pre>Path: ${encode(pathname)}</pre> + ${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: ` + <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> + ` + }); +}
\ No newline at end of file diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts index bb2cf33ad..28f568827 100644 --- a/packages/astro/src/core/preview/index.ts +++ b/packages/astro/src/core/preview/index.ts @@ -7,6 +7,7 @@ import send from 'send'; import { fileURLToPath } from 'url'; import * as msg from '../dev/messages.js'; import { error, info } from '../logger.js'; +import { subpathNotUsedTemplate } from '../dev/template/4xx.js'; interface PreviewOptions { logging: LogOptions; @@ -22,10 +23,17 @@ interface PreviewServer { /** 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 + '/' : '/'; // Create the preview server, send static files out of the `dist/` directory. const server = http.createServer((req, res) => { - send(req, req.url!, { + 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); }); @@ -48,7 +56,7 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO server .listen(port, hostname, () => { info(logging, 'preview', msg.devStart({ startupTime: performance.now() - startServerTime })); - info(logging, 'preview', msg.devHost({ host: `http://${hostname}:${port}/` })); + info(logging, 'preview', msg.devHost({ host: `http://${hostname}:${port}${base}` })); resolve(server); }) .on('error', (err: NodeJS.ErrnoException) => { |