comment style fixes (#1614)

This commit is contained in:
Fred K. Schott 2021-10-20 22:09:35 -07:00 committed by Drew Powers
parent 90b9c6e086
commit 5da14ca8ed
14 changed files with 75 additions and 151 deletions

View file

@ -12,7 +12,6 @@ module.exports = {
'@typescript-eslint/no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-use-before-define': 'off', '@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/no-var-requires': 'off',
'multiline-comment-style': ['warn', 'starred-block'],
'no-console': 'warn', 'no-console': 'warn',
'no-shadow': 'error', 'no-shadow': 'error',
'prefer-const': 'off', 'prefer-const': 'off',

View file

@ -106,6 +106,7 @@ export interface AstroUserConfig {
* export interface AstroUserConfig extends z.input<typeof AstroConfigSchema> { * export interface AstroUserConfig extends z.input<typeof AstroConfigSchema> {
* } * }
*/ */
export type AstroConfig = z.output<typeof AstroConfigSchema>; export type AstroConfig = z.output<typeof AstroConfigSchema>;
export type AsyncRendererComponentFn<U> = (Component: any, props: any, children: string | undefined, metadata?: AstroComponentMetadata) => Promise<U>; export type AsyncRendererComponentFn<U> = (Component: any, props: any, children: string | undefined, metadata?: AstroComponentMetadata) => Promise<U>;

View file

@ -51,16 +51,9 @@ class AstroBuilder {
this.manifest = createRouteManifest({ config }); this.manifest = createRouteManifest({ config });
} }
/** Build all pages */
async build() { async build() {
const timer: Record<string, number> = {}; // keep track of performance timers
/*
* Setup
* Create the Vite server with production build settings
*/
timer.viteStart = performance.now();
const { logging, origin } = this; const { logging, origin } = this;
const timer: Record<string, number> = {viteStart: performance.now()};
const viteConfig = await createVite( const viteConfig = await createVite(
{ {
mode: this.mode, mode: this.mode,
@ -76,25 +69,22 @@ class AstroBuilder {
this.viteServer = viteServer; this.viteServer = viteServer;
debug(logging, 'build', timerMessage('Vite started', timer.viteStart)); debug(logging, 'build', timerMessage('Vite started', timer.viteStart));
/*
* Render pages
* Convert .astro -> .html
*/
timer.renderStart = performance.now(); timer.renderStart = performance.now();
const assets: Record<string, string> = {}; // additional assets to be written const assets: Record<string, string> = {};
const allPages: Record<string, RouteData & { paths: string[] }> = {}; const allPages: Record<string, RouteData & { paths: string[] }> = {};
// Collect all routes ahead-of-time, before we start the build.
// pre-render: determine all possible routes from dynamic pages // NOTE: This enforces that `getStaticPaths()` is only called once per route,
// and is then cached across all future SSR builds. In the past, we've had trouble
// with parallelized builds without guaranteeing that this is called first.
await Promise.all( await Promise.all(
this.manifest.routes.map(async (route) => { this.manifest.routes.map(async (route) => {
// static route // static route:
if (route.pathname) { if (route.pathname) {
allPages[route.component] = { ...route, paths: [route.pathname] }; allPages[route.component] = { ...route, paths: [route.pathname] };
return; return;
} }
// dynamic route // dynamic route:
const result = await this.getStaticPathsForRoute(route); const result = await this.getStaticPathsForRoute(route);
// handle RSS while generating routes
if (result.rss?.xml) { if (result.rss?.xml) {
const rssFile = new URL(result.rss.url.replace(/^\/?/, './'), this.config.dist); const rssFile = new URL(result.rss.url.replace(/^\/?/, './'), this.config.dist);
if (assets[fileURLToPath(rssFile)]) { if (assets[fileURLToPath(rssFile)]) {
@ -106,7 +96,10 @@ class AstroBuilder {
}) })
); );
// render: convert Astro to HTML // After all routes have been collected, start building them.
// TODO: test parallel vs. serial performance. Promise.all() may be
// making debugging harder without any perf gain. If parallel is best,
// then we should set a max number of parallel builds.
const input: InputHTMLOptions[] = []; const input: InputHTMLOptions[] = [];
await Promise.all( await Promise.all(
Object.entries(allPages).map(([component, route]) => Object.entries(allPages).map(([component, route]) =>
@ -132,10 +125,8 @@ class AstroBuilder {
); );
debug(logging, 'build', timerMessage('All pages rendered', timer.renderStart)); debug(logging, 'build', timerMessage('All pages rendered', timer.renderStart));
/* // Bundle the assets in your final build: This currently takes the HTML output
* Production Build // of every page (stored in memory) and bundles the assets pointed to on those pages.
* Use Vites build process to generate files
*/
timer.buildStart = performance.now(); timer.buildStart = performance.now();
await vite.build({ await vite.build({
logLevel: 'error', logLevel: 'error',
@ -160,13 +151,7 @@ class AstroBuilder {
}); });
debug(logging, 'build', timerMessage('Vite build finished', timer.buildStart)); debug(logging, 'build', timerMessage('Vite build finished', timer.buildStart));
/* // Write any additionally generated assets to disk.
* Post-build files
* Write other files to disk Vite may not know about.
* TODO: is there a way to handle these as part of the previous step?
*/
// RSS
timer.assetsStart = performance.now(); timer.assetsStart = performance.now();
Object.keys(assets).map((k) => { Object.keys(assets).map((k) => {
if (!assets[k]) return; if (!assets[k]) return;
@ -177,9 +162,10 @@ class AstroBuilder {
}); });
debug(logging, 'build', timerMessage('Additional assets copied', timer.assetsStart)); debug(logging, 'build', timerMessage('Additional assets copied', timer.assetsStart));
// Sitemap // Build your final sitemap.
timer.sitemapStart = performance.now(); timer.sitemapStart = performance.now();
if (this.config.buildOptions.sitemap && this.config.buildOptions.site) { if (this.config.buildOptions.sitemap && this.config.buildOptions.site) {
const sitemapStart = performance.now();
const sitemap = generateSitemap(input.map(({ name }) => new URL(`/${name}`, this.config.buildOptions.site).href)); const sitemap = generateSitemap(input.map(({ name }) => new URL(`/${name}`, this.config.buildOptions.site).href));
const sitemapPath = new URL('./sitemap.xml', this.config.dist); const sitemapPath = new URL('./sitemap.xml', this.config.dist);
await fs.promises.mkdir(new URL('./', sitemapPath), { recursive: true }); await fs.promises.mkdir(new URL('./', sitemapPath), { recursive: true });
@ -187,10 +173,7 @@ class AstroBuilder {
} }
debug(logging, 'build', timerMessage('Sitemap built', timer.sitemapStart)); debug(logging, 'build', timerMessage('Sitemap built', timer.sitemapStart));
/* // You're done! Time to clean up.
* Clean up
* Close the Vite server instance, and print stats
*/
await viteServer.close(); await viteServer.close();
if (logging.level && levels[logging.level] <= levels['info']) { if (logging.level && levels[logging.level] <= levels['info']) {
await this.printStats({ cwd: this.config.dist, pageCount: input.length }); await this.printStats({ cwd: this.config.dist, pageCount: input.length });

View file

@ -72,10 +72,8 @@ export const AstroConfigSchema = z.object({
/** Turn raw config values into normalized values */ /** Turn raw config values into normalized values */
export async function validateConfig(userConfig: any, root: string): Promise<AstroConfig> { export async function validateConfig(userConfig: any, root: string): Promise<AstroConfig> {
const fileProtocolRoot = pathToFileURL(root + path.sep); const fileProtocolRoot = pathToFileURL(root + path.sep);
/* // We need to extend the global schema to add transforms that are relative to root.
* We need to extend the global schema to add transforms that are relative to root. // This is type checked against the global schema to make sure we still match.
* This is type checked against the global schema to make sure we still match.
*/
const AstroConfigRelativeSchema = AstroConfigSchema.extend({ const AstroConfigRelativeSchema = AstroConfigSchema.extend({
projectRoot: z projectRoot: z
.string() .string()

View file

@ -5,6 +5,7 @@ import type { LogOptions } from '../logger';
import type { HmrContext, ModuleNode } from '../vite'; import type { HmrContext, ModuleNode } from '../vite';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import {promisify} from 'util';
import connect from 'connect'; import connect from 'connect';
import mime from 'mime'; import mime from 'mime';
import { performance } from 'perf_hooks'; import { performance } from 'perf_hooks';
@ -68,24 +69,16 @@ export class AstroDevServer {
this.manifest = createRouteManifest({ config }); this.manifest = createRouteManifest({ config });
} }
/** Start dev server */
async start() { async start() {
/* const devStart = performance.now();
* Setup
* Create the Vite serer in dev mode
*/
const devStart = performance.now(); // profile startup time
this.viteServer = await this.createViteServer();
// middlewares // Setup the dev server and connect it to Vite (via middleware)
this.viteServer = await this.createViteServer();
this.app.use((req, res, next) => this.handleRequest(req, res, next)); this.app.use((req, res, next) => this.handleRequest(req, res, next));
this.app.use(this.viteServer.middlewares); this.app.use(this.viteServer.middlewares);
this.app.use((req, res, next) => this.renderError(req, res, next)); this.app.use((req, res, next) => this.renderError(req, res, next));
/* // Listen on port (and retry if taken)
* Listen
* Start external connect server and listen on configured port
*/
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const onError = (err: NodeJS.ErrnoException) => { const onError = (err: NodeJS.ErrnoException) => {
if (err.code && err.code === 'EADDRINUSE') { if (err.code && err.code === 'EADDRINUSE') {
@ -97,7 +90,6 @@ export class AstroDevServer {
reject(err); reject(err);
} }
}; };
this.httpServer = this.app.listen(this.port, this.hostname, () => { this.httpServer = this.app.listen(this.port, this.hostname, () => {
info(this.logging, 'astro', msg.devStart({ startupTime: performance.now() - devStart })); 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}` }));
@ -107,13 +99,15 @@ export class AstroDevServer {
}); });
} }
/** Stop dev server */
async stop() { async stop() {
this.httpServer?.close(); // close HTTP server if (this.viteServer) {
if (this.viteServer) await this.viteServer.close(); // close Vite server await this.viteServer.close();
}
if (this.httpServer) {
await promisify(this.httpServer.close)();
}
} }
/** Handle HMR */
public async handleHotUpdate({ file, modules }: HmrContext): Promise<void | ModuleNode[]> { public async handleHotUpdate({ file, modules }: HmrContext): Promise<void | ModuleNode[]> {
if (!this.viteServer) throw new Error(`AstroDevServer.start() not called`); if (!this.viteServer) throw new Error(`AstroDevServer.start() not called`);
@ -164,7 +158,6 @@ export class AstroDevServer {
} }
} }
/** Set up Vite server */
private async createViteServer() { private async createViteServer() {
const viteConfig = await createVite( const viteConfig = await createVite(
{ {
@ -197,11 +190,9 @@ export class AstroDevServer {
this.manifest = createRouteManifest({ config: this.config }); this.manifest = createRouteManifest({ config: this.config });
}); });
viteServer.watcher.on('change', () => { viteServer.watcher.on('change', () => {
/* // No need to rebuild routes on file content changes.
* No need to rebuild routes on file content changes. // However, we DO want to clear the cache in case
* However, we DO want to clear the cache in case // the change caused a getStaticPaths() return to change.
* the change caused a getStaticPaths() return to change.
*/
this.routeCache = {}; this.routeCache = {};
}); });

View file

@ -13,10 +13,8 @@ function getLoggerLocale(): string {
const defaultLocale = 'en-US'; const defaultLocale = 'en-US';
if (process.env.LANG) { if (process.env.LANG) {
const extractedLocale = process.env.LANG.split('.')[0].replace(/_/g, '-'); const extractedLocale = process.env.LANG.split('.')[0].replace(/_/g, '-');
/* // Check if language code is atleast two characters long (ie. en, es).
* Check if language code is at least two characters long (ie. en, es). // NOTE: if "c" locale is encountered, the default locale will be returned.
* NOTE: if "c" locale is encountered, the default locale will be returned.
*/
if (extractedLocale.length < 2) return defaultLocale; if (extractedLocale.length < 2) return defaultLocale;
else return extractedLocale; else return extractedLocale;
} else return defaultLocale; } else return defaultLocale;

View file

@ -38,11 +38,9 @@ const cache = new Map<string, Promise<Renderer>>();
// TODO: improve validation and error handling here. // TODO: improve validation and error handling here.
async function resolveRenderer(viteServer: ViteDevServer, renderer: string) { async function resolveRenderer(viteServer: ViteDevServer, renderer: string) {
const resolvedRenderer: any = {}; const resolvedRenderer: any = {};
/* // We can dynamically import the renderer by itself because it shouldn't have
* We can dynamically import the renderer by itself because it shouldn't have // any non-standard imports, the index is just meta info.
* any non-standard imports, the index is just meta info. // The other entrypoints need to be loaded through Vite.
* The other entrypoints need to be loaded through Vite.
*/
const { const {
default: { name, client, polyfills, hydrationPolyfills, server }, default: { name, client, polyfills, hydrationPolyfills, server },
} = await import(renderer); } = await import(renderer);
@ -75,20 +73,11 @@ async function resolveRenderers(viteServer: ViteDevServer, ids: string[]): Promi
/** use Vite to SSR */ /** use Vite to SSR */
export async function ssr({ astroConfig, filePath, logging, mode, origin, pathname, route, routeCache, viteServer }: SSROptions): Promise<string> { export async function ssr({ astroConfig, filePath, logging, mode, origin, pathname, route, routeCache, viteServer }: SSROptions): Promise<string> {
try { try {
/* // Important: This needs to happen first, in case a renderer provides polyfills.
* Renderers
* Load all renderers to be used for SSR
*/
// Important this happens before load module in case a renderer provides polyfills.
const renderers = await resolveRenderers(viteServer, astroConfig.renderers); const renderers = await resolveRenderers(viteServer, astroConfig.renderers);
// Load the module from the Vite SSR Runtime.
/*
* Pre-render
* Load module through Vite and do pre-render work like dynamic routing
*/
const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance; const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
// Handle dynamic routes
// handle dynamic routes
let params: Params = {}; let params: Params = {};
let pageProps: Props = {}; let pageProps: Props = {};
if (route && !route.pathname) { if (route && !route.pathname) {
@ -118,15 +107,14 @@ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathna
pageProps = { ...matchedStaticPath.props } || {}; pageProps = { ...matchedStaticPath.props } || {};
} }
/* // Validate the page component before rendering the page
* Render
* Convert .astro to .html
*/
const Component = await mod.default; const Component = await mod.default;
if (!Component) throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`); if (!Component) throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`);
if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`); if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`);
// Create the result object that will be passed into the render function.
// This object starts here as an empty shell (not yet the result) but then
// calling the render() function will populate the object with scripts, styles, etc.
const result: SSRResult = { const result: SSRResult = {
styles: new Set(), styles: new Set(),
scripts: new Set(), scripts: new Set(),
@ -135,7 +123,6 @@ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathna
const site = new URL(origin); const site = new URL(origin);
const url = new URL('.' + pathname, site); const url = new URL('.' + pathname, site);
const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin); const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin);
return { return {
__proto__: astroGlobal, __proto__: astroGlobal,
props, props,
@ -154,15 +141,10 @@ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathna
}; };
let html = await renderPage(result, Component, pageProps, null); let html = await renderPage(result, Component, pageProps, null);
// run transformIndexHtml() in development to add HMR client to the page.
/*
* Dev Server
* Apply Vite HMR, Astro HMR, and other dev-only transformations needed from Vite plugins.
*/
if (mode === 'development') { if (mode === 'development') {
html = await viteServer.transformIndexHtml(fileURLToPath(filePath), html, pathname); html = await viteServer.transformIndexHtml(fileURLToPath(filePath), html, pathname);
} }
return html; return html;
} catch (e: any) { } catch (e: any) {
viteServer.ssrFixStacktrace(e); viteServer.ssrFixStacktrace(e);
@ -183,7 +165,7 @@ ${frame}
throw err; throw err;
} }
// Vite error (already formatted) // Generic error (probably from Vite, and already formatted)
throw e; throw e;
} }
} }

View file

@ -49,22 +49,18 @@ export function parseNpmName(spec: string): { scope?: string; name: string; subp
/** 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 '';
const lines = src.replace(/\r\n/g, '\n').split('\n'); const lines = src.replace(/\r\n/g, '\n').split('\n');
// grab 2 lines before, and 3 lines after focused line // grab 2 lines before, and 3 lines after focused line
const visibleLines = []; const visibleLines = [];
for (let n = -2; n <= 2; n++) { for (let n = -2; n <= 2; n++) {
if (lines[loc.line + n]) visibleLines.push(loc.line + n); if (lines[loc.line + n]) visibleLines.push(loc.line + n);
} }
// figure out gutter width // figure out gutter width
let gutterWidth = 0; let gutterWidth = 0;
for (const lineNo of visibleLines) { for (const lineNo of visibleLines) {
let w = `> ${lineNo}`; let w = `> ${lineNo}`;
if (w.length > gutterWidth) gutterWidth = w.length; if (w.length > gutterWidth) gutterWidth = w.length;
} }
// print lines // print lines
let output = ''; let output = '';
for (const lineNo of visibleLines) { for (const lineNo of visibleLines) {

View file

@ -10,18 +10,14 @@ export { createMetadata } from './metadata.js';
const { generate, GENERATOR } = astring; const { generate, GENERATOR } = astring;
/* // A more robust version alternative to `JSON.stringify` that can handle most values
* A more robust version alternative to `JSON.stringify` that can handle most values // see https://github.com/remcohaszing/estree-util-value-to-estree#readme
* See https://github.com/remcohaszing/estree-util-value-to-estree#readme
*/
const customGenerator: astring.Generator = { const customGenerator: astring.Generator = {
...GENERATOR, ...GENERATOR,
Literal(node, state) { Literal(node, state) {
if (node.raw != null) { if (node.raw != null) {
/* // escape closing script tags in strings so browsers wouldn't interpret them as
* escape closing script tags in strings so browsers wouldn't interpret them as // closing the actual end tag in HTML
* closing the actual end tag in HTML
*/
state.write(node.raw.replace('</script>', '<\\/script>')); state.write(node.raw.replace('</script>', '<\\/script>'));
} else { } else {
GENERATOR.Literal(node, state); GENERATOR.Literal(node, state);
@ -39,11 +35,9 @@ async function _render(child: any): Promise<any> {
if (Array.isArray(child)) { if (Array.isArray(child)) {
return (await Promise.all(child.map((value) => _render(value)))).join('\n'); return (await Promise.all(child.map((value) => _render(value)))).join('\n');
} else if (typeof child === 'function') { } else if (typeof child === 'function') {
/* // Special: If a child is a function, call it automatically.
* Special: If a child is a function, call it automatically. // This lets you do {() => ...} without the extra boilerplate
* This lets you do {() => ...} without the extra boilerplate // of wrapping it in a function and calling it.
* of wrapping it in a function and calling it.
*/
return _render(child()); return _render(child());
} else if (typeof child === 'string') { } else if (typeof child === 'string') {
return child; return child;
@ -267,7 +261,7 @@ function createFetchContentFn(url: URL) {
...mod.frontmatter, ...mod.frontmatter,
content: mod.metadata, content: mod.metadata,
file: new URL(spec, url), file: new URL(spec, url),
url: urlSpec.includes('/pages/') && urlSpec.replace(/^.*\/pages\//, '/').replace(/\.md$/, ''), url: urlSpec.includes('/pages/') && urlSpec.replace(/^.*\/pages\//, '/').replace(/\.md$/, '')
}; };
}) })
.filter(Boolean); .filter(Boolean);

View file

@ -23,10 +23,8 @@ export default function astro({ config, devServer }: AstroPluginOptions): Plugin
return null; return null;
} }
/* // Optimization: only run on a probably match
* Optimization: only run on a probably match // Open this up if need for post-pass extends past fetchContent
* Open this up if need for post-pass extends past fetchContent
*/
if (!code.includes('fetchContent')) { if (!code.includes('fetchContent')) {
return null; return null;
} }

View file

@ -28,14 +28,16 @@ export default function astro({ config, devServer }: AstroPluginOptions): Plugin
let tsResult: TransformResult | undefined; let tsResult: TransformResult | undefined;
try { try {
// `.astro` -> `.ts` // Transform from `.astro` to valid `.ts`
// use `sourcemap: "both"` so that sourcemap is included in the code
// result passed to esbuild, but also available in the catch handler.
tsResult = await transform(source, { tsResult = await transform(source, {
site: config.buildOptions.site, site: config.buildOptions.site,
sourcefile: id, sourcefile: id,
sourcemap: 'both', sourcemap: 'both',
internalURL: 'astro/internal', internalURL: 'astro/internal',
}); });
// `.ts` -> `.js` // Compile `.ts` to `.js`
const { code, map } = await esbuild.transform(tsResult.code, { loader: 'ts', sourcemap: 'external', sourcefile: id }); const { code, map } = await esbuild.transform(tsResult.code, { loader: 'ts', sourcemap: 'external', sourcefile: id });
return { return {

View file

@ -15,10 +15,8 @@ function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {
return false; return false;
} }
/* // This matches any JS-like file (that we know of)
* This matches any JS-like file (that we know of) // See https://regex101.com/r/Cgofir/1
* See https://regex101.com/r/Cgofir/1
*/
const SUPPORTED_FILES = /\.(astro|svelte|vue|[cm]?js|jsx|[cm]?ts|tsx)$/; const SUPPORTED_FILES = /\.(astro|svelte|vue|[cm]?js|jsx|[cm]?ts|tsx)$/;
const DEFINE_FETCH = `import fetch from 'node-fetch';\n`; const DEFINE_FETCH = `import fetch from 'node-fetch';\n`;
@ -28,17 +26,14 @@ export default function pluginFetch(): Plugin {
enforce: 'post', enforce: 'post',
async transform(code, id, opts) { async transform(code, id, opts) {
const ssr = isSSR(opts); const ssr = isSSR(opts);
// If this isn't an SSR pass, `fetch` will already be available! // If this isn't an SSR pass, `fetch` will already be available!
if (!ssr) { if (!ssr) {
return null; return null;
} }
// Only transform JS-like files // Only transform JS-like files
if (!id.match(SUPPORTED_FILES)) { if (!id.match(SUPPORTED_FILES)) {
return null; return null;
} }
// Optimization: only run on probable matches // Optimization: only run on probable matches
if (!code.includes('fetch')) { if (!code.includes('fetch')) {
return null; return null;
@ -46,14 +41,11 @@ export default function pluginFetch(): Plugin {
const s = new MagicString(code); const s = new MagicString(code);
s.prepend(DEFINE_FETCH); s.prepend(DEFINE_FETCH);
const result = s.toString(); const result = s.toString();
const map = s.generateMap({ const map = s.generateMap({
source: id, source: id,
includeContent: true, includeContent: true,
}); });
return { code: result, map }; return { code: result, map };
}, },
}; };

View file

@ -18,10 +18,8 @@ const IMPORT_STATEMENTS: Record<string, string> = {
preact: "import { h } from 'preact'", preact: "import { h } from 'preact'",
'solid-js': "import 'solid-js/web'", 'solid-js': "import 'solid-js/web'",
}; };
/* // The `tsx` loader in esbuild will remove unused imports, so we need to
* The `tsx` loader in esbuild will remove unused imports, so we need to // be careful about esbuild not treating h, React, Fragment, etc. as unused.
* be careful about esbuild not treating h, React, Fragment, etc. as unused.
*/
const PREVENT_UNUSED_IMPORTS = ';;(React,Fragment,h);'; const PREVENT_UNUSED_IMPORTS = ';;(React,Fragment,h);';
interface AstroPluginJSXOptions { interface AstroPluginJSXOptions {
@ -55,23 +53,15 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin
} }
} }
/* // Attempt: Single JSX renderer
* Single JSX renderer // If we only have one renderer, we can skip a bunch of work!
* If we only have one renderer, we can skip a bunch of work!
*/
if (JSX_RENDERERS.size === 1) { if (JSX_RENDERERS.size === 1) {
return transformJSX({ code, id, renderer: [...JSX_RENDERERS.values()][0], ssr: ssr || false }); return transformJSX({ code, id, renderer: [...JSX_RENDERERS.values()][0], ssr: ssr || false });
} }
/* // Attempt: Multiple JSX renderers
* Multiple JSX renderers // Determine for each .jsx or .tsx file what it wants to use to Render
* Determine for each .jsx or .tsx file what it wants to use to Render
*/
// we need valid JS here, so we can use `h` and `Fragment` as placeholders // we need valid JS here, so we can use `h` and `Fragment` as placeholders
// try and guess renderer from imports (file cant import React and Preact)
// NOTE(fks, matthewp): Make sure that you're transforming the original contents here. // NOTE(fks, matthewp): Make sure that you're transforming the original contents here.
const { code: codeToScan } = await esbuild.transform(code + PREVENT_UNUSED_IMPORTS, { const { code: codeToScan } = await esbuild.transform(code + PREVENT_UNUSED_IMPORTS, {
loader: getLoader(path.extname(id)), loader: getLoader(path.extname(id)),

View file

@ -20,7 +20,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
if (id.endsWith('.md')) { if (id.endsWith('.md')) {
let source = await fs.promises.readFile(id, 'utf8'); let source = await fs.promises.readFile(id, 'utf8');
// `.md` -> `.astro` // Transform from `.md` to valid `.astro`
let render = config.markdownOptions.render; let render = config.markdownOptions.render;
let renderOpts = {}; let renderOpts = {};
if (Array.isArray(render)) { if (Array.isArray(render)) {
@ -46,14 +46,14 @@ ${setup}
astroResult = `${prelude}\n${astroResult}`; astroResult = `${prelude}\n${astroResult}`;
} }
// `.astro` -> `.ts` // Transform from `.astro` to valid `.ts`
let { code: tsResult } = await transform(astroResult, { sourcefile: id, sourcemap: 'inline', internalURL: 'astro/internal' }); let { code: tsResult } = await transform(astroResult, { sourcefile: id, sourcemap: 'inline', internalURL: 'astro/internal' });
tsResult = `\nexport const metadata = ${JSON.stringify(metadata)}; tsResult = `\nexport const metadata = ${JSON.stringify(metadata)};
export const frontmatter = ${JSON.stringify(content)}; export const frontmatter = ${JSON.stringify(content)};
${tsResult}`; ${tsResult}`;
// `.ts` -> `.js` // Compile from `.ts` to `.js`
const { code, map } = await esbuild.transform(tsResult, { loader: 'ts', sourcemap: 'inline', sourcefile: id }); const { code, map } = await esbuild.transform(tsResult, { loader: 'ts', sourcemap: 'inline', sourcefile: id });
return { return {