refactor dev to use vite server (#2494)
This commit is contained in:
parent
9c48c2b58e
commit
d7149f9b2f
21 changed files with 222 additions and 449 deletions
7
.changeset/empty-snails-allow.md
Normal file
7
.changeset/empty-snails-allow.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
'astro': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Refactor dev server to use vite server internally.
|
||||||
|
|
||||||
|
This should be an invisible change, and no breaking changes are expected from this change. However, it is a big enough refactor that some unexpected changes may occur. If you've experienced a regression in the dev server, it is most likely a bug!
|
3
LICENSE
3
LICENSE
|
@ -37,8 +37,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This license applies to parts of the `packages/create-astro` subdirectory originating from the
|
This license applies to parts of the `packages/create-astro` and `packages/astro` subdirectories originating from the https://github.com/sveltejs/kit repository:
|
||||||
https://github.com/sveltejs/kit repository:
|
|
||||||
|
|
||||||
Copyright (c) 2020 [these people](https://github.com/sveltejs/kit/graphs/contributors)
|
Copyright (c) 2020 [these people](https://github.com/sveltejs/kit/graphs/contributors)
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,6 @@
|
||||||
"astring": "^1.7.5",
|
"astring": "^1.7.5",
|
||||||
"ci-info": "^3.2.0",
|
"ci-info": "^3.2.0",
|
||||||
"common-ancestor-path": "^1.0.1",
|
"common-ancestor-path": "^1.0.1",
|
||||||
"connect": "^3.7.0",
|
|
||||||
"eol": "^0.9.1",
|
"eol": "^0.9.1",
|
||||||
"es-module-lexer": "^0.9.3",
|
"es-module-lexer": "^0.9.3",
|
||||||
"esbuild": "0.13.7",
|
"esbuild": "0.13.7",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
import type { AstroConfig } from '../@types/astro';
|
import type { AstroConfig } from '../@types/astro';
|
||||||
import type { LogOptions } from '../core/logger';
|
import type { LogOptions } from '../core/logger.js';
|
||||||
|
|
||||||
import * as colors from 'kleur/colors';
|
import * as colors from 'kleur/colors';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import type { AstroConfig } from '../@types/astro';
|
import type { AstroConfig } from '../@types/astro';
|
||||||
import type { AstroDevServer } from './dev';
|
|
||||||
import type { LogOptions } from './logger';
|
import type { LogOptions } from './logger';
|
||||||
|
|
||||||
import { builtinModules } from 'module';
|
import { builtinModules } from 'module';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import vite from './vite.js';
|
import vite from './vite.js';
|
||||||
import astroVitePlugin from '../vite-plugin-astro/index.js';
|
import astroVitePlugin from '../vite-plugin-astro/index.js';
|
||||||
|
import astroViteServerPlugin from '../vite-plugin-astro-server/index.js';
|
||||||
import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.js';
|
import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.js';
|
||||||
import configAliasVitePlugin from '../vite-plugin-config-alias/index.js';
|
import configAliasVitePlugin from '../vite-plugin-config-alias/index.js';
|
||||||
import markdownVitePlugin from '../vite-plugin-markdown/index.js';
|
import markdownVitePlugin from '../vite-plugin-markdown/index.js';
|
||||||
|
@ -34,12 +34,11 @@ export type ViteConfigWithSSR = vite.InlineConfig & { ssr?: { external?: string[
|
||||||
|
|
||||||
interface CreateViteOptions {
|
interface CreateViteOptions {
|
||||||
astroConfig: AstroConfig;
|
astroConfig: AstroConfig;
|
||||||
devServer?: AstroDevServer;
|
|
||||||
logging: LogOptions;
|
logging: LogOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a common starting point for all Vite actions */
|
/** Return a common starting point for all Vite actions */
|
||||||
export async function createVite(inlineConfig: ViteConfigWithSSR, { astroConfig, logging, devServer }: CreateViteOptions): Promise<ViteConfigWithSSR> {
|
export async function createVite(inlineConfig: ViteConfigWithSSR, { astroConfig, logging }: CreateViteOptions): Promise<ViteConfigWithSSR> {
|
||||||
// First, start with the Vite configuration that Astro core needs
|
// First, start with the Vite configuration that Astro core needs
|
||||||
let viteConfig: ViteConfigWithSSR = {
|
let viteConfig: ViteConfigWithSSR = {
|
||||||
cacheDir: fileURLToPath(new URL('./node_modules/.vite/', astroConfig.projectRoot)), // using local caches allows Astro to be used in monorepos, etc.
|
cacheDir: fileURLToPath(new URL('./node_modules/.vite/', astroConfig.projectRoot)), // using local caches allows Astro to be used in monorepos, etc.
|
||||||
|
@ -50,10 +49,11 @@ export async function createVite(inlineConfig: ViteConfigWithSSR, { astroConfig,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
configAliasVitePlugin({ config: astroConfig }),
|
configAliasVitePlugin({ config: astroConfig }),
|
||||||
astroVitePlugin({ config: astroConfig, devServer, logging }),
|
astroVitePlugin({ config: astroConfig, logging }),
|
||||||
markdownVitePlugin({ config: astroConfig, devServer }),
|
astroViteServerPlugin({ config: astroConfig, logging }),
|
||||||
|
markdownVitePlugin({ config: astroConfig }),
|
||||||
jsxVitePlugin({ config: astroConfig, logging }),
|
jsxVitePlugin({ config: astroConfig, logging }),
|
||||||
astroPostprocessVitePlugin({ config: astroConfig, devServer }),
|
astroPostprocessVitePlugin({ config: astroConfig }),
|
||||||
],
|
],
|
||||||
publicDir: fileURLToPath(astroConfig.public),
|
publicDir: fileURLToPath(astroConfig.public),
|
||||||
root: fileURLToPath(astroConfig.projectRoot),
|
root: fileURLToPath(astroConfig.projectRoot),
|
||||||
|
|
|
@ -1,398 +1,41 @@
|
||||||
import type { NextFunction } from 'connect';
|
|
||||||
import type http from 'http';
|
|
||||||
import type { AstroConfig, ManifestData, RouteCache, RouteData } from '../../@types/astro';
|
|
||||||
import type { LogOptions } from '../logger';
|
|
||||||
import type { HmrContext, ModuleNode } from '../vite';
|
|
||||||
|
|
||||||
import path from 'path';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { promisify } from 'util';
|
|
||||||
import connect from 'connect';
|
|
||||||
import mime from 'mime';
|
|
||||||
import { polyfill } from '@astropub/webapi';
|
import { polyfill } from '@astropub/webapi';
|
||||||
|
import type { AddressInfo } from 'net';
|
||||||
import { performance } from 'perf_hooks';
|
import { performance } from 'perf_hooks';
|
||||||
import stripAnsi from 'strip-ansi';
|
import type { AstroConfig } from '../../@types/astro';
|
||||||
import vite from '../vite.js';
|
|
||||||
import { defaultLogOptions, error, info } from '../logger.js';
|
|
||||||
import { ssr } from '../ssr/index.js';
|
|
||||||
import { STYLE_EXTENSIONS } from '../ssr/css.js';
|
|
||||||
import { collectResources } from '../ssr/html.js';
|
|
||||||
import { createRouteManifest, matchRoute } from '../ssr/routing.js';
|
|
||||||
import { createVite } from '../create-vite.js';
|
import { createVite } from '../create-vite.js';
|
||||||
import * as msg from './messages.js';
|
import { defaultLogOptions, info, LogOptions } from '../logger.js';
|
||||||
import notFoundTemplate, { subpathNotUsedTemplate } from './template/4xx.js';
|
import vite from '../vite.js';
|
||||||
import serverErrorTemplate from './template/5xx.js';
|
import * as msg from '../messages.js';
|
||||||
|
|
||||||
export interface DevOptions {
|
export interface DevOptions {
|
||||||
logging: LogOptions;
|
logging: LogOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DevServer {
|
export interface DevServer {
|
||||||
hostname: string;
|
address: AddressInfo;
|
||||||
port: number;
|
|
||||||
server: connect.Server;
|
|
||||||
stop(): Promise<void>;
|
stop(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** `astro dev` */
|
/** `astro dev` */
|
||||||
export default async function dev(config: AstroConfig, options: DevOptions = { logging: defaultLogOptions }): Promise<DevServer> {
|
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
|
const devStart = performance.now();
|
||||||
|
// polyfill WebAPIs for Node.js runtime
|
||||||
polyfill(globalThis, {
|
polyfill(globalThis, {
|
||||||
exclude: 'window document',
|
exclude: 'window document',
|
||||||
});
|
});
|
||||||
|
// start the server
|
||||||
|
const viteUserConfig = vite.mergeConfig({ mode: 'development' }, config.vite || {});
|
||||||
|
const viteConfig = await createVite(viteUserConfig, { astroConfig: config, logging: options.logging });
|
||||||
|
const viteServer = await vite.createServer(viteConfig);
|
||||||
|
await viteServer.listen(config.devOptions.port);
|
||||||
|
const address = viteServer.httpServer!.address() as AddressInfo;
|
||||||
|
// Log to console
|
||||||
|
const site = config.buildOptions.site ? new URL(config.buildOptions.site) : undefined;
|
||||||
|
info(options.logging, 'astro', msg.devStart({ startupTime: performance.now() - devStart }));
|
||||||
|
info(options.logging, 'astro', msg.devHost({ address, site, https: !!viteUserConfig.server?.https }));
|
||||||
|
|
||||||
// start dev server
|
|
||||||
const server = new AstroDevServer(config, options);
|
|
||||||
await server.start();
|
|
||||||
|
|
||||||
// attempt shutdown
|
|
||||||
process.on('SIGTERM', () => server.stop());
|
|
||||||
return {
|
return {
|
||||||
hostname: server.hostname,
|
address,
|
||||||
port: server.port,
|
stop: () => viteServer.close(),
|
||||||
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: routePathname,
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,17 +2,18 @@
|
||||||
* Dev server messages (organized here to prevent clutter)
|
* Dev server messages (organized here to prevent clutter)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { AddressInfo } from 'net';
|
||||||
import { bold, dim, green, magenta, yellow } from 'kleur/colors';
|
import { bold, dim, green, magenta, yellow } from 'kleur/colors';
|
||||||
import { pad } from './util.js';
|
import { pad } from './dev/util.js';
|
||||||
|
|
||||||
/** Display */
|
/** Display */
|
||||||
export function req({ url, statusCode, reqTime }: { url: string; statusCode: number; reqTime: number }): string {
|
export function req({ url, statusCode, reqTime }: { url: string; statusCode: number; reqTime?: number }): string {
|
||||||
let color = dim;
|
let color = dim;
|
||||||
if (statusCode >= 500) color = magenta;
|
if (statusCode >= 500) color = magenta;
|
||||||
else if (statusCode >= 400) color = yellow;
|
else if (statusCode >= 400) color = yellow;
|
||||||
else if (statusCode >= 300) color = dim;
|
else if (statusCode >= 300) color = dim;
|
||||||
else if (statusCode >= 200) color = green;
|
else if (statusCode >= 200) color = green;
|
||||||
return `${color(statusCode)} ${pad(url, 40)} ${dim(Math.round(reqTime) + 'ms')}`;
|
return `${color(statusCode)} ${pad(url, 40)} ${reqTime ? dim(Math.round(reqTime) + 'ms') : ''}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Display */
|
/** Display */
|
||||||
|
@ -27,8 +28,10 @@ export function devStart({ startupTime }: { startupTime: number }): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Display dev server host */
|
/** Display dev server host */
|
||||||
export function devHost({ host }: { host: string }): string {
|
export function devHost({ address, https, site }: { address: AddressInfo; https: boolean; site: URL | undefined }): string {
|
||||||
return `Local: ${bold(magenta(host))}`;
|
const rootPath = site ? site.pathname : '/';
|
||||||
|
const displayUrl = `${https ? 'https' : 'http'}://${address.address}:${address.port}${rootPath}`;
|
||||||
|
return `Local: ${bold(magenta(displayUrl))}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Display port in use */
|
/** Display port in use */
|
|
@ -7,9 +7,9 @@ import { performance } from 'perf_hooks';
|
||||||
import send from 'send';
|
import send from 'send';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import * as msg from '../dev/messages.js';
|
import * as msg from '../messages.js';
|
||||||
import { error, info } from '../logger.js';
|
import { error, info } from '../logger.js';
|
||||||
import { subpathNotUsedTemplate, notFoundTemplate, default as template } from '../dev/template/4xx.js';
|
import { subpathNotUsedTemplate, notFoundTemplate, default as template } from '../../template/4xx.js';
|
||||||
import { appendForwardSlash, trimSlashes } from '../path.js';
|
import { appendForwardSlash, trimSlashes } from '../path.js';
|
||||||
|
|
||||||
interface PreviewOptions {
|
interface PreviewOptions {
|
||||||
|
@ -43,7 +43,7 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Base request URL. */
|
/** Base request URL. */
|
||||||
let baseURL = new URL(appendForwardSlash(config.buildOptions.site || ''), defaultOrigin);
|
let baseURL = new URL(config.buildOptions.site || '/', defaultOrigin);
|
||||||
|
|
||||||
// Create the preview server, send static files out of the `dist/` directory.
|
// Create the preview server, send static files out of the `dist/` directory.
|
||||||
const server = http.createServer((req, res) => {
|
const server = http.createServer((req, res) => {
|
||||||
|
@ -126,7 +126,7 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO
|
||||||
httpServer = server.listen(port, hostname, () => {
|
httpServer = server.listen(port, hostname, () => {
|
||||||
if (!showedListenMsg) {
|
if (!showedListenMsg) {
|
||||||
info(logging, 'astro', msg.devStart({ startupTime: performance.now() - timerStart }));
|
info(logging, 'astro', msg.devStart({ startupTime: performance.now() - timerStart }));
|
||||||
info(logging, 'astro', msg.devHost({ host: `http://${hostname}:${port}${baseURL.pathname}` }));
|
info(logging, 'astro', msg.devHost({ address: { family: 'ipv4', address: hostname, port }, https: false, site: baseURL }));
|
||||||
}
|
}
|
||||||
showedListenMsg = true;
|
showedListenMsg = true;
|
||||||
resolve();
|
resolve();
|
||||||
|
|
|
@ -48,6 +48,11 @@ export function parseNpmName(spec: string): { scope?: string; name: string; subp
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Coalesce any throw variable to an Error instance. */
|
||||||
|
export function createSafeError(err: any): Error {
|
||||||
|
return err instanceof Error || (err && err.name && err.message) ? err : new Error(JSON.stringify(err));
|
||||||
|
}
|
||||||
|
|
||||||
/** generate code frame from esbuild error */
|
/** generate code frame from esbuild error */
|
||||||
export function codeFrame(src: string, loc: ErrorPayload['err']['loc']): string {
|
export function codeFrame(src: string, loc: ErrorPayload['err']['loc']): string {
|
||||||
if (!loc) return '';
|
if (!loc) return '';
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
import type * as t from '@babel/types';
|
import type * as t from '@babel/types';
|
||||||
import type { Plugin } from '../core/vite';
|
import type { Plugin } from '../core/vite';
|
||||||
import type { AstroConfig } from '../@types/astro';
|
import type { AstroConfig } from '../@types/astro';
|
||||||
import type { AstroDevServer } from '../core/dev/index';
|
|
||||||
|
|
||||||
import * as babelTraverse from '@babel/traverse';
|
import * as babelTraverse from '@babel/traverse';
|
||||||
import * as babel from '@babel/core';
|
import * as babel from '@babel/core';
|
||||||
|
|
||||||
interface AstroPluginOptions {
|
interface AstroPluginOptions {
|
||||||
config: AstroConfig;
|
config: AstroConfig;
|
||||||
devServer?: AstroDevServer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// esbuild transforms the component-scoped Astro into Astro2, so need to check both.
|
// esbuild transforms the component-scoped Astro into Astro2, so need to check both.
|
||||||
const validAstroGlobalNames = new Set(['Astro', 'Astro2']);
|
const validAstroGlobalNames = new Set(['Astro', 'Astro2']);
|
||||||
|
|
||||||
export default function astro({ config, devServer }: AstroPluginOptions): Plugin {
|
export default function astro({ config }: AstroPluginOptions): Plugin {
|
||||||
return {
|
return {
|
||||||
name: '@astrojs/vite-plugin-astro-postprocess',
|
name: 'astro:postprocess',
|
||||||
async transform(code, id) {
|
async transform(code, id) {
|
||||||
// Currently only supported in ".astro" & ".md" files
|
// Currently only supported in ".astro" & ".md" files
|
||||||
if (!id.endsWith('.astro') && !id.endsWith('.md')) {
|
if (!id.endsWith('.astro') && !id.endsWith('.md')) {
|
||||||
|
|
156
packages/astro/src/vite-plugin-astro-server/index.ts
Normal file
156
packages/astro/src/vite-plugin-astro-server/index.ts
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
import type vite from '../core/vite';
|
||||||
|
import type http from 'http';
|
||||||
|
import type { AstroConfig, ManifestData, RouteCache, RouteData } from '../@types/astro';
|
||||||
|
import { info, LogOptions } from '../core/logger.js';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { createRouteManifest, matchRoute } from '../core/ssr/routing.js';
|
||||||
|
import mime from 'mime';
|
||||||
|
import stripAnsi from 'strip-ansi';
|
||||||
|
import { createSafeError } from '../core/util.js';
|
||||||
|
import { ssr } from '../core/ssr/index.js';
|
||||||
|
import * as msg from '../core/messages.js';
|
||||||
|
|
||||||
|
import notFoundTemplate, { subpathNotUsedTemplate } from '../template/4xx.js';
|
||||||
|
import serverErrorTemplate from '../template/5xx.js';
|
||||||
|
|
||||||
|
interface AstroPluginOptions {
|
||||||
|
config: AstroConfig;
|
||||||
|
logging: LogOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BAD_VITE_MIDDLEWARE = ['viteIndexHtmlMiddleware', 'vite404Middleware', 'viteSpaFallbackMiddleware'];
|
||||||
|
function removeViteHttpMiddleware(server: vite.Connect.Server) {
|
||||||
|
for (let i = server.stack.length - 1; i > 0; i--) {
|
||||||
|
// @ts-expect-error using internals until https://github.com/vitejs/vite/pull/4640 is merged
|
||||||
|
if (BAD_VITE_MIDDLEWARE.includes(server.stack[i].handle.name)) {
|
||||||
|
server.stack.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeHtmlResponse(res: http.ServerResponse, statusCode: number, html: string) {
|
||||||
|
res.writeHead(statusCode, {
|
||||||
|
'Content-Type': mime.getType('.html') as string,
|
||||||
|
'Content-Length': Buffer.byteLength(html, 'utf8'),
|
||||||
|
});
|
||||||
|
res.write(html);
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handle404Response(origin: string, config: AstroConfig, req: http.IncomingMessage, res: http.ServerResponse) {
|
||||||
|
const site = config.buildOptions.site ? new URL(config.buildOptions.site) : undefined;
|
||||||
|
const devRoot = site ? site.pathname : '/';
|
||||||
|
const pathname = decodeURI(new URL(origin + req.url).pathname);
|
||||||
|
let html = '';
|
||||||
|
if (pathname === '/' && !pathname.startsWith(devRoot)) {
|
||||||
|
html = subpathNotUsedTemplate(devRoot, pathname);
|
||||||
|
} else {
|
||||||
|
html = notFoundTemplate({ statusCode: 404, title: 'Not found', tabTitle: '404: Not Found', pathname });
|
||||||
|
}
|
||||||
|
writeHtmlResponse(res, 404, html);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handle500Response(viteServer: vite.ViteDevServer, origin: string, req: http.IncomingMessage, res: http.ServerResponse, err: any) {
|
||||||
|
const pathname = decodeURI(new URL(origin + req.url).pathname);
|
||||||
|
const html = serverErrorTemplate({
|
||||||
|
statusCode: 500,
|
||||||
|
title: 'Internal Error',
|
||||||
|
tabTitle: '500: Error',
|
||||||
|
message: stripAnsi(err.message),
|
||||||
|
url: err.url || undefined,
|
||||||
|
stack: stripAnsi(err.stack),
|
||||||
|
});
|
||||||
|
const transformedHtml = await viteServer.transformIndexHtml(pathname, html, pathname);
|
||||||
|
writeHtmlResponse(res, 500, transformedHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The main logic to route dev server requests to pages in Astro. */
|
||||||
|
async function handleRequest(
|
||||||
|
routeCache: RouteCache,
|
||||||
|
viteServer: vite.ViteDevServer,
|
||||||
|
logging: LogOptions,
|
||||||
|
manifest: ManifestData,
|
||||||
|
config: AstroConfig,
|
||||||
|
req: http.IncomingMessage,
|
||||||
|
res: http.ServerResponse
|
||||||
|
) {
|
||||||
|
const reqStart = performance.now();
|
||||||
|
const site = config.buildOptions.site ? new URL(config.buildOptions.site) : undefined;
|
||||||
|
const devRoot = site ? site.pathname : '/';
|
||||||
|
const origin = `${viteServer.config.server.https ? 'https' : 'http'}://${req.headers.host}`;
|
||||||
|
const pathname = decodeURI(new URL(origin + req.url).pathname);
|
||||||
|
const rootRelativeUrl = pathname.substring(devRoot.length - 1);
|
||||||
|
try {
|
||||||
|
if (!pathname.startsWith(devRoot)) {
|
||||||
|
info(logging, 'astro', msg.req({ url: pathname, statusCode: 404 }));
|
||||||
|
return handle404Response(origin, config, req, res);
|
||||||
|
}
|
||||||
|
// Attempt to match the URL to a valid page route.
|
||||||
|
// If that fails, switch the response to a 404 response.
|
||||||
|
let route = matchRoute(rootRelativeUrl, manifest);
|
||||||
|
const statusCode = route ? 200 : 404;
|
||||||
|
// If no match found, lookup a custom 404 page to render, if one exists.
|
||||||
|
if (!route) {
|
||||||
|
const relPages = config.pages.href.replace(config.projectRoot.href, '');
|
||||||
|
route = manifest.routes.find((r) => r.component === relPages + '404.astro');
|
||||||
|
}
|
||||||
|
// If still no match is found, respond with a generic 404 page.
|
||||||
|
if (!route) {
|
||||||
|
info(logging, 'astro', msg.req({ url: pathname, statusCode: 404 }));
|
||||||
|
handle404Response(origin, config, req, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Route successfully matched! Render it.
|
||||||
|
const html = await ssr({
|
||||||
|
astroConfig: config,
|
||||||
|
filePath: new URL(`./${route.component}`, config.projectRoot),
|
||||||
|
logging,
|
||||||
|
mode: 'development',
|
||||||
|
origin,
|
||||||
|
pathname: rootRelativeUrl,
|
||||||
|
route,
|
||||||
|
routeCache: routeCache,
|
||||||
|
viteServer: viteServer,
|
||||||
|
});
|
||||||
|
info(logging, 'astro', msg.req({ url: pathname, statusCode, reqTime: performance.now() - reqStart }));
|
||||||
|
writeHtmlResponse(res, statusCode, html);
|
||||||
|
} catch (_err: any) {
|
||||||
|
info(logging, 'astro', msg.req({ url: pathname, statusCode: 500 }));
|
||||||
|
const err = createSafeError(_err);
|
||||||
|
handle500Response(viteServer, origin, req, res, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function createPlugin({ config, logging }: AstroPluginOptions): vite.Plugin {
|
||||||
|
return {
|
||||||
|
name: 'astro:server',
|
||||||
|
configureServer(viteServer) {
|
||||||
|
const pagesDirectory = fileURLToPath(config.pages);
|
||||||
|
let routeCache: RouteCache = {};
|
||||||
|
let manifest: ManifestData = createRouteManifest({ config: config }, logging);
|
||||||
|
/** rebuild the route cache + manifest if the changed file impacts routing. */
|
||||||
|
function rebuildManifestIfNeeded(file: string) {
|
||||||
|
if (file.startsWith(pagesDirectory)) {
|
||||||
|
routeCache = {};
|
||||||
|
manifest = createRouteManifest({ config: config }, logging);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Rebuild route manifest on file change, if needed.
|
||||||
|
viteServer.watcher.on('add', rebuildManifestIfNeeded);
|
||||||
|
viteServer.watcher.on('unlink', rebuildManifestIfNeeded);
|
||||||
|
// No need to rebuild routes on content-only changes.
|
||||||
|
// However, we DO want to clear the cache in case
|
||||||
|
// the change caused a getStaticPaths() return to change.
|
||||||
|
viteServer.watcher.on('change', () => (routeCache = {}));
|
||||||
|
return () => {
|
||||||
|
removeViteHttpMiddleware(viteServer.middlewares);
|
||||||
|
viteServer.middlewares.use(async (req, res) => {
|
||||||
|
if (!req.url || !req.method) {
|
||||||
|
throw new Error('Incomplete request');
|
||||||
|
}
|
||||||
|
handleRequest(routeCache, viteServer, logging, manifest, config, req, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
import type vite from '../core/vite';
|
import type vite from '../core/vite';
|
||||||
import type { AstroConfig } from '../@types/astro';
|
import type { AstroConfig } from '../@types/astro';
|
||||||
import type { LogOptions } from '../core/logger';
|
import type { LogOptions } from '../core/logger.js';
|
||||||
|
|
||||||
import esbuild from 'esbuild';
|
import esbuild from 'esbuild';
|
||||||
import npath from 'path';
|
import npath from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { AstroDevServer } from '../core/dev/index.js';
|
|
||||||
import { getViteTransform, TransformHook } from './styles.js';
|
import { getViteTransform, TransformHook } from './styles.js';
|
||||||
import { parseAstroRequest } from './query.js';
|
import { parseAstroRequest } from './query.js';
|
||||||
import { cachedCompilation, invalidateCompilation } from './compile.js';
|
import { cachedCompilation, invalidateCompilation } from './compile.js';
|
||||||
|
@ -15,7 +14,6 @@ const FRONTMATTER_PARSE_REGEXP = /^\-\-\-(.*)^\-\-\-/ms;
|
||||||
interface AstroPluginOptions {
|
interface AstroPluginOptions {
|
||||||
config: AstroConfig;
|
config: AstroConfig;
|
||||||
logging: LogOptions;
|
logging: LogOptions;
|
||||||
devServer?: AstroDevServer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Transform .astro files for Vite */
|
/** Transform .astro files for Vite */
|
||||||
|
@ -36,7 +34,7 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu
|
||||||
const isBrowserPath = (path: string) => path.startsWith(srcRootWeb);
|
const isBrowserPath = (path: string) => path.startsWith(srcRootWeb);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: '@astrojs/vite-plugin-astro',
|
name: 'astro:build',
|
||||||
enforce: 'pre', // run transforms before other plugins can
|
enforce: 'pre', // run transforms before other plugins can
|
||||||
configResolved(resolvedConfig) {
|
configResolved(resolvedConfig) {
|
||||||
viteTransform = getViteTransform(resolvedConfig);
|
viteTransform = getViteTransform(resolvedConfig);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { AstroConfig, RouteCache } from '../@types/astro';
|
import type { AstroConfig, RouteCache } from '../@types/astro';
|
||||||
import type { LogOptions } from '../core/logger';
|
import type { LogOptions } from '../core/logger.js';
|
||||||
import type { ViteDevServer, Plugin as VitePlugin } from '../core/vite';
|
import type { ViteDevServer, Plugin as VitePlugin } from '../core/vite';
|
||||||
import type { OutputChunk, PreRenderedChunk } from 'rollup';
|
import type { OutputChunk, PreRenderedChunk } from 'rollup';
|
||||||
import type { AllPagesData } from '../core/build/types';
|
import type { AllPagesData } from '../core/build/types';
|
||||||
|
|
|
@ -78,7 +78,7 @@ export default function configAliasVitePlugin(astroConfig: { projectRoot?: URL;
|
||||||
if (!configAlias) return {} as vite.PluginOption;
|
if (!configAlias) return {} as vite.PluginOption;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: '@astrojs/vite-plugin-config-alias',
|
name: 'astro:tsconfig-alias',
|
||||||
enforce: 'pre',
|
enforce: 'pre',
|
||||||
async resolveId(sourceId: string, importer, options) {
|
async resolveId(sourceId: string, importer, options) {
|
||||||
/** Resolved ID conditionally handled by any other resolver. (this gives priority to all other resolvers) */
|
/** Resolved ID conditionally handled by any other resolver. (this gives priority to all other resolvers) */
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { TransformResult } from 'rollup';
|
import type { TransformResult } from 'rollup';
|
||||||
import type { Plugin, ResolvedConfig } from '../core/vite';
|
import type { Plugin, ResolvedConfig } from '../core/vite';
|
||||||
import type { AstroConfig, Renderer } from '../@types/astro';
|
import type { AstroConfig, Renderer } from '../@types/astro';
|
||||||
import type { LogOptions } from '../core/logger';
|
import type { LogOptions } from '../core/logger.js';
|
||||||
|
|
||||||
import babel from '@babel/core';
|
import babel from '@babel/core';
|
||||||
import esbuild from 'esbuild';
|
import esbuild from 'esbuild';
|
||||||
|
@ -98,7 +98,7 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin
|
||||||
let viteConfig: ResolvedConfig;
|
let viteConfig: ResolvedConfig;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: '@astrojs/vite-plugin-jsx',
|
name: 'astro:jsx',
|
||||||
enforce: 'pre', // run transforms before other plugins
|
enforce: 'pre', // run transforms before other plugins
|
||||||
configResolved(resolvedConfig) {
|
configResolved(resolvedConfig) {
|
||||||
viteConfig = resolvedConfig;
|
viteConfig = resolvedConfig;
|
||||||
|
|
|
@ -4,17 +4,15 @@ import type { AstroConfig } from '../@types/astro';
|
||||||
import esbuild from 'esbuild';
|
import esbuild from 'esbuild';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { transform } from '@astrojs/compiler';
|
import { transform } from '@astrojs/compiler';
|
||||||
import { AstroDevServer } from '../core/dev/index.js';
|
|
||||||
|
|
||||||
interface AstroPluginOptions {
|
interface AstroPluginOptions {
|
||||||
config: AstroConfig;
|
config: AstroConfig;
|
||||||
devServer?: AstroDevServer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Transform .astro files for Vite */
|
/** Transform .astro files for Vite */
|
||||||
export default function markdown({ config }: AstroPluginOptions): Plugin {
|
export default function markdown({ config }: AstroPluginOptions): Plugin {
|
||||||
return {
|
return {
|
||||||
name: '@astrojs/vite-plugin-markdown',
|
name: 'astro:markdown',
|
||||||
enforce: 'pre', // run transforms before other plugins can
|
enforce: 'pre', // run transforms before other plugins can
|
||||||
async load(id) {
|
async load(id) {
|
||||||
if (id.endsWith('.md')) {
|
if (id.endsWith('.md')) {
|
||||||
|
|
|
@ -73,10 +73,10 @@ export async function loadFixture(inlineConfig) {
|
||||||
return {
|
return {
|
||||||
build: (opts = {}) => build(config, { mode: 'development', logging: 'error', ...opts }),
|
build: (opts = {}) => build(config, { mode: 'development', logging: 'error', ...opts }),
|
||||||
startDevServer: async (opts = {}) => {
|
startDevServer: async (opts = {}) => {
|
||||||
const devServer = await dev(config, { logging: 'error', ...opts });
|
const devResult = await dev(config, { logging: 'error', ...opts });
|
||||||
config.devOptions.port = devServer.port; // update port
|
config.devOptions.port = devResult.address.port; // update port
|
||||||
inlineConfig.devOptions.port = devServer.port;
|
inlineConfig.devOptions.port = devResult.address.port;
|
||||||
return devServer;
|
return devResult;
|
||||||
},
|
},
|
||||||
config,
|
config,
|
||||||
fetch: (url, init) => fetch(`http://${config.devOptions.hostname}:${config.devOptions.port}${url.replace(/^\/?/, '/')}`, init),
|
fetch: (url, init) => fetch(`http://${config.devOptions.hostname}:${config.devOptions.port}${url.replace(/^\/?/, '/')}`, init),
|
||||||
|
|
35
yarn.lock
35
yarn.lock
|
@ -3185,16 +3185,6 @@ condense-whitespace@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/condense-whitespace/-/condense-whitespace-1.0.0.tgz#8376d98ef028e6cb2cd2468e28ce42c5c65ab1a9"
|
resolved "https://registry.yarnpkg.com/condense-whitespace/-/condense-whitespace-1.0.0.tgz#8376d98ef028e6cb2cd2468e28ce42c5c65ab1a9"
|
||||||
integrity sha1-g3bZjvAo5sss0kaOKM5CxcZasak=
|
integrity sha1-g3bZjvAo5sss0kaOKM5CxcZasak=
|
||||||
|
|
||||||
connect@^3.7.0:
|
|
||||||
version "3.7.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8"
|
|
||||||
integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==
|
|
||||||
dependencies:
|
|
||||||
debug "2.6.9"
|
|
||||||
finalhandler "1.1.2"
|
|
||||||
parseurl "~1.3.3"
|
|
||||||
utils-merge "1.0.1"
|
|
||||||
|
|
||||||
consola@^2.15.3:
|
consola@^2.15.3:
|
||||||
version "2.15.3"
|
version "2.15.3"
|
||||||
resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550"
|
resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550"
|
||||||
|
@ -4479,19 +4469,6 @@ fill-range@^7.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
to-regex-range "^5.0.1"
|
to-regex-range "^5.0.1"
|
||||||
|
|
||||||
finalhandler@1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
|
|
||||||
integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
|
|
||||||
dependencies:
|
|
||||||
debug "2.6.9"
|
|
||||||
encodeurl "~1.0.2"
|
|
||||||
escape-html "~1.0.3"
|
|
||||||
on-finished "~2.3.0"
|
|
||||||
parseurl "~1.3.3"
|
|
||||||
statuses "~1.5.0"
|
|
||||||
unpipe "~1.0.0"
|
|
||||||
|
|
||||||
find-babel-config@^1.2.0:
|
find-babel-config@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.0.tgz#a9b7b317eb5b9860cda9d54740a8c8337a2283a2"
|
resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.0.tgz#a9b7b317eb5b9860cda9d54740a8c8337a2283a2"
|
||||||
|
@ -7168,11 +7145,6 @@ parse5@^6.0.0, parse5@^6.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
||||||
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
||||||
|
|
||||||
parseurl@~1.3.3:
|
|
||||||
version "1.3.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
|
||||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
|
||||||
|
|
||||||
path-browserify@^1.0.1:
|
path-browserify@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
|
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
|
||||||
|
@ -9329,7 +9301,7 @@ unocss@^0.15.5:
|
||||||
"@unocss/reset" "0.15.6"
|
"@unocss/reset" "0.15.6"
|
||||||
"@unocss/vite" "0.15.6"
|
"@unocss/vite" "0.15.6"
|
||||||
|
|
||||||
unpipe@1.0.0, unpipe@~1.0.0:
|
unpipe@1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||||
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
|
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
|
||||||
|
@ -9387,11 +9359,6 @@ util@^0.12.0:
|
||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
which-typed-array "^1.1.2"
|
which-typed-array "^1.1.2"
|
||||||
|
|
||||||
utils-merge@1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
|
||||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
|
||||||
|
|
||||||
uuid@^2.0.1:
|
uuid@^2.0.1:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
|
||||||
|
|
Loading…
Reference in a new issue