summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Fred K. Schott <fkschott@gmail.com> 2021-03-24 16:01:28 -0700
committerGravatar GitHub <noreply@github.com> 2021-03-24 16:01:28 -0700
commita72ab10c623022860691d6a095b74dea70cc6f69 (patch)
tree0d94c752ae0d63027ec91a1fbf745a93745be6ee /src
parent3c24faa8cab428b17ba2f8e083f5296b1b931fe1 (diff)
downloadastro-a72ab10c623022860691d6a095b74dea70cc6f69.tar.gz
astro-a72ab10c623022860691d6a095b74dea70cc6f69.tar.zst
astro-a72ab10c623022860691d6a095b74dea70cc6f69.zip
Redesign pages, remove layout nesting (#24)
* wip * new svelte-style prop declaration is working * got it working! * revert h changes * format * style lang update
Diffstat (limited to 'src')
-rw-r--r--src/@types/astro.ts4
-rw-r--r--src/codegen/index.ts18
-rw-r--r--src/dev.ts2
-rw-r--r--src/frontend/render/react.ts8
-rw-r--r--src/micromark-encode.ts2
-rw-r--r--src/runtime.ts8
-rw-r--r--src/transform2.ts91
7 files changed, 54 insertions, 79 deletions
diff --git a/src/@types/astro.ts b/src/@types/astro.ts
index 9ec2aec53..8a92983f8 100644
--- a/src/@types/astro.ts
+++ b/src/@types/astro.ts
@@ -11,7 +11,7 @@ export interface AstroConfig {
dist: string;
projectRoot: URL;
astroRoot: URL;
- extensions?: Record<string, ValidExtensionPlugins>
+ extensions?: Record<string, ValidExtensionPlugins>;
}
export interface JsxItem {
@@ -21,7 +21,7 @@ export interface JsxItem {
export interface TransformResult {
script: string;
- head: JsxItem | undefined;
+ props: string[];
items: JsxItem[];
}
diff --git a/src/codegen/index.ts b/src/codegen/index.ts
index d248b2a60..2eb289887 100644
--- a/src/codegen/index.ts
+++ b/src/codegen/index.ts
@@ -99,20 +99,17 @@ const defaultExtensions: Readonly<Record<string, ValidExtensionPlugins>> = {
'.astro': 'astro',
'.jsx': 'react',
'.vue': 'vue',
- '.svelte': 'svelte'
+ '.svelte': 'svelte',
};
function getComponentWrapper(_name: string, { type, url }: ComponentInfo, compileOptions: CompileOptions) {
- const {
- resolve,
- extensions = defaultExtensions
- } = compileOptions;
+ const { resolve, extensions = defaultExtensions } = compileOptions;
const [name, kind] = _name.split(':');
const plugin = extensions[type] || defaultExtensions[type];
- if(!plugin) {
+ if (!plugin) {
throw new Error(`No supported plugin found for extension ${type}`);
}
@@ -142,7 +139,9 @@ function getComponentWrapper(_name: string, { type, url }: ComponentInfo, compil
case 'react': {
if (kind === 'dynamic') {
return {
- wrapper: `__react_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve('react')}', '${resolve('react-dom')}')`,
+ wrapper: `__react_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve(
+ 'react'
+ )}', '${resolve('react-dom')}')`,
wrapperImport: `import {__react_dynamic} from '${internalImport('render/react.js')}';`,
};
} else {
@@ -215,6 +214,9 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
// Compile scripts as TypeScript, always
const script = compileScriptSafe(ast.module ? ast.module.content : '');
+ // Collect all exported variables for props
+ const scannedExports = eslexer.parse(script)[1].filter((n) => n !== 'setup' && n !== 'layout');
+
// Todo: Validate that `h` and `Fragment` aren't defined in the script
const [scriptImports] = eslexer.parse(script, 'optional-sourcename');
const components = Object.fromEntries(
@@ -380,7 +382,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
return {
script: script + '\n' + Array.from(additionalImports).join('\n'),
- head: headItem,
items,
+ props: scannedExports,
};
}
diff --git a/src/dev.ts b/src/dev.ts
index 5c80133a7..efa7f1f6c 100644
--- a/src/dev.ts
+++ b/src/dev.ts
@@ -72,4 +72,4 @@ export default async function (astroConfig: AstroConfig) {
function formatErrorForBrowser(error: Error) {
// TODO make this pretty.
return error.toString();
-} \ No newline at end of file
+}
diff --git a/src/frontend/render/react.ts b/src/frontend/render/react.ts
index d55d30c00..cd037c35f 100644
--- a/src/frontend/render/react.ts
+++ b/src/frontend/render/react.ts
@@ -3,13 +3,7 @@ import ReactDOMServer from 'react-dom/server';
export function __react_static(ReactComponent: any) {
return (attrs: Record<string, any>, ...children: any): string => {
- let html = ReactDOMServer.renderToString(
- React.createElement(
- ReactComponent,
- attrs,
- children
- )
- );
+ let html = ReactDOMServer.renderToString(React.createElement(ReactComponent, attrs, children));
return html;
};
}
diff --git a/src/micromark-encode.ts b/src/micromark-encode.ts
index d205d13e3..e3b328224 100644
--- a/src/micromark-encode.ts
+++ b/src/micromark-encode.ts
@@ -32,4 +32,4 @@ const plugin: HtmlExtension = {
},
};
-export { plugin as encodeMarkdown }; \ No newline at end of file
+export { plugin as encodeMarkdown };
diff --git a/src/runtime.ts b/src/runtime.ts
index 889224548..aff5ee7d2 100644
--- a/src/runtime.ts
+++ b/src/runtime.ts
@@ -69,6 +69,7 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
href: fullurl.toString(),
},
children: [],
+ props: {},
})) as string;
return {
@@ -103,7 +104,7 @@ export async function createRuntime(astroConfig: AstroConfig, logging: LogOption
// Workaround for SKY-251
const astroPlugOptions: {
resolve?: (s: string) => string;
- extensions?: Record<string, string>
+ extensions?: Record<string, string>;
} = { extensions };
if (existsSync(new URL('./package-lock.json', projectRoot))) {
const pkgLockStr = await readFile(new URL('./package-lock.json', projectRoot), 'utf-8');
@@ -128,10 +129,7 @@ export async function createRuntime(astroConfig: AstroConfig, logging: LogOption
},
packageOptions: {
knownEntrypoints: ['preact-render-to-string'],
- external: [
- '@vue/server-renderer',
- 'node-fetch'
- ],
+ external: ['@vue/server-renderer', 'node-fetch'],
},
});
const snowpack = await startSnowpackServer({
diff --git a/src/transform2.ts b/src/transform2.ts
index 27956fb9f..47c3659e7 100644
--- a/src/transform2.ts
+++ b/src/transform2.ts
@@ -52,7 +52,7 @@ async function convertMdToJsx(
contents: string,
{ compileOptions, filename, fileID }: { compileOptions: CompileOptions; filename: string; fileID: string }
): Promise<TransformResult> {
- const { data: _frontmatterData, content } = matter(contents);
+ const { data: frontmatterData, content } = matter(contents);
const { headers, headersExtension } = createMarkdownHeadersCollector();
const mdHtml = micromark(content, {
allowDangerousHtml: true,
@@ -60,31 +60,27 @@ async function convertMdToJsx(
htmlExtensions: [gfmHtml, encodeMarkdown, headersExtension],
});
- const setupContext = {
- ..._frontmatterData,
- content: {
- frontmatter: _frontmatterData,
- headers,
- source: content,
- html: mdHtml,
- },
+ // TODO: Warn if reserved word is used in "frontmatterData"
+ const contentData: any = {
+ ...frontmatterData,
+ headers,
+ source: content,
+ html: mdHtml,
};
let imports = '';
- for(let [ComponentName, specifier] of Object.entries(_frontmatterData.import || {})) {
+ for (let [ComponentName, specifier] of Object.entries(frontmatterData.import || {})) {
imports += `import ${ComponentName} from '${specifier}';\n`;
}
// </script> can't be anywhere inside of a JS string, otherwise the HTML parser fails.
// Break it up here so that the HTML parser won't detect it.
- const stringifiedSetupContext = JSON.stringify(setupContext).replace(/\<\/script\>/g, `</scrip" + "t>`);
+ const stringifiedSetupContext = JSON.stringify(contentData).replace(/\<\/script\>/g, `</scrip" + "t>`);
- const raw = `---
+ const raw = `---
${imports}
- ${_frontmatterData.layout ? `export const layout = ${JSON.stringify(_frontmatterData.layout)};` : ''}
- export function setup({context}) {
- return {context: ${stringifiedSetupContext} };
- }
+ ${frontmatterData.layout ? `const __layout = ${JSON.stringify(frontmatterData.layout)};` : ''}
+ const __content = ${stringifiedSetupContext};
---
<section>${mdHtml}</section>`;
@@ -115,11 +111,10 @@ export async function compileComponent(
{ compileOptions = defaultCompileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string }
): Promise<CompileResult> {
const sourceJsx = await transformFromSource(source, { compileOptions, filename, projectRoot });
- const headItem = sourceJsx.head;
- const headItemJsx = !headItem ? 'null' : headItem.jsx;
// sort <style> tags first
// TODO: remove these and inject in <head>
+ const isPage = path.extname(filename) === '.md' || sourceJsx.items.some((item) => item.name === 'html');
sourceJsx.items.sort((a, b) => (a.name === 'style' && b.name !== 'style' ? -1 : 0));
// return template
@@ -127,61 +122,47 @@ export async function compileComponent(
// <script astro></script>
${sourceJsx.script}
-// \`__render()\`: Render the contents of the Astro module. "<slot:*>" elements are not
-// included (see below).
+// \`__render()\`: Render the contents of the Astro module.
import { h, Fragment } from '${internalImport('h.js')}';
-export function __slothead(children, context) { return h(Fragment, null, ${headItemJsx}); }
-function __render(props, children, context) { return h(Fragment, null, ${sourceJsx.items.map(({ jsx }) => jsx).join(',')}); }
+function __render(props, ...children) {
+ ${sourceJsx.props.map((p) => `${p} = props.${p} ?? ${p};`).join('\n')}
+ return h(Fragment, null, ${sourceJsx.items.map(({ jsx }) => jsx).join(',')});
+}
export default __render;
`;
- if (headItemJsx) {
+ 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.
-// If the page exports a defined "layout", then load + render those first. "context", "astro:head",
-// and "slot:body" should all inherit from parent layouts, merging together in the correct order.
-export async function __renderPage({request, children}) {
+export async function __renderPage({request, children, props}) {
+
const currentChild = {
- __slothead,
- __render,
setup: typeof setup === 'undefined' ? (passthrough) => passthrough : setup,
- layout: typeof layout === 'undefined' ? undefined : layout,
+ layout: typeof __layout === 'undefined' ? undefined : __layout,
+ content: typeof __content === 'undefined' ? undefined : __content,
+ __render,
};
- // find all layouts, going up the layout chain.
+ const fetch = (await import('node-fetch')).default;
+ await currentChild.setup({request, fetch});
+ const childBodyResult = await currentChild.__render(props, children);
+
+ // find layout, if one was given.
if (currentChild.layout) {
- const layoutComponent = (await import('/_astro/layouts/' + layout.replace(/.*layouts\\//, "").replace(/\.astro$/, '.js')));
+ const layoutComponent = (await import('/_astro/layouts/' + currentChild.layout.replace(/.*layouts\\//, "").replace(/\.astro$/, '.js')));
return layoutComponent.__renderPage({
request,
- children: [currentChild, ...children],
+ props: {content: currentChild.content},
+ children: [childBodyResult],
});
}
-
- const isRoot = true;
- const merge = (await import('deepmerge')).default;
- const fetch = (await import('node-fetch')).default;
- // call all children setup scripts, in order, and return.
- let mergedContext = {};
- for (const child of [currentChild, ...children]) {
- const childSetupResult = await child.setup({request, fetch, context: mergedContext});
- mergedContext = childSetupResult.context ? merge(mergedContext, childSetupResult.context) : mergedContext;
- }
-
- Object.freeze(mergedContext);
-
- let headResult;
- let bodyResult;
- for (const child of children.reverse()) {
- headResult = await child.__slothead([headResult], mergedContext);
- bodyResult = await child.__render(undefined, [bodyResult], mergedContext);
- }
- return h(Fragment, null, [
- h("head", null, currentChild.__slothead([headResult], mergedContext)),
- h("body", null, currentChild.__render(undefined, [bodyResult], mergedContext)),
- ]);
+ return childBodyResult;
};\n`;
+ } else {
+ modJsx += `
+export async function __renderPage() { throw new Error("No <html> page element found!"); }\n`;
}
return {