summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Drew Powers <1369770+drwpow@users.noreply.github.com> 2021-03-25 16:59:38 -0600
committerGravatar GitHub <noreply@github.com> 2021-03-25 16:59:38 -0600
commit04a443a8887257e86d824bb22686a527f84c0875 (patch)
treeb64a7cd8fa56e4c549a1b60afa63e611779eda53 /src
parent3db595937719b89956c594e4a76ee68ae8de098a (diff)
downloadastro-04a443a8887257e86d824bb22686a527f84c0875.tar.gz
astro-04a443a8887257e86d824bb22686a527f84c0875.tar.zst
astro-04a443a8887257e86d824bb22686a527f84c0875.zip
Add React component SSR (#28)
* Add React component SSR * Add React component SSR
Diffstat (limited to 'src')
-rw-r--r--src/compiler/codegen.ts29
-rw-r--r--src/compiler/index.ts12
-rw-r--r--src/runtime.ts17
3 files changed, 32 insertions, 26 deletions
diff --git a/src/compiler/codegen.ts b/src/compiler/codegen.ts
index 58c7a6c9d..c0d963703 100644
--- a/src/compiler/codegen.ts
+++ b/src/compiler/codegen.ts
@@ -10,7 +10,7 @@ import { walk } from 'estree-walker';
import babelParser from '@babel/parser';
import _babelGenerator from '@babel/generator';
import traverse from '@babel/traverse';
-import { ImportDeclaration,ExportNamedDeclaration, VariableDeclarator, Identifier, VariableDeclaration } from '@babel/types';
+import { ImportDeclaration, ExportNamedDeclaration, VariableDeclarator, Identifier, VariableDeclaration } from '@babel/types';
import { type } from 'node:os';
const babelGenerator: typeof _babelGenerator =
@@ -111,10 +111,7 @@ const defaultExtensions: Readonly<Record<string, ValidExtensionPlugins>> = {
'.svelte': 'svelte',
};
-type DynamicImportMap = Map<
- 'vue' | 'react' | 'react-dom' | 'preact',
- string
->;
+type DynamicImportMap = Map<'vue' | 'react' | 'react-dom' | 'preact', string>;
function getComponentWrapper(_name: string, { type, plugin, url }: ComponentInfo, dynamicImports: DynamicImportMap) {
const [name, kind] = _name.split(':');
@@ -136,7 +133,9 @@ function getComponentWrapper(_name: string, { type, plugin, url }: ComponentInfo
case 'preact': {
if (kind === 'dynamic') {
return {
- wrapper: `__preact_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${dynamicImports.get('preact')!}')`,
+ wrapper: `__preact_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${dynamicImports.get(
+ 'preact'
+ )!}')`,
wrapperImport: `import {__preact_dynamic} from '${internalImport('render/preact.js')}';`,
};
} else {
@@ -177,7 +176,9 @@ function getComponentWrapper(_name: string, { type, plugin, url }: ComponentInfo
case 'vue': {
if (kind === 'dynamic') {
return {
- wrapper: `__vue_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.vue.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${dynamicImports.get('vue')!}')`,
+ wrapper: `__vue_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.vue.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${dynamicImports.get(
+ 'vue'
+ )!}')`,
wrapperImport: `import {__vue_dynamic} from '${internalImport('render/vue.js')}';`,
};
} else {
@@ -207,8 +208,8 @@ function compileExpressionSafe(raw: string): string {
async function acquireDynamicComponentImports(plugins: Set<ValidExtensionPlugins>, resolve: (s: string) => Promise<string>): Promise<DynamicImportMap> {
const importMap: DynamicImportMap = new Map();
- for(let plugin of plugins) {
- switch(plugin) {
+ for (let plugin of plugins) {
+ switch (plugin) {
case 'vue': {
importMap.set('vue', await resolve('vue'));
break;
@@ -236,9 +237,9 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
const componentExports: ExportNamedDeclaration[] = [];
let script = '';
- let propsStatement: string = '';
+ let propsStatement = '';
const importExportStatements: Set<string> = new Set();
- const components: Record<string, { type: string; url: string, plugin: string | undefined }> = {};
+ const components: Record<string, { type: string; url: string; plugin: string | undefined }> = {};
const componentPlugins = new Set<ValidExtensionPlugins>();
if (ast.module) {
@@ -277,9 +278,9 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
components[componentName] = {
type: componentType,
plugin,
- url: importUrl
+ url: importUrl,
};
- if(plugin) {
+ if (plugin) {
componentPlugins.add(plugin);
}
importExportStatements.add(ast.module.content.slice(componentImport.start!, componentImport.end!));
@@ -293,7 +294,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
for (const componentExport of componentProps) {
propsStatement += `${(componentExport.id as Identifier).name}`;
if (componentExport.init) {
- propsStatement += `= ${babelGenerator(componentExport.init!).code }`;
+ propsStatement += `= ${babelGenerator(componentExport.init!).code}`;
}
propsStatement += `,`;
}
diff --git a/src/compiler/index.ts b/src/compiler/index.ts
index fea6b8a29..945d9bfc5 100644
--- a/src/compiler/index.ts
+++ b/src/compiler/index.ts
@@ -105,12 +105,11 @@ async function transformFromSource(
}
}
-
export async function compileComponent(
source: string,
{ compileOptions = defaultCompileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string }
): Promise<CompileResult> {
- const sourceJsx = await transformFromSource(source, { compileOptions, filename, projectRoot });
+ const sourceJsx = await transformFromSource(source, { compileOptions, filename, projectRoot });
const isPage = path.extname(filename) === '.md' || sourceJsx.items.some((item) => item.name === 'html');
// sort <style> tags first
sourceJsx.items.sort((a, b) => (a.name === 'style' && b.name !== 'style' ? -1 : 0));
@@ -124,9 +123,9 @@ ${sourceJsx.imports.join('\n')}
// \`__render()\`: Render the contents of the Astro module.
import { h, Fragment } from '${internalImport('h.js')}';
-async function __render(props, ...children) {
+async function __render(props, ...children) {
${sourceJsx.script}
- return h(Fragment, null, ${sourceJsx.items.map(({ jsx }) => jsx).join(',')});
+ return h(Fragment, null, ${sourceJsx.items.map(({ jsx }) => jsx).join(',')});
}
export default __render;
`;
@@ -134,9 +133,8 @@ export default __render;
if (isPage) {
modJsx += `
// \`__renderPage()\`: Render the contents of the Astro module as a page. This is a special flow,
-// triggered by loading a component directly by URL.
+// triggered by loading a component directly by URL.
export async function __renderPage({request, children, props}) {
-
const currentChild = {
setup: typeof setup === 'undefined' ? (passthrough) => passthrough : setup,
layout: typeof __layout === 'undefined' ? undefined : __layout,
@@ -155,7 +153,7 @@ export async function __renderPage({request, children, props}) {
props: {content: currentChild.content},
children: [childBodyResult],
});
- }
+ }
return childBodyResult;
};\n`;
diff --git a/src/runtime.ts b/src/runtime.ts
index 5dd391fdd..c8431fdb0 100644
--- a/src/runtime.ts
+++ b/src/runtime.ts
@@ -63,7 +63,7 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
try {
const mod = await snowpackRuntime.importModule(selectedPageUrl);
- const html = (await mod.exports.__renderPage({
+ let html = (await mod.exports.__renderPage({
request: {
host: fullurl.hostname,
path: fullurl.pathname,
@@ -73,6 +73,15 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
props: {},
})) as string;
+ // inject styles
+ // TODO: handle this in compiler
+ const styleTags = Array.isArray(mod.css) && mod.css.length ? mod.css.reduce((markup, url) => `${markup}\n<link rel="stylesheet" type="text/css" href="${url}" />`, '') : ``;
+ if (html.indexOf('</head>') !== -1) {
+ html = html.replace('</head>', `${styleTags}</head>`);
+ } else {
+ html = styleTags + html;
+ }
+
return {
statusCode: 200,
contents: html,
@@ -99,7 +108,7 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
interface RuntimeOptions {
logging: LogOptions;
- env: 'dev' | 'build'
+ env: 'dev' | 'build';
}
export async function createRuntime(astroConfig: AstroConfig, { env, logging }: RuntimeOptions) {
@@ -113,9 +122,7 @@ export async function createRuntime(astroConfig: AstroConfig, { env, logging }:
extensions?: Record<string, string>;
} = {
extensions,
- resolve: env === 'dev' ?
- async (pkgName: string) => snowpack.getUrlForPackage(pkgName) :
- async (pkgName: string) => `/_snowpack/pkg/${pkgName}.js`
+ resolve: env === 'dev' ? async (pkgName: string) => snowpack.getUrlForPackage(pkgName) : async (pkgName: string) => `/_snowpack/pkg/${pkgName}.js`,
};
/*if (existsSync(new URL('./package-lock.json', projectRoot))) {
const pkgLockStr = await readFile(new URL('./package-lock.json', projectRoot), 'utf-8');