diff options
author | 2021-04-09 14:09:13 -0400 | |
---|---|---|
committer | 2021-04-09 14:09:13 -0400 | |
commit | ad9c3b1d8dbf1c3aff75497271347ed36ea38a0b (patch) | |
tree | 8e0aed5ea1783df8322e1db589e84f9579152ba3 /src/compiler/transform/index.ts | |
parent | 084845f79d064626d5f5069ce7b945e3b44bdbd7 (diff) | |
download | astro-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/transform/index.ts')
-rw-r--r-- | src/compiler/transform/index.ts | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/compiler/transform/index.ts b/src/compiler/transform/index.ts new file mode 100644 index 000000000..6a81b92b0 --- /dev/null +++ b/src/compiler/transform/index.ts @@ -0,0 +1,98 @@ +import type { Ast, TemplateNode } from '../../parser/interfaces'; +import type { NodeVisitor, TransformOptions, Transformer, VisitorFn } from '../../@types/transformer'; + +import { walk } from 'estree-walker'; + +// 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[]>; + leave: Map<string, VisitorFn[]>; +} + +/** Add visitors to given collection */ +function addVisitor(visitor: NodeVisitor, collection: VisitorCollection, nodeName: string, event: 'enter' | 'leave') { + if (typeof visitor[event] !== 'function') return; + if (!collection[event]) collection[event] = new Map<string, VisitorFn[]>(); + + const visitors = collection[event].get(nodeName) || []; + visitors.push(visitor[event] as any); + collection[event].set(nodeName, visitors); +} + +/** 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 (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(transformer.finalize); +} + +/** Utility for formatting visitors */ +function createVisitorCollection() { + return { + enter: new Map<string, VisitorFn[]>(), + leave: new Map<string, VisitorFn[]>(), + }; +} + +/** Walk AST with collected visitors */ +function walkAstWithVisitors(tmpl: TemplateNode, collection: VisitorCollection) { + walk(tmpl, { + enter(node, parent, key, index) { + if (collection.enter.has(node.type)) { + const fns = collection.enter.get(node.type)!; + for (let fn of fns) { + fn.call(this, node, parent, key, index); + } + } + }, + leave(node, parent, key, index) { + if (collection.leave.has(node.type)) { + const fns = collection.leave.get(node.type)!; + for (let fn of fns) { + fn.call(this, node, parent, key, index); + } + } + }, + }); +} + +/** + * Transform + * Step 2/3 in Astro SSR. + * 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 transform(ast: Ast, opts: TransformOptions) { + const htmlVisitors = createVisitorCollection(); + const cssVisitors = createVisitorCollection(); + const finalizers: Array<() => Promise<void>> = []; + + const optimizers = [transformStyles(opts), transformDoctype(opts), transformModuleScripts(opts), transformCodeBlocks(ast.module)]; + + for (const optimizer of optimizers) { + collectVisitors(optimizer, htmlVisitors, cssVisitors, finalizers); + } + + walkAstWithVisitors(ast.css, cssVisitors); + walkAstWithVisitors(ast.html, htmlVisitors); + + // Run all of the finalizer functions in parallel because why not. + await Promise.all(finalizers.map((fn) => fn())); +} |