diff options
author | 2021-04-30 16:33:35 -0500 | |
---|---|---|
committer | 2021-04-30 16:33:35 -0500 | |
commit | 4df1347156cf2632ea2f3475d3a5f8f08d197cc3 (patch) | |
tree | 9d50de89dfe62827c32a8a4046120af4ab61dc0c /packages/astro/src/compiler/transform/index.ts | |
parent | 1d498facc8f78a3ffbfecd05cc6ecd45e8a4a1ae (diff) | |
download | astro-4df1347156cf2632ea2f3475d3a5f8f08d197cc3.tar.gz astro-4df1347156cf2632ea2f3475d3a5f8f08d197cc3.tar.zst astro-4df1347156cf2632ea2f3475d3a5f8f08d197cc3.zip |
Migrate to `yarn` monorepo (#157)
* chore: use monorepo
* chore: scaffold astro-scripts
* chore: move tests inside packages/astro
* chore: refactor tests, add scripts
* chore: move parser to own module
* chore: move runtime to packages/astro
* fix: move parser to own package
* test: fix prettier-plugin-astro tests
* fix: tests
* chore: update package-lock
* chore: add changesets
* fix: cleanup examples
* fix: starter example
* chore: update changeset config
* chore: update changeset config
* chore: setup changeset release workflow
* chore: bump lockfiles
* chore: prism => astro-prism
* fix: tsc --emitDeclarationOnly
* chore: final cleanup, switch to yarn
* chore: add lerna
* chore: update workflows to yarn
* chore: update workflows
* chore: remove lint workflow
* chore: add astro-dev script
* chore: add symlinked README
Diffstat (limited to 'packages/astro/src/compiler/transform/index.ts')
-rw-r--r-- | packages/astro/src/compiler/transform/index.ts | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/packages/astro/src/compiler/transform/index.ts b/packages/astro/src/compiler/transform/index.ts new file mode 100644 index 000000000..2fe0d0e73 --- /dev/null +++ b/packages/astro/src/compiler/transform/index.ts @@ -0,0 +1,100 @@ +import type { Ast, TemplateNode } from 'astro-parser'; +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)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + 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)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + 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())); +} |