summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorGravatar Matthew Phillips <matthew@matthewphillips.info> 2021-04-09 14:09:13 -0400
committerGravatar GitHub <noreply@github.com> 2021-04-09 14:09:13 -0400
commitad9c3b1d8dbf1c3aff75497271347ed36ea38a0b (patch)
tree8e0aed5ea1783df8322e1db589e84f9579152ba3 /src/compiler
parent084845f79d064626d5f5069ce7b945e3b44bdbd7 (diff)
downloadastro-ad9c3b1d8dbf1c3aff75497271347ed36ea38a0b.tar.gz
astro-ad9c3b1d8dbf1c3aff75497271347ed36ea38a0b.tar.zst
astro-ad9c3b1d8dbf1c3aff75497271347ed36ea38a0b.zip
Parse inner JSX as Astro (#67)
* Parse inner JSX as Astro This completes the compiler changes, updating the parser so that it parses inner "JSX" as Astro. It does this by finding the start and end of HTML tags and feeds that back into the parser. The result is a structure like this: ``` { type: 'MustacheTag', expression: [ { type: 'Expression', codeStart: 'colors.map(color => (', codeEnd: '}}' children: [ { type: 'Fragment', children: [ { type: 'Element', name: 'div' } ] } ] } ] } ``` There is a new Node type, `Expression`. Note that `MustacheTag` remains in the tree, all it contains is an Expression though. I could spend some time trying to remove it, there's just a few places that expect it to exist. * Update import to the transform * Transform prism components into expressions
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/codegen.ts67
-rw-r--r--src/compiler/index.ts8
-rw-r--r--src/compiler/transform/doctype.ts (renamed from src/compiler/optimize/doctype.ts)6
-rw-r--r--src/compiler/transform/index.ts (renamed from src/compiler/optimize/index.ts)36
-rw-r--r--src/compiler/transform/module-scripts.ts (renamed from src/compiler/optimize/module-scripts.ts)6
-rw-r--r--src/compiler/transform/postcss-scoped-styles/index.ts (renamed from src/compiler/optimize/postcss-scoped-styles/index.ts)0
-rw-r--r--src/compiler/transform/prism.ts (renamed from src/compiler/optimize/prism.ts)11
-rw-r--r--src/compiler/transform/styles.ts (renamed from src/compiler/optimize/styles.ts)10
8 files changed, 72 insertions, 72 deletions
diff --git a/src/compiler/codegen.ts b/src/compiler/codegen.ts
index 59cc2c702..8bcf3f49d 100644
--- a/src/compiler/codegen.ts
+++ b/src/compiler/codegen.ts
@@ -78,9 +78,10 @@ function getAttributes(attrs: Attribute[]): Record<string, string> {
continue;
}
switch (val.type) {
- case 'MustacheTag':
- result[attr.name] = '(' + val.content + ')';
+ case 'MustacheTag': {
+ result[attr.name] = '(' + val.expression.codeStart + ')';
continue;
+ }
case 'Text':
result[attr.name] = JSON.stringify(getTextFromAttribute(val));
continue;
@@ -93,13 +94,21 @@ function getAttributes(attrs: Attribute[]): Record<string, string> {
/** Get value from a TemplateNode Attribute (text attributes only!) */
function getTextFromAttribute(attr: any): string {
- if (attr.raw !== undefined) {
- return attr.raw;
- }
- if (attr.data !== undefined) {
- return attr.data;
+ switch(attr.type) {
+ case 'Text': {
+ if (attr.raw !== undefined) {
+ return attr.raw;
+ }
+ if (attr.data !== undefined) {
+ return attr.data;
+ }
+ break;
+ }
+ case 'MustacheTag': {
+ return attr.expression.codeStart;
+ }
}
- throw new Error('UNKNOWN attr');
+ throw new Error(`Unknown attribute type ${attr.type}`);
}
/** Convert TemplateNode attributes to string */
@@ -238,7 +247,7 @@ function getComponentWrapper(_name: string, { type, plugin, url }: ComponentInfo
}
}
-/** Evaluate mustache expression (safely) */
+/** Evaluate expression (safely) */
function compileExpressionSafe(raw: string): string {
let { code } = transformSync(raw, {
loader: 'tsx',
@@ -468,33 +477,19 @@ function compileHtml(enterNode: TemplateNode, state: CodegenState, compileOption
walk(enterNode, {
enter(node: TemplateNode) {
switch (node.type) {
- case 'MustacheTag':
- let code = compileExpressionSafe(node.content);
-
- let matches: RegExpExecArray[] = [];
- let match: RegExpExecArray | null | undefined;
- const H_COMPONENT_SCANNER = /h\(['"]?([A-Z].*?)['"]?,/gs;
- const regex = new RegExp(H_COMPONENT_SCANNER);
- while ((match = regex.exec(code))) {
- matches.push(match);
- }
- for (const astroComponent of matches.reverse()) {
- const name = astroComponent[1];
- const [componentName, componentKind] = name.split(':');
- if (!components[componentName]) {
- throw new Error(`Unknown Component: ${componentName}`);
- }
- const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], { astroConfig, dynamicImports, filename });
- if (wrapperImport) {
- importExportStatements.add(wrapperImport);
- }
- if (wrapper !== name) {
- code = code.slice(0, astroComponent.index + 2) + wrapper + code.slice(astroComponent.index + astroComponent[0].length - 1);
- }
+ case 'Expression': {
+ let child = '';
+ if(node.children!.length) {
+ child = compileHtml(node.children![0], state, compileOptions);
}
- outSource += `,(${code.trim().replace(/\;$/, '')})`;
+ let raw = node.codeStart + child + node.codeEnd;
+ // TODO Do we need to compile this now, or should we compile the entire module at the end?
+ let code = compileExpressionSafe(raw).trim().replace(/\;$/, '');
+ outSource += `,(${code})`;
this.skip();
- return;
+ break;
+ }
+ case 'MustacheTag':
case 'Comment':
return;
case 'Fragment':
@@ -557,11 +552,11 @@ function compileHtml(enterNode: TemplateNode, state: CodegenState, compileOption
leave(node, parent, prop, index) {
switch (node.type) {
case 'Text':
- case 'MustacheTag':
case 'Attribute':
case 'Comment':
- return;
case 'Fragment':
+ case 'Expression':
+ case 'MustacheTag':
return;
case 'Slot':
case 'Head':
diff --git a/src/compiler/index.ts b/src/compiler/index.ts
index d33527b9b..db50abec8 100644
--- a/src/compiler/index.ts
+++ b/src/compiler/index.ts
@@ -11,7 +11,7 @@ import { parse } from '../parser/index.js';
import { createMarkdownHeadersCollector } from './markdown/micromark-collect-headers.js';
import { encodeMarkdown } from './markdown/micromark-encode.js';
import { encodeAstroMdx } from './markdown/micromark-mdx-astro.js';
-import { optimize } from './optimize/index.js';
+import { transform } from './transform/index.js';
import { codegen } from './codegen.js';
/** Return Astro internal import URL */
@@ -29,7 +29,7 @@ interface ConvertAstroOptions {
* .astro -> .jsx
* Core function processing .astro files. Initiates all 3 phases of compilation:
* 1. Parse
- * 2. Optimize
+ * 2. Transform
* 3. Codegen
*/
async function convertAstroToJsx(template: string, opts: ConvertAstroOptions): Promise<TransformResult> {
@@ -40,8 +40,8 @@ async function convertAstroToJsx(template: string, opts: ConvertAstroOptions): P
filename,
});
- // 2. Optimize the AST
- await optimize(ast, opts);
+ // 2. Transform the AST
+ await transform(ast, opts);
// 3. Turn AST into JSX
return await codegen(ast, opts);
diff --git a/src/compiler/optimize/doctype.ts b/src/compiler/transform/doctype.ts
index 176880c08..d19b01f81 100644
--- a/src/compiler/optimize/doctype.ts
+++ b/src/compiler/transform/doctype.ts
@@ -1,7 +1,7 @@
-import { Optimizer } from '../../@types/optimizer';
+import { Transformer } from '../../@types/transformer';
-/** Optimize <!doctype> tg */
-export default function (_opts: { filename: string; fileID: string }): Optimizer {
+/** Transform <!doctype> tg */
+export default function (_opts: { filename: string; fileID: string }): Transformer {
let hasDoctype = false;
return {
diff --git a/src/compiler/optimize/index.ts b/src/compiler/transform/index.ts
index fcbd6e950..6a81b92b0 100644
--- a/src/compiler/optimize/index.ts
+++ b/src/compiler/transform/index.ts
@@ -1,13 +1,13 @@
import type { Ast, TemplateNode } from '../../parser/interfaces';
-import type { NodeVisitor, OptimizeOptions, Optimizer, VisitorFn } from '../../@types/optimizer';
+import type { NodeVisitor, TransformOptions, Transformer, VisitorFn } from '../../@types/transformer';
import { walk } from 'estree-walker';
-// Optimizers
-import optimizeStyles from './styles.js';
-import optimizeDoctype from './doctype.js';
-import optimizeModuleScripts from './module-scripts.js';
-import optimizeCodeBlocks from './prism.js';
+// Transformers
+import transformStyles from './styles.js';
+import transformDoctype from './doctype.js';
+import transformModuleScripts from './module-scripts.js';
+import transformCodeBlocks from './prism.js';
interface VisitorCollection {
enter: Map<string, VisitorFn[]>;
@@ -24,23 +24,23 @@ function addVisitor(visitor: NodeVisitor, collection: VisitorCollection, nodeNam
collection[event].set(nodeName, visitors);
}
-/** Compile visitor actions from optimizer */
-function collectVisitors(optimizer: Optimizer, htmlVisitors: VisitorCollection, cssVisitors: VisitorCollection, finalizers: Array<() => Promise<void>>) {
- if (optimizer.visitors) {
- if (optimizer.visitors.html) {
- for (const [nodeName, visitor] of Object.entries(optimizer.visitors.html)) {
+/** Compile visitor actions from transformer */
+function collectVisitors(transformer: Transformer, htmlVisitors: VisitorCollection, cssVisitors: VisitorCollection, finalizers: Array<() => Promise<void>>) {
+ if (transformer.visitors) {
+ if (transformer.visitors.html) {
+ for (const [nodeName, visitor] of Object.entries(transformer.visitors.html)) {
addVisitor(visitor, htmlVisitors, nodeName, 'enter');
addVisitor(visitor, htmlVisitors, nodeName, 'leave');
}
}
- if (optimizer.visitors.css) {
- for (const [nodeName, visitor] of Object.entries(optimizer.visitors.css)) {
+ if (transformer.visitors.css) {
+ for (const [nodeName, visitor] of Object.entries(transformer.visitors.css)) {
addVisitor(visitor, cssVisitors, nodeName, 'enter');
addVisitor(visitor, cssVisitors, nodeName, 'leave');
}
}
}
- finalizers.push(optimizer.finalize);
+ finalizers.push(transformer.finalize);
}
/** Utility for formatting visitors */
@@ -74,17 +74,17 @@ function walkAstWithVisitors(tmpl: TemplateNode, collection: VisitorCollection)
}
/**
- * Optimize
+ * Transform
* Step 2/3 in Astro SSR.
- * Optimize is the point at which we mutate the AST before sending off to
+ * Transform is the point at which we mutate the AST before sending off to
* Codegen, and then to Snowpack. In some ways, it‘s a preprocessor.
*/
-export async function optimize(ast: Ast, opts: OptimizeOptions) {
+export async function transform(ast: Ast, opts: TransformOptions) {
const htmlVisitors = createVisitorCollection();
const cssVisitors = createVisitorCollection();
const finalizers: Array<() => Promise<void>> = [];
- const optimizers = [optimizeStyles(opts), optimizeDoctype(opts), optimizeModuleScripts(opts), optimizeCodeBlocks(ast.module)];
+ const optimizers = [transformStyles(opts), transformDoctype(opts), transformModuleScripts(opts), transformCodeBlocks(ast.module)];
for (const optimizer of optimizers) {
collectVisitors(optimizer, htmlVisitors, cssVisitors, finalizers);
diff --git a/src/compiler/optimize/module-scripts.ts b/src/compiler/transform/module-scripts.ts
index 9d4949215..aff1ec4f6 100644
--- a/src/compiler/optimize/module-scripts.ts
+++ b/src/compiler/transform/module-scripts.ts
@@ -1,11 +1,11 @@
-import type { Optimizer } from '../../@types/optimizer';
+import type { Transformer } from '../../@types/transformer';
import type { CompileOptions } from '../../@types/compiler';
import path from 'path';
import { getAttrValue, setAttrValue } from '../../ast.js';
-/** Optimize <script type="module"> */
-export default function ({ compileOptions, filename }: { compileOptions: CompileOptions; filename: string; fileID: string }): Optimizer {
+/** Transform <script type="module"> */
+export default function ({ compileOptions, filename }: { compileOptions: CompileOptions; filename: string; fileID: string }): Transformer {
const { astroConfig } = compileOptions;
const { astroRoot } = astroConfig;
const fileUrl = new URL(`file://${filename}`);
diff --git a/src/compiler/optimize/postcss-scoped-styles/index.ts b/src/compiler/transform/postcss-scoped-styles/index.ts
index 23350869c..23350869c 100644
--- a/src/compiler/optimize/postcss-scoped-styles/index.ts
+++ b/src/compiler/transform/postcss-scoped-styles/index.ts
diff --git a/src/compiler/optimize/prism.ts b/src/compiler/transform/prism.ts
index 5c5364796..628dcce7e 100644
--- a/src/compiler/optimize/prism.ts
+++ b/src/compiler/transform/prism.ts
@@ -1,4 +1,4 @@
-import type { Optimizer } from '../../@types/optimizer';
+import type { Transformer } from '../../@types/transformer';
import type { Script } from '../../parser/interfaces';
import { getAttrValue } from '../../ast.js';
@@ -11,7 +11,7 @@ function escape(code: string) {
});
}
-export default function (module: Script): Optimizer {
+export default function (module: Script): Transformer {
let usesPrism = false;
return {
@@ -61,7 +61,12 @@ export default function (module: Script): Optimizer {
value: [
{
type: 'MustacheTag',
- content: '`' + escape(code) + '`',
+ expression: {
+ type: 'Expression',
+ codeStart: '`' + escape(code) + '`',
+ codeEnd: '',
+ children: []
+ }
},
],
},
diff --git a/src/compiler/optimize/styles.ts b/src/compiler/transform/styles.ts
index 807d869c9..d8e3196a1 100644
--- a/src/compiler/optimize/styles.ts
+++ b/src/compiler/transform/styles.ts
@@ -7,7 +7,7 @@ import postcssKeyframes from 'postcss-icss-keyframes';
import findUp from 'find-up';
import sass from 'sass';
import type { RuntimeMode } from '../../@types/astro';
-import type { OptimizeOptions, Optimizer } from '../../@types/optimizer';
+import type { TransformOptions, Transformer } from '../../@types/transformer';
import type { TemplateNode } from '../../parser/interfaces';
import { debug } from '../../logger.js';
import astroScopedStyles, { NEVER_SCOPED_TAGS } from './postcss-scoped-styles/index.js';
@@ -152,8 +152,8 @@ async function transformStyle(code: string, { type, filename, scopedClass, mode
return { css, type: styleType };
}
-/** Optimize <style> tags */
-export default function optimizeStyles({ compileOptions, filename, fileID }: OptimizeOptions): Optimizer {
+/** Transform <style> tags */
+export default function transformStyles({ compileOptions, filename, fileID }: TransformOptions): Transformer {
const styleNodes: TemplateNode[] = []; // <style> tags to be updated
const styleTransformPromises: Promise<StyleTransformResult>[] = []; // async style transform results to be finished in finalize();
const scopedClass = `astro-${hashFromFilename(fileID)}`; // this *should* generate same hash from fileID every time
@@ -218,9 +218,9 @@ export default function optimizeStyles({ compileOptions, filename, fileID }: Opt
}
} else if (attr.value[k].type === 'MustacheTag' && attr.value[k]) {
// don‘t add same scopedClass twice (this check is a little more basic, but should suffice)
- if (!attr.value[k].content.includes(`' ${scopedClass}'`)) {
+ if (!attr.value[k].expression.codeStart.includes(`' ${scopedClass}'`)) {
// MustacheTag
- attr.value[k].content = `(${attr.value[k].content}) + ' ${scopedClass}'`;
+ attr.value[k].expression.codeStart = `(${attr.value[k].expression.codeStart}) + ' ${scopedClass}'`;
}
}
}