summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/@types/astro.ts132
-rw-r--r--src/@types/compiler.ts10
-rw-r--r--src/@types/estree-walker.d.ts25
-rw-r--r--src/@types/micromark-extension-gfm.d.ts3
-rw-r--r--src/@types/micromark.ts11
-rw-r--r--src/@types/postcss-icss-keyframes.d.ts5
-rw-r--r--src/@types/renderer.ts37
-rw-r--r--src/@types/tailwind.d.ts2
-rw-r--r--src/@types/transformer.ts23
-rw-r--r--src/ast.ts28
-rw-r--r--src/build.ts303
-rw-r--r--src/build/bundle.ts313
-rw-r--r--src/build/rss.ts68
-rw-r--r--src/build/sitemap.ts15
-rw-r--r--src/build/static.ts28
-rw-r--r--src/build/util.ts9
-rw-r--r--src/cli.ts127
-rw-r--r--src/compiler/codegen/content.ts78
-rw-r--r--src/compiler/codegen/index.ts686
-rw-r--r--src/compiler/codegen/utils.ts39
-rw-r--r--src/compiler/index.ts176
-rw-r--r--src/compiler/markdown/micromark-collect-headers.ts38
-rw-r--r--src/compiler/markdown/micromark-encode.ts36
-rw-r--r--src/compiler/markdown/micromark-mdx-astro.ts22
-rw-r--r--src/compiler/markdown/micromark.d.ts11
-rw-r--r--src/compiler/transform/doctype.ts36
-rw-r--r--src/compiler/transform/index.ts100
-rw-r--r--src/compiler/transform/module-scripts.ts43
-rw-r--r--src/compiler/transform/postcss-scoped-styles/index.ts106
-rw-r--r--src/compiler/transform/prism.ts89
-rw-r--r--src/compiler/transform/styles.ts290
-rw-r--r--src/config.ts85
-rw-r--r--src/dev.ts97
-rw-r--r--src/frontend/500.astro128
-rw-r--r--src/frontend/SvelteWrapper.svelte7
-rw-r--r--src/frontend/SvelteWrapper.svelte.client.ts166
-rw-r--r--src/frontend/SvelteWrapper.svelte.server.ts12
-rw-r--r--src/frontend/h.ts65
-rw-r--r--src/frontend/render/preact.ts31
-rw-r--r--src/frontend/render/react.ts29
-rw-r--r--src/frontend/render/renderer.ts61
-rw-r--r--src/frontend/render/svelte.ts26
-rw-r--r--src/frontend/render/utils.ts52
-rw-r--r--src/frontend/render/vue.ts65
-rw-r--r--src/frontend/runtime/svelte.ts10
-rw-r--r--src/logger.ts143
-rw-r--r--src/parser/README.md3
-rw-r--r--src/parser/Stats.ts83
-rw-r--r--src/parser/config.ts1
-rw-r--r--src/parser/index.ts1
-rw-r--r--src/parser/interfaces.ts145
-rw-r--r--src/parser/parse/index.ts270
-rw-r--r--src/parser/parse/read/context.ts72
-rw-r--r--src/parser/parse/read/expression.ts251
-rw-r--r--src/parser/parse/read/script.ts60
-rw-r--r--src/parser/parse/read/style.ts40
-rw-r--r--src/parser/parse/state/fragment.ts21
-rw-r--r--src/parser/parse/state/mustache.ts413
-rw-r--r--src/parser/parse/state/setup.ts35
-rw-r--r--src/parser/parse/state/tag.ts579
-rw-r--r--src/parser/parse/state/text.ts24
-rw-r--r--src/parser/parse/utils/bracket.ts27
-rw-r--r--src/parser/parse/utils/entities.ts2034
-rw-r--r--src/parser/parse/utils/html.ts143
-rw-r--r--src/parser/parse/utils/node.ts30
-rw-r--r--src/parser/utils/error.ts46
-rw-r--r--src/parser/utils/full_char_code_at.ts11
-rw-r--r--src/parser/utils/fuzzymatch.ts233
-rw-r--r--src/parser/utils/get_code_frame.ts29
-rw-r--r--src/parser/utils/link.ts5
-rw-r--r--src/parser/utils/list.ts5
-rw-r--r--src/parser/utils/names.ts142
-rw-r--r--src/parser/utils/namespaces.ts13
-rw-r--r--src/parser/utils/nodes_match.ts35
-rw-r--r--src/parser/utils/patterns.ts3
-rw-r--r--src/parser/utils/trim.ts17
-rw-r--r--src/runtime.ts365
-rw-r--r--src/search.ts141
78 files changed, 0 insertions, 9143 deletions
diff --git a/src/@types/astro.ts b/src/@types/astro.ts
deleted file mode 100644
index 049105970..000000000
--- a/src/@types/astro.ts
+++ /dev/null
@@ -1,132 +0,0 @@
-export interface AstroConfigRaw {
- dist: string;
- projectRoot: string;
- astroRoot: string;
- public: string;
- jsx?: string;
-}
-
-export type ValidExtensionPlugins = 'astro' | 'react' | 'preact' | 'svelte' | 'vue';
-
-export interface AstroConfig {
- dist: string;
- projectRoot: URL;
- astroRoot: URL;
- public: URL;
- extensions?: Record<string, ValidExtensionPlugins>;
- /** Options specific to `astro build` */
- buildOptions: {
- /** Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. */
- site?: string;
- /** Generate sitemap (set to "false" to disable) */
- sitemap: boolean;
- };
- /** Options for the development server run with `astro dev`. */
- devOptions: {
- /** The port to run the dev server on. */
- port: number;
- projectRoot?: string;
- };
-}
-
-export type AstroUserConfig = Omit<AstroConfig, 'buildOptions' | 'devOptions'> & {
- buildOptions: {
- sitemap: boolean;
- };
- devOptions: {
- port?: number;
- projectRoot?: string;
- };
-};
-
-export interface JsxItem {
- name: string;
- jsx: string;
-}
-
-export interface TransformResult {
- script: string;
- imports: string[];
- html: string;
- css?: string;
- /** If this page exports a collection, the JS to be executed as a string */
- createCollection?: string;
-}
-
-export interface CompileResult {
- result: TransformResult;
- contents: string;
- css?: string;
-}
-
-export type RuntimeMode = 'development' | 'production';
-
-export type Params = Record<string, string | number>;
-
-export interface CreateCollection<T = any> {
- data: ({ params }: { params: Params }) => T[];
- routes?: Params[];
- /** tool for generating current page URL */
- permalink?: ({ params }: { params: Params }) => string;
- /** page size */
- pageSize?: number;
- /** Generate RSS feed from data() */
- rss?: CollectionRSS<T>;
-}
-
-export interface CollectionRSS<T = any> {
- /** (required) Title of the RSS Feed */
- title: string;
- /** (required) Description of the RSS Feed */
- description: string;
- /** Specify arbitrary metadata on opening <xml> tag */
- xmlns?: Record<string, string>;
- /** Specify custom data in opening of file */
- customData?: string;
- /** Return data about each item */
- item: (
- item: T
- ) => {
- /** (required) Title of item */
- title: string;
- /** (required) Link to item */
- link: string;
- /** Publication date of item */
- pubDate?: Date;
- /** Item description */
- description?: string;
- /** Append some other XML-valid data to this item */
- customData?: string;
- };
-}
-
-export interface CollectionResult<T = any> {
- /** result */
- data: T[];
-
- /** metadata */
- /** the count of the first item on the page, starting from 0 */
- start: number;
- /** the count of the last item on the page, starting from 0 */
- end: number;
- /** total number of results */
- total: number;
- page: {
- /** the current page number, starting from 1 */
- current: number;
- /** number of items per page (default: 25) */
- size: number;
- /** number of last page */
- last: number;
- };
- url: {
- /** url of the current page */
- current: string;
- /** url of the previous page (if there is one) */
- prev?: string;
- /** url of the next page (if there is one) */
- next?: string;
- };
- /** Matched parameters, if any */
- params: Params;
-}
diff --git a/src/@types/compiler.ts b/src/@types/compiler.ts
deleted file mode 100644
index 7da0afaf2..000000000
--- a/src/@types/compiler.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import type { LogOptions } from '../logger';
-import type { AstroConfig, RuntimeMode, ValidExtensionPlugins } from './astro';
-
-export interface CompileOptions {
- logging: LogOptions;
- resolvePackageUrl: (p: string) => Promise<string>;
- astroConfig: AstroConfig;
- extensions?: Record<string, ValidExtensionPlugins>;
- mode: RuntimeMode;
-}
diff --git a/src/@types/estree-walker.d.ts b/src/@types/estree-walker.d.ts
deleted file mode 100644
index a3b7da859..000000000
--- a/src/@types/estree-walker.d.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { BaseNode } from 'estree-walker';
-
-declare module 'estree-walker' {
- export function walk<T = BaseNode>(
- ast: T,
- {
- enter,
- leave,
- }: {
- enter?: (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, key: string, index: number) => void;
- leave?: (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, key: string, index: number) => void;
- }
- ): T;
-
- export function asyncWalk<T = BaseNode>(
- ast: T,
- {
- enter,
- leave,
- }: {
- enter?: (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, key: string, index: number) => void;
- leave?: (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, key: string, index: number) => void;
- }
- ): T;
-}
diff --git a/src/@types/micromark-extension-gfm.d.ts b/src/@types/micromark-extension-gfm.d.ts
deleted file mode 100644
index ebdfe3b3a..000000000
--- a/src/@types/micromark-extension-gfm.d.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-// TODO: add types (if helpful)
-declare module 'micromark-extension-gfm';
-declare module 'micromark-extension-gfm/html.js';
diff --git a/src/@types/micromark.ts b/src/@types/micromark.ts
deleted file mode 100644
index 9725aabb9..000000000
--- a/src/@types/micromark.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export interface MicromarkExtensionContext {
- sliceSerialize(node: any): string;
- raw(value: string): void;
-}
-
-export type MicromarkExtensionCallback = (this: MicromarkExtensionContext, node: any) => void;
-
-export interface MicromarkExtension {
- enter?: Record<string, MicromarkExtensionCallback>;
- exit?: Record<string, MicromarkExtensionCallback>;
-}
diff --git a/src/@types/postcss-icss-keyframes.d.ts b/src/@types/postcss-icss-keyframes.d.ts
deleted file mode 100644
index 14c330b6e..000000000
--- a/src/@types/postcss-icss-keyframes.d.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-declare module 'postcss-icss-keyframes' {
- import type { Plugin } from 'postcss';
-
- export default function (options: { generateScopedName(keyframesName: string, filepath: string, css: string): string }): Plugin;
-}
diff --git a/src/@types/renderer.ts b/src/@types/renderer.ts
deleted file mode 100644
index f89cb6664..000000000
--- a/src/@types/renderer.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import type { Component as VueComponent } from 'vue';
-import type { ComponentType as PreactComponent } from 'preact';
-import type { ComponentType as ReactComponent } from 'react';
-import type { SvelteComponent } from 'svelte';
-
-export interface DynamicRenderContext {
- componentUrl: string;
- componentExport: string;
- frameworkUrls: string;
-}
-
-export interface ComponentRenderer<T> {
- renderStatic: StaticRendererGenerator<T>;
- jsxPragma?: (...args: any) => any;
- jsxPragmaName?: string;
- render(context: { root: string; Component: string; props: string; [key: string]: string }): string;
- imports?: Record<string, string[]>;
-}
-
-export interface ComponentContext {
- 'data-astro-id': string;
- root: string;
-}
-
-export type SupportedComponentRenderer =
- | ComponentRenderer<VueComponent>
- | ComponentRenderer<PreactComponent>
- | ComponentRenderer<ReactComponent>
- | ComponentRenderer<SvelteComponent>;
-export type StaticRenderer = (props: Record<string, any>, ...children: any[]) => Promise<string>;
-export type StaticRendererGenerator<T = any> = (Component: T) => StaticRenderer;
-export type DynamicRenderer = (props: Record<string, any>, ...children: any[]) => Promise<string>;
-export type DynamicRendererContext<T = any> = (Component: T, renderContext: DynamicRenderContext) => DynamicRenderer;
-export type DynamicRendererGenerator = (
- wrapperStart: string | ((context: ComponentContext) => string),
- wrapperEnd: string | ((context: ComponentContext) => string)
-) => DynamicRendererContext;
diff --git a/src/@types/tailwind.d.ts b/src/@types/tailwind.d.ts
deleted file mode 100644
index d25eaae2f..000000000
--- a/src/@types/tailwind.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// we shouldn‘t have this as a dependency for Astro, but we may dynamically import it if a user requests it, so let TS know about it
-declare module 'tailwindcss';
diff --git a/src/@types/transformer.ts b/src/@types/transformer.ts
deleted file mode 100644
index 7f4167558..000000000
--- a/src/@types/transformer.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { TemplateNode } from '../parser/interfaces';
-import type { CompileOptions } from './compiler';
-
-export type VisitorFn<T = TemplateNode> = (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, type: string, index: number) => void;
-
-export interface NodeVisitor {
- enter?: VisitorFn;
- leave?: VisitorFn;
-}
-
-export interface Transformer {
- visitors?: {
- html?: Record<string, NodeVisitor>;
- css?: Record<string, NodeVisitor>;
- };
- finalize: () => Promise<void>;
-}
-
-export interface TransformOptions {
- compileOptions: CompileOptions;
- filename: string;
- fileID: string;
-}
diff --git a/src/ast.ts b/src/ast.ts
deleted file mode 100644
index faf4b3eb6..000000000
--- a/src/ast.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { Attribute } from './parser/interfaces';
-
-// AST utility functions
-
-/** Get TemplateNode attribute from name */
-export function getAttr(attributes: Attribute[], name: string): Attribute | undefined {
- const attr = attributes.find((a) => a.name === name);
- return attr;
-}
-
-/** Get TemplateNode attribute by value */
-export function getAttrValue(attributes: Attribute[], name: string): string | undefined {
- const attr = getAttr(attributes, name);
- if (attr) {
- return attr.value[0]?.data;
- }
-}
-
-/** Set TemplateNode attribute value */
-export function setAttrValue(attributes: Attribute[], name: string, value: string): void {
- const attr = attributes.find((a) => a.name === name);
- if (attr) {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- attr.value[0]!.data = value;
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- attr.value[0]!.raw = value;
- }
-}
diff --git a/src/build.ts b/src/build.ts
deleted file mode 100644
index 392b0a920..000000000
--- a/src/build.ts
+++ /dev/null
@@ -1,303 +0,0 @@
-import type { AstroConfig, RuntimeMode } from './@types/astro';
-import type { LogOptions } from './logger';
-import type { AstroRuntime, LoadResult } from './runtime';
-
-import { existsSync, promises as fsPromises } from 'fs';
-import { bold, green, yellow, underline } from 'kleur/colors';
-import path from 'path';
-import cheerio from 'cheerio';
-import { fileURLToPath } from 'url';
-import { fdir } from 'fdir';
-import { defaultLogDestination, error, info, trapWarn } from './logger.js';
-import { createRuntime } from './runtime.js';
-import { bundle, collectDynamicImports } from './build/bundle.js';
-import { generateRSS } from './build/rss.js';
-import { generateSitemap } from './build/sitemap.js';
-import { collectStatics } from './build/static.js';
-import { canonicalURL } from './build/util.js';
-
-
-const { mkdir, readFile, writeFile } = fsPromises;
-
-interface PageBuildOptions {
- astroRoot: URL;
- dist: URL;
- filepath: URL;
- runtime: AstroRuntime;
- site?: string;
- sitemap: boolean;
- statics: Set<string>;
-}
-
-interface PageResult {
- canonicalURLs: string[];
- rss?: string;
- statusCode: number;
-}
-
-const logging: LogOptions = {
- level: 'debug',
- dest: defaultLogDestination,
-};
-
-/** Return contents of src/pages */
-async function allPages(root: URL) {
- const api = new fdir()
- .filter((p) => /\.(astro|md)$/.test(p))
- .withFullPaths()
- .crawl(fileURLToPath(root));
- const files = await api.withPromise();
- return files as string[];
-}
-
-/** Utility for merging two Set()s */
-function mergeSet(a: Set<string>, b: Set<string>) {
- for (let str of b) {
- a.add(str);
- }
- return a;
-}
-
-/** Utility for writing to file (async) */
-async function writeFilep(outPath: URL, bytes: string | Buffer, encoding: 'utf8' | null) {
- const outFolder = new URL('./', outPath);
- await mkdir(outFolder, { recursive: true });
- await writeFile(outPath, bytes, encoding || 'binary');
-}
-
-/** Utility for writing a build result to disk */
-async function writeResult(result: LoadResult, outPath: URL, encoding: null | 'utf8') {
- if (result.statusCode === 500 || result.statusCode === 404) {
- error(logging, 'build', result.error || result.statusCode);
- } else if (result.statusCode !== 200) {
- error(logging, 'build', `Unexpected load result (${result.statusCode}) for ${fileURLToPath(outPath)}`);
- } else {
- const bytes = result.contents;
- await writeFilep(outPath, bytes, encoding);
- }
-}
-
-/** Collection utility */
-function getPageType(filepath: URL): 'collection' | 'static' {
- if (/\$[^.]+.astro$/.test(filepath.pathname)) return 'collection';
- return 'static';
-}
-
-/** Build collection */
-async function buildCollectionPage({ astroRoot, dist, filepath, runtime, site, statics }: PageBuildOptions): Promise<PageResult> {
- const rel = path.relative(fileURLToPath(astroRoot) + '/pages', fileURLToPath(filepath)); // pages/index.astro
- const pagePath = `/${rel.replace(/\$([^.]+)\.astro$/, '$1')}`;
- const builtURLs = new Set<string>(); // !important: internal cache that prevents building the same URLs
-
- /** Recursively build collection URLs */
- async function loadCollection(url: string): Promise<LoadResult | undefined> {
- if (builtURLs.has(url)) return; // this stops us from recursively building the same pages over and over
- const result = await runtime.load(url);
- builtURLs.add(url);
- if (result.statusCode === 200) {
- const outPath = new URL('./' + url + '/index.html', dist);
- await writeResult(result, outPath, 'utf8');
- mergeSet(statics, collectStatics(result.contents.toString('utf8')));
- }
- return result;
- }
-
- const result = (await loadCollection(pagePath)) as LoadResult;
-
- if (result.statusCode >= 500) {
- throw new Error((result as any).error);
- }
- if (result.statusCode === 200 && !result.collectionInfo) {
- throw new Error(`[${rel}]: Collection page must export createCollection() function`);
- }
-
- let rss: string | undefined;
-
- // note: for pages that require params (/tag/:tag), we will get a 404 but will still get back collectionInfo that tell us what the URLs should be
- if (result.collectionInfo) {
- // build subsequent pages
- await Promise.all(
- [...result.collectionInfo.additionalURLs].map(async (url) => {
- // for the top set of additional URLs, we render every new URL generated
- const addlResult = await loadCollection(url);
- builtURLs.add(url);
- if (addlResult && addlResult.collectionInfo) {
- // believe it or not, we may still have a few unbuilt pages left. this is our last crawl:
- await Promise.all([...addlResult.collectionInfo.additionalURLs].map(async (url2) => loadCollection(url2)));
- }
- })
- );
-
- if (result.collectionInfo.rss) {
- if (!site) throw new Error(`[${rel}] createCollection() tried to generate RSS but "buildOptions.site" missing in astro.config.mjs`);
- rss = generateRSS({ ...(result.collectionInfo.rss as any), site }, rel.replace(/\$([^.]+)\.astro$/, '$1'));
- }
- }
-
- return {
- canonicalURLs: [...builtURLs].filter((url) => !url.endsWith('/1')), // note: canonical URLs are controlled by the collection, so these are canonical (but exclude "/1" pages as those are duplicates of the index)
- statusCode: result.statusCode,
- rss,
- };
-}
-
-/** Build static page */
-async function buildStaticPage({ astroRoot, dist, filepath, runtime, sitemap, statics }: PageBuildOptions): Promise<PageResult> {
- const rel = path.relative(fileURLToPath(astroRoot) + '/pages', fileURLToPath(filepath)); // pages/index.astro
- const pagePath = `/${rel.replace(/\.(astro|md)$/, '')}`;
- let canonicalURLs: string[] = [];
-
- let relPath = './' + rel.replace(/\.(astro|md)$/, '.html');
- if (!relPath.endsWith('index.html')) {
- relPath = relPath.replace(/\.html$/, '/index.html');
- }
-
- const outPath = new URL(relPath, dist);
- const result = await runtime.load(pagePath);
-
- await writeResult(result, outPath, 'utf8');
-
- if (result.statusCode === 200) {
- mergeSet(statics, collectStatics(result.contents.toString('utf8')));
-
- // get Canonical URL (if user has specified one manually, use that)
- if (sitemap) {
- const $ = cheerio.load(result.contents);
- const canonicalTag = $('link[rel="canonical"]');
- canonicalURLs.push(canonicalTag.attr('href') || pagePath.replace(/index$/, ''));
- }
- }
-
- return {
- canonicalURLs,
- statusCode: result.statusCode,
- };
-}
-
-/** The primary build action */
-export async function build(astroConfig: AstroConfig): Promise<0 | 1> {
- const { projectRoot, astroRoot } = astroConfig;
- const pageRoot = new URL('./pages/', astroRoot);
- const componentRoot = new URL('./components/', astroRoot);
- const dist = new URL(astroConfig.dist + '/', projectRoot);
-
- const runtimeLogging: LogOptions = {
- level: 'error',
- dest: defaultLogDestination,
- };
-
- const mode: RuntimeMode = 'production';
- const runtime = await createRuntime(astroConfig, { mode, logging: runtimeLogging });
- const { runtimeConfig } = runtime;
- const { backendSnowpack: snowpack } = runtimeConfig;
- const resolvePackageUrl = (pkgName: string) => snowpack.getUrlForPackage(pkgName);
-
- const imports = new Set<string>();
- const statics = new Set<string>();
- const collectImportsOptions = { astroConfig, logging, resolvePackageUrl, mode };
-
- const pages = await allPages(pageRoot);
- let builtURLs: string[] = [];
-
-
- try {
- info(logging , 'build', yellow('! building pages...'));
- // Vue also console.warns, this silences it.
- const release = trapWarn();
- await Promise.all(
- pages.map(async (pathname) => {
- const filepath = new URL(`file://${pathname}`);
-
- const pageType = getPageType(filepath);
- const pageOptions: PageBuildOptions = { astroRoot, dist, filepath, runtime, site: astroConfig.buildOptions.site, sitemap: astroConfig.buildOptions.sitemap, statics };
- if (pageType === 'collection') {
- const { canonicalURLs, rss } = await buildCollectionPage(pageOptions);
- builtURLs.push(...canonicalURLs);
- if (rss) {
- const basename = path
- .relative(fileURLToPath(astroRoot) + '/pages', pathname)
- .replace(/^\$/, '')
- .replace(/\.astro$/, '');
- await writeFilep(new URL(`file://${path.join(fileURLToPath(dist), 'feed', basename + '.xml')}`), rss, 'utf8');
- }
- } else {
- const { canonicalURLs } = await buildStaticPage(pageOptions);
- builtURLs.push(...canonicalURLs);
- }
-
- mergeSet(imports, await collectDynamicImports(filepath, collectImportsOptions));
- })
- );
- info(logging, 'build', green('✔'), 'pages built.');
- release();
- } catch (err) {
- error(logging, 'generate', err);
- await runtime.shutdown();
- return 1;
- }
-
- info(logging, 'build', yellow('! scanning pages...'));
- for (const pathname of await allPages(componentRoot)) {
- mergeSet(imports, await collectDynamicImports(new URL(`file://${pathname}`), collectImportsOptions));
- }
- info(logging, 'build', green('✔'), 'pages scanned.');
-
- if (imports.size > 0) {
- try {
- info(logging, 'build', yellow('! bundling client-side code.'));
- await bundle(imports, { dist, runtime, astroConfig });
- info(logging, 'build', green('✔'), 'bundling complete.');
- } catch (err) {
- error(logging, 'build', err);
- await runtime.shutdown();
- return 1;
- }
- }
-
- for (let url of statics) {
- const outPath = new URL('.' + url, dist);
- const result = await runtime.load(url);
-
- await writeResult(result, outPath, null);
- }
-
- if (existsSync(astroConfig.public)) {
- info(logging, 'build', yellow(`! copying public folder...`));
- const pub = astroConfig.public;
- const publicFiles = (await new fdir().withFullPaths().crawl(fileURLToPath(pub)).withPromise()) as string[];
- for (const filepath of publicFiles) {
- const fileUrl = new URL(`file://${filepath}`);
- const rel = path.relative(pub.pathname, fileUrl.pathname);
- const outUrl = new URL('./' + rel, dist);
-
- const bytes = await readFile(fileUrl);
- await writeFilep(outUrl, bytes, null);
- }
- info(logging, 'build', green('✔'), 'public folder copied.');
- } else {
- if(path.basename(astroConfig.public.toString()) !=='public'){
- info(logging, 'tip', yellow(`! no public folder ${astroConfig.public} found...`));
- }
- }
- // build sitemap
- if (astroConfig.buildOptions.sitemap && astroConfig.buildOptions.site) {
- info(logging, 'build', yellow('! creating a sitemap...'));
- const sitemap = generateSitemap(builtURLs.map((url) => ({ canonicalURL: canonicalURL(url, astroConfig.buildOptions.site) })));
- await writeFile(new URL('./sitemap.xml', dist), sitemap, 'utf8');
- info(logging, 'build', green('✔'), 'sitemap built.');
- } else if (astroConfig.buildOptions.sitemap) {
- info(logging, 'tip', `Set "buildOptions.site" in astro.config.mjs to generate a sitemap.xml`);
- }
-
- builtURLs.sort((a, b) => a.localeCompare(b, 'en', { numeric: true }));
- info(logging, 'build', underline('Pages'));
- const lastIndex = builtURLs.length - 1;
- builtURLs.forEach((url, index) => {
- const sep = index === 0 ? '┌' : index === lastIndex ? '└' : '├';
- info(logging, null, ' ' + sep, url === '/' ? url : url + '/');
- });
-
- await runtime.shutdown();
- info(logging, 'build', bold(green('▶ Build Complete!')));
- return 0;
-}
diff --git a/src/build/bundle.ts b/src/build/bundle.ts
deleted file mode 100644
index d590e85df..000000000
--- a/src/build/bundle.ts
+++ /dev/null
@@ -1,313 +0,0 @@
-import type { AstroConfig, RuntimeMode, ValidExtensionPlugins } from '../@types/astro';
-import type { ImportDeclaration } from '@babel/types';
-import type { InputOptions, OutputOptions } from 'rollup';
-import type { AstroRuntime } from '../runtime';
-import type { LogOptions } from '../logger';
-
-import esbuild from 'esbuild';
-import { promises as fsPromises } from 'fs';
-import { fileURLToPath } from 'url';
-import { parse } from '../parser/index.js';
-import { transform } from '../compiler/transform/index.js';
-import { convertMdToAstroSource } from '../compiler/index.js';
-import { getAttrValue } from '../ast.js';
-import { walk } from 'estree-walker';
-import babelParser from '@babel/parser';
-import path from 'path';
-import { rollup } from 'rollup';
-import { terser } from 'rollup-plugin-terser';
-
-const { transformSync } = esbuild;
-const { readFile } = fsPromises;
-
-type DynamicImportMap = Map<'vue' | 'react' | 'react-dom' | 'preact' | 'svelte', string>;
-
-/** Add framework runtimes when needed */
-async function acquireDynamicComponentImports(plugins: Set<ValidExtensionPlugins>, resolvePackageUrl: (s: string) => Promise<string>): Promise<DynamicImportMap> {
- const importMap: DynamicImportMap = new Map();
- for (let plugin of plugins) {
- switch (plugin) {
- case 'svelte': {
- importMap.set('svelte', await resolvePackageUrl('svelte'));
- break;
- }
- case 'vue': {
- importMap.set('vue', await resolvePackageUrl('vue'));
- break;
- }
- case 'react': {
- importMap.set('react', await resolvePackageUrl('react'));
- importMap.set('react-dom', await resolvePackageUrl('react-dom'));
- break;
- }
- case 'preact': {
- importMap.set('preact', await resolvePackageUrl('preact'));
- break;
- }
- }
- }
- return importMap;
-}
-
-/** Evaluate mustache expression (safely) */
-function compileExpressionSafe(raw: string): string {
- let { code } = transformSync(raw, {
- loader: 'tsx',
- jsxFactory: 'h',
- jsxFragment: 'Fragment',
- charset: 'utf8',
- });
- return code;
-}
-
-const defaultExtensions: Readonly<Record<string, ValidExtensionPlugins>> = {
- '.jsx': 'react',
- '.tsx': 'react',
- '.svelte': 'svelte',
- '.vue': 'vue',
-};
-
-interface CollectDynamic {
- astroConfig: AstroConfig;
- resolvePackageUrl: (s: string) => Promise<string>;
- logging: LogOptions;
- mode: RuntimeMode;
-}
-
-/** Gather necessary framework runtimes for dynamic components */
-export async function collectDynamicImports(filename: URL, { astroConfig, logging, resolvePackageUrl, mode }: CollectDynamic) {
- const imports = new Set<string>();
-
- // Only astro files
- if (!filename.pathname.endsWith('.astro') && !filename.pathname.endsWith('.md')) {
- return imports;
- }
-
- const extensions = astroConfig.extensions || defaultExtensions;
-
- let source = await readFile(filename, 'utf-8');
- if (filename.pathname.endsWith('.md')) {
- source = await convertMdToAstroSource(source);
- }
-
- const ast = parse(source, {
- filename,
- });
-
- if (!ast.module) {
- return imports;
- }
-
- await transform(ast, {
- filename: fileURLToPath(filename),
- fileID: '',
- compileOptions: {
- astroConfig,
- resolvePackageUrl,
- logging,
- mode,
- },
- });
-
- const componentImports: ImportDeclaration[] = [];
- const components: Record<string, { plugin: ValidExtensionPlugins; type: string; specifier: string }> = {};
- const plugins = new Set<ValidExtensionPlugins>();
-
- const program = babelParser.parse(ast.module.content, {
- sourceType: 'module',
- plugins: ['jsx', 'typescript', 'topLevelAwait'],
- }).program;
-
- const { body } = program;
- let i = body.length;
- while (--i >= 0) {
- const node = body[i];
- if (node.type === 'ImportDeclaration') {
- componentImports.push(node);
- }
- }
-
- for (const componentImport of componentImports) {
- const importUrl = componentImport.source.value;
- const componentType = path.posix.extname(importUrl);
- const componentName = path.posix.basename(importUrl, componentType);
- const plugin = extensions[componentType] || defaultExtensions[componentType];
- plugins.add(plugin);
- components[componentName] = {
- plugin,
- type: componentType,
- specifier: importUrl,
- };
- }
-
- const dynamic = await acquireDynamicComponentImports(plugins, resolvePackageUrl);
-
- /** Add dynamic component runtimes to imports */
- function appendImports(rawName: string, importUrl: URL) {
- const [componentName, componentType] = rawName.split(':');
- if (!componentType) {
- return;
- }
-
- if (!components[componentName]) {
- throw new Error(`Unknown Component: ${componentName}`);
- }
-
- const defn = components[componentName];
- const fileUrl = new URL(defn.specifier, importUrl);
- let rel = path.posix.relative(astroConfig.astroRoot.pathname, fileUrl.pathname);
-
- switch (defn.plugin) {
- case 'preact': {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- imports.add(dynamic.get('preact')!);
- rel = rel.replace(/\.[^.]+$/, '.js');
- break;
- }
- case 'react': {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- imports.add(dynamic.get('react')!);
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- imports.add(dynamic.get('react-dom')!);
- rel = rel.replace(/\.[^.]+$/, '.js');
- break;
- }
- case 'vue': {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- imports.add(dynamic.get('vue')!);
- rel = rel.replace(/\.[^.]+$/, '.vue.js');
- break;
- }
- case 'svelte': {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- imports.add(dynamic.get('svelte')!);
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- imports.add('/_astro_internal/runtime/svelte.js');
- rel = rel.replace(/\.[^.]+$/, '.svelte.js');
- break;
- }
- }
-
- imports.add(`/_astro/${rel}`);
- }
-
- walk(ast.html, {
- enter(node) {
- switch (node.type) {
- case 'Element': {
- if (node.name !== 'script') return;
- if (getAttrValue(node.attributes, 'type') !== 'module') return;
-
- const src = getAttrValue(node.attributes, 'src');
-
- if (src && src.startsWith('/')) {
- imports.add(src);
- }
- break;
- }
-
- case 'MustacheTag': {
- let code: string;
- try {
- code = compileExpressionSafe(node.content);
- } catch {
- return;
- }
-
- 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 foundImport of matches.reverse()) {
- const name = foundImport[1];
- appendImports(name, filename);
- }
- break;
- }
- case 'InlineComponent': {
- if (/^[A-Z]/.test(node.name)) {
- appendImports(node.name, filename);
- return;
- }
-
- break;
- }
- }
- },
- });
-
- return imports;
-}
-
-interface BundleOptions {
- runtime: AstroRuntime;
- dist: URL;
- astroConfig: AstroConfig;
-}
-
-/** The primary bundling/optimization action */
-export async function bundle(imports: Set<string>, { runtime, dist }: BundleOptions) {
- const ROOT = 'astro:root';
- const root = `
- ${[...imports].map((url) => `import '${url}';`).join('\n')}
- `;
-
- const inputOptions: InputOptions = {
- input: [...imports],
- plugins: [
- {
- name: 'astro:build',
- resolveId(source: string, imported?: string) {
- if (source === ROOT) {
- return source;
- }
- if (source.startsWith('/')) {
- return source;
- }
-
- if (imported) {
- const outUrl = new URL(source, 'http://example.com' + imported);
- return outUrl.pathname;
- }
-
- return null;
- },
- async load(id: string) {
- if (id === ROOT) {
- return root;
- }
-
- const result = await runtime.load(id);
-
- if (result.statusCode !== 200) {
- return null;
- }
-
- return result.contents.toString('utf-8');
- },
- },
- ],
- };
-
- const build = await rollup(inputOptions);
-
- const outputOptions: OutputOptions = {
- dir: fileURLToPath(dist),
- format: 'esm',
- exports: 'named',
- entryFileNames(chunk) {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return chunk.facadeModuleId!.substr(1);
- },
- plugins: [
- // We are using terser for the demo, but might switch to something else long term
- // Look into that rather than adding options here.
- terser(),
- ],
- };
-
- await build.write(outputOptions);
-}
diff --git a/src/build/rss.ts b/src/build/rss.ts
deleted file mode 100644
index b75ed908b..000000000
--- a/src/build/rss.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import type { CollectionRSS } from '../@types/astro';
-import parser from 'fast-xml-parser';
-import { canonicalURL } from './util.js';
-
-/** Validates createCollection.rss */
-export function validateRSS(rss: CollectionRSS, filename: string): void {
- if (!rss.title) throw new Error(`[${filename}] rss.title required`);
- if (!rss.description) throw new Error(`[${filename}] rss.description required`);
- if (typeof rss.item !== 'function') throw new Error(`[${filename}] rss.item() function required`);
-}
-
-/** Generate RSS 2.0 feed */
-export function generateRSS<T>(input: { data: T[]; site: string } & CollectionRSS<T>, filename: string): string {
- let xml = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"`;
-
- validateRSS(input as any, filename);
-
- // xmlns
- if (input.xmlns) {
- for (const [k, v] of Object.entries(input.xmlns)) {
- xml += ` xmlns:${k}="${v}"`;
- }
- }
- xml += `>`;
- xml += `<channel>`;
-
- // title, description, customData
- xml += `<title><![CDATA[${input.title}]]></title>`;
- xml += `<description><![CDATA[${input.description}]]></description>`;
- xml += `<link>${canonicalURL('/feed/' + filename + '.xml', input.site)}</link>`;
- if (typeof input.customData === 'string') xml += input.customData;
-
- // items
- if (!Array.isArray(input.data) || !input.data.length) throw new Error(`[${filename}] data() returned no items. Can’t generate RSS feed.`);
- for (const item of input.data) {
- xml += `<item>`;
- const result = input.item(item);
- // validate
- if (typeof result !== 'object') throw new Error(`[${filename}] rss.item() expected to return an object, returned ${typeof result}.`);
- if (!result.title) throw new Error(`[${filename}] rss.item() returned object but required "title" is missing.`);
- if (!result.link) throw new Error(`[${filename}] rss.item() returned object but required "link" is missing.`);
- xml += `<title><![CDATA[${result.title}]]></title>`;
- xml += `<link>${canonicalURL(result.link, input.site)}</link>`;
- if (result.description) xml += `<description><![CDATA[${result.description}]]></description>`;
- if (result.pubDate) {
- // note: this should be a Date, but if user provided a string or number, we can work with that, too.
- if (typeof result.pubDate === 'number' || typeof result.pubDate === 'string') {
- result.pubDate = new Date(result.pubDate);
- } else if (result.pubDate instanceof Date === false) {
- throw new Error('[${filename}] rss.item().pubDate must be a Date');
- }
- xml += `<pubDate>${result.pubDate.toUTCString()}</pubDate>`;
- }
- if (typeof result.customData === 'string') xml += result.customData;
- xml += `</item>`;
- }
-
- xml += `</channel></rss>`;
-
- // validate user’s inputs to see if it’s valid XML
- const isValid = parser.validate(xml);
- if (isValid !== true) {
- // If valid XML, isValid will be `true`. Otherwise, this will be an error object. Throw.
- throw new Error(isValid as any);
- }
-
- return xml;
-}
diff --git a/src/build/sitemap.ts b/src/build/sitemap.ts
deleted file mode 100644
index 1cb3f3e40..000000000
--- a/src/build/sitemap.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-export interface PageMeta {
- /** (required) The canonical URL of the page */
- canonicalURL: string;
-}
-
-/** Construct sitemap.xml given a set of URLs */
-export function generateSitemap(pages: PageMeta[]): string {
- let sitemap = `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`;
- pages.sort((a, b) => a.canonicalURL.localeCompare(b.canonicalURL, 'en', { numeric: true })); // sort alphabetically
- for (const page of pages) {
- sitemap += `<url><loc>${page.canonicalURL}</loc></url>`;
- }
- sitemap += `</urlset>\n`;
- return sitemap;
-}
diff --git a/src/build/static.ts b/src/build/static.ts
deleted file mode 100644
index af99c33cb..000000000
--- a/src/build/static.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { Element } from 'domhandler';
-import cheerio from 'cheerio';
-
-/** Given an HTML string, collect <link> and <img> tags */
-export function collectStatics(html: string) {
- const statics = new Set<string>();
-
- const $ = cheerio.load(html);
-
- const append = (el: Element, attr: string) => {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const value: string = $(el).attr(attr)!;
- if (value.startsWith('http') || $(el).attr('rel') === 'alternate') {
- return;
- }
- statics.add(value);
- };
-
- $('link[href]').each((i, el) => {
- append(el, 'href');
- });
-
- $('img[src]').each((i, el) => {
- append(el, 'src');
- });
-
- return statics;
-}
diff --git a/src/build/util.ts b/src/build/util.ts
deleted file mode 100644
index 505e6f183..000000000
--- a/src/build/util.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import path from 'path';
-
-/** Normalize URL to its canonical form */
-export function canonicalURL(url: string, base?: string): string {
- return new URL(
- path.extname(url) ? url : url.replace(/(\/+)?$/, '/'), // add trailing slash if there’s no extension
- base
- ).href;
-}
diff --git a/src/cli.ts b/src/cli.ts
deleted file mode 100644
index be0dfe27a..000000000
--- a/src/cli.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-/* eslint-disable no-console */
-import type { AstroConfig } from './@types/astro';
-
-import * as colors from 'kleur/colors';
-import { promises as fsPromises } from 'fs';
-import yargs from 'yargs-parser';
-
-import { loadConfig } from './config.js';
-import { build } from './build.js';
-import devServer from './dev.js';
-
-const { readFile } = fsPromises;
-const buildAndExit = async (...args: Parameters<typeof build>) => {
- const ret = await build(...args);
- process.exit(ret);
-};
-
-type Arguments = yargs.Arguments;
-type cliCommand = 'help' | 'version' | 'dev' | 'build';
-interface CLIState {
- cmd: cliCommand;
- options: {
- projectRoot?: string;
- sitemap?: boolean;
- port?: number;
- config?: string;
- };
-}
-
-/** Determine which action the user requested */
-function resolveArgs(flags: Arguments): CLIState {
- const options: CLIState['options'] = {
- projectRoot: typeof flags.projectRoot === 'string' ? flags.projectRoot: undefined,
- sitemap: typeof flags.sitemap === 'boolean' ? flags.sitemap : undefined,
- port: typeof flags.port === 'number' ? flags.port : undefined,
- config: typeof flags.config === 'string' ? flags.config : undefined
- };
-
- if (flags.version) {
- return { cmd: 'version', options };
- } else if (flags.help) {
- return { cmd: 'help', options };
- }
-
- const cmd = flags._[2];
- switch (cmd) {
- case 'dev':
- return { cmd: 'dev', options };
- case 'build':
- return { cmd: 'build', options };
- default:
- return { cmd: 'help', options };
- }
-}
-
-/** Display --help flag */
-function printHelp() {
- console.error(` ${colors.bold('astro')} - Futuristic web development tool.
-
- ${colors.bold('Commands:')}
- astro dev Run Astro in development mode.
- astro build Build a pre-compiled production version of your site.
-
- ${colors.bold('Flags:')}
- --config <path> Specify the path to the Astro config file.
- --project-root <path> Specify the path to the project root folder.
- --no-sitemap Disable sitemap generation (build only).
- --version Show the version number and exit.
- --help Show this help message.
-`);
-}
-
-/** Display --version flag */
-async function printVersion() {
- const pkg = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf-8'));
- console.error(pkg.version);
-}
-
-/** Merge CLI flags & config options (CLI flags take priority) */
-function mergeCLIFlags(astroConfig: AstroConfig, flags: CLIState['options']) {
- if (typeof flags.sitemap === 'boolean') astroConfig.buildOptions.sitemap = flags.sitemap;
- if (typeof flags.port === 'number') astroConfig.devOptions.port = flags.port;
-}
-
-/** Handle `astro run` command */
-async function runCommand(rawRoot: string, cmd: (a: AstroConfig) => Promise<void>, options: CLIState['options']) {
- try {
- const projectRoot = options.projectRoot || rawRoot;
- const astroConfig = await loadConfig(projectRoot, options.config);
- mergeCLIFlags(astroConfig, options);
-
- return cmd(astroConfig);
- } catch (err) {
- console.error(colors.red(err.toString() || err));
- process.exit(1);
- }
-}
-
-const cmdMap = new Map([
- ['build', buildAndExit],
- ['dev', devServer],
-]);
-
-/** The primary CLI action */
-export async function cli(args: string[]) {
- const flags = yargs(args);
- const state = resolveArgs(flags);
-
- switch (state.cmd) {
- case 'help': {
- printHelp();
- process.exit(1);
- break;
- }
- case 'version': {
- await printVersion();
- process.exit(0);
- break;
- }
- case 'build':
- case 'dev': {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const cmd = cmdMap.get(state.cmd)!;
- runCommand(flags._[3], cmd, state.options);
- }
- }
-}
diff --git a/src/compiler/codegen/content.ts b/src/compiler/codegen/content.ts
deleted file mode 100644
index fb8f9e307..000000000
--- a/src/compiler/codegen/content.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import path from 'path';
-import { fdir, PathsOutput } from 'fdir';
-
-/**
- * Handling for import.meta.glob and import.meta.globEager
- */
-
-interface GlobOptions {
- namespace: string;
- filename: string;
-}
-
-interface GlobResult {
- /** Array of import statements to inject */
- imports: Set<string>;
- /** Replace original code with */
- code: string;
-}
-
-const crawler = new fdir();
-
-/** General glob handling */
-function globSearch(spec: string, { filename }: { filename: string }): string[] {
- try {
- // Note: fdir’s glob requires you to do some work finding the closest non-glob folder.
- // For example, this fails: .glob("./post/*.md").crawl("/…/src/pages") ❌
- // …but this doesn’t: .glob("*.md").crawl("/…/src/pages/post") ✅
- let globDir = '';
- let glob = spec;
- for (const part of spec.split('/')) {
- if (!part.includes('*')) {
- // iterate through spec until first '*' is reached
- globDir = path.posix.join(globDir, part); // this must be POSIX-style
- glob = glob.replace(`${part}/`, ''); // move parent dirs off spec, and onto globDir
- } else {
- // at first '*', exit
- break;
- }
- }
-
- const cwd = path.join(path.dirname(filename), globDir.replace(/\//g, path.sep)); // this must match OS (could be '/' or '\')
- let found = crawler.glob(glob).crawl(cwd).sync() as PathsOutput;
- if (!found.length) {
- throw new Error(`No files matched "${spec}" from ${filename}`);
- }
- return found.map((importPath) => {
- if (importPath.startsWith('http') || importPath.startsWith('.')) return importPath;
- return `./` + globDir + '/' + importPath;
- });
- } catch (err) {
- throw new Error(`No files matched "${spec}" from ${filename}`);
- }
-}
-
-/** Astro.fetchContent() */
-export function fetchContent(spec: string, { namespace, filename }: GlobOptions): GlobResult {
- let code = '';
- const imports = new Set<string>();
- const importPaths = globSearch(spec, { filename });
-
- // gather imports
- importPaths.forEach((importPath, j) => {
- const id = `${namespace}_${j}`;
- imports.add(`import { __content as ${id} } from '${importPath}';`);
-
- // add URL if this appears within the /pages/ directory (probably can be improved)
- const fullPath = path.resolve(path.dirname(filename), importPath);
- if (fullPath.includes(`${path.sep}pages${path.sep}`)) {
- const url = importPath.replace(/^\./, '').replace(/\.md$/, '');
- imports.add(`${id}.url = '${url}';`);
- }
- });
-
- // generate replacement code
- code += `${namespace} = [${importPaths.map((_, j) => `${namespace}_${j}`).join(',')}];\n`;
-
- return { imports, code };
-}
diff --git a/src/compiler/codegen/index.ts b/src/compiler/codegen/index.ts
deleted file mode 100644
index 5113799d6..000000000
--- a/src/compiler/codegen/index.ts
+++ /dev/null
@@ -1,686 +0,0 @@
-import type { CompileOptions } from '../../@types/compiler';
-import type { AstroConfig, ValidExtensionPlugins } from '../../@types/astro';
-import type { Ast, Script, Style, TemplateNode } from '../../parser/interfaces';
-import type { TransformResult } from '../../@types/astro';
-
-import eslexer from 'es-module-lexer';
-import esbuild from 'esbuild';
-import path from 'path';
-import { walk } from 'estree-walker';
-import _babelGenerator from '@babel/generator';
-import babelParser from '@babel/parser';
-import { codeFrameColumns } from '@babel/code-frame';
-import * as babelTraverse from '@babel/traverse';
-import { ImportDeclaration, ExportNamedDeclaration, VariableDeclarator, Identifier } from '@babel/types';
-import { warn } from '../../logger.js';
-import { fetchContent } from './content.js';
-import { isFetchContent } from './utils.js';
-import { yellow } from 'kleur/colors';
-
-const traverse: typeof babelTraverse.default = (babelTraverse.default as any).default;
-const babelGenerator: typeof _babelGenerator =
- // @ts-ignore
- _babelGenerator.default;
-const { transformSync } = esbuild;
-
-interface Attribute {
- start: number;
- end: number;
- type: 'Attribute';
- name: string;
- value: TemplateNode[] | boolean;
-}
-
-interface CodeGenOptions {
- compileOptions: CompileOptions;
- filename: string;
- fileID: string;
-}
-
-/** Format Astro internal import URL */
-function internalImport(internalPath: string) {
- return `/_astro_internal/${internalPath}`;
-}
-
-/** Retrieve attributes from TemplateNode */
-function getAttributes(attrs: Attribute[]): Record<string, string> {
- let result: Record<string, string> = {};
- for (const attr of attrs) {
- if (attr.value === true) {
- result[attr.name] = JSON.stringify(attr.value);
- continue;
- }
- if (attr.value === false || attr.value === undefined) {
- // note: attr.value shouldn’t be `undefined`, but a bad transform would cause a compile error here, so prevent that
- continue;
- }
- if (attr.value.length > 1) {
- result[attr.name] =
- '(' +
- attr.value
- .map((v: TemplateNode) => {
- if (v.content) {
- return v.content;
- } else {
- return JSON.stringify(getTextFromAttribute(v));
- }
- })
- .join('+') +
- ')';
- continue;
- }
- const val = attr.value[0];
- if (!val) {
- result[attr.name] = '(' + val + ')';
- continue;
- }
- switch (val.type) {
- case 'MustacheTag': {
- // FIXME: this won't work when JSX element can appear in attributes (rare but possible).
- result[attr.name] = '(' + val.expression.codeChunks[0] + ')';
- continue;
- }
- case 'Text':
- result[attr.name] = JSON.stringify(getTextFromAttribute(val));
- continue;
- default:
- throw new Error(`UNKNOWN: ${val.type}`);
- }
- }
- return result;
-}
-
-/** Get value from a TemplateNode Attribute (text attributes only!) */
-function getTextFromAttribute(attr: any): string {
- switch (attr.type) {
- case 'Text': {
- if (attr.raw !== undefined) {
- return attr.raw;
- }
- if (attr.data !== undefined) {
- return attr.data;
- }
- break;
- }
- case 'MustacheTag': {
- // FIXME: this won't work when JSX element can appear in attributes (rare but possible).
- return attr.expression.codeChunks[0];
- }
- }
- throw new Error(`Unknown attribute type ${attr.type}`);
-}
-
-/** Convert TemplateNode attributes to string */
-function generateAttributes(attrs: Record<string, string>): string {
- let result = '{';
- for (const [key, val] of Object.entries(attrs)) {
- result += JSON.stringify(key) + ':' + val + ',';
- }
- return result + '}';
-}
-
-interface ComponentInfo {
- type: string;
- url: string;
- plugin: string | undefined;
-}
-
-const defaultExtensions: Readonly<Record<string, ValidExtensionPlugins>> = {
- '.astro': 'astro',
- '.jsx': 'react',
- '.tsx': 'react',
- '.vue': 'vue',
- '.svelte': 'svelte',
-};
-
-type DynamicImportMap = Map<'vue' | 'react' | 'react-dom' | 'preact' | 'svelte', string>;
-
-interface GetComponentWrapperOptions {
- filename: string;
- astroConfig: AstroConfig;
- dynamicImports: DynamicImportMap;
-}
-
-/** Generate Astro-friendly component import */
-function getComponentWrapper(_name: string, { type, plugin, url }: ComponentInfo, opts: GetComponentWrapperOptions) {
- const { astroConfig, dynamicImports, filename } = opts;
- const { astroRoot } = astroConfig;
- const [name, kind] = _name.split(':');
- const currFileUrl = new URL(`file://${filename}`);
-
- if (!plugin) {
- throw new Error(`No supported plugin found for ${type ? `extension ${type}` : `${url} (try adding an extension)`}`);
- }
-
- const getComponentUrl = (ext = '.js') => {
- const outUrl = new URL(url, currFileUrl);
- return '/_astro/' + path.posix.relative(astroRoot.pathname, outUrl.pathname).replace(/\.[^.]+$/, ext);
- };
-
- switch (plugin) {
- case 'astro': {
- if (kind) {
- throw new Error(`Astro does not support :${kind}`);
- }
- return {
- wrapper: name,
- wrapperImport: ``,
- };
- }
- case 'preact': {
- if (['load', 'idle', 'visible'].includes(kind)) {
- return {
- wrapper: `__preact_${kind}(${name}, ${JSON.stringify({
- componentUrl: getComponentUrl(),
- componentExport: 'default',
- frameworkUrls: {
- preact: dynamicImports.get('preact'),
- },
- })})`,
- wrapperImport: `import {__preact_${kind}} from '${internalImport('render/preact.js')}';`,
- };
- }
-
- return {
- wrapper: `__preact_static(${name})`,
- wrapperImport: `import {__preact_static} from '${internalImport('render/preact.js')}';`,
- };
- }
- case 'react': {
- if (['load', 'idle', 'visible'].includes(kind)) {
- return {
- wrapper: `__react_${kind}(${name}, ${JSON.stringify({
- componentUrl: getComponentUrl(),
- componentExport: 'default',
- frameworkUrls: {
- react: dynamicImports.get('react'),
- 'react-dom': dynamicImports.get('react-dom'),
- },
- })})`,
- wrapperImport: `import {__react_${kind}} from '${internalImport('render/react.js')}';`,
- };
- }
-
- return {
- wrapper: `__react_static(${name})`,
- wrapperImport: `import {__react_static} from '${internalImport('render/react.js')}';`,
- };
- }
- case 'svelte': {
- if (['load', 'idle', 'visible'].includes(kind)) {
- return {
- wrapper: `__svelte_${kind}(${name}, ${JSON.stringify({
- componentUrl: getComponentUrl('.svelte.js'),
- componentExport: 'default',
- frameworkUrls: {
- 'svelte-runtime': internalImport('runtime/svelte.js'),
- },
- })})`,
- wrapperImport: `import {__svelte_${kind}} from '${internalImport('render/svelte.js')}';`,
- };
- }
-
- return {
- wrapper: `__svelte_static(${name})`,
- wrapperImport: `import {__svelte_static} from '${internalImport('render/svelte.js')}';`,
- };
- }
- case 'vue': {
- if (['load', 'idle', 'visible'].includes(kind)) {
- return {
- wrapper: `__vue_${kind}(${name}, ${JSON.stringify({
- componentUrl: getComponentUrl('.vue.js'),
- componentExport: 'default',
- frameworkUrls: {
- vue: dynamicImports.get('vue'),
- },
- })})`,
- wrapperImport: `import {__vue_${kind}} from '${internalImport('render/vue.js')}';`,
- };
- }
-
- return {
- wrapper: `__vue_static(${name})`,
- wrapperImport: `import {__vue_static} from '${internalImport('render/vue.js')}';`,
- };
- }
- default: {
- throw new Error(`Unknown component type`);
- }
- }
-}
-
-/** Evaluate expression (safely) */
-function compileExpressionSafe(raw: string): string {
- let { code } = transformSync(raw, {
- loader: 'tsx',
- jsxFactory: 'h',
- jsxFragment: 'Fragment',
- charset: 'utf8',
- });
- return code;
-}
-
-/** Build dependency map of dynamic component runtime frameworks */
-async function acquireDynamicComponentImports(plugins: Set<ValidExtensionPlugins>, resolvePackageUrl: (s: string) => Promise<string>): Promise<DynamicImportMap> {
- const importMap: DynamicImportMap = new Map();
- for (let plugin of plugins) {
- switch (plugin) {
- case 'vue': {
- importMap.set('vue', await resolvePackageUrl('vue'));
- break;
- }
- case 'react': {
- importMap.set('react', await resolvePackageUrl('react'));
- importMap.set('react-dom', await resolvePackageUrl('react-dom'));
- break;
- }
- case 'preact': {
- importMap.set('preact', await resolvePackageUrl('preact'));
- break;
- }
- case 'svelte': {
- importMap.set('svelte', await resolvePackageUrl('svelte'));
- break;
- }
- }
- }
- return importMap;
-}
-
-type Components = Record<string, { type: string; url: string; plugin: string | undefined }>;
-
-interface CompileResult {
- script: string;
- componentPlugins: Set<ValidExtensionPlugins>;
- createCollection?: string;
-}
-
-interface CodegenState {
- filename: string;
- components: Components;
- css: string[];
- importExportStatements: Set<string>;
- dynamicImports: DynamicImportMap;
-}
-
-/** Compile/prepare Astro frontmatter scripts */
-function compileModule(module: Script, state: CodegenState, compileOptions: CompileOptions): CompileResult {
- const { extensions = defaultExtensions } = compileOptions;
-
- const componentImports: ImportDeclaration[] = [];
- const componentProps: VariableDeclarator[] = [];
- const componentExports: ExportNamedDeclaration[] = [];
-
- const contentImports = new Map<string, { spec: string; declarator: string }>();
-
- let script = '';
- let propsStatement = '';
- let contentCode = ''; // code for handling Astro.fetchContent(), if any;
- let createCollection = ''; // function for executing collection
- const componentPlugins = new Set<ValidExtensionPlugins>();
-
- if (module) {
- const parseOptions: babelParser.ParserOptions = {
- sourceType: 'module',
- plugins: ['jsx', 'typescript', 'topLevelAwait'],
- };
- let parseResult;
- try {
- parseResult = babelParser.parse(module.content, parseOptions);
- } catch (err) {
- const location = { start: err.loc };
- const frame = codeFrameColumns(module.content, location);
- err.frame = frame;
- err.filename = state.filename;
- err.start = err.loc;
- throw err;
- }
- const program = parseResult.program;
-
- const { body } = program;
- let i = body.length;
- while (--i >= 0) {
- const node = body[i];
- switch (node.type) {
- case 'ExportNamedDeclaration': {
- if (!node.declaration) break;
- // const replacement = extract_exports(node);
-
- if (node.declaration.type === 'VariableDeclaration') {
- // case 1: prop (export let title)
-
- const declaration = node.declaration.declarations[0];
- if ((declaration.id as Identifier).name === '__layout' || (declaration.id as Identifier).name === '__content') {
- componentExports.push(node);
- } else {
- componentProps.push(declaration);
- }
- body.splice(i, 1);
- } else if (node.declaration.type === 'FunctionDeclaration') {
- // case 2: createCollection (export async function)
- if (!node.declaration.id || node.declaration.id.name !== 'createCollection') break;
- createCollection = module.content.substring(node.declaration.start || 0, node.declaration.end || 0);
-
- // remove node
- body.splice(i, 1);
- }
- break;
- }
- case 'FunctionDeclaration': {
- break;
- }
- case 'ImportDeclaration': {
- componentImports.push(node);
- body.splice(i, 1); // remove node
- break;
- }
- case 'VariableDeclaration': {
- for (const declaration of node.declarations) {
- // only select Astro.fetchContent() calls here. this utility filters those out for us.
- if (!isFetchContent(declaration)) continue;
-
- // remove node
- body.splice(i, 1);
-
- // a bit of munging
- let { id, init } = declaration;
- if (!id || !init || id.type !== 'Identifier') continue;
- if (init.type === 'AwaitExpression') {
- init = init.argument;
- const shortname = path.relative(compileOptions.astroConfig.projectRoot.pathname, state.filename);
- warn(compileOptions.logging, shortname, yellow('awaiting Astro.fetchContent() not necessary'));
- }
- if (init.type !== 'CallExpression') continue;
-
- // gather data
- const namespace = id.name;
-
- if ((init as any).arguments[0].type !== 'StringLiteral') {
- throw new Error(`[Astro.fetchContent] Only string literals allowed, ex: \`Astro.fetchContent('./post/*.md')\`\n ${state.filename}`);
- }
- const spec = (init as any).arguments[0].value;
- if (typeof spec === 'string') contentImports.set(namespace, { spec, declarator: node.kind });
- }
- break;
- }
- }
- }
-
- for (const componentImport of componentImports) {
- const importUrl = componentImport.source.value;
- const componentType = path.posix.extname(importUrl);
- const specifier = componentImport.specifiers[0];
- if (!specifier) continue; // this is unused
- // set componentName to default import if used (user), or use filename if no default import (mostly internal use)
- const componentName = specifier.type === 'ImportDefaultSpecifier' ? specifier.local.name : path.posix.basename(importUrl, componentType);
- const plugin = extensions[componentType] || defaultExtensions[componentType];
- state.components[componentName] = {
- type: componentType,
- plugin,
- url: importUrl,
- };
- if (plugin) {
- componentPlugins.add(plugin);
- }
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- state.importExportStatements.add(module.content.slice(componentImport.start!, componentImport.end!));
- }
- for (const componentImport of componentExports) {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- state.importExportStatements.add(module.content.slice(componentImport.start!, componentImport.end!));
- }
-
- if (componentProps.length > 0) {
- propsStatement = 'let {';
- for (const componentExport of componentProps) {
- propsStatement += `${(componentExport.id as Identifier).name}`;
- if (componentExport.init) {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- propsStatement += `= ${babelGenerator(componentExport.init!).code}`;
- }
- propsStatement += `,`;
- }
- propsStatement += `} = props;\n`;
- }
-
- // handle createCollection, if any
- if (createCollection) {
- // TODO: improve this? while transforming in-place isn’t great, this happens at most once per-route
- const ast = babelParser.parse(createCollection, {
- sourceType: 'module',
- });
- traverse(ast, {
- enter({ node }) {
- switch (node.type) {
- case 'VariableDeclaration': {
- for (const declaration of node.declarations) {
- // only select Astro.fetchContent() calls here. this utility filters those out for us.
- if (!isFetchContent(declaration)) continue;
-
- // a bit of munging
- let { id, init } = declaration;
- if (!id || !init || id.type !== 'Identifier') continue;
- if (init.type === 'AwaitExpression') {
- init = init.argument;
- const shortname = path.relative(compileOptions.astroConfig.projectRoot.pathname, state.filename);
- warn(compileOptions.logging, shortname, yellow('awaiting Astro.fetchContent() not necessary'));
- }
- if (init.type !== 'CallExpression') continue;
-
- // gather data
- const namespace = id.name;
-
- if ((init as any).arguments[0].type !== 'StringLiteral') {
- throw new Error(`[Astro.fetchContent] Only string literals allowed, ex: \`Astro.fetchContent('./post/*.md')\`\n ${state.filename}`);
- }
- const spec = (init as any).arguments[0].value;
- if (typeof spec !== 'string') break;
-
- const globResult = fetchContent(spec, { namespace, filename: state.filename });
-
- let imports = '';
- for (const importStatement of globResult.imports) {
- imports += importStatement + '\n';
- }
-
- createCollection =
- imports + '\nexport ' + createCollection.substring(0, declaration.start || 0) + globResult.code + createCollection.substring(declaration.end || 0);
- }
- break;
- }
- }
- },
- });
- }
-
- // Astro.fetchContent()
- for (const [namespace, { spec }] of contentImports.entries()) {
- const globResult = fetchContent(spec, { namespace, filename: state.filename });
- for (const importStatement of globResult.imports) {
- state.importExportStatements.add(importStatement);
- }
- contentCode += globResult.code;
- }
-
- script = propsStatement + contentCode + babelGenerator(program).code;
- }
-
- return {
- script,
- componentPlugins,
- createCollection: createCollection || undefined,
- };
-}
-
-/** Compile styles */
-function compileCss(style: Style, state: CodegenState) {
- walk(style, {
- enter(node: TemplateNode) {
- if (node.type === 'Style') {
- state.css.push(node.content.styles); // if multiple <style> tags, combine together
- this.skip();
- }
- },
- leave(node: TemplateNode) {
- if (node.type === 'Style') {
- this.remove(); // this will be optimized in a global CSS file; remove so it‘s not accidentally inlined
- }
- },
- });
-}
-
-/** Compile page markup */
-function compileHtml(enterNode: TemplateNode, state: CodegenState, compileOptions: CompileOptions) {
- const { components, css, importExportStatements, dynamicImports, filename } = state;
- const { astroConfig } = compileOptions;
-
- let outSource = '';
- walk(enterNode, {
- enter(node: TemplateNode) {
- switch (node.type) {
- case 'Expression': {
- let children: string[] = [];
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- for (const child of node.children!) {
- children.push(compileHtml(child, state, compileOptions));
- }
- let raw = '';
- let nextChildIndex = 0;
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- for (const chunk of node.codeChunks!) {
- raw += chunk;
- if (nextChildIndex < children.length) {
- raw += children[nextChildIndex++];
- }
- }
- // 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();
- break;
- }
- case 'MustacheTag':
- case 'Comment':
- return;
- case 'Fragment':
- break;
- case 'Slot':
- case 'Head':
- case 'InlineComponent':
- case 'Title':
- case 'Element': {
- const name: string = node.name;
- if (!name) {
- throw new Error('AHHHH');
- }
- const attributes = getAttributes(node.attributes);
-
- outSource += outSource === '' ? '' : ',';
- if (node.type === 'Slot') {
- outSource += `(children`;
- return;
- }
- const COMPONENT_NAME_SCANNER = /^[A-Z]/;
- if (!COMPONENT_NAME_SCANNER.test(name)) {
- outSource += `h("${name}", ${attributes ? generateAttributes(attributes) : 'null'}`;
- return;
- }
- const [componentName, componentKind] = name.split(':');
- const componentImportData = components[componentName];
- if (!componentImportData) {
- throw new Error(`Unknown Component: ${componentName}`);
- }
- const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], { astroConfig, dynamicImports, filename });
- if (wrapperImport) {
- importExportStatements.add(wrapperImport);
- }
-
- outSource += `h(${wrapper}, ${attributes ? generateAttributes(attributes) : 'null'}`;
- return;
- }
- case 'Attribute': {
- this.skip();
- return;
- }
- case 'Style': {
- css.push(node.content.styles); // if multiple <style> tags, combine together
- this.skip();
- return;
- }
- case 'Text': {
- const text = getTextFromAttribute(node);
- if (!text.trim()) {
- return;
- }
- outSource += ',' + JSON.stringify(text);
- return;
- }
- default:
- throw new Error('Unexpected (enter) node type: ' + node.type);
- }
- },
- leave(node, parent, prop, index) {
- switch (node.type) {
- case 'Text':
- case 'Attribute':
- case 'Comment':
- case 'Fragment':
- case 'Expression':
- case 'MustacheTag':
- return;
- case 'Slot':
- case 'Head':
- case 'Body':
- case 'Title':
- case 'Element':
- case 'InlineComponent':
- outSource += ')';
- return;
- case 'Style': {
- this.remove(); // this will be optimized in a global CSS file; remove so it‘s not accidentally inlined
- return;
- }
- default:
- throw new Error('Unexpected (leave) node type: ' + node.type);
- }
- },
- });
-
- return outSource;
-}
-
-/**
- * Codegen
- * Step 3/3 in Astro SSR.
- * This is the final pass over a document AST before it‘s converted to an h() function
- * and handed off to Snowpack to build.
- * @param {Ast} AST The parsed AST to crawl
- * @param {object} CodeGenOptions
- */
-export async function codegen(ast: Ast, { compileOptions, filename }: CodeGenOptions): Promise<TransformResult> {
- await eslexer.init;
-
- const state: CodegenState = {
- filename,
- components: {},
- css: [],
- importExportStatements: new Set(),
- dynamicImports: new Map(),
- };
-
- const { script, componentPlugins, createCollection } = compileModule(ast.module, state, compileOptions);
- state.dynamicImports = await acquireDynamicComponentImports(componentPlugins, compileOptions.resolvePackageUrl);
-
- compileCss(ast.css, state);
-
- const html = compileHtml(ast.html, state, compileOptions);
-
- return {
- script: script,
- imports: Array.from(state.importExportStatements),
- html,
- css: state.css.length ? state.css.join('\n\n') : undefined,
- createCollection,
- };
-}
diff --git a/src/compiler/codegen/utils.ts b/src/compiler/codegen/utils.ts
deleted file mode 100644
index e1c558bc4..000000000
--- a/src/compiler/codegen/utils.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Codegen utils
- */
-
-import type { VariableDeclarator } from '@babel/types';
-
-/** Is this an import.meta.* built-in? You can pass an optional 2nd param to see if the name matches as well. */
-export function isImportMetaDeclaration(declaration: VariableDeclarator, metaName?: string): boolean {
- let { init } = declaration;
- if (!init) return false; // definitely not import.meta
- // this could be `await import.meta`; if so, evaluate that:
- if (init.type === 'AwaitExpression') {
- init = init.argument;
- }
- // continue evaluating
- if (init.type !== 'CallExpression' || init.callee.type !== 'MemberExpression' || init.callee.object.type !== 'MetaProperty') return false;
- // optional: if metaName specified, match that
- if (metaName && (init.callee.property.type !== 'Identifier' || init.callee.property.name !== metaName)) return false;
- return true;
-}
-
-/** Is this an Astro.fetchContent() call? */
-export function isFetchContent(declaration: VariableDeclarator): boolean {
- let { init } = declaration;
- if (!init) return false; // definitely not import.meta
- // this could be `await import.meta`; if so, evaluate that:
- if (init.type === 'AwaitExpression') {
- init = init.argument;
- }
- // continue evaluating
- if (
- init.type !== 'CallExpression' ||
- init.callee.type !== 'MemberExpression' ||
- (init.callee.object as any).name !== 'Astro' ||
- (init.callee.property as any).name !== 'fetchContent'
- )
- return false;
- return true;
-}
diff --git a/src/compiler/index.ts b/src/compiler/index.ts
deleted file mode 100644
index 7e7bfc4c6..000000000
--- a/src/compiler/index.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-import type { CompileResult, TransformResult } from '../@types/astro';
-import type { CompileOptions } from '../@types/compiler.js';
-
-import path from 'path';
-import micromark from 'micromark';
-import gfmSyntax from 'micromark-extension-gfm';
-import matter from 'gray-matter';
-import gfmHtml from 'micromark-extension-gfm/html.js';
-
-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 { transform } from './transform/index.js';
-import { codegen } from './codegen/index.js';
-
-/** Return Astro internal import URL */
-function internalImport(internalPath: string) {
- return `/_astro_internal/${internalPath}`;
-}
-
-interface ConvertAstroOptions {
- compileOptions: CompileOptions;
- filename: string;
- fileID: string;
-}
-
-/**
- * .astro -> .jsx
- * Core function processing .astro files. Initiates all 3 phases of compilation:
- * 1. Parse
- * 2. Transform
- * 3. Codegen
- */
-async function convertAstroToJsx(template: string, opts: ConvertAstroOptions): Promise<TransformResult> {
- const { filename } = opts;
-
- // 1. Parse
- const ast = parse(template, {
- filename,
- });
-
- // 2. Transform the AST
- await transform(ast, opts);
-
- // 3. Turn AST into JSX
- return await codegen(ast, opts);
-}
-
-/**
- * .md -> .astro source
- */
-export async function convertMdToAstroSource(contents: string): Promise<string> {
- const { data: frontmatterData, content } = matter(contents);
- const { headers, headersExtension } = createMarkdownHeadersCollector();
- const { htmlAstro, mdAstro } = encodeAstroMdx();
- const mdHtml = micromark(content, {
- allowDangerousHtml: true,
- extensions: [gfmSyntax(), ...htmlAstro],
- htmlExtensions: [gfmHtml, encodeMarkdown, headersExtension, mdAstro],
- });
-
- // TODO: Warn if reserved word is used in "frontmatterData"
- const contentData: any = {
- ...frontmatterData,
- headers,
- source: content,
- };
-
- let imports = '';
- 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(contentData).replace(/\<\/script\>/g, `</scrip" + "t>`);
-
- return `---
- ${imports}
- ${frontmatterData.layout ? `import {__renderPage as __layout} from '${frontmatterData.layout}';` : 'const __layout = undefined;'}
- export const __content = ${stringifiedSetupContext};
----
-<section>${mdHtml}</section>`;
-}
-
-/**
- * .md -> .jsx
- * Core function processing Markdown, but along the way also calls convertAstroToJsx().
- */
-async function convertMdToJsx(
- contents: string,
- { compileOptions, filename, fileID }: { compileOptions: CompileOptions; filename: string; fileID: string }
-): Promise<TransformResult> {
- const raw = await convertMdToAstroSource(contents);
- const convertOptions = { compileOptions, filename, fileID };
- return await convertAstroToJsx(raw, convertOptions);
-}
-
-type SupportedExtensions = '.astro' | '.md';
-
-/** Given a file, process it either as .astro or .md. */
-async function transformFromSource(
- contents: string,
- { compileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string }
-): Promise<TransformResult> {
- const fileID = path.relative(projectRoot, filename);
- switch (path.extname(filename) as SupportedExtensions) {
- case '.astro':
- return await convertAstroToJsx(contents, { compileOptions, filename, fileID });
- case '.md':
- return await convertMdToJsx(contents, { compileOptions, filename, fileID });
- default:
- throw new Error('Not Supported!');
- }
-}
-
-/** Return internal code that gets processed in Snowpack */
-export async function compileComponent(
- source: string,
- { compileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string }
-): Promise<CompileResult> {
- const result = await transformFromSource(source, { compileOptions, filename, projectRoot });
-
- // return template
- let modJsx = `
-import fetch from 'node-fetch';
-
-// <script astro></script>
-${result.imports.join('\n')}
-
-// \`__render()\`: Render the contents of the Astro module.
-import { h, Fragment } from '${internalImport('h.js')}';
-const __astroRequestSymbol = Symbol('astro.request');
-async function __render(props, ...children) {
- const Astro = {
- request: props[__astroRequestSymbol]
- };
-
- ${result.script}
- return h(Fragment, null, ${result.html});
-}
-export default __render;
-
-${result.createCollection || ''}
-
-// \`__renderPage()\`: Render the contents of the Astro module as a page. This is a special flow,
-// triggered by loading a component directly by URL.
-export async function __renderPage({request, children, props}) {
- const currentChild = {
- layout: typeof __layout === 'undefined' ? undefined : __layout,
- content: typeof __content === 'undefined' ? undefined : __content,
- __render,
- };
-
- props[__astroRequestSymbol] = request;
- const childBodyResult = await currentChild.__render(props, children);
-
- // find layout, if one was given.
- if (currentChild.layout) {
- return currentChild.layout({
- request,
- props: {content: currentChild.content},
- children: [childBodyResult],
- });
- }
-
- return childBodyResult;
-};\n`;
-
- return {
- result,
- contents: modJsx,
- css: result.css,
- };
-}
diff --git a/src/compiler/markdown/micromark-collect-headers.ts b/src/compiler/markdown/micromark-collect-headers.ts
deleted file mode 100644
index 69781231a..000000000
--- a/src/compiler/markdown/micromark-collect-headers.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import slugger from 'github-slugger';
-
-/**
- * Create Markdown Headers Collector
- * NOTE: micromark has terrible TS types. Instead of fighting with the
- * limited/broken TS types that they ship, we just reach for our good friend, "any".
- */
-export function createMarkdownHeadersCollector() {
- const headers: any[] = [];
- let currentHeader: any;
- return {
- headers,
- headersExtension: {
- enter: {
- atxHeading(node: any) {
- currentHeader = {};
- headers.push(currentHeader);
- this.buffer();
- },
- atxHeadingSequence(node: any) {
- currentHeader.depth = this.sliceSerialize(node).length;
- },
- atxHeadingText(node: any) {
- currentHeader.text = this.sliceSerialize(node);
- },
- } as any,
- exit: {
- atxHeading(node: any) {
- currentHeader.slug = slugger.slug(currentHeader.text);
- this.resume();
- this.tag(`<h${currentHeader.depth} id="${currentHeader.slug}">`);
- this.raw(currentHeader.text);
- this.tag(`</h${currentHeader.depth}>`);
- },
- } as any,
- } as any,
- };
-}
diff --git a/src/compiler/markdown/micromark-encode.ts b/src/compiler/markdown/micromark-encode.ts
deleted file mode 100644
index 635ab3b54..000000000
--- a/src/compiler/markdown/micromark-encode.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import type { Token } from 'micromark/dist/shared-types';
-import type { MicromarkExtension, MicromarkExtensionContext } from '../../@types/micromark';
-
-const characterReferences = {
- '"': 'quot',
- '&': 'amp',
- '<': 'lt',
- '>': 'gt',
- '{': 'lbrace',
- '}': 'rbrace',
-};
-
-type EncodedChars = '"' | '&' | '<' | '>' | '{' | '}';
-
-/** Encode HTML entity */
-function encode(value: string): string {
- return value.replace(/["&<>{}]/g, (raw: string) => {
- return '&' + characterReferences[raw as EncodedChars] + ';';
- });
-}
-
-/** Encode Markdown node */
-function encodeToken(this: MicromarkExtensionContext) {
- const token: Token = arguments[0];
- const value = this.sliceSerialize(token);
- this.raw(encode(value));
-}
-
-const plugin: MicromarkExtension = {
- exit: {
- codeTextData: encodeToken,
- codeFlowValue: encodeToken,
- },
-};
-
-export { plugin as encodeMarkdown };
diff --git a/src/compiler/markdown/micromark-mdx-astro.ts b/src/compiler/markdown/micromark-mdx-astro.ts
deleted file mode 100644
index b978ad407..000000000
--- a/src/compiler/markdown/micromark-mdx-astro.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { MicromarkExtension } from '../../@types/micromark';
-import mdxExpression from 'micromark-extension-mdx-expression';
-import mdxJsx from 'micromark-extension-mdx-jsx';
-
-/**
- * Keep MDX.
- */
-export function encodeAstroMdx() {
- const extension: MicromarkExtension = {
- enter: {
- mdxJsxFlowTag(node: any) {
- const mdx = this.sliceSerialize(node);
- this.raw(mdx);
- },
- },
- };
-
- return {
- htmlAstro: [mdxExpression(), mdxJsx()],
- mdAstro: extension,
- };
-}
diff --git a/src/compiler/markdown/micromark.d.ts b/src/compiler/markdown/micromark.d.ts
deleted file mode 100644
index fd094306e..000000000
--- a/src/compiler/markdown/micromark.d.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-declare module 'micromark-extension-mdx-expression' {
- import type { HtmlExtension } from 'micromark/dist/shared-types';
-
- export default function (): HtmlExtension;
-}
-
-declare module 'micromark-extension-mdx-jsx' {
- import type { HtmlExtension } from 'micromark/dist/shared-types';
-
- export default function (): HtmlExtension;
-}
diff --git a/src/compiler/transform/doctype.ts b/src/compiler/transform/doctype.ts
deleted file mode 100644
index e871f5b48..000000000
--- a/src/compiler/transform/doctype.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Transformer } from '../../@types/transformer';
-
-/** Transform <!doctype> tg */
-export default function (_opts: { filename: string; fileID: string }): Transformer {
- let hasDoctype = false;
-
- return {
- visitors: {
- html: {
- Element: {
- enter(node, parent, _key, index) {
- if (node.name === '!doctype') {
- hasDoctype = true;
- }
- if (node.name === 'html' && !hasDoctype) {
- const dtNode = {
- start: 0,
- end: 0,
- attributes: [{ type: 'Attribute', name: 'html', value: true, start: 0, end: 0 }],
- children: [],
- name: '!doctype',
- type: 'Element',
- };
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- parent.children!.splice(index, 0, dtNode);
- hasDoctype = true;
- }
- },
- },
- },
- },
- async finalize() {
- // Nothing happening here.
- },
- };
-}
diff --git a/src/compiler/transform/index.ts b/src/compiler/transform/index.ts
deleted file mode 100644
index 02a98709b..000000000
--- a/src/compiler/transform/index.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-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)) {
- // 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()));
-}
diff --git a/src/compiler/transform/module-scripts.ts b/src/compiler/transform/module-scripts.ts
deleted file mode 100644
index aff1ec4f6..000000000
--- a/src/compiler/transform/module-scripts.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import type { Transformer } from '../../@types/transformer';
-import type { CompileOptions } from '../../@types/compiler';
-
-import path from 'path';
-import { getAttrValue, setAttrValue } from '../../ast.js';
-
-/** 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}`);
-
- return {
- visitors: {
- html: {
- Element: {
- enter(node) {
- let name = node.name;
- if (name !== 'script') {
- return;
- }
-
- let type = getAttrValue(node.attributes, 'type');
- if (type !== 'module') {
- return;
- }
-
- let src = getAttrValue(node.attributes, 'src');
- if (!src || !src.startsWith('.')) {
- return;
- }
-
- const srcUrl = new URL(src, fileUrl);
- const fromAstroRoot = path.posix.relative(astroRoot.pathname, srcUrl.pathname);
- const absoluteUrl = `/_astro/${fromAstroRoot}`;
- setAttrValue(node.attributes, 'src', absoluteUrl);
- },
- },
- },
- },
- async finalize() {},
- };
-}
diff --git a/src/compiler/transform/postcss-scoped-styles/index.ts b/src/compiler/transform/postcss-scoped-styles/index.ts
deleted file mode 100644
index 23350869c..000000000
--- a/src/compiler/transform/postcss-scoped-styles/index.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import { Declaration, Plugin } from 'postcss';
-
-interface AstroScopedOptions {
- className: string;
-}
-
-interface Selector {
- start: number;
- end: number;
- value: string;
-}
-
-const CSS_SEPARATORS = new Set([' ', ',', '+', '>', '~']);
-const KEYFRAME_PERCENT = /\d+\.?\d*%/;
-
-/** HTML tags that should never get scoped classes */
-export const NEVER_SCOPED_TAGS = new Set<string>(['base', 'body', 'font', 'frame', 'frameset', 'head', 'html', 'link', 'meta', 'noframes', 'noscript', 'script', 'style', 'title']);
-
-/**
- * Scope Rules
- * Given a selector string (`.btn>span,.nav>span`), add an additional CSS class to every selector (`.btn.myClass>span.myClass,.nav.myClass>span.myClass`)
- * @param {string} selector The minified selector string to parse. Cannot contain arbitrary whitespace (other than child selector syntax).
- * @param {string} className The CSS class to apply.
- */
-export function scopeRule(selector: string, className: string) {
- // if this is a keyframe keyword, return original selector
- if (selector === 'from' || selector === 'to' || KEYFRAME_PERCENT.test(selector)) {
- return selector;
- }
-
- // For everything else, parse & scope
- const c = className.replace(/^\.?/, '.'); // make sure class always has leading '.'
- const selectors: Selector[] = [];
- let ss = selector; // final output
-
- // Pass 1: parse selector string; extract top-level selectors
- {
- let start = 0;
- let lastValue = '';
- let parensOpen = false;
- for (let n = 0; n < ss.length; n++) {
- const isEnd = n === selector.length - 1;
- if (selector[n] === '(') parensOpen = true;
- if (selector[n] === ')') parensOpen = false;
- if (isEnd || (parensOpen === false && CSS_SEPARATORS.has(selector[n]))) {
- lastValue = selector.substring(start, isEnd ? undefined : n);
- if (!lastValue) continue;
- selectors.push({ start, end: isEnd ? n + 1 : n, value: lastValue });
- start = n + 1;
- }
- }
- }
-
- // Pass 2: starting from end, transform selectors w/ scoped class
- for (let i = selectors.length - 1; i >= 0; i--) {
- const { start, end, value } = selectors[i];
- const head = ss.substring(0, start);
- const tail = ss.substring(end);
-
- // replace '*' with className
- if (value === '*') {
- ss = head + c + tail;
- continue;
- }
-
- // leave :global() alone!
- if (value.startsWith(':global(')) {
- ss =
- head +
- ss
- .substring(start, end)
- .replace(/^:global\(/, '')
- .replace(/\)$/, '') +
- tail;
- continue;
- }
-
- // don‘t scope body, title, etc.
- if (NEVER_SCOPED_TAGS.has(value)) {
- ss = head + value + tail;
- continue;
- }
-
- // scope everything else
- let newSelector = ss.substring(start, end);
- const pseudoIndex = newSelector.indexOf(':');
- if (pseudoIndex > 0) {
- // if there‘s a pseudoclass (:focus)
- ss = head + newSelector.substring(start, pseudoIndex) + c + newSelector.substr(pseudoIndex) + tail;
- } else {
- ss = head + newSelector + c + tail;
- }
- }
-
- return ss;
-}
-
-/** PostCSS Scope plugin */
-export default function astroScopedStyles(options: AstroScopedOptions): Plugin {
- return {
- postcssPlugin: '@astro/postcss-scoped-styles',
- Rule(rule) {
- rule.selector = scopeRule(rule.selector, options.className);
- },
- };
-}
diff --git a/src/compiler/transform/prism.ts b/src/compiler/transform/prism.ts
deleted file mode 100644
index 1bb024a84..000000000
--- a/src/compiler/transform/prism.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import type { Transformer } from '../../@types/transformer';
-import type { Script } from '../../parser/interfaces';
-import { getAttrValue } from '../../ast.js';
-
-const PRISM_IMPORT = `import Prism from 'astro/components/Prism.astro';\n`;
-const prismImportExp = /import Prism from ['"]astro\/components\/Prism.astro['"]/;
-/** escaping code samples that contain template string replacement parts, ${foo} or example. */
-function escape(code: string) {
- return code.replace(/[`$]/g, (match) => {
- return '\\' + match;
- });
-}
-/** default export - Transform prism */
-export default function (module: Script): Transformer {
- let usesPrism = false;
-
- return {
- visitors: {
- html: {
- Element: {
- enter(node) {
- if (node.name !== 'code') return;
- const className = getAttrValue(node.attributes, 'class') || '';
- const classes = className.split(' ');
-
- let lang;
- for (let cn of classes) {
- const matches = /language-(.+)/.exec(cn);
- if (matches) {
- lang = matches[1];
- }
- }
-
- if (!lang) return;
-
- let code;
- if (node.children?.length) {
- code = node.children[0].data;
- }
-
- const repl = {
- start: 0,
- end: 0,
- type: 'InlineComponent',
- name: 'Prism',
- attributes: [
- {
- type: 'Attribute',
- name: 'lang',
- value: [
- {
- type: 'Text',
- raw: lang,
- data: lang,
- },
- ],
- },
- {
- type: 'Attribute',
- name: 'code',
- value: [
- {
- type: 'MustacheTag',
- expression: {
- type: 'Expression',
- codeChunks: ['`' + escape(code) + '`'],
- children: [],
- },
- },
- ],
- },
- ],
- children: [],
- };
-
- this.replace(repl);
- usesPrism = true;
- },
- },
- },
- },
- async finalize() {
- // Add the Prism import if needed.
- if (usesPrism && !prismImportExp.test(module.content)) {
- module.content = PRISM_IMPORT + module.content;
- }
- },
- };
-}
diff --git a/src/compiler/transform/styles.ts b/src/compiler/transform/styles.ts
deleted file mode 100644
index 53585651f..000000000
--- a/src/compiler/transform/styles.ts
+++ /dev/null
@@ -1,290 +0,0 @@
-import crypto from 'crypto';
-import fs from 'fs';
-import { createRequire } from 'module';
-import path from 'path';
-import { fileURLToPath } from 'url';
-import autoprefixer from 'autoprefixer';
-import postcss, { Plugin } from 'postcss';
-import postcssKeyframes from 'postcss-icss-keyframes';
-import findUp from 'find-up';
-import sass from 'sass';
-import type { RuntimeMode } from '../../@types/astro';
-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';
-
-type StyleType = 'css' | 'scss' | 'sass' | 'postcss';
-
-declare global {
- interface ImportMeta {
- /** https://nodejs.org/api/esm.html#esm_import_meta_resolve_specifier_parent */
- resolve(specifier: string, parent?: string): Promise<any>;
- }
-}
-
-const getStyleType: Map<string, StyleType> = new Map([
- ['.css', 'css'],
- ['.pcss', 'postcss'],
- ['.sass', 'sass'],
- ['.scss', 'scss'],
- ['css', 'css'],
- ['sass', 'sass'],
- ['scss', 'scss'],
- ['text/css', 'css'],
- ['text/sass', 'sass'],
- ['text/scss', 'scss'],
-]);
-
-/** Should be deterministic, given a unique filename */
-function hashFromFilename(filename: string): string {
- const hash = crypto.createHash('sha256');
- return hash
- .update(filename.replace(/\\/g, '/'))
- .digest('base64')
- .toString()
- .replace(/[^A-Za-z0-9-]/g, '')
- .substr(0, 8);
-}
-
-export interface StyleTransformResult {
- css: string;
- type: StyleType;
-}
-
-interface StylesMiniCache {
- nodeModules: Map<string, string>; // filename: node_modules location
- tailwindEnabled?: boolean; // cache once per-run
-}
-
-/** Simple cache that only exists in memory per-run. Prevents the same lookups from happening over and over again within the same build or dev server session. */
-const miniCache: StylesMiniCache = {
- nodeModules: new Map<string, string>(),
-};
-
-export interface TransformStyleOptions {
- type?: string;
- filename: string;
- scopedClass: string;
- mode: RuntimeMode;
-}
-
-/** given a class="" string, does it contain a given class? */
-function hasClass(classList: string, className: string): boolean {
- if (!className) return false;
- for (const c of classList.split(' ')) {
- if (className === c.trim()) return true;
- }
- return false;
-}
-
-/** Convert styles to scoped CSS */
-async function transformStyle(code: string, { type, filename, scopedClass, mode }: TransformStyleOptions): Promise<StyleTransformResult> {
- let styleType: StyleType = 'css'; // important: assume CSS as default
- if (type) {
- styleType = getStyleType.get(type) || styleType;
- }
-
- // add file path to includePaths
- let includePaths: string[] = [path.dirname(filename)];
-
- // include node_modules to includePaths (allows @use-ing node modules, if it can be located)
- const cachedNodeModulesDir = miniCache.nodeModules.get(filename);
- if (cachedNodeModulesDir) {
- includePaths.push(cachedNodeModulesDir);
- } else {
- const nodeModulesDir = await findUp('node_modules', { type: 'directory', cwd: path.dirname(filename) });
- if (nodeModulesDir) {
- miniCache.nodeModules.set(filename, nodeModulesDir);
- includePaths.push(nodeModulesDir);
- }
- }
-
- // 1. Preprocess (currently only Sass supported)
- let css = '';
- switch (styleType) {
- case 'css': {
- css = code;
- break;
- }
- case 'sass':
- case 'scss': {
- css = sass.renderSync({ data: code, includePaths }).css.toString('utf8');
- break;
- }
- default: {
- throw new Error(`Unsupported: <style lang="${styleType}">`);
- }
- }
-
- // 2. Post-process (PostCSS)
- const postcssPlugins: Plugin[] = [];
-
- // 2a. Tailwind (only if project uses Tailwind)
- if (miniCache.tailwindEnabled) {
- try {
- const require = createRequire(import.meta.url);
- const tw = require.resolve('tailwindcss', { paths: [import.meta.url, process.cwd()] });
- postcssPlugins.push(require(tw) as any);
- } catch (err) {
- // eslint-disable-next-line no-console
- console.error(err);
- throw new Error(`tailwindcss not installed. Try running \`npm install tailwindcss\` and trying again.`);
- }
- }
-
- // 2b. Astro scoped styles (always on)
- postcssPlugins.push(astroScopedStyles({ className: scopedClass }));
-
- // 2c. Scoped @keyframes
- postcssPlugins.push(
- postcssKeyframes({
- generateScopedName(keyframesName) {
- return `${keyframesName}-${scopedClass}`;
- },
- })
- );
-
- // 2d. Autoprefixer (always on)
- postcssPlugins.push(autoprefixer());
-
- // 2e. Run PostCSS
- css = await postcss(postcssPlugins)
- .process(css, { from: filename, to: undefined })
- .then((result) => result.css);
-
- return { css, type: styleType };
-}
-
-/** 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
-
- // find Tailwind config, if first run (cache for subsequent runs)
- if (miniCache.tailwindEnabled === undefined) {
- const tailwindNames = ['tailwind.config.js', 'tailwind.config.mjs'];
- for (const loc of tailwindNames) {
- const tailwindLoc = path.join(fileURLToPath(compileOptions.astroConfig.projectRoot), loc);
- if (fs.existsSync(tailwindLoc)) {
- miniCache.tailwindEnabled = true; // Success! We have a Tailwind config file.
- debug(compileOptions.logging, 'tailwind', 'Found config. Enabling.');
- break;
- }
- }
- if (miniCache.tailwindEnabled !== true) miniCache.tailwindEnabled = false; // We couldn‘t find one; mark as false
- debug(compileOptions.logging, 'tailwind', 'No config found. Skipping.');
- }
-
- return {
- visitors: {
- html: {
- Element: {
- enter(node) {
- // 1. if <style> tag, transform it and continue to next node
- if (node.name === 'style') {
- // Same as ast.css (below)
- const code = Array.isArray(node.children) ? node.children.map(({ data }: any) => data).join('\n') : '';
- if (!code) return;
- const langAttr = (node.attributes || []).find(({ name }: any) => name === 'lang');
- styleNodes.push(node);
- styleTransformPromises.push(
- transformStyle(code, {
- type: (langAttr && langAttr.value[0] && langAttr.value[0].data) || undefined,
- filename,
- scopedClass,
- mode: compileOptions.mode,
- })
- );
- return;
- }
-
- // 2. add scoped HTML classes
- if (NEVER_SCOPED_TAGS.has(node.name)) return; // only continue if this is NOT a <script> tag, etc.
- // Note: currently we _do_ scope web components/custom elements. This seems correct?
-
- if (!node.attributes) node.attributes = [];
- const classIndex = node.attributes.findIndex(({ name }: any) => name === 'class');
- if (classIndex === -1) {
- // 3a. element has no class="" attribute; add one and append scopedClass
- node.attributes.push({ start: -1, end: -1, type: 'Attribute', name: 'class', value: [{ type: 'Text', raw: scopedClass, data: scopedClass }] });
- } else {
- // 3b. element has class=""; append scopedClass
- const attr = node.attributes[classIndex];
- for (let k = 0; k < attr.value.length; k++) {
- if (attr.value[k].type === 'Text') {
- // don‘t add same scopedClass twice
- if (!hasClass(attr.value[k].data, scopedClass)) {
- // string literal
- attr.value[k].raw += ' ' + scopedClass;
- attr.value[k].data += ' ' + scopedClass;
- }
- } 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].expression.codeChunks[0].includes(`' ${scopedClass}'`)) {
- // MustacheTag
- // FIXME: this won't work when JSX element can appear in attributes (rare but possible).
- attr.value[k].expression.codeChunks[0] = `(${attr.value[k].expression.codeChunks[0]}) + ' ${scopedClass}'`;
- }
- }
- }
- }
- },
- },
- },
- // CSS: compile styles, apply CSS Modules scoping
- css: {
- Style: {
- enter(node) {
- // Same as ast.html (above)
- // Note: this is duplicated from html because of the compiler we‘re using; in a future version we should combine these
- if (!node.content || !node.content.styles) return;
- const code = node.content.styles;
- const langAttr = (node.attributes || []).find(({ name }: any) => name === 'lang');
- styleNodes.push(node);
- styleTransformPromises.push(
- transformStyle(code, {
- type: (langAttr && langAttr.value[0] && langAttr.value[0].data) || undefined,
- filename,
- scopedClass,
- mode: compileOptions.mode,
- })
- );
- },
- },
- },
- },
- async finalize() {
- const styleTransforms = await Promise.all(styleTransformPromises);
-
- styleTransforms.forEach((result, n) => {
- if (styleNodes[n].attributes) {
- // 1. Replace with final CSS
- const isHeadStyle = !styleNodes[n].content;
- if (isHeadStyle) {
- // Note: <style> tags in <head> have different attributes/rules, because of the parser. Unknown why
- (styleNodes[n].children as any) = [{ ...(styleNodes[n].children as any)[0], data: result.css }];
- } else {
- styleNodes[n].content.styles = result.css;
- }
-
- // 2. Update <style> attributes
- const styleTypeIndex = styleNodes[n].attributes.findIndex(({ name }: any) => name === 'type');
- // add type="text/css"
- if (styleTypeIndex !== -1) {
- styleNodes[n].attributes[styleTypeIndex].value[0].raw = 'text/css';
- styleNodes[n].attributes[styleTypeIndex].value[0].data = 'text/css';
- } else {
- styleNodes[n].attributes.push({ name: 'type', type: 'Attribute', value: [{ type: 'Text', raw: 'text/css', data: 'text/css' }] });
- }
- // remove lang="*"
- const styleLangIndex = styleNodes[n].attributes.findIndex(({ name }: any) => name === 'lang');
- if (styleLangIndex !== -1) styleNodes[n].attributes.splice(styleLangIndex, 1);
- // TODO: add data-astro for later
- // styleNodes[n].attributes.push({ name: 'data-astro', type: 'Attribute', value: true });
- }
- });
- },
- };
-}
diff --git a/src/config.ts b/src/config.ts
deleted file mode 100644
index 8f3ebaf5a..000000000
--- a/src/config.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import type { AstroConfig } from './@types/astro';
-import { join as pathJoin, resolve as pathResolve } from 'path';
-import { existsSync } from 'fs';
-
-/** Type util */
-const type = (thing: any): string => (Array.isArray(thing) ? 'Array' : typeof thing);
-
-/** Throws error if a user provided an invalid config. Manually-implemented to avoid a heavy validation library. */
-function validateConfig(config: any): void {
- // basic
- if (config === undefined || config === null) throw new Error(`[astro config] Config empty!`);
- if (typeof config !== 'object') throw new Error(`[astro config] Expected object, received ${typeof config}`);
-
- // strings
- for (const key of ['projectRoot', 'astroRoot', 'dist', 'public', 'site']) {
- if (config[key] !== undefined && config[key] !== null && typeof config[key] !== 'string') {
- throw new Error(`[astro config] ${key}: ${JSON.stringify(config[key])}\n Expected string, received ${type(config[key])}.`);
- }
- }
-
- // booleans
- for (const key of ['sitemap']) {
- if (config[key] !== undefined && config[key] !== null && typeof config[key] !== 'boolean') {
- throw new Error(`[astro config] ${key}: ${JSON.stringify(config[key])}\n Expected boolean, received ${type(config[key])}.`);
- }
- }
-
- if(typeof config.devOptions?.port !== 'number') {
- throw new Error(`[astro config] devOptions.port: Expected number, received ${type(config.devOptions?.port)}`)
- }
-}
-
-/** Set default config values */
-function configDefaults(userConfig?: any): any {
- const config: any = { ...(userConfig || {}) };
-
- if (!config.projectRoot) config.projectRoot = '.';
- if (!config.astroRoot) config.astroRoot = './src';
- if (!config.dist) config.dist = './dist';
- if (!config.public) config.public = './public';
- if (!config.devOptions) config.devOptions = {};
- if (!config.devOptions.port) config.devOptions.port = 3000;
- if (!config.buildOptions) config.buildOptions = {};
- if (typeof config.buildOptions.sitemap === 'undefined') config.buildOptions.sitemap = true;
-
- return config;
-}
-
-/** Turn raw config values into normalized values */
-function normalizeConfig(userConfig: any, root: string): AstroConfig {
- const config: any = { ...(userConfig || {}) };
-
- const fileProtocolRoot = `file://${root}/`;
- config.projectRoot = new URL(config.projectRoot + '/', fileProtocolRoot);
- config.astroRoot = new URL(config.astroRoot + '/', fileProtocolRoot);
- config.public = new URL(config.public + '/', fileProtocolRoot);
-
- return config as AstroConfig;
-}
-
-/** Attempt to load an `astro.config.mjs` file */
-export async function loadConfig(rawRoot: string | undefined, configFileName = 'astro.config.mjs'): Promise<AstroConfig> {
- if (typeof rawRoot === 'undefined') {
- rawRoot = process.cwd();
- }
-
- const root = pathResolve(rawRoot);
- const astroConfigPath = pathJoin(root, configFileName);
-
- // load
- let config: any;
- if (existsSync(astroConfigPath)) {
- config = configDefaults((await import(astroConfigPath)).default);
- } else {
- config = configDefaults();
- }
-
- // validate
- validateConfig(config);
-
- // normalize
- config = normalizeConfig(config, root);
-
- return config as AstroConfig;
-}
diff --git a/src/dev.ts b/src/dev.ts
deleted file mode 100644
index 4ca8e28e9..000000000
--- a/src/dev.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import type { AstroConfig } from './@types/astro';
-import type { LogOptions } from './logger.js';
-
-import { logger as snowpackLogger } from 'snowpack';
-import { bold, green } from 'kleur/colors';
-import http from 'http';
-import { relative as pathRelative } from 'path';
-import { performance } from 'perf_hooks';
-import { defaultLogDestination, error, info, parseError } from './logger.js';
-import { createRuntime } from './runtime.js';
-
-const hostname = '127.0.0.1';
-
-// Disable snowpack from writing to stdout/err.
-snowpackLogger.level = 'silent';
-
-const logging: LogOptions = {
- level: 'debug',
- dest: defaultLogDestination,
-};
-
-/** The primary dev action */
-export default async function dev(astroConfig: AstroConfig) {
- const startServerTime = performance.now();
- const { projectRoot } = astroConfig;
-
- const runtime = await createRuntime(astroConfig, { mode: 'development', logging });
-
- const server = http.createServer(async (req, res) => {
- const result = await runtime.load(req.url);
-
- switch (result.statusCode) {
- case 200: {
- if (result.contentType) {
- res.setHeader('Content-Type', result.contentType);
- }
- res.statusCode = 200;
- res.write(result.contents);
- res.end();
- break;
- }
- case 404: {
- const fullurl = new URL(req.url || '/', 'https://example.org/');
- const reqPath = decodeURI(fullurl.pathname);
- error(logging, 'static', 'Not found', reqPath);
- res.statusCode = 404;
-
- const fourOhFourResult = await runtime.load('/404');
- if (fourOhFourResult.statusCode === 200) {
- if (fourOhFourResult.contentType) {
- res.setHeader('Content-Type', fourOhFourResult.contentType);
- }
- res.write(fourOhFourResult.contents);
- } else {
- res.setHeader('Content-Type', 'text/plain');
- res.write('Not Found');
- }
- res.end();
- break;
- }
- case 500: {
- switch (result.type) {
- case 'parse-error': {
- const err = result.error;
- err.filename = pathRelative(projectRoot.pathname, err.filename);
- parseError(logging, err);
- break;
- }
- default: {
- error(logging, 'executing astro', result.error);
- break;
- }
- }
- res.statusCode = 500;
-
- let errorResult = await runtime.load(`/500?error=${encodeURIComponent(result.error.stack || result.error.toString())}`);
- if(errorResult.statusCode === 200) {
- if (errorResult.contentType) {
- res.setHeader('Content-Type', errorResult.contentType);
- }
- res.write(errorResult.contents);
- } else {
- res.write(result.error.toString());
- }
- res.end();
- break;
- }
- }
- });
-
- const port = astroConfig.devOptions.port;
- server.listen(port, hostname, () => {
- const endServerTime = performance.now();
- info(logging, 'dev server', green(`Server started in ${Math.floor(endServerTime - startServerTime)}ms.`));
- info(logging, 'dev server', `${green('Local:')} http://${hostname}:${port}/`);
- });
-}
diff --git a/src/frontend/500.astro b/src/frontend/500.astro
deleted file mode 100644
index 01fab8bea..000000000
--- a/src/frontend/500.astro
+++ /dev/null
@@ -1,128 +0,0 @@
----
-import Prism from 'astro/components/Prism.astro';
-let title = 'Uh oh...';
-
-const error = Astro.request.url.searchParams.get('error');
----
-
-<!doctype html>
-<html lang="en">
- <head>
- <title>Error 500</title>
- <link rel="preconnect"href="https://fonts.gstatic.com">
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&family=IBM+Plex+Sans&display=swap">
- <link rel="stylesheet" href="http://cdn.skypack.dev/prism-themes/themes/prism-material-dark.css">
-
- <style>
- * {
- box-sizing: border-box;
- margin: 0;
- }
-
- :global(:root) {
- --font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
- --font-mono: "IBM Plex Mono", Consolas, "Andale Mono WT", "Andale Mono",
- "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono",
- "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco,
- "Courier New", Courier, monospace;
- --color-gray-800: #1F2937;
- --color-gray-500: #6B7280;
- --color-gray-400: #9CA3AF;
- --color-gray-100: #F3F4F6;
- --color-red: #FF1639;
- }
-
- html, body {
- width: 100vw;
- height: 100%;
- min-height: 100vh;
-
- font-family: var(--font-sans);
- font-weight: 400;
- background: var(--color-gray-100);
- text-align: center;
- }
-
- body {
- display: grid;
- place-content: center;
- }
-
- header {
- display: flex;
- flex-direction: column;
- align-items: center;
- font-family: var(--font-sans);
- font-size: 2.5rem;
- font-size: clamp(24px, calc(2vw + 1rem), 2.5rem);
- }
-
- header h1 {
- margin: 0.25em;
- margin-right: 0;
- font-weight: 400;
- letter-spacing: -2px;
- line-height: 1;
- }
-
- header h1 .title {
- color: var(--color-gray-400);
- white-space: nowrap;
- }
-
- header svg {
- margin-bottom: -0.125em;
- color: var(--color-red);
- }
-
- p {
- font-size: 1.75rem;
- font-size: clamp(14px, calc(2vw + 0.5rem), 1.75rem);
- flex: 1;
- }
-
- .error-message {
- display: grid;
- justify-content: center;
- margin-top: 4rem;
- }
-
- .error-message :global(code[class*="language-"]) {
- background: var(--color-gray-800);
- }
- .error-message :global(pre) {
- margin: 0;
- font-family: var(--font-mono);
- font-size: 0.85rem;
- background: var(--color-gray-800);
- border-radius: 8px;
- }
-
- .error-message :global(.token.punctuation) {
- color: var(--color-gray-400);
- }
-
- .error-message :global(.token.operator) {
- color: var(--color-gray-400);
- }
- </style>
- </head>
- <body>
- <main>
- <header>
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" width="1.75em" height="1.75em">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
- </svg>
- <h1><span class="error">500 Error </span><span class="title">{title}</span></h1>
- </header>
-
- <article>
- <p>Astro had some trouble loading this page.</p>
-
- <div class="error-message">
- <Prism lang="shell" code={error} />
- </div>
- </article>
- </main>
- </body>
-</html>
diff --git a/src/frontend/SvelteWrapper.svelte b/src/frontend/SvelteWrapper.svelte
deleted file mode 100644
index eb4cbb7d9..000000000
--- a/src/frontend/SvelteWrapper.svelte
+++ /dev/null
@@ -1,7 +0,0 @@
-<script>
-const { __astro_component: Component, __astro_children, ...props } = $$props;
-</script>
-
-<Component {...props}>
- {@html __astro_children}
-</Component>
diff --git a/src/frontend/SvelteWrapper.svelte.client.ts b/src/frontend/SvelteWrapper.svelte.client.ts
deleted file mode 100644
index 9df168895..000000000
--- a/src/frontend/SvelteWrapper.svelte.client.ts
+++ /dev/null
@@ -1,166 +0,0 @@
-/* eslint-disable */
-// @ts-nocheck
-// TODO: don't precompile this, but it works for now
-import {
- HtmlTag,
- SvelteComponentDev,
- assign,
- claim_component,
- create_component,
- destroy_component,
- detach_dev,
- dispatch_dev,
- empty,
- exclude_internal_props,
- get_spread_object,
- get_spread_update,
- init,
- insert_dev,
- mount_component,
- noop,
- not_equal,
- transition_in,
- transition_out,
- validate_slots,
-} from 'svelte/internal';
-
-const file = 'App.svelte';
-
-// (5:0) <Component {...props}>
-function create_default_slot(ctx) {
- let html_tag;
- let html_anchor;
-
- const block = {
- c: function create() {
- html_anchor = empty();
- this.h();
- },
- l: function claim(nodes) {
- html_anchor = empty();
- this.h();
- },
- h: function hydrate() {
- html_tag = new HtmlTag(html_anchor);
- },
- m: function mount(target, anchor) {
- html_tag.m(/*__astro_children*/ ctx[1], target, anchor);
- insert_dev(target, html_anchor, anchor);
- },
- p: noop,
- d: function destroy(detaching) {
- if (detaching) detach_dev(html_anchor);
- if (detaching) html_tag.d();
- },
- };
-
- dispatch_dev('SvelteRegisterBlock', {
- block,
- id: create_default_slot.name,
- type: 'slot',
- source: '(5:0) <Component {...props}>',
- ctx,
- });
-
- return block;
-}
-
-function create_fragment(ctx) {
- let component;
- let current;
- const component_spread_levels = [/*props*/ ctx[2]];
-
- let component_props = {
- $$slots: { default: [create_default_slot] },
- $$scope: { ctx },
- };
-
- for (let i = 0; i < component_spread_levels.length; i += 1) {
- component_props = assign(component_props, component_spread_levels[i]);
- }
-
- component = new /*Component*/ ctx[0]({ props: component_props, $$inline: true });
-
- const block = {
- c: function create() {
- create_component(component.$$.fragment);
- },
- l: function claim(nodes) {
- claim_component(component.$$.fragment, nodes);
- },
- m: function mount(target, anchor) {
- mount_component(component, target, anchor);
- current = true;
- },
- p: function update(ctx, [dirty]) {
- const component_changes = dirty & /*props*/ 4 ? get_spread_update(component_spread_levels, [get_spread_object(/*props*/ ctx[2])]) : {};
-
- if (dirty & /*$$scope*/ 16) {
- component_changes.$$scope = { dirty, ctx };
- }
-
- component.$set(component_changes);
- },
- i: function intro(local) {
- if (current) return;
- transition_in(component.$$.fragment, local);
- current = true;
- },
- o: function outro(local) {
- transition_out(component.$$.fragment, local);
- current = false;
- },
- d: function destroy(detaching) {
- destroy_component(component, detaching);
- },
- };
-
- dispatch_dev('SvelteRegisterBlock', {
- block,
- id: create_fragment.name,
- type: 'component',
- source: '',
- ctx,
- });
-
- return block;
-}
-
-function instance($$self, $$props, $$invalidate) {
- let { $$slots: slots = {}, $$scope } = $$props;
- validate_slots('App', slots, []);
- const { __astro_component: Component, __astro_children, ...props } = $$props;
-
- $$self.$$set = ($$new_props) => {
- $$invalidate(3, ($$props = assign(assign({}, $$props), exclude_internal_props($$new_props))));
- };
-
- $$self.$capture_state = () => ({ Component, __astro_children, props });
-
- $$self.$inject_state = ($$new_props) => {
- $$invalidate(3, ($$props = assign(assign({}, $$props), $$new_props)));
- };
-
- if ($$props && '$$inject' in $$props) {
- $$self.$inject_state($$props.$$inject);
- }
-
- $$props = exclude_internal_props($$props);
- return [Component, __astro_children, props];
-}
-
-class App extends SvelteComponentDev {
- constructor(options) {
- super(options);
- init(this, options, instance, create_fragment, not_equal, {});
-
- dispatch_dev('SvelteRegisterComponent', {
- component: this,
- tagName: 'App',
- options,
- id: create_fragment.name,
- });
- }
-}
-
-export default App;
diff --git a/src/frontend/SvelteWrapper.svelte.server.ts b/src/frontend/SvelteWrapper.svelte.server.ts
deleted file mode 100644
index c5a25ff03..000000000
--- a/src/frontend/SvelteWrapper.svelte.server.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-/* eslint-disable */
-// @ts-nocheck
-// TODO: don't precompile this, but it works for now
-/* App.svelte generated by Svelte v3.37.0 */
-import { create_ssr_component, validate_component } from 'svelte/internal';
-
-const App = create_ssr_component(($$result, $$props, $$bindings, slots) => {
- const { __astro_component: Component, __astro_children, ...props } = $$props;
- return `${validate_component(Component, 'Component').$$render($$result, Object.assign(props), {}, { default: () => `${__astro_children}` })}`;
-});
-
-export default App;
diff --git a/src/frontend/h.ts b/src/frontend/h.ts
deleted file mode 100644
index c1e21dc95..000000000
--- a/src/frontend/h.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-export type HProps = Record<string, string> | null | undefined;
-export type HChild = string | undefined | (() => string);
-export type AstroComponent = (props: HProps, ...children: Array<HChild>) => string;
-export type HTag = string | AstroComponent;
-
-const voidTags = new Set(['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
-
-/** Generator for primary h() function */
-function* _h(tag: string, attrs: HProps, children: Array<HChild>) {
- if (tag === '!doctype') {
- yield '<!doctype ';
- if (attrs) {
- yield Object.keys(attrs).join(' ');
- }
- yield '>';
- return;
- }
-
- yield `<${tag}`;
- if (attrs) {
- yield ' ';
- for (let [key, value] of Object.entries(attrs)) {
- yield `${key}="${value}"`;
- }
- }
- yield '>';
-
- // Void tags have no children.
- if (voidTags.has(tag)) {
- return;
- }
-
- for (let child of children) {
- // Special: If a child is a function, call it automatically.
- // This lets you do {() => ...} without the extra boilerplate
- // of wrapping it in a function and calling it.
- if (typeof child === 'function') {
- yield child();
- } else if (typeof child === 'string') {
- yield child;
- } else if (!child) {
- // do nothing, safe to ignore falsey values.
- } else {
- yield child;
- }
- }
-
- yield `</${tag}>`;
-}
-
-/** Astro‘s primary h() function. Allows it to use JSX-like syntax. */
-export async function h(tag: HTag, attrs: HProps, ...pChildren: Array<Promise<HChild>>) {
- const children = await Promise.all(pChildren.flat(Infinity));
- if (typeof tag === 'function') {
- // We assume it's an astro component
- return tag(attrs, ...children);
- }
-
- return Array.from(_h(tag, attrs, children)).join('');
-}
-
-/** Fragment helper, similar to React.Fragment */
-export function Fragment(_: HProps, ...children: Array<string>) {
- return children.join('');
-}
diff --git a/src/frontend/render/preact.ts b/src/frontend/render/preact.ts
deleted file mode 100644
index 5c50b6fe3..000000000
--- a/src/frontend/render/preact.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { h, render, ComponentType } from 'preact';
-import { renderToString } from 'preact-render-to-string';
-import { childrenToVnodes } from './utils';
-import type { ComponentRenderer } from '../../@types/renderer';
-import { createRenderer } from './renderer';
-
-// This prevents tree-shaking of render.
-Function.prototype(render);
-
-const Preact: ComponentRenderer<ComponentType> = {
- jsxPragma: h,
- jsxPragmaName: 'h',
- renderStatic(Component) {
- return async (props, ...children) => {
- return renderToString(h(Component, props, childrenToVnodes(h, children)));
- };
- },
- imports: {
- preact: ['render', 'Fragment', 'h'],
- },
- render({ Component, root, props, children }) {
- return `render(h(${Component}, ${props}, h(Fragment, null, ...${children})), ${root})`;
- },
-};
-
-const renderer = createRenderer(Preact);
-
-export const __preact_static = renderer.static;
-export const __preact_load = renderer.load;
-export const __preact_idle = renderer.idle;
-export const __preact_visible = renderer.visible;
diff --git a/src/frontend/render/react.ts b/src/frontend/render/react.ts
deleted file mode 100644
index 51c0c9729..000000000
--- a/src/frontend/render/react.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import type { ComponentRenderer } from '../../@types/renderer';
-import React, { ComponentType } from 'react';
-import ReactDOMServer from 'react-dom/server';
-import { createRenderer } from './renderer';
-import { childrenToVnodes } from './utils';
-
-const ReactRenderer: ComponentRenderer<ComponentType> = {
- jsxPragma: React.createElement,
- jsxPragmaName: 'React.createElement',
- renderStatic(Component) {
- return async (props, ...children) => {
- return ReactDOMServer.renderToString(React.createElement(Component, props, childrenToVnodes(React.createElement, children)));
- };
- },
- imports: {
- react: ['default: React'],
- 'react-dom': ['default: ReactDOM'],
- },
- render({ Component, root, children, props }) {
- return `ReactDOM.hydrate(React.createElement(${Component}, ${props}, React.createElement(React.Fragment, null, ...${children})), ${root})`;
- },
-};
-
-const renderer = createRenderer(ReactRenderer);
-
-export const __react_static = renderer.static;
-export const __react_load = renderer.load;
-export const __react_idle = renderer.idle;
-export const __react_visible = renderer.visible;
diff --git a/src/frontend/render/renderer.ts b/src/frontend/render/renderer.ts
deleted file mode 100644
index a5cc9d581..000000000
--- a/src/frontend/render/renderer.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import type { DynamicRenderContext, DynamicRendererGenerator, SupportedComponentRenderer, StaticRendererGenerator } from '../../@types/renderer';
-import { childrenToH } from './utils';
-
-/** Initialize Astro Component renderer for Static and Dynamic components */
-export function createRenderer(renderer: SupportedComponentRenderer) {
- const _static: StaticRendererGenerator = (Component) => renderer.renderStatic(Component);
- const _imports = (context: DynamicRenderContext) => {
- const values = Object.values(renderer.imports ?? {})
- .reduce((acc, v) => {
- return [...acc, `{ ${v.join(', ')} }`];
- }, [])
- .join(', ');
- const libs = Object.keys(renderer.imports ?? {})
- .reduce((acc: string[], lib: string) => {
- return [...acc, `import("${context.frameworkUrls[lib as any]}")`];
- }, [])
- .join(',');
- return `const [{${context.componentExport}: Component}, ${values}] = await Promise.all([import("${context.componentUrl}")${renderer.imports ? ', ' + libs : ''}]);`;
- };
- const serializeProps = ({ children: _, ...props }: Record<string, any>) => JSON.stringify(props);
- const createContext = () => {
- const astroId = `${Math.floor(Math.random() * 1e16)}`;
- return { ['data-astro-id']: astroId, root: `document.querySelector('[data-astro-id="${astroId}"]')`, Component: 'Component' };
- };
- const createDynamicRender: DynamicRendererGenerator = (wrapperStart, wrapperEnd) => (Component, renderContext) => {
- const innerContext = createContext();
- return async (props, ...children) => {
- let value: string;
- try {
- value = await _static(Component)(props, ...children);
- } catch (e) {
- value = '';
- }
- value = `<div data-astro-id="${innerContext['data-astro-id']}" style="display:contents">${value}</div>`;
-
- const script = `
- ${typeof wrapperStart === 'function' ? wrapperStart(innerContext) : wrapperStart}
- ${_imports(renderContext)}
- ${renderer.render({
- ...innerContext,
- props: serializeProps(props),
- children: `[${childrenToH(renderer, children) ?? ''}]`,
- childrenAsString: `\`${children}\``,
- })}
- ${typeof wrapperEnd === 'function' ? wrapperEnd(innerContext) : wrapperEnd}
- `;
-
- return [value, `<script type="module">${script.trim()}</script>`].join('\n');
- };
- };
-
- return {
- static: _static,
- load: createDynamicRender('(async () => {', '})()'),
- idle: createDynamicRender('requestIdleCallback(async () => {', '})'),
- visible: createDynamicRender(
- 'const o = new IntersectionObserver(async ([entry]) => { if (!entry.isIntersecting) { return; } o.disconnect();',
- ({ root }) => `}); Array.from(${root}.children).forEach(child => o.observe(child))`
- ),
- };
-}
diff --git a/src/frontend/render/svelte.ts b/src/frontend/render/svelte.ts
deleted file mode 100644
index 13e2b8f58..000000000
--- a/src/frontend/render/svelte.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import type { ComponentRenderer } from '../../@types/renderer';
-import type { SvelteComponent } from 'svelte';
-import { createRenderer } from './renderer';
-import SvelteWrapper from '../SvelteWrapper.svelte.server';
-
-const SvelteRenderer: ComponentRenderer<SvelteComponent> = {
- renderStatic(Component) {
- return async (props, ...children) => {
- const { html } = SvelteWrapper.render({ __astro_component: Component, __astro_children: children.join('\n'), ...props });
- return html;
- };
- },
- imports: {
- 'svelte-runtime': ['default: render'],
- },
- render({ Component, root, props, childrenAsString }) {
- return `render(${root}, ${Component}, ${props}, ${childrenAsString});`;
- },
-};
-
-const renderer = createRenderer(SvelteRenderer);
-
-export const __svelte_static = renderer.static;
-export const __svelte_load = renderer.load;
-export const __svelte_idle = renderer.idle;
-export const __svelte_visible = renderer.visible;
diff --git a/src/frontend/render/utils.ts b/src/frontend/render/utils.ts
deleted file mode 100644
index 9d55626fe..000000000
--- a/src/frontend/render/utils.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import unified from 'unified';
-import parse from 'rehype-parse';
-import toH from 'hast-to-hyperscript';
-import { ComponentRenderer } from '../../@types/renderer';
-import moize from 'moize';
-
-/** @internal */
-function childrenToTree(children: string[]) {
- return children.map((child) => (unified().use(parse, { fragment: true }).parse(child) as any).children.pop());
-}
-
-/**
- * Converts an HTML fragment string into vnodes for rendering via provided framework
- * @param h framework's `createElement` function
- * @param children the HTML string children
- */
-export const childrenToVnodes = moize.deep(function childrenToVnodes(h: any, children: string[]) {
- const tree = childrenToTree(children);
- const vnodes = tree.map((subtree) => {
- if (subtree.type === 'text') return subtree.value;
- return toH(h, subtree);
- });
- return vnodes;
-});
-
-/**
- * Converts an HTML fragment string into h function calls as a string
- * @param h framework's `createElement` function
- * @param children the HTML string children
- */
-export const childrenToH = moize.deep(function childrenToH(renderer: ComponentRenderer<any>, children: string[]): any {
- if (!renderer.jsxPragma) return;
- const tree = childrenToTree(children);
- const innerH = (name: any, attrs: Record<string, any> | null = null, _children: string[] | null = null) => {
- const vnode = renderer.jsxPragma?.(name, attrs, _children);
- const childStr = _children ? `, [${_children.map((child) => serializeChild(child)).join(',')}]` : '';
- /* fix(react): avoid hard-coding keys into the serialized tree */
- if (attrs && attrs.key) attrs.key = undefined;
- const __SERIALIZED = `${renderer.jsxPragmaName}("${name}", ${attrs ? JSON.stringify(attrs) : 'null'}${childStr})` as string;
- return { ...vnode, __SERIALIZED };
- };
- const serializeChild = (child: unknown) => {
- if (['string', 'number', 'boolean'].includes(typeof child)) return JSON.stringify(child);
- if (child === null) return `null`;
- if ((child as any).__SERIALIZED) return (child as any).__SERIALIZED;
- return innerH(child).__SERIALIZED;
- };
- return tree.map((subtree) => {
- if (subtree.type === 'text') return JSON.stringify(subtree.value);
- return toH(innerH, subtree).__SERIALIZED
- });
-});
diff --git a/src/frontend/render/vue.ts b/src/frontend/render/vue.ts
deleted file mode 100644
index 57c3c8276..000000000
--- a/src/frontend/render/vue.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import type { ComponentRenderer } from '../../@types/renderer';
-import type { Component as VueComponent } from 'vue';
-import { renderToString } from '@vue/server-renderer';
-import { defineComponent, createSSRApp, h as createElement } from 'vue';
-import { createRenderer } from './renderer';
-
-// This prevents tree-shaking of render.
-Function.prototype(renderToString);
-
-/**
- * Users might attempt to use :vueAttribute syntax to pass primitive values.
- * If so, try to JSON.parse them to get the primitives
- */
-function cleanPropsForVue(obj: Record<string, any>) {
- let cleaned = {} as any;
- for (let [key, value] of Object.entries(obj)) {
- if (key.startsWith(':')) {
- key = key.slice(1);
- if (typeof value === 'string') {
- try {
- value = JSON.parse(value);
- } catch (e) {}
- }
- }
- cleaned[key] = value;
- }
- return cleaned;
-}
-
-const Vue: ComponentRenderer<VueComponent> = {
- jsxPragma: createElement,
- jsxPragmaName: 'createElement',
- renderStatic(Component) {
- return async (props, ...children) => {
- const App = defineComponent({
- components: {
- Component,
- },
- data() {
- return { props };
- },
- template: `<Component v-bind="props">${children.join('\n')}</Component>`,
- });
-
- const app = createSSRApp(App);
- const html = await renderToString(app);
- return html;
- };
- },
- imports: {
- vue: ['createApp', 'h: createElement'],
- },
- render({ Component, root, props, children }) {
- const vueProps = cleanPropsForVue(JSON.parse(props));
- return `const App = { render: () => createElement(${Component}, ${JSON.stringify(vueProps)}, { default: () => ${children} }) };
-createApp(App).mount(${root});`;
- },
-};
-
-const renderer = createRenderer(Vue);
-
-export const __vue_static = renderer.static;
-export const __vue_load = renderer.load;
-export const __vue_idle = renderer.idle;
-export const __vue_visible = renderer.visible;
diff --git a/src/frontend/runtime/svelte.ts b/src/frontend/runtime/svelte.ts
deleted file mode 100644
index 78b6af6b6..000000000
--- a/src/frontend/runtime/svelte.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import SvelteWrapper from '../SvelteWrapper.svelte.client';
-import type { SvelteComponent } from 'svelte';
-
-export default (target: Element, component: SvelteComponent, props: any, children: string) => {
- new SvelteWrapper({
- target,
- props: { __astro_component: component, __astro_children: children, ...props },
- hydrate: true,
- });
-};
diff --git a/src/logger.ts b/src/logger.ts
deleted file mode 100644
index 7bfde67d9..000000000
--- a/src/logger.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-import type { CompileError } from './parser/utils/error.js';
-import { bold, blue, red, grey, underline } from 'kleur/colors';
-import { Writable } from 'stream';
-import { format as utilFormat } from 'util';
-
-type ConsoleStream = Writable & {
- fd: 1 | 2;
-};
-
-export const defaultLogDestination = new Writable({
- objectMode: true,
- write(event: LogMessage, _, callback) {
- let dest: ConsoleStream = process.stderr;
- if (levels[event.level] < levels['error']) {
- dest = process.stdout;
- }
- let type = event.type;
- if(type !== null) {
- if (event.level === 'info') {
- type = bold(blue(type));
- } else if (event.level === 'error') {
- type = bold(red(type));
- }
-
- dest.write(`[${type}] `);
- }
-
- dest.write(utilFormat(...event.args));
- dest.write('\n');
-
- callback();
- },
-});
-
-interface LogWritable<T> extends Writable {
- write: (chunk: T) => boolean;
-}
-
-export type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'; // same as Pino
-export type LoggerEvent = 'debug' | 'info' | 'warn' | 'error';
-
-export interface LogOptions {
- dest: LogWritable<LogMessage>;
- level: LoggerLevel;
-}
-
-export const defaultLogOptions: LogOptions = {
- dest: defaultLogDestination,
- level: 'info',
-};
-
-export interface LogMessage {
- type: string | null;
- level: LoggerLevel;
- message: string;
- args: Array<any>;
-}
-
-const levels: Record<LoggerLevel, number> = {
- debug: 20,
- info: 30,
- warn: 40,
- error: 50,
- silent: 90,
-};
-
-/** Full logging API */
-export function log(opts: LogOptions = defaultLogOptions, level: LoggerLevel, type: string | null, ...args: Array<any>) {
- const event: LogMessage = {
- type,
- level,
- args,
- message: '',
- };
-
- // test if this level is enabled or not
- if (levels[opts.level] > levels[level]) {
- return; // do nothing
- }
-
- opts.dest.write(event);
-}
-
-/** Emit a message only shown in debug mode */
-export function debug(opts: LogOptions, type: string | null, ...messages: Array<any>) {
- return log(opts, 'debug', type, ...messages);
-}
-
-/** Emit a general info message (be careful using this too much!) */
-export function info(opts: LogOptions, type: string | null, ...messages: Array<any>) {
- return log(opts, 'info', type, ...messages);
-}
-
-/** Emit a warning a user should be aware of */
-export function warn(opts: LogOptions, type: string | null, ...messages: Array<any>) {
- return log(opts, 'warn', type, ...messages);
-}
-
-/** Emit a fatal error message the user should address. */
-export function error(opts: LogOptions, type: string | null, ...messages: Array<any>) {
- return log(opts, 'error', type, ...messages);
-}
-
-/** Pretty format error for display */
-export function parseError(opts: LogOptions, err: CompileError) {
- let frame = err.frame
- // Switch colons for pipes
- .replace(/^([0-9]+)(:)/gm, `${bold('$1')} │`)
- // Make the caret red.
- .replace(/(?<=^\s+)(\^)/gm, bold(red(' ^')))
- // Add identation
- .replace(/^/gm, ' ');
-
- error(
- opts,
- 'parse-error',
- `
-
- ${underline(bold(grey(`${err.filename}:${err.start.line}:${err.start.column}`)))}
-
- ${bold(red(`𝘅 ${err.message}`))}
-
-${frame}
-`
- );
-}
-
-// A default logger for when too lazy to pass LogOptions around.
-export const logger = {
- debug: debug.bind(null, defaultLogOptions),
- info: info.bind(null, defaultLogOptions),
- warn: warn.bind(null, defaultLogOptions),
- error: error.bind(null, defaultLogOptions),
-};
-
-// For silencing libraries that go directly to console.warn
-export function trapWarn(cb: (...args: any[]) => void = () =>{}) {
- const warn = console.warn;
- console.warn = function(...args: any[]) {
- cb(...args);
- };
- return () => console.warn = warn;
-} \ No newline at end of file
diff --git a/src/parser/README.md b/src/parser/README.md
deleted file mode 100644
index f44d45ecf..000000000
--- a/src/parser/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# `astro/compiler`
-
-This directory is a fork of `svelte/compiler`. It is meant to stay as close to the original source as possible, so that upstream changes are easy to integrate. Everything svelte-specific and unrelated to parsing (compiler, preprocess, etc) has been removed. \ No newline at end of file
diff --git a/src/parser/Stats.ts b/src/parser/Stats.ts
deleted file mode 100644
index 5f7f991f8..000000000
--- a/src/parser/Stats.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-// @ts-nocheck
-
-const now =
- typeof process !== 'undefined' && process.hrtime
- ? () => {
- const t = process.hrtime();
- return t[0] * 1e3 + t[1] / 1e6;
- }
- : () => self.performance.now();
-
-interface Timing {
- label: string;
- start: number;
- end: number;
- children: Timing[];
-}
-
-/** Format benchmarks */
-function collapse_timings(timings) {
- const result = {};
- timings.forEach((timing) => {
- result[timing.label] = Object.assign(
- {
- total: timing.end - timing.start,
- },
- timing.children && collapse_timings(timing.children)
- );
- });
- return result;
-}
-
-export default class Stats {
- start_time: number;
- current_timing: Timing;
- current_children: Timing[];
- timings: Timing[];
- stack: Timing[];
-
- constructor() {
- this.start_time = now();
- this.stack = [];
- this.current_children = this.timings = [];
- }
-
- start(label) {
- const timing = {
- label,
- start: now(),
- end: null,
- children: [],
- };
-
- this.current_children.push(timing);
- this.stack.push(timing);
-
- this.current_timing = timing;
- this.current_children = timing.children;
- }
-
- stop(label) {
- if (label !== this.current_timing.label) {
- throw new Error(`Mismatched timing labels (expected ${this.current_timing.label}, got ${label})`);
- }
-
- this.current_timing.end = now();
- this.stack.pop();
- this.current_timing = this.stack[this.stack.length - 1];
- this.current_children = this.current_timing ? this.current_timing.children : this.timings;
- }
-
- render() {
- const timings = Object.assign(
- {
- total: now() - this.start_time,
- },
- collapse_timings(this.timings)
- );
-
- return {
- timings,
- };
- }
-}
diff --git a/src/parser/config.ts b/src/parser/config.ts
deleted file mode 100644
index e6d0f65a7..000000000
--- a/src/parser/config.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const test = typeof process !== 'undefined' && process.env.TEST;
diff --git a/src/parser/index.ts b/src/parser/index.ts
deleted file mode 100644
index 718199c94..000000000
--- a/src/parser/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default as parse } from './parse/index.js';
diff --git a/src/parser/interfaces.ts b/src/parser/interfaces.ts
deleted file mode 100644
index 3273b8be1..000000000
--- a/src/parser/interfaces.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-import type { SourceMap } from 'magic-string';
-
-export interface BaseNode {
- start: number;
- end: number;
- type: string;
- children?: TemplateNode[];
- [prop_name: string]: any;
-}
-
-export interface Fragment extends BaseNode {
- type: 'Fragment';
- children: TemplateNode[];
-}
-
-export interface Text extends BaseNode {
- type: 'Text';
- data: string;
- raw: string;
-}
-
-export interface Attribute extends BaseNode {
- type: 'Attribute';
- name: string;
- value: Text[];
-}
-
-export interface MustacheTag extends BaseNode {
- type: 'MustacheTag';
- content: string;
-}
-
-export type DirectiveType = 'Action' | 'Animation' | 'Binding' | 'Class' | 'EventHandler' | 'Let' | 'Ref' | 'Transition';
-
-interface BaseDirective extends BaseNode {
- type: DirectiveType;
- expression: null | Node;
- name: string;
- modifiers: string[];
-}
-
-export interface Transition extends BaseDirective {
- type: 'Transition';
- intro: boolean;
- outro: boolean;
-}
-
-export type Directive = BaseDirective | Transition;
-
-export type TemplateNode = Text | MustacheTag | BaseNode | Directive | Transition;
-
-export interface Expression {
- type: 'Expression';
- start: number;
- end: number;
- codeChunks: string[];
- children: BaseNode[];
-}
-
-export interface Parser {
- readonly template: string;
- readonly filename?: string;
-
- index: number;
- stack: Node[];
-
- html: Node;
- css: Node;
- js: Node;
- meta_tags: Map<string, string>;
-}
-
-export interface Script extends BaseNode {
- type: 'Script';
- context: 'runtime' | 'setup';
- content: string;
-}
-
-export interface Style extends BaseNode {
- type: 'Style';
- attributes: any[]; // TODO
- content: {
- start: number;
- end: number;
- styles: string;
- };
-}
-
-export interface Ast {
- html: TemplateNode;
- css: Style;
- module: Script;
- // instance: Script;
-}
-
-export interface Warning {
- start?: { line: number; column: number; pos?: number };
- end?: { line: number; column: number };
- pos?: number;
- code: string;
- message: string;
- filename?: string;
- frame?: string;
- toString: () => string;
-}
-
-export type ModuleFormat = 'esm' | 'cjs';
-
-export type CssHashGetter = (args: { name: string; filename: string | undefined; css: string; hash: (input: string) => string }) => string;
-
-export interface Visitor {
- enter: (node: Node) => void;
- leave?: (node: Node) => void;
-}
-
-export interface AppendTarget {
- slots: Record<string, string>;
- slot_stack: string[];
-}
-
-export interface Var {
- name: string;
- export_name?: string; // the `bar` in `export { foo as bar }`
- injected?: boolean;
- module?: boolean;
- mutated?: boolean;
- reassigned?: boolean;
- referenced?: boolean; // referenced from template scope
- referenced_from_script?: boolean; // referenced from script
- writable?: boolean;
-
- // used internally, but not exposed
- global?: boolean;
- internal?: boolean; // event handlers, bindings
- initialised?: boolean;
- hoistable?: boolean;
- subscribable?: boolean;
- is_reactive_dependency?: boolean;
- imported?: boolean;
-}
-
-export interface CssResult {
- code: string;
- map: SourceMap;
-}
diff --git a/src/parser/parse/index.ts b/src/parser/parse/index.ts
deleted file mode 100644
index 124e125ef..000000000
--- a/src/parser/parse/index.ts
+++ /dev/null
@@ -1,270 +0,0 @@
-// @ts-nocheck
-
-import { isIdentifierStart, isIdentifierChar } from 'acorn';
-import fragment from './state/fragment.js';
-import { whitespace } from '../utils/patterns.js';
-import { reserved } from '../utils/names.js';
-import full_char_code_at from '../utils/full_char_code_at.js';
-import { TemplateNode, Ast, ParserOptions, Fragment, Style, Script } from '../interfaces.js';
-import error from '../utils/error.js';
-
-type ParserState = (parser: Parser) => ParserState | void;
-
-interface LastAutoClosedTag {
- tag: string;
- reason: string;
- depth: number;
-}
-
-export class Parser {
- readonly template: string;
- readonly filename?: string;
- readonly customElement: boolean;
-
- index = 0;
- stack: TemplateNode[] = [];
-
- html: Fragment;
- css: Style[] = [];
- js: Script[] = [];
- meta_tags = {};
- last_auto_closed_tag?: LastAutoClosedTag;
-
- constructor(template: string, options: ParserOptions) {
- if (typeof template !== 'string') {
- throw new TypeError('Template must be a string');
- }
-
- this.template = template.replace(/\s+$/, '');
- this.filename = options.filename;
- this.customElement = options.customElement;
-
- this.html = {
- start: null,
- end: null,
- type: 'Fragment',
- children: [],
- };
-
- this.stack.push(this.html);
-
- let state: ParserState = fragment;
-
- while (this.index < this.template.length) {
- state = state(this) || fragment;
- }
-
- if (this.stack.length > 1) {
- const current = this.current();
-
- const type = current.type === 'Element' ? `<${current.name}>` : 'Block';
- const slug = current.type === 'Element' ? 'element' : 'block';
-
- this.error(
- {
- code: `unclosed-${slug}`,
- message: `${type} was left open`,
- },
- current.start
- );
- }
-
- if (state !== fragment) {
- this.error({
- code: 'unexpected-eof',
- message: 'Unexpected end of input',
- });
- }
-
- if (this.html.children.length) {
- let start = this.html.children[0].start;
- while (whitespace.test(template[start])) start += 1;
-
- let end = this.html.children[this.html.children.length - 1].end;
- while (whitespace.test(template[end - 1])) end -= 1;
-
- this.html.start = start;
- this.html.end = end;
- } else {
- this.html.start = this.html.end = null;
- }
- }
-
- current() {
- return this.stack[this.stack.length - 1];
- }
-
- acorn_error(err: any) {
- this.error(
- {
- code: 'parse-error',
- message: err.message.replace(/ \(\d+:\d+\)$/, ''),
- },
- err.pos
- );
- }
-
- error({ code, message }: { code: string; message: string }, index = this.index) {
- error(message, {
- name: 'ParseError',
- code,
- source: this.template,
- start: index,
- filename: this.filename,
- });
- }
-
- eat(str: string, required?: boolean, message?: string) {
- if (this.match(str)) {
- this.index += str.length;
- return true;
- }
-
- if (required) {
- this.error({
- code: `unexpected-${this.index === this.template.length ? 'eof' : 'token'}`,
- message: message || `Expected ${str}`,
- });
- }
-
- return false;
- }
-
- match(str: string) {
- return this.template.slice(this.index, this.index + str.length) === str;
- }
-
- match_regex(pattern: RegExp) {
- const match = pattern.exec(this.template.slice(this.index));
- if (!match || match.index !== 0) return null;
-
- return match[0];
- }
-
- allow_whitespace() {
- while (this.index < this.template.length && whitespace.test(this.template[this.index])) {
- this.index++;
- }
- }
-
- read(pattern: RegExp) {
- const result = this.match_regex(pattern);
- if (result) this.index += result.length;
- return result;
- }
-
- read_identifier(allow_reserved = false) {
- const start = this.index;
-
- let i = this.index;
-
- const code = full_char_code_at(this.template, i);
- if (!isIdentifierStart(code, true)) return null;
-
- i += code <= 0xffff ? 1 : 2;
-
- while (i < this.template.length) {
- const code = full_char_code_at(this.template, i);
-
- if (!isIdentifierChar(code, true)) break;
- i += code <= 0xffff ? 1 : 2;
- }
-
- const identifier = this.template.slice(this.index, (this.index = i));
-
- if (!allow_reserved && reserved.has(identifier)) {
- this.error(
- {
- code: 'unexpected-reserved-word',
- message: `'${identifier}' is a reserved word in JavaScript and cannot be used here`,
- },
- start
- );
- }
-
- return identifier;
- }
-
- read_until(pattern: RegExp) {
- if (this.index >= this.template.length) {
- this.error({
- code: 'unexpected-eof',
- message: 'Unexpected end of input',
- });
- }
-
- const start = this.index;
- const match = pattern.exec(this.template.slice(start));
-
- if (match) {
- this.index = start + match.index;
- return this.template.slice(start, this.index);
- }
-
- this.index = this.template.length;
- return this.template.slice(start);
- }
-
- require_whitespace() {
- if (!whitespace.test(this.template[this.index])) {
- this.error({
- code: 'missing-whitespace',
- message: 'Expected whitespace',
- });
- }
-
- this.allow_whitespace();
- }
-}
-
-/**
- * Parse
- * Step 1/3 in Astro SSR.
- * This is the first pass over .astro files and the step at which we convert a string to an AST for us to crawl.
- */
-export default function parse(template: string, options: ParserOptions = {}): Ast {
- const parser = new Parser(template, options);
-
- // TODO we may want to allow multiple <style> tags —
- // one scoped, one global. for now, only allow one
- if (parser.css.length > 1) {
- parser.error(
- {
- code: 'duplicate-style',
- message: 'You can only have one <style> tag per Astro file',
- },
- parser.css[1].start
- );
- }
-
- // const instance_scripts = parser.js.filter((script) => script.context === 'default');
- // const module_scripts = parser.js.filter((script) => script.context === 'module');
- const astro_scripts = parser.js.filter((script) => script.context === 'setup');
-
- if (astro_scripts.length > 1) {
- parser.error(
- {
- code: 'invalid-script',
- message: 'A component can only have one frontmatter (---) script',
- },
- astro_scripts[1].start
- );
- }
-
- // if (module_scripts.length > 1) {
- // parser.error(
- // {
- // code: 'invalid-script',
- // message: 'A component can only have one <script context="module"> element',
- // },
- // module_scripts[1].start
- // );
- // }
-
- return {
- html: parser.html,
- css: parser.css[0],
- // instance: instance_scripts[0],
- module: astro_scripts[0],
- };
-}
diff --git a/src/parser/parse/read/context.ts b/src/parser/parse/read/context.ts
deleted file mode 100644
index 565c66d18..000000000
--- a/src/parser/parse/read/context.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-// @ts-nocheck
-
-import { Parser } from '../index.js';
-import { isIdentifierStart } from 'acorn';
-import full_char_code_at from '../../utils/full_char_code_at.js';
-import { is_bracket_open, is_bracket_close, is_bracket_pair, get_bracket_close } from '../utils/bracket.js';
-import { parse_expression_at } from './expression.js';
-import { Pattern } from 'estree';
-
-export default function read_context(parser: Parser): Pattern & { start: number; end: number } {
- const start = parser.index;
- let i = parser.index;
-
- const code = full_char_code_at(parser.template, i);
- if (isIdentifierStart(code, true)) {
- return {
- type: 'Identifier',
- name: parser.read_identifier(),
- start,
- end: parser.index,
- };
- }
-
- if (!is_bracket_open(code)) {
- parser.error({
- code: 'unexpected-token',
- message: 'Expected identifier or destructure pattern',
- });
- }
-
- const bracket_stack = [code];
- i += code <= 0xffff ? 1 : 2;
-
- while (i < parser.template.length) {
- const code = full_char_code_at(parser.template, i);
- if (is_bracket_open(code)) {
- bracket_stack.push(code);
- } else if (is_bracket_close(code)) {
- if (!is_bracket_pair(bracket_stack[bracket_stack.length - 1], code)) {
- parser.error({
- code: 'unexpected-token',
- message: `Expected ${String.fromCharCode(get_bracket_close(bracket_stack[bracket_stack.length - 1]))}`,
- });
- }
- bracket_stack.pop();
- if (bracket_stack.length === 0) {
- i += code <= 0xffff ? 1 : 2;
- break;
- }
- }
- i += code <= 0xffff ? 1 : 2;
- }
-
- parser.index = i;
-
- const pattern_string = parser.template.slice(start, i);
- try {
- // the length of the `space_with_newline` has to be start - 1
- // because we added a `(` in front of the pattern_string,
- // which shifted the entire string to right by 1
- // so we offset it by removing 1 character in the `space_with_newline`
- // to achieve that, we remove the 1st space encountered,
- // so it will not affect the `column` of the node
- let space_with_newline = parser.template.slice(0, start).replace(/[^\n]/g, ' ');
- const first_space = space_with_newline.indexOf(' ');
- space_with_newline = space_with_newline.slice(0, first_space) + space_with_newline.slice(first_space + 1);
-
- return (parse_expression_at(`${space_with_newline}(${pattern_string} = 1)`, start - 1) as any).left;
- } catch (error) {
- parser.acorn_error(error);
- }
-}
diff --git a/src/parser/parse/read/expression.ts b/src/parser/parse/read/expression.ts
deleted file mode 100644
index 9d0d09175..000000000
--- a/src/parser/parse/read/expression.ts
+++ /dev/null
@@ -1,251 +0,0 @@
-import type { BaseNode, Expression } from '../../interfaces';
-import { Parser } from '../index.js';
-import parseAstro from '../index.js';
-
-interface ParseState {
- source: string;
- start: number;
- index: number;
- curlyCount: number;
- bracketCount: number;
- root: Expression;
-}
-
-function peek_char(state: ParseState) {
- return state.source[state.index];
-}
-
-function peek_nonwhitespace(state: ParseState) {
- let index = state.index;
- do {
- let char = state.source[index];
- if (!/\s/.test(char)) {
- return char;
- }
- index++;
- } while (index < state.source.length);
-}
-
-function next_char(state: ParseState) {
- return state.source[state.index++];
-}
-
-function in_bounds(state: ParseState) {
- return state.index < state.source.length;
-}
-
-function consume_string(state: ParseState, stringChar: string) {
- let inEscape;
- do {
- const char = next_char(state);
-
- if (inEscape) {
- inEscape = false;
- } else if (char === '\\') {
- inEscape = true;
- } else if (char === stringChar) {
- break;
- }
- } while (in_bounds(state));
-}
-
-function consume_multiline_comment(state: ParseState) {
- do {
- const char = next_char(state);
-
- if (char === '*' && peek_char(state) === '/') {
- break;
- }
- } while (in_bounds(state));
-}
-
-function consume_line_comment(state: ParseState) {
- do {
- const char = next_char(state);
- if (char === '\n') {
- break;
- }
- } while (in_bounds(state));
-}
-
-const voidElements = new Set(['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
-
-function consume_tag(state: ParseState) {
- const start = state.index - 1;
- let tagName = '';
- let inTag = false;
- let inStart = true;
- let selfClosed = false;
- let inClose = false;
-
- let bracketIndex = 1;
- do {
- const char = next_char(state);
-
- switch (char) {
- case "'":
- case '"': {
- consume_string(state, char);
- break;
- }
- case '<': {
- inTag = false;
- tagName = '';
-
- if (peek_nonwhitespace(state) === '/') {
- inClose = true;
- bracketIndex--;
- } else {
- inStart = true;
- bracketIndex++;
- }
- break;
- }
- case '>': {
- // An arrow function, probably
- if (!inStart && !inClose) {
- break;
- }
-
- bracketIndex--;
-
- const addExpectedBrackets =
- // Void elements don't need a closing
- !voidElements.has(tagName.toLowerCase()) &&
- // Self-closing don't need a closing
- !selfClosed &&
- // If we're in a start tag, we expect to find 2 more brackets
- !inClose;
-
- if (addExpectedBrackets) {
- bracketIndex += 2;
- }
-
- inTag = false;
- selfClosed = false;
- inStart = false;
- inClose = false;
- break;
- }
- case ' ': {
- inTag = true;
- break;
- }
- case '/': {
- if (inStart) {
- selfClosed = true;
- }
- break;
- }
- default: {
- if (!inTag) {
- tagName += char;
- }
- break;
- }
- }
-
- // Unclosed tags
- if (state.curlyCount <= 0) {
- break;
- }
-
- if (bracketIndex === 0) {
- break;
- }
- } while (in_bounds(state));
-
- const source = state.source.substring(start, state.index);
-
- const ast = parseAstro(source);
- const fragment = ast.html;
-
- return fragment;
-}
-
-function consume_expression(source: string, start: number): Expression {
- const expr: Expression = {
- type: 'Expression',
- start,
- end: Number.NaN,
- codeChunks: [],
- children: [],
- };
-
- let codeStart: number = start;
-
- const state: ParseState = {
- source,
- start,
- index: start,
- curlyCount: 1,
- bracketCount: 0,
- root: expr,
- };
-
- do {
- const char = next_char(state);
-
- switch (char) {
- case '{': {
- state.curlyCount++;
- break;
- }
- case '}': {
- state.curlyCount--;
- break;
- }
- case '<': {
- const chunk = source.substring(codeStart, state.index - 1);
- expr.codeChunks.push(chunk);
- const tag = consume_tag(state);
- expr.children.push(tag);
- codeStart = state.index;
- break;
- }
- case "'":
- case '"':
- case '`': {
- consume_string(state, char);
- break;
- }
- case '/': {
- switch (peek_char(state)) {
- case '/': {
- consume_line_comment(state);
- break;
- }
- case '*': {
- consume_multiline_comment(state);
- break;
- }
- }
- }
- }
- } while (in_bounds(state) && state.curlyCount > 0);
-
- expr.end = state.index - 1;
-
- if (expr.children.length || !expr.codeChunks.length) {
- expr.codeChunks.push(source.substring(codeStart, expr.end));
- }
-
- return expr;
-}
-
-export const parse_expression_at = (source: string, index: number): Expression => {
- const expression = consume_expression(source, index);
-
- return expression;
-};
-
-// @ts-ignore
-export default function read_expression(parser: Parser) {
- try {
- const expression = parse_expression_at(parser.template, parser.index);
- parser.index = expression.end;
- return expression;
- } catch (err) {
- parser.acorn_error(err);
- }
-}
diff --git a/src/parser/parse/read/script.ts b/src/parser/parse/read/script.ts
deleted file mode 100644
index 9b8d71110..000000000
--- a/src/parser/parse/read/script.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-// @ts-nocheck
-
-import type { Node } from 'estree';
-import { Parser } from '../index.js';
-import { Script } from '../../interfaces.js';
-
-const script_closing_tag = '</script>';
-
-function get_context(parser: Parser, attributes: any[], start: number): 'runtime' | 'setup' {
- const context = attributes.find((attribute) => attribute.name === 'astro');
- if (!context) return 'runtime';
- if (context.value === true) return 'setup';
-
- if (context.value.length !== 1 || context.value[0].type !== 'Text') {
- parser.error(
- {
- code: 'invalid-script',
- message: 'astro attribute must be static',
- },
- start
- );
- }
-
- const value = context.value[0].data;
-
- if (value !== 'setup') {
- parser.error(
- {
- code: 'invalid-script',
- message: 'If the "astro" attribute has a value, its value must be "setup"',
- },
- context.start
- );
- }
-
- return value;
-}
-
-export default function read_script(parser: Parser, start: number, attributes: Node[]): Script {
- const script_start = parser.index;
- const script_end = parser.template.indexOf(script_closing_tag, script_start);
-
- if (script_end === -1) {
- parser.error({
- code: 'unclosed-script',
- message: '<script> must have a closing tag',
- });
- }
-
- const source = parser.template.slice(0, script_start).replace(/[^\n]/g, ' ') + parser.template.slice(script_start, script_end);
- parser.index = script_end + script_closing_tag.length;
-
- return {
- type: 'Script',
- start,
- end: parser.index,
- context: get_context(parser, attributes, start),
- content: source,
- };
-}
diff --git a/src/parser/parse/read/style.ts b/src/parser/parse/read/style.ts
deleted file mode 100644
index f23d7b10e..000000000
--- a/src/parser/parse/read/style.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Parser } from '../index.js';
-import { Style } from '../../interfaces.js';
-
-interface Attribute {
- start: number;
- end: number;
- type: 'Attribute';
- name: string;
- value: {
- raw: string;
- data: string;
- }[];
-}
-
-export default function read_style(parser: Parser, start: number, attributes: Attribute[]): Style {
- const content_start = parser.index;
- const styles = parser.read_until(/<\/style>/);
- const content_end = parser.index;
- parser.eat('</style>', true);
- const end = parser.index;
-
- return {
- type: 'Style',
- start,
- end,
- attributes,
- content: {
- start: content_start,
- end: content_end,
- styles,
- },
- };
-}
-
-function is_ref_selector(a: any, b: any) {
- // TODO add CSS node types
- if (!b) return false;
-
- return a.type === 'TypeSelector' && a.name === 'ref' && b.type === 'PseudoClassSelector';
-}
diff --git a/src/parser/parse/state/fragment.ts b/src/parser/parse/state/fragment.ts
deleted file mode 100644
index 97398b227..000000000
--- a/src/parser/parse/state/fragment.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import tag from './tag.js';
-import setup from './setup.js';
-import mustache from './mustache.js';
-import text from './text.js';
-import { Parser } from '../index.js';
-
-export default function fragment(parser: Parser) {
- if (parser.html.children.length === 0 && parser.match_regex(/^---/m)) {
- return setup;
- }
-
- if (parser.match('<')) {
- return tag;
- }
-
- if (parser.match('{')) {
- return mustache;
- }
-
- return text;
-}
diff --git a/src/parser/parse/state/mustache.ts b/src/parser/parse/state/mustache.ts
deleted file mode 100644
index 79372d8d9..000000000
--- a/src/parser/parse/state/mustache.ts
+++ /dev/null
@@ -1,413 +0,0 @@
-import read_context from '../read/context.js';
-import read_expression from '../read/expression.js';
-import { closing_tag_omitted } from '../utils/html.js';
-import { whitespace } from '../../utils/patterns.js';
-import { trim_start, trim_end } from '../../utils/trim.js';
-import { to_string } from '../utils/node.js';
-import { Parser } from '../index.js';
-import { TemplateNode } from '../../interfaces.js';
-
-type TODO = any;
-
-function trim_whitespace(block: TemplateNode, trim_before: boolean, trim_after: boolean) {
- if (!block.children || block.children.length === 0) return; // AwaitBlock
-
- const first_child = block.children[0];
- const last_child = block.children[block.children.length - 1];
-
- if (first_child.type === 'Text' && trim_before) {
- first_child.data = trim_start(first_child.data);
- if (!first_child.data) block.children.shift();
- }
-
- if (last_child.type === 'Text' && trim_after) {
- last_child.data = trim_end(last_child.data);
- if (!last_child.data) block.children.pop();
- }
-
- if (block.else) {
- trim_whitespace(block.else, trim_before, trim_after);
- }
-
- if (first_child.elseif) {
- trim_whitespace(first_child, trim_before, trim_after);
- }
-}
-
-export default function mustache(parser: Parser) {
- const start = parser.index;
- parser.index += 1;
-
- parser.allow_whitespace();
-
- // {/if}, {/each}, {/await} or {/key}
- if (parser.eat('/')) {
- let block = parser.current();
- let expected: TODO;
-
- if (closing_tag_omitted(block.name)) {
- block.end = start;
- parser.stack.pop();
- block = parser.current();
- }
-
- if (block.type === 'ElseBlock' || block.type === 'PendingBlock' || block.type === 'ThenBlock' || block.type === 'CatchBlock') {
- block.end = start;
- parser.stack.pop();
- block = parser.current();
-
- expected = 'await';
- }
-
- if (block.type === 'IfBlock') {
- expected = 'if';
- } else if (block.type === 'EachBlock') {
- expected = 'each';
- } else if (block.type === 'AwaitBlock') {
- expected = 'await';
- } else if (block.type === 'KeyBlock') {
- expected = 'key';
- } else {
- parser.error({
- code: 'unexpected-block-close',
- message: 'Unexpected block closing tag',
- });
- }
-
- parser.eat(expected, true);
- parser.allow_whitespace();
- parser.eat('}', true);
-
- while (block.elseif) {
- block.end = parser.index;
- parser.stack.pop();
- block = parser.current();
-
- if (block.else) {
- block.else.end = start;
- }
- }
-
- // strip leading/trailing whitespace as necessary
- const char_before = parser.template[block.start - 1];
- const char_after = parser.template[parser.index];
- const trim_before = !char_before || whitespace.test(char_before);
- const trim_after = !char_after || whitespace.test(char_after);
-
- trim_whitespace(block, trim_before, trim_after);
-
- block.end = parser.index;
- parser.stack.pop();
- } else if (parser.eat(':else')) {
- if (parser.eat('if')) {
- parser.error({
- code: 'invalid-elseif',
- message: "'elseif' should be 'else if'",
- });
- }
-
- parser.allow_whitespace();
-
- // :else if
- if (parser.eat('if')) {
- const block = parser.current();
- if (block.type !== 'IfBlock') {
- parser.error({
- code: 'invalid-elseif-placement',
- message: parser.stack.some((block) => block.type === 'IfBlock')
- ? `Expected to close ${to_string(block)} before seeing {:else if ...} block`
- : 'Cannot have an {:else if ...} block outside an {#if ...} block',
- });
- }
-
- parser.require_whitespace();
-
- const expression = read_expression(parser);
-
- parser.allow_whitespace();
- parser.eat('}', true);
-
- block.else = {
- start: parser.index,
- end: null,
- type: 'ElseBlock',
- children: [
- {
- start: parser.index,
- end: null,
- type: 'IfBlock',
- elseif: true,
- expression,
- children: [],
- },
- ],
- };
-
- parser.stack.push(block.else.children[0]);
- } else {
- // :else
- const block = parser.current();
- if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
- parser.error({
- code: 'invalid-else-placement',
- message: parser.stack.some((block) => block.type === 'IfBlock' || block.type === 'EachBlock')
- ? `Expected to close ${to_string(block)} before seeing {:else} block`
- : 'Cannot have an {:else} block outside an {#if ...} or {#each ...} block',
- });
- }
-
- parser.allow_whitespace();
- parser.eat('}', true);
-
- block.else = {
- start: parser.index,
- end: null,
- type: 'ElseBlock',
- children: [],
- };
-
- parser.stack.push(block.else);
- }
- } else if (parser.match(':then') || parser.match(':catch')) {
- const block = parser.current();
- const is_then = parser.eat(':then') || !parser.eat(':catch');
-
- if (is_then) {
- if (block.type !== 'PendingBlock') {
- parser.error({
- code: 'invalid-then-placement',
- message: parser.stack.some((block) => block.type === 'PendingBlock')
- ? `Expected to close ${to_string(block)} before seeing {:then} block`
- : 'Cannot have an {:then} block outside an {#await ...} block',
- });
- }
- } else {
- if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') {
- parser.error({
- code: 'invalid-catch-placement',
- message: parser.stack.some((block) => block.type === 'ThenBlock' || block.type === 'PendingBlock')
- ? `Expected to close ${to_string(block)} before seeing {:catch} block`
- : 'Cannot have an {:catch} block outside an {#await ...} block',
- });
- }
- }
-
- block.end = start;
- parser.stack.pop();
- const await_block = parser.current();
-
- if (!parser.eat('}')) {
- parser.require_whitespace();
- await_block[is_then ? 'value' : 'error'] = read_context(parser);
- parser.allow_whitespace();
- parser.eat('}', true);
- }
-
- const new_block: TemplateNode = {
- start,
- // @ts-ignore
- end: null,
- type: is_then ? 'ThenBlock' : 'CatchBlock',
- children: [],
- skip: false,
- };
-
- await_block[is_then ? 'then' : 'catch'] = new_block;
- parser.stack.push(new_block);
- } else if (parser.eat('#')) {
- // {#if foo}, {#each foo} or {#await foo}
- let type;
-
- if (parser.eat('if')) {
- type = 'IfBlock';
- } else if (parser.eat('each')) {
- type = 'EachBlock';
- } else if (parser.eat('await')) {
- type = 'AwaitBlock';
- } else if (parser.eat('key')) {
- type = 'KeyBlock';
- } else {
- parser.error({
- code: 'expected-block-type',
- message: 'Expected if, each, await or key',
- });
- }
-
- parser.require_whitespace();
-
- const expression = read_expression(parser);
-
- // @ts-ignore
- const block: TemplateNode =
- type === 'AwaitBlock'
- ? {
- start,
- end: null,
- type,
- expression,
- value: null,
- error: null,
- pending: {
- start: null,
- end: null,
- type: 'PendingBlock',
- children: [],
- skip: true,
- },
- then: {
- start: null,
- end: null,
- type: 'ThenBlock',
- children: [],
- skip: true,
- },
- catch: {
- start: null,
- end: null,
- type: 'CatchBlock',
- children: [],
- skip: true,
- },
- }
- : {
- start,
- end: null,
- type,
- expression,
- children: [],
- };
-
- parser.allow_whitespace();
-
- // {#each} blocks must declare a context – {#each list as item}
- if (type === 'EachBlock') {
- parser.eat('as', true);
- parser.require_whitespace();
-
- block.context = read_context(parser);
-
- parser.allow_whitespace();
-
- if (parser.eat(',')) {
- parser.allow_whitespace();
- block.index = parser.read_identifier();
- if (!block.index) {
- parser.error({
- code: 'expected-name',
- message: 'Expected name',
- });
- }
-
- parser.allow_whitespace();
- }
-
- if (parser.eat('(')) {
- parser.allow_whitespace();
-
- block.key = read_expression(parser);
- parser.allow_whitespace();
- parser.eat(')', true);
- parser.allow_whitespace();
- }
- }
-
- const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then');
- if (await_block_shorthand) {
- parser.require_whitespace();
- block.value = read_context(parser);
- parser.allow_whitespace();
- }
-
- const await_block_catch_shorthand = !await_block_shorthand && type === 'AwaitBlock' && parser.eat('catch');
- if (await_block_catch_shorthand) {
- parser.require_whitespace();
- block.error = read_context(parser);
- parser.allow_whitespace();
- }
-
- parser.eat('}', true);
-
- // @ts-ignore
- parser.current().children.push(block);
- parser.stack.push(block);
-
- if (type === 'AwaitBlock') {
- let child_block;
- if (await_block_shorthand) {
- block.then.skip = false;
- child_block = block.then;
- } else if (await_block_catch_shorthand) {
- block.catch.skip = false;
- child_block = block.catch;
- } else {
- block.pending.skip = false;
- child_block = block.pending;
- }
-
- child_block.start = parser.index;
- parser.stack.push(child_block);
- }
- } else if (parser.eat('@html')) {
- // {@html content} tag
- parser.require_whitespace();
-
- const expression = read_expression(parser);
-
- parser.allow_whitespace();
- parser.eat('}', true);
-
- // @ts-ignore
- parser.current().children.push({
- start,
- end: parser.index,
- type: 'RawMustacheTag',
- expression,
- });
- } else if (parser.eat('@debug')) {
- // let identifiers;
-
- // // Implies {@debug} which indicates "debug all"
- // if (parser.read(/\s*}/)) {
- // identifiers = [];
- // } else {
- // const expression = read_expression(parser);
-
- // identifiers = expression.type === 'SequenceExpression'
- // ? expression.expressions
- // : [expression];
-
- // identifiers.forEach(node => {
- // if (node.type !== 'Identifier') {
- // parser.error({
- // code: 'invalid-debug-args',
- // message: '{@debug ...} arguments must be identifiers, not arbitrary expressions'
- // }, node.start);
- // }
- // });
-
- // parser.allow_whitespace();
- // parser.eat('}', true);
- // }
-
- // parser.current().children.push({
- // start,
- // end: parser.index,
- // type: 'DebugTag',
- // identifiers
- // });
- throw new Error('@debug not yet supported');
- } else {
- const expression = read_expression(parser);
-
- parser.allow_whitespace();
- parser.eat('}', true);
-
- // @ts-ignore
- parser.current().children.push({
- start,
- end: parser.index,
- type: 'MustacheTag',
- expression,
- });
- }
-}
diff --git a/src/parser/parse/state/setup.ts b/src/parser/parse/state/setup.ts
deleted file mode 100644
index f64d8c52b..000000000
--- a/src/parser/parse/state/setup.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-// @ts-nocheck
-
-import { Parser } from '../index.js';
-
-export default function setup(parser: Parser): void {
- // TODO: Error if not at top of file? currently, we ignore / just treat as text.
- // if (parser.html.children.length > 0) {
- // parser.error({
- // code: 'unexpected-token',
- // message: 'Frontmatter scripts only supported at the top of file.',
- // });
- // }
-
- const start = parser.index;
- parser.index += 3;
- const content_start = parser.index;
- const setupScriptContent = parser.read_until(/^---/m);
- const content_end = parser.index;
- parser.eat('---', true);
- const end = parser.index;
- parser.js.push({
- type: 'Script',
- context: 'setup',
- start,
- end,
- content: setupScriptContent,
- // attributes,
- // content: {
- // start: content_start,
- // end: content_end,
- // styles,
- // },
- });
- return;
-}
diff --git a/src/parser/parse/state/tag.ts b/src/parser/parse/state/tag.ts
deleted file mode 100644
index a8b919a49..000000000
--- a/src/parser/parse/state/tag.ts
+++ /dev/null
@@ -1,579 +0,0 @@
-// @ts-nocheck
-
-import read_expression from '../read/expression.js';
-import read_script from '../read/script.js';
-import read_style from '../read/style.js';
-import { decode_character_references, closing_tag_omitted } from '../utils/html.js';
-import { is_void } from '../../utils/names.js';
-import { Parser } from '../index.js';
-import { Directive, DirectiveType, TemplateNode, Text } from '../../interfaces.js';
-import fuzzymatch from '../../utils/fuzzymatch.js';
-import list from '../../utils/list.js';
-
-// eslint-disable-next-line no-useless-escape
-const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
-
-const meta_tags = new Map([
- ['astro:head', 'Head'],
- // ['slot:body', 'Body'],
- // ['astro:options', 'Options'],
- // ['astro:window', 'Window'],
- // ['astro:body', 'Body'],
-]);
-
-const valid_meta_tags = Array.from(meta_tags.keys()); //.concat('astro:self', 'astro:component', 'astro:fragment');
-
-const specials = new Map([
- // Now handled as "setup" in setup.ts
- // [
- // 'script',
- // {
- // read: read_script,
- // property: 'js',
- // },
- // ],
- [
- 'style',
- {
- read: read_style,
- property: 'css',
- },
- ],
-]);
-
-const SELF = /^astro:self(?=[\s/>])/;
-const COMPONENT = /^astro:component(?=[\s/>])/;
-const SLOT = /^astro:fragment(?=[\s/>])/;
-const HEAD = /^head(?=[\s/>])/;
-
-function parent_is_head(stack) {
- let i = stack.length;
- while (i--) {
- const { type } = stack[i];
- if (type === 'Head') return true;
- if (type === 'Element' || type === 'InlineComponent') return false;
- }
- return false;
-}
-
-export default function tag(parser: Parser) {
- const start = parser.index++;
-
- let parent = parser.current();
-
- if (parser.eat('!--')) {
- const data = parser.read_until(/-->/);
- parser.eat('-->', true, 'comment was left open, expected -->');
-
- parser.current().children.push({
- start,
- end: parser.index,
- type: 'Comment',
- data,
- });
-
- return;
- }
-
- const is_closing_tag = parser.eat('/');
-
- const name = read_tag_name(parser);
-
- if (meta_tags.has(name)) {
- const slug = meta_tags.get(name).toLowerCase();
- if (is_closing_tag) {
- if ((name === 'astro:window' || name === 'astro:body') && parser.current().children.length) {
- parser.error(
- {
- code: `invalid-${slug}-content`,
- message: `<${name}> cannot have children`,
- },
- parser.current().children[0].start
- );
- }
- } else {
- if (name in parser.meta_tags) {
- parser.error(
- {
- code: `duplicate-${slug}`,
- message: `A component can only have one <${name}> tag`,
- },
- start
- );
- }
-
- if (parser.stack.length > 1) {
- parser.error(
- {
- code: `invalid-${slug}-placement`,
- message: `<${name}> tags cannot be inside elements or blocks`,
- },
- start
- );
- }
-
- parser.meta_tags[name] = true;
- }
- }
-
- const type = meta_tags.has(name)
- ? meta_tags.get(name)
- : /[A-Z]/.test(name[0]) || name === 'astro:self' || name === 'astro:component'
- ? 'InlineComponent'
- : name === 'astro:fragment'
- ? 'SlotTemplate'
- : name === 'title' && parent_is_head(parser.stack)
- ? 'Title'
- : name === 'slot' && !parser.customElement
- ? 'Slot'
- : 'Element';
-
- const element: TemplateNode = {
- start,
- end: null, // filled in later
- type,
- name,
- attributes: [],
- children: [],
- };
-
- parser.allow_whitespace();
-
- if (is_closing_tag) {
- if (is_void(name)) {
- parser.error(
- {
- code: 'invalid-void-content',
- message: `<${name}> is a void element and cannot have children, or a closing tag`,
- },
- start
- );
- }
-
- parser.eat('>', true);
-
- // close any elements that don't have their own closing tags, e.g. <div><p></div>
- while (parent.name !== name) {
- if (parent.type !== 'Element') {
- const message =
- parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name
- ? `</${name}> attempted to close <${name}> that was already automatically closed by <${parser.last_auto_closed_tag.reason}>`
- : `</${name}> attempted to close an element that was not open`;
- parser.error(
- {
- code: 'invalid-closing-tag',
- message,
- },
- start
- );
- }
-
- parent.end = start;
- parser.stack.pop();
-
- parent = parser.current();
- }
-
- parent.end = parser.index;
- parser.stack.pop();
-
- if (parser.last_auto_closed_tag && parser.stack.length < parser.last_auto_closed_tag.depth) {
- parser.last_auto_closed_tag = null;
- }
-
- return;
- } else if (closing_tag_omitted(parent.name, name)) {
- parent.end = start;
- parser.stack.pop();
- parser.last_auto_closed_tag = {
- tag: parent.name,
- reason: name,
- depth: parser.stack.length,
- };
- }
-
- const unique_names: Set<string> = new Set();
-
- let attribute;
- while ((attribute = read_attribute(parser, unique_names))) {
- element.attributes.push(attribute);
- parser.allow_whitespace();
- }
-
- if (name === 'astro:component') {
- const index = element.attributes.findIndex((attr) => attr.type === 'Attribute' && attr.name === 'this');
- if (!~index) {
- parser.error(
- {
- code: 'missing-component-definition',
- message: "<astro:component> must have a 'this' attribute",
- },
- start
- );
- }
-
- const definition = element.attributes.splice(index, 1)[0];
- if (definition.value === true || definition.value.length !== 1 || definition.value[0].type === 'Text') {
- parser.error(
- {
- code: 'invalid-component-definition',
- message: 'invalid component definition',
- },
- definition.start
- );
- }
-
- element.expression = definition.value[0].expression;
- }
-
- // special cases – top-level <script> and <style>
- if (specials.has(name) && parser.stack.length === 1) {
- const special = specials.get(name);
-
- parser.eat('>', true);
- const content = special.read(parser, start, element.attributes);
- if (content) parser[special.property].push(content);
- return;
- }
-
- parser.current().children.push(element);
-
- const self_closing = parser.eat('/') || is_void(name);
-
- parser.eat('>', true);
-
- if (self_closing) {
- // don't push self-closing elements onto the stack
- element.end = parser.index;
- } else if (name === 'textarea') {
- // special case
- element.children = read_sequence(parser, () => parser.template.slice(parser.index, parser.index + 11) === '</textarea>');
- parser.read(/<\/textarea>/);
- element.end = parser.index;
- } else if (name === 'script' || name === 'style') {
- // special case
- const start = parser.index;
- const data = parser.read_until(new RegExp(`</${name}>`));
- const end = parser.index;
- element.children.push({ start, end, type: 'Text', data });
- parser.eat(`</${name}>`, true);
- element.end = parser.index;
- } else {
- parser.stack.push(element);
- }
-}
-
-function read_tag_name(parser: Parser) {
- const start = parser.index;
-
- if (parser.read(SELF)) {
- // check we're inside a block, otherwise this
- // will cause infinite recursion
- let i = parser.stack.length;
- let legal = false;
-
- while (i--) {
- const fragment = parser.stack[i];
- if (fragment.type === 'IfBlock' || fragment.type === 'EachBlock' || fragment.type === 'InlineComponent') {
- legal = true;
- break;
- }
- }
-
- if (!legal) {
- parser.error(
- {
- code: 'invalid-self-placement',
- message: '<astro:self> components can only exist inside {#if} blocks, {#each} blocks, or slots passed to components',
- },
- start
- );
- }
-
- return 'astro:self';
- }
-
- if (parser.read(COMPONENT)) return 'astro:component';
-
- if (parser.read(SLOT)) return 'astro:fragment';
-
- if (parser.read(HEAD)) return 'head';
-
- const name = parser.read_until(/(\s|\/|>)/);
-
- if (meta_tags.has(name)) return name;
-
- if (name.startsWith('astro:')) {
- const match = fuzzymatch(name.slice(7), valid_meta_tags);
-
- let message = `Valid <astro:...> tag names are ${list(valid_meta_tags)}`;
- if (match) message += ` (did you mean '${match}'?)`;
-
- parser.error(
- {
- code: 'invalid-tag-name',
- message,
- },
- start
- );
- }
-
- if (!valid_tag_name.test(name)) {
- parser.error(
- {
- code: 'invalid-tag-name',
- message: 'Expected valid tag name',
- },
- start
- );
- }
-
- return name;
-}
-
-function read_attribute(parser: Parser, unique_names: Set<string>) {
- const start = parser.index;
-
- function check_unique(name: string) {
- if (unique_names.has(name)) {
- parser.error(
- {
- code: 'duplicate-attribute',
- message: 'Attributes need to be unique',
- },
- start
- );
- }
- unique_names.add(name);
- }
-
- if (parser.eat('{')) {
- parser.allow_whitespace();
-
- if (parser.eat('...')) {
- const { expression } = read_expression(parser);
-
- parser.allow_whitespace();
- parser.eat('}', true);
-
- return {
- start,
- end: parser.index,
- type: 'Spread',
- expression,
- };
- } else {
- const value_start = parser.index;
-
- const name = parser.read_identifier();
- parser.allow_whitespace();
- parser.eat('}', true);
-
- check_unique(name);
-
- return {
- start,
- end: parser.index,
- type: 'Attribute',
- name,
- value: [
- {
- start: value_start,
- end: value_start + name.length,
- type: 'AttributeShorthand',
- expression: {
- start: value_start,
- end: value_start + name.length,
- type: 'Identifier',
- name,
- },
- },
- ],
- };
- }
- }
-
- // eslint-disable-next-line no-useless-escape
- const name = parser.read_until(/[\s=\/>"']/);
- if (!name) return null;
-
- let end = parser.index;
-
- parser.allow_whitespace();
-
- const colon_index = name.indexOf(':');
- const type = colon_index !== -1 && get_directive_type(name.slice(0, colon_index));
-
- let value: any[] | true = true;
- if (parser.eat('=')) {
- parser.allow_whitespace();
- value = read_attribute_value(parser);
- end = parser.index;
- } else if (parser.match_regex(/["']/)) {
- parser.error(
- {
- code: 'unexpected-token',
- message: 'Expected =',
- },
- parser.index
- );
- }
-
- if (type) {
- const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|');
-
- if (type === 'Binding' && directive_name !== 'this') {
- check_unique(directive_name);
- } else if (type !== 'EventHandler' && type !== 'Action') {
- check_unique(name);
- }
-
- if (type === 'Ref') {
- parser.error(
- {
- code: 'invalid-ref-directive',
- message: `The ref directive is no longer supported — use \`bind:this={${directive_name}}\` instead`,
- },
- start
- );
- }
-
- if (type === 'Class' && directive_name === '') {
- parser.error(
- {
- code: 'invalid-class-directive',
- message: 'Class binding name cannot be empty',
- },
- start + colon_index + 1
- );
- }
-
- if (value[0]) {
- if ((value as any[]).length > 1 || value[0].type === 'Text') {
- parser.error(
- {
- code: 'invalid-directive-value',
- message: 'Directive value must be a JavaScript expression enclosed in curly braces',
- },
- value[0].start
- );
- }
- }
-
- const directive: Directive = {
- start,
- end,
- type,
- name: directive_name,
- modifiers,
- expression: (value[0] && value[0].expression) || null,
- };
-
- if (type === 'Transition') {
- const direction = name.slice(0, colon_index);
- directive.intro = direction === 'in' || direction === 'transition';
- directive.outro = direction === 'out' || direction === 'transition';
- }
-
- if (!directive.expression && (type === 'Binding' || type === 'Class')) {
- directive.expression = {
- start: directive.start + colon_index + 1,
- end: directive.end,
- type: 'Identifier',
- name: directive.name,
- } as any;
- }
-
- return directive;
- }
-
- check_unique(name);
-
- return {
- start,
- end,
- type: 'Attribute',
- name,
- value,
- };
-}
-
-function get_directive_type(name: string): DirectiveType {
- if (name === 'use') return 'Action';
- if (name === 'animate') return 'Animation';
- if (name === 'bind') return 'Binding';
- if (name === 'class') return 'Class';
- if (name === 'on') return 'EventHandler';
- if (name === 'let') return 'Let';
- if (name === 'ref') return 'Ref';
- if (name === 'in' || name === 'out' || name === 'transition') return 'Transition';
-}
-
-function read_attribute_value(parser: Parser) {
- const quote_mark = parser.eat("'") ? "'" : parser.eat('"') ? '"' : null;
-
- const regex = quote_mark === "'" ? /'/ : quote_mark === '"' ? /"/ : /(\/>|[\s"'=<>`])/;
-
- const value = read_sequence(parser, () => !!parser.match_regex(regex));
-
- if (quote_mark) parser.index += 1;
- return value;
-}
-
-function read_sequence(parser: Parser, done: () => boolean): TemplateNode[] {
- let current_chunk: Text = {
- start: parser.index,
- end: null,
- type: 'Text',
- raw: '',
- data: null,
- };
-
- function flush() {
- if (current_chunk.raw) {
- current_chunk.data = decode_character_references(current_chunk.raw);
- current_chunk.end = parser.index;
- chunks.push(current_chunk);
- }
- }
-
- const chunks: TemplateNode[] = [];
-
- while (parser.index < parser.template.length) {
- const index = parser.index;
-
- if (done()) {
- flush();
- return chunks;
- } else if (parser.eat('{')) {
- flush();
-
- parser.allow_whitespace();
- const expression = read_expression(parser);
- parser.allow_whitespace();
- parser.eat('}', true);
-
- chunks.push({
- start: index,
- end: parser.index,
- type: 'MustacheTag',
- expression,
- });
-
- current_chunk = {
- start: parser.index,
- end: null,
- type: 'Text',
- raw: '',
- data: null,
- };
- } else {
- current_chunk.raw += parser.template[parser.index++];
- }
- }
-
- parser.error({
- code: 'unexpected-eof',
- message: 'Unexpected end of input',
- });
-}
diff --git a/src/parser/parse/state/text.ts b/src/parser/parse/state/text.ts
deleted file mode 100644
index cca83f2d4..000000000
--- a/src/parser/parse/state/text.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-// @ts-nocheck
-
-import { decode_character_references } from '../utils/html.js';
-import { Parser } from '../index.js';
-
-export default function text(parser: Parser) {
- const start = parser.index;
-
- let data = '';
-
- while (parser.index < parser.template.length && !parser.match('---') && !parser.match('<') && !parser.match('{')) {
- data += parser.template[parser.index++];
- }
-
- const node = {
- start,
- end: parser.index,
- type: 'Text',
- raw: data,
- data: decode_character_references(data),
- };
-
- parser.current().children.push(node);
-}
diff --git a/src/parser/parse/utils/bracket.ts b/src/parser/parse/utils/bracket.ts
deleted file mode 100644
index 7e885ad78..000000000
--- a/src/parser/parse/utils/bracket.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-// @ts-nocheck
-
-const SQUARE_BRACKET_OPEN = '['.charCodeAt(0);
-const SQUARE_BRACKET_CLOSE = ']'.charCodeAt(0);
-const CURLY_BRACKET_OPEN = '{'.charCodeAt(0);
-const CURLY_BRACKET_CLOSE = '}'.charCodeAt(0);
-
-export function is_bracket_open(code) {
- return code === SQUARE_BRACKET_OPEN || code === CURLY_BRACKET_OPEN;
-}
-
-export function is_bracket_close(code) {
- return code === SQUARE_BRACKET_CLOSE || code === CURLY_BRACKET_CLOSE;
-}
-
-export function is_bracket_pair(open, close) {
- return (open === SQUARE_BRACKET_OPEN && close === SQUARE_BRACKET_CLOSE) || (open === CURLY_BRACKET_OPEN && close === CURLY_BRACKET_CLOSE);
-}
-
-export function get_bracket_close(open) {
- if (open === SQUARE_BRACKET_OPEN) {
- return SQUARE_BRACKET_CLOSE;
- }
- if (open === CURLY_BRACKET_OPEN) {
- return CURLY_BRACKET_CLOSE;
- }
-}
diff --git a/src/parser/parse/utils/entities.ts b/src/parser/parse/utils/entities.ts
deleted file mode 100644
index e554664eb..000000000
--- a/src/parser/parse/utils/entities.ts
+++ /dev/null
@@ -1,2034 +0,0 @@
-// https://dev.w3.org/html5/html-author/charref
-export default {
- CounterClockwiseContourIntegral: 8755,
- ClockwiseContourIntegral: 8754,
- DoubleLongLeftRightArrow: 10234,
- DiacriticalDoubleAcute: 733,
- NotSquareSupersetEqual: 8931,
- CloseCurlyDoubleQuote: 8221,
- DoubleContourIntegral: 8751,
- FilledVerySmallSquare: 9642,
- NegativeVeryThinSpace: 8203,
- NotPrecedesSlantEqual: 8928,
- NotRightTriangleEqual: 8941,
- NotSucceedsSlantEqual: 8929,
- CapitalDifferentialD: 8517,
- DoubleLeftRightArrow: 8660,
- DoubleLongRightArrow: 10233,
- EmptyVerySmallSquare: 9643,
- NestedGreaterGreater: 8811,
- NotDoubleVerticalBar: 8742,
- NotLeftTriangleEqual: 8940,
- NotSquareSubsetEqual: 8930,
- OpenCurlyDoubleQuote: 8220,
- ReverseUpEquilibrium: 10607,
- DoubleLongLeftArrow: 10232,
- DownLeftRightVector: 10576,
- LeftArrowRightArrow: 8646,
- NegativeMediumSpace: 8203,
- RightArrowLeftArrow: 8644,
- SquareSupersetEqual: 8850,
- leftrightsquigarrow: 8621,
- DownRightTeeVector: 10591,
- DownRightVectorBar: 10583,
- LongLeftRightArrow: 10231,
- Longleftrightarrow: 10234,
- NegativeThickSpace: 8203,
- PrecedesSlantEqual: 8828,
- ReverseEquilibrium: 8651,
- RightDoubleBracket: 10215,
- RightDownTeeVector: 10589,
- RightDownVectorBar: 10581,
- RightTriangleEqual: 8885,
- SquareIntersection: 8851,
- SucceedsSlantEqual: 8829,
- blacktriangleright: 9656,
- longleftrightarrow: 10231,
- DoubleUpDownArrow: 8661,
- DoubleVerticalBar: 8741,
- DownLeftTeeVector: 10590,
- DownLeftVectorBar: 10582,
- FilledSmallSquare: 9724,
- GreaterSlantEqual: 10878,
- LeftDoubleBracket: 10214,
- LeftDownTeeVector: 10593,
- LeftDownVectorBar: 10585,
- LeftTriangleEqual: 8884,
- NegativeThinSpace: 8203,
- NotReverseElement: 8716,
- NotTildeFullEqual: 8775,
- RightAngleBracket: 10217,
- RightUpDownVector: 10575,
- SquareSubsetEqual: 8849,
- VerticalSeparator: 10072,
- blacktriangledown: 9662,
- blacktriangleleft: 9666,
- leftrightharpoons: 8651,
- rightleftharpoons: 8652,
- twoheadrightarrow: 8608,
- DiacriticalAcute: 180,
- DiacriticalGrave: 96,
- DiacriticalTilde: 732,
- DoubleRightArrow: 8658,
- DownArrowUpArrow: 8693,
- EmptySmallSquare: 9723,
- GreaterEqualLess: 8923,
- GreaterFullEqual: 8807,
- LeftAngleBracket: 10216,
- LeftUpDownVector: 10577,
- LessEqualGreater: 8922,
- NonBreakingSpace: 160,
- NotRightTriangle: 8939,
- NotSupersetEqual: 8841,
- RightTriangleBar: 10704,
- RightUpTeeVector: 10588,
- RightUpVectorBar: 10580,
- UnderParenthesis: 9181,
- UpArrowDownArrow: 8645,
- circlearrowright: 8635,
- downharpoonright: 8642,
- ntrianglerighteq: 8941,
- rightharpoondown: 8641,
- rightrightarrows: 8649,
- twoheadleftarrow: 8606,
- vartriangleright: 8883,
- CloseCurlyQuote: 8217,
- ContourIntegral: 8750,
- DoubleDownArrow: 8659,
- DoubleLeftArrow: 8656,
- DownRightVector: 8641,
- LeftRightVector: 10574,
- LeftTriangleBar: 10703,
- LeftUpTeeVector: 10592,
- LeftUpVectorBar: 10584,
- LowerRightArrow: 8600,
- NotGreaterEqual: 8817,
- NotGreaterTilde: 8821,
- NotLeftTriangle: 8938,
- OverParenthesis: 9180,
- RightDownVector: 8642,
- ShortRightArrow: 8594,
- UpperRightArrow: 8599,
- bigtriangledown: 9661,
- circlearrowleft: 8634,
- curvearrowright: 8631,
- downharpoonleft: 8643,
- leftharpoondown: 8637,
- leftrightarrows: 8646,
- nLeftrightarrow: 8654,
- nleftrightarrow: 8622,
- ntrianglelefteq: 8940,
- rightleftarrows: 8644,
- rightsquigarrow: 8605,
- rightthreetimes: 8908,
- straightepsilon: 1013,
- trianglerighteq: 8885,
- vartriangleleft: 8882,
- DiacriticalDot: 729,
- DoubleRightTee: 8872,
- DownLeftVector: 8637,
- GreaterGreater: 10914,
- HorizontalLine: 9472,
- InvisibleComma: 8291,
- InvisibleTimes: 8290,
- LeftDownVector: 8643,
- LeftRightArrow: 8596,
- Leftrightarrow: 8660,
- LessSlantEqual: 10877,
- LongRightArrow: 10230,
- Longrightarrow: 10233,
- LowerLeftArrow: 8601,
- NestedLessLess: 8810,
- NotGreaterLess: 8825,
- NotLessGreater: 8824,
- NotSubsetEqual: 8840,
- NotVerticalBar: 8740,
- OpenCurlyQuote: 8216,
- ReverseElement: 8715,
- RightTeeVector: 10587,
- RightVectorBar: 10579,
- ShortDownArrow: 8595,
- ShortLeftArrow: 8592,
- SquareSuperset: 8848,
- TildeFullEqual: 8773,
- UpperLeftArrow: 8598,
- ZeroWidthSpace: 8203,
- curvearrowleft: 8630,
- doublebarwedge: 8966,
- downdownarrows: 8650,
- hookrightarrow: 8618,
- leftleftarrows: 8647,
- leftrightarrow: 8596,
- leftthreetimes: 8907,
- longrightarrow: 10230,
- looparrowright: 8620,
- nshortparallel: 8742,
- ntriangleright: 8939,
- rightarrowtail: 8611,
- rightharpoonup: 8640,
- trianglelefteq: 8884,
- upharpoonright: 8638,
- ApplyFunction: 8289,
- DifferentialD: 8518,
- DoubleLeftTee: 10980,
- DoubleUpArrow: 8657,
- LeftTeeVector: 10586,
- LeftVectorBar: 10578,
- LessFullEqual: 8806,
- LongLeftArrow: 10229,
- Longleftarrow: 10232,
- NotTildeEqual: 8772,
- NotTildeTilde: 8777,
- Poincareplane: 8460,
- PrecedesEqual: 10927,
- PrecedesTilde: 8830,
- RightArrowBar: 8677,
- RightTeeArrow: 8614,
- RightTriangle: 8883,
- RightUpVector: 8638,
- SucceedsEqual: 10928,
- SucceedsTilde: 8831,
- SupersetEqual: 8839,
- UpEquilibrium: 10606,
- VerticalTilde: 8768,
- VeryThinSpace: 8202,
- bigtriangleup: 9651,
- blacktriangle: 9652,
- divideontimes: 8903,
- fallingdotseq: 8786,
- hookleftarrow: 8617,
- leftarrowtail: 8610,
- leftharpoonup: 8636,
- longleftarrow: 10229,
- looparrowleft: 8619,
- measuredangle: 8737,
- ntriangleleft: 8938,
- shortparallel: 8741,
- smallsetminus: 8726,
- triangleright: 9657,
- upharpoonleft: 8639,
- DownArrowBar: 10515,
- DownTeeArrow: 8615,
- ExponentialE: 8519,
- GreaterEqual: 8805,
- GreaterTilde: 8819,
- HilbertSpace: 8459,
- HumpDownHump: 8782,
- Intersection: 8898,
- LeftArrowBar: 8676,
- LeftTeeArrow: 8612,
- LeftTriangle: 8882,
- LeftUpVector: 8639,
- NotCongruent: 8802,
- NotLessEqual: 8816,
- NotLessTilde: 8820,
- Proportional: 8733,
- RightCeiling: 8969,
- RoundImplies: 10608,
- ShortUpArrow: 8593,
- SquareSubset: 8847,
- UnderBracket: 9141,
- VerticalLine: 124,
- blacklozenge: 10731,
- exponentiale: 8519,
- risingdotseq: 8787,
- triangledown: 9663,
- triangleleft: 9667,
- CircleMinus: 8854,
- CircleTimes: 8855,
- Equilibrium: 8652,
- GreaterLess: 8823,
- LeftCeiling: 8968,
- LessGreater: 8822,
- MediumSpace: 8287,
- NotPrecedes: 8832,
- NotSucceeds: 8833,
- OverBracket: 9140,
- RightVector: 8640,
- Rrightarrow: 8667,
- RuleDelayed: 10740,
- SmallCircle: 8728,
- SquareUnion: 8852,
- SubsetEqual: 8838,
- UpDownArrow: 8597,
- Updownarrow: 8661,
- VerticalBar: 8739,
- backepsilon: 1014,
- blacksquare: 9642,
- circledcirc: 8858,
- circleddash: 8861,
- curlyeqprec: 8926,
- curlyeqsucc: 8927,
- diamondsuit: 9830,
- eqslantless: 10901,
- expectation: 8496,
- nRightarrow: 8655,
- nrightarrow: 8603,
- preccurlyeq: 8828,
- precnapprox: 10937,
- quaternions: 8461,
- straightphi: 981,
- succcurlyeq: 8829,
- succnapprox: 10938,
- thickapprox: 8776,
- updownarrow: 8597,
- Bernoullis: 8492,
- CirclePlus: 8853,
- EqualTilde: 8770,
- Fouriertrf: 8497,
- ImaginaryI: 8520,
- Laplacetrf: 8466,
- LeftVector: 8636,
- Lleftarrow: 8666,
- NotElement: 8713,
- NotGreater: 8815,
- Proportion: 8759,
- RightArrow: 8594,
- RightFloor: 8971,
- Rightarrow: 8658,
- TildeEqual: 8771,
- TildeTilde: 8776,
- UnderBrace: 9183,
- UpArrowBar: 10514,
- UpTeeArrow: 8613,
- circledast: 8859,
- complement: 8705,
- curlywedge: 8911,
- eqslantgtr: 10902,
- gtreqqless: 10892,
- lessapprox: 10885,
- lesseqqgtr: 10891,
- lmoustache: 9136,
- longmapsto: 10236,
- mapstodown: 8615,
- mapstoleft: 8612,
- nLeftarrow: 8653,
- nleftarrow: 8602,
- precapprox: 10935,
- rightarrow: 8594,
- rmoustache: 9137,
- sqsubseteq: 8849,
- sqsupseteq: 8850,
- subsetneqq: 10955,
- succapprox: 10936,
- supsetneqq: 10956,
- upuparrows: 8648,
- varepsilon: 949,
- varnothing: 8709,
- Backslash: 8726,
- CenterDot: 183,
- CircleDot: 8857,
- Congruent: 8801,
- Coproduct: 8720,
- DoubleDot: 168,
- DownArrow: 8595,
- DownBreve: 785,
- Downarrow: 8659,
- HumpEqual: 8783,
- LeftArrow: 8592,
- LeftFloor: 8970,
- Leftarrow: 8656,
- LessTilde: 8818,
- Mellintrf: 8499,
- MinusPlus: 8723,
- NotCupCap: 8813,
- NotExists: 8708,
- OverBrace: 9182,
- PlusMinus: 177,
- Therefore: 8756,
- ThinSpace: 8201,
- TripleDot: 8411,
- UnionPlus: 8846,
- backprime: 8245,
- backsimeq: 8909,
- bigotimes: 10754,
- centerdot: 183,
- checkmark: 10003,
- complexes: 8450,
- dotsquare: 8865,
- downarrow: 8595,
- gtrapprox: 10886,
- gtreqless: 8923,
- heartsuit: 9829,
- leftarrow: 8592,
- lesseqgtr: 8922,
- nparallel: 8742,
- nshortmid: 8740,
- nsubseteq: 8840,
- nsupseteq: 8841,
- pitchfork: 8916,
- rationals: 8474,
- spadesuit: 9824,
- subseteqq: 10949,
- subsetneq: 8842,
- supseteqq: 10950,
- supsetneq: 8843,
- therefore: 8756,
- triangleq: 8796,
- varpropto: 8733,
- DDotrahd: 10513,
- DotEqual: 8784,
- Integral: 8747,
- LessLess: 10913,
- NotEqual: 8800,
- NotTilde: 8769,
- PartialD: 8706,
- Precedes: 8826,
- RightTee: 8866,
- Succeeds: 8827,
- SuchThat: 8715,
- Superset: 8835,
- Uarrocir: 10569,
- UnderBar: 818,
- andslope: 10840,
- angmsdaa: 10664,
- angmsdab: 10665,
- angmsdac: 10666,
- angmsdad: 10667,
- angmsdae: 10668,
- angmsdaf: 10669,
- angmsdag: 10670,
- angmsdah: 10671,
- angrtvbd: 10653,
- approxeq: 8778,
- awconint: 8755,
- backcong: 8780,
- barwedge: 8965,
- bbrktbrk: 9142,
- bigoplus: 10753,
- bigsqcup: 10758,
- biguplus: 10756,
- bigwedge: 8896,
- boxminus: 8863,
- boxtimes: 8864,
- capbrcup: 10825,
- circledR: 174,
- circledS: 9416,
- cirfnint: 10768,
- clubsuit: 9827,
- cupbrcap: 10824,
- curlyvee: 8910,
- cwconint: 8754,
- doteqdot: 8785,
- dotminus: 8760,
- drbkarow: 10512,
- dzigrarr: 10239,
- elinters: 9191,
- emptyset: 8709,
- eqvparsl: 10725,
- fpartint: 10765,
- geqslant: 10878,
- gesdotol: 10884,
- gnapprox: 10890,
- hksearow: 10533,
- hkswarow: 10534,
- imagline: 8464,
- imagpart: 8465,
- infintie: 10717,
- integers: 8484,
- intercal: 8890,
- intlarhk: 10775,
- laemptyv: 10676,
- ldrushar: 10571,
- leqslant: 10877,
- lesdotor: 10883,
- llcorner: 8990,
- lnapprox: 10889,
- lrcorner: 8991,
- lurdshar: 10570,
- mapstoup: 8613,
- multimap: 8888,
- naturals: 8469,
- otimesas: 10806,
- parallel: 8741,
- plusacir: 10787,
- pointint: 10773,
- precneqq: 10933,
- precnsim: 8936,
- profalar: 9006,
- profline: 8978,
- profsurf: 8979,
- raemptyv: 10675,
- realpart: 8476,
- rppolint: 10770,
- rtriltri: 10702,
- scpolint: 10771,
- setminus: 8726,
- shortmid: 8739,
- smeparsl: 10724,
- sqsubset: 8847,
- sqsupset: 8848,
- subseteq: 8838,
- succneqq: 10934,
- succnsim: 8937,
- supseteq: 8839,
- thetasym: 977,
- thicksim: 8764,
- timesbar: 10801,
- triangle: 9653,
- triminus: 10810,
- trpezium: 9186,
- ulcorner: 8988,
- urcorner: 8989,
- varkappa: 1008,
- varsigma: 962,
- vartheta: 977,
- Because: 8757,
- Cayleys: 8493,
- Cconint: 8752,
- Cedilla: 184,
- Diamond: 8900,
- DownTee: 8868,
- Element: 8712,
- Epsilon: 917,
- Implies: 8658,
- LeftTee: 8867,
- NewLine: 10,
- NoBreak: 8288,
- NotLess: 8814,
- Omicron: 927,
- OverBar: 175,
- Product: 8719,
- UpArrow: 8593,
- Uparrow: 8657,
- Upsilon: 933,
- alefsym: 8501,
- angrtvb: 8894,
- angzarr: 9084,
- asympeq: 8781,
- backsim: 8765,
- because: 8757,
- bemptyv: 10672,
- between: 8812,
- bigcirc: 9711,
- bigodot: 10752,
- bigstar: 9733,
- boxplus: 8862,
- ccupssm: 10832,
- cemptyv: 10674,
- cirscir: 10690,
- coloneq: 8788,
- congdot: 10861,
- cudarrl: 10552,
- cudarrr: 10549,
- cularrp: 10557,
- curarrm: 10556,
- dbkarow: 10511,
- ddagger: 8225,
- ddotseq: 10871,
- demptyv: 10673,
- diamond: 8900,
- digamma: 989,
- dotplus: 8724,
- dwangle: 10662,
- epsilon: 949,
- eqcolon: 8789,
- equivDD: 10872,
- gesdoto: 10882,
- gtquest: 10876,
- gtrless: 8823,
- harrcir: 10568,
- intprod: 10812,
- isindot: 8949,
- larrbfs: 10527,
- larrsim: 10611,
- lbrksld: 10639,
- lbrkslu: 10637,
- ldrdhar: 10599,
- lesdoto: 10881,
- lessdot: 8918,
- lessgtr: 8822,
- lesssim: 8818,
- lotimes: 10804,
- lozenge: 9674,
- ltquest: 10875,
- luruhar: 10598,
- maltese: 10016,
- minusdu: 10794,
- napprox: 8777,
- natural: 9838,
- nearrow: 8599,
- nexists: 8708,
- notinva: 8713,
- notinvb: 8951,
- notinvc: 8950,
- notniva: 8716,
- notnivb: 8958,
- notnivc: 8957,
- npolint: 10772,
- nsqsube: 8930,
- nsqsupe: 8931,
- nvinfin: 10718,
- nwarrow: 8598,
- olcross: 10683,
- omicron: 959,
- orderof: 8500,
- orslope: 10839,
- pertenk: 8241,
- planckh: 8462,
- pluscir: 10786,
- plussim: 10790,
- plustwo: 10791,
- precsim: 8830,
- quatint: 10774,
- questeq: 8799,
- rarrbfs: 10528,
- rarrsim: 10612,
- rbrksld: 10638,
- rbrkslu: 10640,
- rdldhar: 10601,
- realine: 8475,
- rotimes: 10805,
- ruluhar: 10600,
- searrow: 8600,
- simplus: 10788,
- simrarr: 10610,
- subedot: 10947,
- submult: 10945,
- subplus: 10943,
- subrarr: 10617,
- succsim: 8831,
- supdsub: 10968,
- supedot: 10948,
- suphsub: 10967,
- suplarr: 10619,
- supmult: 10946,
- supplus: 10944,
- swarrow: 8601,
- topfork: 10970,
- triplus: 10809,
- tritime: 10811,
- uparrow: 8593,
- upsilon: 965,
- uwangle: 10663,
- vzigzag: 10650,
- zigrarr: 8669,
- Aacute: 193,
- Abreve: 258,
- Agrave: 192,
- Assign: 8788,
- Atilde: 195,
- Barwed: 8966,
- Bumpeq: 8782,
- Cacute: 262,
- Ccaron: 268,
- Ccedil: 199,
- Colone: 10868,
- Conint: 8751,
- CupCap: 8781,
- Dagger: 8225,
- Dcaron: 270,
- DotDot: 8412,
- Dstrok: 272,
- Eacute: 201,
- Ecaron: 282,
- Egrave: 200,
- Exists: 8707,
- ForAll: 8704,
- Gammad: 988,
- Gbreve: 286,
- Gcedil: 290,
- HARDcy: 1066,
- Hstrok: 294,
- Iacute: 205,
- Igrave: 204,
- Itilde: 296,
- Jsercy: 1032,
- Kcedil: 310,
- Lacute: 313,
- Lambda: 923,
- Lcaron: 317,
- Lcedil: 315,
- Lmidot: 319,
- Lstrok: 321,
- Nacute: 323,
- Ncaron: 327,
- Ncedil: 325,
- Ntilde: 209,
- Oacute: 211,
- Odblac: 336,
- Ograve: 210,
- Oslash: 216,
- Otilde: 213,
- Otimes: 10807,
- Racute: 340,
- Rarrtl: 10518,
- Rcaron: 344,
- Rcedil: 342,
- SHCHcy: 1065,
- SOFTcy: 1068,
- Sacute: 346,
- Scaron: 352,
- Scedil: 350,
- Square: 9633,
- Subset: 8912,
- Supset: 8913,
- Tcaron: 356,
- Tcedil: 354,
- Tstrok: 358,
- Uacute: 218,
- Ubreve: 364,
- Udblac: 368,
- Ugrave: 217,
- Utilde: 360,
- Vdashl: 10982,
- Verbar: 8214,
- Vvdash: 8874,
- Yacute: 221,
- Zacute: 377,
- Zcaron: 381,
- aacute: 225,
- abreve: 259,
- agrave: 224,
- andand: 10837,
- angmsd: 8737,
- angsph: 8738,
- apacir: 10863,
- approx: 8776,
- atilde: 227,
- barvee: 8893,
- barwed: 8965,
- becaus: 8757,
- bernou: 8492,
- bigcap: 8898,
- bigcup: 8899,
- bigvee: 8897,
- bkarow: 10509,
- bottom: 8869,
- bowtie: 8904,
- boxbox: 10697,
- bprime: 8245,
- brvbar: 166,
- bullet: 8226,
- bumpeq: 8783,
- cacute: 263,
- capand: 10820,
- capcap: 10827,
- capcup: 10823,
- capdot: 10816,
- ccaron: 269,
- ccedil: 231,
- circeq: 8791,
- cirmid: 10991,
- colone: 8788,
- commat: 64,
- compfn: 8728,
- conint: 8750,
- coprod: 8720,
- copysr: 8471,
- cularr: 8630,
- cupcap: 10822,
- cupcup: 10826,
- cupdot: 8845,
- curarr: 8631,
- curren: 164,
- cylcty: 9005,
- dagger: 8224,
- daleth: 8504,
- dcaron: 271,
- dfisht: 10623,
- divide: 247,
- divonx: 8903,
- dlcorn: 8990,
- dlcrop: 8973,
- dollar: 36,
- drcorn: 8991,
- drcrop: 8972,
- dstrok: 273,
- eacute: 233,
- easter: 10862,
- ecaron: 283,
- ecolon: 8789,
- egrave: 232,
- egsdot: 10904,
- elsdot: 10903,
- emptyv: 8709,
- emsp13: 8196,
- emsp14: 8197,
- eparsl: 10723,
- eqcirc: 8790,
- equals: 61,
- equest: 8799,
- female: 9792,
- ffilig: 64259,
- ffllig: 64260,
- forall: 8704,
- frac12: 189,
- frac13: 8531,
- frac14: 188,
- frac15: 8533,
- frac16: 8537,
- frac18: 8539,
- frac23: 8532,
- frac25: 8534,
- frac34: 190,
- frac35: 8535,
- frac38: 8540,
- frac45: 8536,
- frac56: 8538,
- frac58: 8541,
- frac78: 8542,
- gacute: 501,
- gammad: 989,
- gbreve: 287,
- gesdot: 10880,
- gesles: 10900,
- gtlPar: 10645,
- gtrarr: 10616,
- gtrdot: 8919,
- gtrsim: 8819,
- hairsp: 8202,
- hamilt: 8459,
- hardcy: 1098,
- hearts: 9829,
- hellip: 8230,
- hercon: 8889,
- homtht: 8763,
- horbar: 8213,
- hslash: 8463,
- hstrok: 295,
- hybull: 8259,
- hyphen: 8208,
- iacute: 237,
- igrave: 236,
- iiiint: 10764,
- iinfin: 10716,
- incare: 8453,
- inodot: 305,
- intcal: 8890,
- iquest: 191,
- isinsv: 8947,
- itilde: 297,
- jsercy: 1112,
- kappav: 1008,
- kcedil: 311,
- kgreen: 312,
- lAtail: 10523,
- lacute: 314,
- lagran: 8466,
- lambda: 955,
- langle: 10216,
- larrfs: 10525,
- larrhk: 8617,
- larrlp: 8619,
- larrpl: 10553,
- larrtl: 8610,
- latail: 10521,
- lbrace: 123,
- lbrack: 91,
- lcaron: 318,
- lcedil: 316,
- ldquor: 8222,
- lesdot: 10879,
- lesges: 10899,
- lfisht: 10620,
- lfloor: 8970,
- lharul: 10602,
- llhard: 10603,
- lmidot: 320,
- lmoust: 9136,
- loplus: 10797,
- lowast: 8727,
- lowbar: 95,
- lparlt: 10643,
- lrhard: 10605,
- lsaquo: 8249,
- lsquor: 8218,
- lstrok: 322,
- lthree: 8907,
- ltimes: 8905,
- ltlarr: 10614,
- ltrPar: 10646,
- mapsto: 8614,
- marker: 9646,
- mcomma: 10793,
- midast: 42,
- midcir: 10992,
- middot: 183,
- minusb: 8863,
- minusd: 8760,
- mnplus: 8723,
- models: 8871,
- mstpos: 8766,
- nVDash: 8879,
- nVdash: 8878,
- nacute: 324,
- ncaron: 328,
- ncedil: 326,
- nearhk: 10532,
- nequiv: 8802,
- nesear: 10536,
- nexist: 8708,
- nltrie: 8940,
- nprcue: 8928,
- nrtrie: 8941,
- nsccue: 8929,
- nsimeq: 8772,
- ntilde: 241,
- numero: 8470,
- nvDash: 8877,
- nvHarr: 10500,
- nvdash: 8876,
- nvlArr: 10498,
- nvrArr: 10499,
- nwarhk: 10531,
- nwnear: 10535,
- oacute: 243,
- odblac: 337,
- odsold: 10684,
- ograve: 242,
- ominus: 8854,
- origof: 8886,
- oslash: 248,
- otilde: 245,
- otimes: 8855,
- parsim: 10995,
- percnt: 37,
- period: 46,
- permil: 8240,
- phmmat: 8499,
- planck: 8463,
- plankv: 8463,
- plusdo: 8724,
- plusdu: 10789,
- plusmn: 177,
- preceq: 10927,
- primes: 8473,
- prnsim: 8936,
- propto: 8733,
- prurel: 8880,
- puncsp: 8200,
- qprime: 8279,
- rAtail: 10524,
- racute: 341,
- rangle: 10217,
- rarrap: 10613,
- rarrfs: 10526,
- rarrhk: 8618,
- rarrlp: 8620,
- rarrpl: 10565,
- rarrtl: 8611,
- ratail: 10522,
- rbrace: 125,
- rbrack: 93,
- rcaron: 345,
- rcedil: 343,
- rdquor: 8221,
- rfisht: 10621,
- rfloor: 8971,
- rharul: 10604,
- rmoust: 9137,
- roplus: 10798,
- rpargt: 10644,
- rsaquo: 8250,
- rsquor: 8217,
- rthree: 8908,
- rtimes: 8906,
- sacute: 347,
- scaron: 353,
- scedil: 351,
- scnsim: 8937,
- searhk: 10533,
- seswar: 10537,
- sfrown: 8994,
- shchcy: 1097,
- sigmaf: 962,
- sigmav: 962,
- simdot: 10858,
- smashp: 10803,
- softcy: 1100,
- solbar: 9023,
- spades: 9824,
- sqsube: 8849,
- sqsupe: 8850,
- square: 9633,
- squarf: 9642,
- ssetmn: 8726,
- ssmile: 8995,
- sstarf: 8902,
- subdot: 10941,
- subset: 8834,
- subsim: 10951,
- subsub: 10965,
- subsup: 10963,
- succeq: 10928,
- supdot: 10942,
- supset: 8835,
- supsim: 10952,
- supsub: 10964,
- supsup: 10966,
- swarhk: 10534,
- swnwar: 10538,
- target: 8982,
- tcaron: 357,
- tcedil: 355,
- telrec: 8981,
- there4: 8756,
- thetav: 977,
- thinsp: 8201,
- thksim: 8764,
- timesb: 8864,
- timesd: 10800,
- topbot: 9014,
- topcir: 10993,
- tprime: 8244,
- tridot: 9708,
- tstrok: 359,
- uacute: 250,
- ubreve: 365,
- udblac: 369,
- ufisht: 10622,
- ugrave: 249,
- ulcorn: 8988,
- ulcrop: 8975,
- urcorn: 8989,
- urcrop: 8974,
- utilde: 361,
- vangrt: 10652,
- varphi: 966,
- varrho: 1009,
- veebar: 8891,
- vellip: 8942,
- verbar: 124,
- wedbar: 10847,
- wedgeq: 8793,
- weierp: 8472,
- wreath: 8768,
- xoplus: 10753,
- xotime: 10754,
- xsqcup: 10758,
- xuplus: 10756,
- xwedge: 8896,
- yacute: 253,
- zacute: 378,
- zcaron: 382,
- zeetrf: 8488,
- AElig: 198,
- Acirc: 194,
- Alpha: 913,
- Amacr: 256,
- Aogon: 260,
- Aring: 197,
- Breve: 728,
- Ccirc: 264,
- Colon: 8759,
- Cross: 10799,
- Dashv: 10980,
- Delta: 916,
- Ecirc: 202,
- Emacr: 274,
- Eogon: 280,
- Equal: 10869,
- Gamma: 915,
- Gcirc: 284,
- Hacek: 711,
- Hcirc: 292,
- IJlig: 306,
- Icirc: 206,
- Imacr: 298,
- Iogon: 302,
- Iukcy: 1030,
- Jcirc: 308,
- Jukcy: 1028,
- Kappa: 922,
- OElig: 338,
- Ocirc: 212,
- Omacr: 332,
- Omega: 937,
- Prime: 8243,
- RBarr: 10512,
- Scirc: 348,
- Sigma: 931,
- THORN: 222,
- TRADE: 8482,
- TSHcy: 1035,
- Theta: 920,
- Tilde: 8764,
- Ubrcy: 1038,
- Ucirc: 219,
- Umacr: 362,
- Union: 8899,
- Uogon: 370,
- UpTee: 8869,
- Uring: 366,
- VDash: 8875,
- Vdash: 8873,
- Wcirc: 372,
- Wedge: 8896,
- Ycirc: 374,
- acirc: 226,
- acute: 180,
- aelig: 230,
- aleph: 8501,
- alpha: 945,
- amacr: 257,
- amalg: 10815,
- angle: 8736,
- angrt: 8735,
- angst: 8491,
- aogon: 261,
- aring: 229,
- asymp: 8776,
- awint: 10769,
- bcong: 8780,
- bdquo: 8222,
- bepsi: 1014,
- blank: 9251,
- blk12: 9618,
- blk14: 9617,
- blk34: 9619,
- block: 9608,
- boxDL: 9559,
- boxDR: 9556,
- boxDl: 9558,
- boxDr: 9555,
- boxHD: 9574,
- boxHU: 9577,
- boxHd: 9572,
- boxHu: 9575,
- boxUL: 9565,
- boxUR: 9562,
- boxUl: 9564,
- boxUr: 9561,
- boxVH: 9580,
- boxVL: 9571,
- boxVR: 9568,
- boxVh: 9579,
- boxVl: 9570,
- boxVr: 9567,
- boxdL: 9557,
- boxdR: 9554,
- boxdl: 9488,
- boxdr: 9484,
- boxhD: 9573,
- boxhU: 9576,
- boxhd: 9516,
- boxhu: 9524,
- boxuL: 9563,
- boxuR: 9560,
- boxul: 9496,
- boxur: 9492,
- boxvH: 9578,
- boxvL: 9569,
- boxvR: 9566,
- boxvh: 9532,
- boxvl: 9508,
- boxvr: 9500,
- breve: 728,
- bsemi: 8271,
- bsime: 8909,
- bsolb: 10693,
- bumpE: 10926,
- bumpe: 8783,
- caret: 8257,
- caron: 711,
- ccaps: 10829,
- ccirc: 265,
- ccups: 10828,
- cedil: 184,
- check: 10003,
- clubs: 9827,
- colon: 58,
- comma: 44,
- crarr: 8629,
- cross: 10007,
- csube: 10961,
- csupe: 10962,
- ctdot: 8943,
- cuepr: 8926,
- cuesc: 8927,
- cupor: 10821,
- cuvee: 8910,
- cuwed: 8911,
- cwint: 8753,
- dashv: 8867,
- dblac: 733,
- ddarr: 8650,
- delta: 948,
- dharl: 8643,
- dharr: 8642,
- diams: 9830,
- disin: 8946,
- doteq: 8784,
- dtdot: 8945,
- dtrif: 9662,
- duarr: 8693,
- duhar: 10607,
- eDDot: 10871,
- ecirc: 234,
- efDot: 8786,
- emacr: 275,
- empty: 8709,
- eogon: 281,
- eplus: 10865,
- epsiv: 949,
- eqsim: 8770,
- equiv: 8801,
- erDot: 8787,
- erarr: 10609,
- esdot: 8784,
- exist: 8707,
- fflig: 64256,
- filig: 64257,
- fllig: 64258,
- fltns: 9649,
- forkv: 10969,
- frasl: 8260,
- frown: 8994,
- gamma: 947,
- gcirc: 285,
- gescc: 10921,
- gimel: 8503,
- gneqq: 8809,
- gnsim: 8935,
- grave: 96,
- gsime: 10894,
- gsiml: 10896,
- gtcir: 10874,
- gtdot: 8919,
- harrw: 8621,
- hcirc: 293,
- hoarr: 8703,
- icirc: 238,
- iexcl: 161,
- iiint: 8749,
- iiota: 8489,
- ijlig: 307,
- imacr: 299,
- image: 8465,
- imath: 305,
- imped: 437,
- infin: 8734,
- iogon: 303,
- iprod: 10812,
- isinE: 8953,
- isins: 8948,
- isinv: 8712,
- iukcy: 1110,
- jcirc: 309,
- jmath: 567,
- jukcy: 1108,
- kappa: 954,
- lAarr: 8666,
- lBarr: 10510,
- langd: 10641,
- laquo: 171,
- larrb: 8676,
- lbarr: 10508,
- lbbrk: 10098,
- lbrke: 10635,
- lceil: 8968,
- ldquo: 8220,
- lescc: 10920,
- lhard: 8637,
- lharu: 8636,
- lhblk: 9604,
- llarr: 8647,
- lltri: 9722,
- lneqq: 8808,
- lnsim: 8934,
- loang: 10220,
- loarr: 8701,
- lobrk: 10214,
- lopar: 10629,
- lrarr: 8646,
- lrhar: 8651,
- lrtri: 8895,
- lsime: 10893,
- lsimg: 10895,
- lsquo: 8216,
- ltcir: 10873,
- ltdot: 8918,
- ltrie: 8884,
- ltrif: 9666,
- mDDot: 8762,
- mdash: 8212,
- micro: 181,
- minus: 8722,
- mumap: 8888,
- nabla: 8711,
- napos: 329,
- natur: 9838,
- ncong: 8775,
- ndash: 8211,
- neArr: 8663,
- nearr: 8599,
- ngsim: 8821,
- nhArr: 8654,
- nharr: 8622,
- nhpar: 10994,
- nlArr: 8653,
- nlarr: 8602,
- nless: 8814,
- nlsim: 8820,
- nltri: 8938,
- notin: 8713,
- notni: 8716,
- nprec: 8832,
- nrArr: 8655,
- nrarr: 8603,
- nrtri: 8939,
- nsime: 8772,
- nsmid: 8740,
- nspar: 8742,
- nsube: 8840,
- nsucc: 8833,
- nsupe: 8841,
- numsp: 8199,
- nwArr: 8662,
- nwarr: 8598,
- ocirc: 244,
- odash: 8861,
- oelig: 339,
- ofcir: 10687,
- ohbar: 10677,
- olarr: 8634,
- olcir: 10686,
- oline: 8254,
- omacr: 333,
- omega: 969,
- operp: 10681,
- oplus: 8853,
- orarr: 8635,
- order: 8500,
- ovbar: 9021,
- parsl: 11005,
- phone: 9742,
- plusb: 8862,
- pluse: 10866,
- pound: 163,
- prcue: 8828,
- prime: 8242,
- prnap: 10937,
- prsim: 8830,
- quest: 63,
- rAarr: 8667,
- rBarr: 10511,
- radic: 8730,
- rangd: 10642,
- range: 10661,
- raquo: 187,
- rarrb: 8677,
- rarrc: 10547,
- rarrw: 8605,
- ratio: 8758,
- rbarr: 10509,
- rbbrk: 10099,
- rbrke: 10636,
- rceil: 8969,
- rdquo: 8221,
- reals: 8477,
- rhard: 8641,
- rharu: 8640,
- rlarr: 8644,
- rlhar: 8652,
- rnmid: 10990,
- roang: 10221,
- roarr: 8702,
- robrk: 10215,
- ropar: 10630,
- rrarr: 8649,
- rsquo: 8217,
- rtrie: 8885,
- rtrif: 9656,
- sbquo: 8218,
- sccue: 8829,
- scirc: 349,
- scnap: 10938,
- scsim: 8831,
- sdotb: 8865,
- sdote: 10854,
- seArr: 8664,
- searr: 8600,
- setmn: 8726,
- sharp: 9839,
- sigma: 963,
- simeq: 8771,
- simgE: 10912,
- simlE: 10911,
- simne: 8774,
- slarr: 8592,
- smile: 8995,
- sqcap: 8851,
- sqcup: 8852,
- sqsub: 8847,
- sqsup: 8848,
- srarr: 8594,
- starf: 9733,
- strns: 175,
- subnE: 10955,
- subne: 8842,
- supnE: 10956,
- supne: 8843,
- swArr: 8665,
- swarr: 8601,
- szlig: 223,
- theta: 952,
- thkap: 8776,
- thorn: 254,
- tilde: 732,
- times: 215,
- trade: 8482,
- trisb: 10701,
- tshcy: 1115,
- twixt: 8812,
- ubrcy: 1118,
- ucirc: 251,
- udarr: 8645,
- udhar: 10606,
- uharl: 8639,
- uharr: 8638,
- uhblk: 9600,
- ultri: 9720,
- umacr: 363,
- uogon: 371,
- uplus: 8846,
- upsih: 978,
- uring: 367,
- urtri: 9721,
- utdot: 8944,
- utrif: 9652,
- uuarr: 8648,
- vBarv: 10985,
- vDash: 8872,
- varpi: 982,
- vdash: 8866,
- veeeq: 8794,
- vltri: 8882,
- vprop: 8733,
- vrtri: 8883,
- wcirc: 373,
- wedge: 8743,
- xcirc: 9711,
- xdtri: 9661,
- xhArr: 10234,
- xharr: 10231,
- xlArr: 10232,
- xlarr: 10229,
- xodot: 10752,
- xrArr: 10233,
- xrarr: 10230,
- xutri: 9651,
- ycirc: 375,
- Aopf: 120120,
- Ascr: 119964,
- Auml: 196,
- Barv: 10983,
- Beta: 914,
- Bopf: 120121,
- Bscr: 8492,
- CHcy: 1063,
- COPY: 169,
- Cdot: 266,
- Copf: 8450,
- Cscr: 119966,
- DJcy: 1026,
- DScy: 1029,
- DZcy: 1039,
- Darr: 8609,
- Dopf: 120123,
- Dscr: 119967,
- Edot: 278,
- Eopf: 120124,
- Escr: 8496,
- Esim: 10867,
- Euml: 203,
- Fopf: 120125,
- Fscr: 8497,
- GJcy: 1027,
- Gdot: 288,
- Gopf: 120126,
- Gscr: 119970,
- Hopf: 8461,
- Hscr: 8459,
- IEcy: 1045,
- IOcy: 1025,
- Idot: 304,
- Iopf: 120128,
- Iota: 921,
- Iscr: 8464,
- Iuml: 207,
- Jopf: 120129,
- Jscr: 119973,
- KHcy: 1061,
- KJcy: 1036,
- Kopf: 120130,
- Kscr: 119974,
- LJcy: 1033,
- Lang: 10218,
- Larr: 8606,
- Lopf: 120131,
- Lscr: 8466,
- Mopf: 120132,
- Mscr: 8499,
- NJcy: 1034,
- Nopf: 8469,
- Nscr: 119977,
- Oopf: 120134,
- Oscr: 119978,
- Ouml: 214,
- Popf: 8473,
- Pscr: 119979,
- QUOT: 34,
- Qopf: 8474,
- Qscr: 119980,
- Rang: 10219,
- Rarr: 8608,
- Ropf: 8477,
- Rscr: 8475,
- SHcy: 1064,
- Sopf: 120138,
- Sqrt: 8730,
- Sscr: 119982,
- Star: 8902,
- TScy: 1062,
- Topf: 120139,
- Tscr: 119983,
- Uarr: 8607,
- Uopf: 120140,
- Upsi: 978,
- Uscr: 119984,
- Uuml: 220,
- Vbar: 10987,
- Vert: 8214,
- Vopf: 120141,
- Vscr: 119985,
- Wopf: 120142,
- Wscr: 119986,
- Xopf: 120143,
- Xscr: 119987,
- YAcy: 1071,
- YIcy: 1031,
- YUcy: 1070,
- Yopf: 120144,
- Yscr: 119988,
- Yuml: 376,
- ZHcy: 1046,
- Zdot: 379,
- Zeta: 918,
- Zopf: 8484,
- Zscr: 119989,
- andd: 10844,
- andv: 10842,
- ange: 10660,
- aopf: 120146,
- apid: 8779,
- apos: 39,
- ascr: 119990,
- auml: 228,
- bNot: 10989,
- bbrk: 9141,
- beta: 946,
- beth: 8502,
- bnot: 8976,
- bopf: 120147,
- boxH: 9552,
- boxV: 9553,
- boxh: 9472,
- boxv: 9474,
- bscr: 119991,
- bsim: 8765,
- bsol: 92,
- bull: 8226,
- bump: 8782,
- cdot: 267,
- cent: 162,
- chcy: 1095,
- cirE: 10691,
- circ: 710,
- cire: 8791,
- comp: 8705,
- cong: 8773,
- copf: 120148,
- copy: 169,
- cscr: 119992,
- csub: 10959,
- csup: 10960,
- dArr: 8659,
- dHar: 10597,
- darr: 8595,
- dash: 8208,
- diam: 8900,
- djcy: 1106,
- dopf: 120149,
- dscr: 119993,
- dscy: 1109,
- dsol: 10742,
- dtri: 9663,
- dzcy: 1119,
- eDot: 8785,
- ecir: 8790,
- edot: 279,
- emsp: 8195,
- ensp: 8194,
- eopf: 120150,
- epar: 8917,
- epsi: 1013,
- escr: 8495,
- esim: 8770,
- euml: 235,
- euro: 8364,
- excl: 33,
- flat: 9837,
- fnof: 402,
- fopf: 120151,
- fork: 8916,
- fscr: 119995,
- gdot: 289,
- geqq: 8807,
- gjcy: 1107,
- gnap: 10890,
- gneq: 10888,
- gopf: 120152,
- gscr: 8458,
- gsim: 8819,
- gtcc: 10919,
- hArr: 8660,
- half: 189,
- harr: 8596,
- hbar: 8463,
- hopf: 120153,
- hscr: 119997,
- iecy: 1077,
- imof: 8887,
- iocy: 1105,
- iopf: 120154,
- iota: 953,
- iscr: 119998,
- isin: 8712,
- iuml: 239,
- jopf: 120155,
- jscr: 119999,
- khcy: 1093,
- kjcy: 1116,
- kopf: 120156,
- kscr: 120000,
- lArr: 8656,
- lHar: 10594,
- lang: 10216,
- larr: 8592,
- late: 10925,
- lcub: 123,
- ldca: 10550,
- ldsh: 8626,
- leqq: 8806,
- ljcy: 1113,
- lnap: 10889,
- lneq: 10887,
- lopf: 120157,
- lozf: 10731,
- lpar: 40,
- lscr: 120001,
- lsim: 8818,
- lsqb: 91,
- ltcc: 10918,
- ltri: 9667,
- macr: 175,
- male: 9794,
- malt: 10016,
- mlcp: 10971,
- mldr: 8230,
- mopf: 120158,
- mscr: 120002,
- nbsp: 160,
- ncap: 10819,
- ncup: 10818,
- ngeq: 8817,
- ngtr: 8815,
- nisd: 8954,
- njcy: 1114,
- nldr: 8229,
- nleq: 8816,
- nmid: 8740,
- nopf: 120159,
- npar: 8742,
- nscr: 120003,
- nsim: 8769,
- nsub: 8836,
- nsup: 8837,
- ntgl: 8825,
- ntlg: 8824,
- oast: 8859,
- ocir: 8858,
- odiv: 10808,
- odot: 8857,
- ogon: 731,
- oint: 8750,
- omid: 10678,
- oopf: 120160,
- opar: 10679,
- ordf: 170,
- ordm: 186,
- oror: 10838,
- oscr: 8500,
- osol: 8856,
- ouml: 246,
- para: 182,
- part: 8706,
- perp: 8869,
- phiv: 966,
- plus: 43,
- popf: 120161,
- prap: 10935,
- prec: 8826,
- prnE: 10933,
- prod: 8719,
- prop: 8733,
- pscr: 120005,
- qint: 10764,
- qopf: 120162,
- qscr: 120006,
- quot: 34,
- rArr: 8658,
- rHar: 10596,
- race: 10714,
- rang: 10217,
- rarr: 8594,
- rcub: 125,
- rdca: 10551,
- rdsh: 8627,
- real: 8476,
- rect: 9645,
- rhov: 1009,
- ring: 730,
- ropf: 120163,
- rpar: 41,
- rscr: 120007,
- rsqb: 93,
- rtri: 9657,
- scap: 10936,
- scnE: 10934,
- sdot: 8901,
- sect: 167,
- semi: 59,
- sext: 10038,
- shcy: 1096,
- sime: 8771,
- simg: 10910,
- siml: 10909,
- smid: 8739,
- smte: 10924,
- solb: 10692,
- sopf: 120164,
- spar: 8741,
- squf: 9642,
- sscr: 120008,
- star: 9734,
- subE: 10949,
- sube: 8838,
- succ: 8827,
- sung: 9834,
- sup1: 185,
- sup2: 178,
- sup3: 179,
- supE: 10950,
- supe: 8839,
- tbrk: 9140,
- tdot: 8411,
- tint: 8749,
- toea: 10536,
- topf: 120165,
- tosa: 10537,
- trie: 8796,
- tscr: 120009,
- tscy: 1094,
- uArr: 8657,
- uHar: 10595,
- uarr: 8593,
- uopf: 120166,
- upsi: 965,
- uscr: 120010,
- utri: 9653,
- uuml: 252,
- vArr: 8661,
- vBar: 10984,
- varr: 8597,
- vert: 124,
- vopf: 120167,
- vscr: 120011,
- wopf: 120168,
- wscr: 120012,
- xcap: 8898,
- xcup: 8899,
- xmap: 10236,
- xnis: 8955,
- xopf: 120169,
- xscr: 120013,
- xvee: 8897,
- yacy: 1103,
- yicy: 1111,
- yopf: 120170,
- yscr: 120014,
- yucy: 1102,
- yuml: 255,
- zdot: 380,
- zeta: 950,
- zhcy: 1078,
- zopf: 120171,
- zscr: 120015,
- zwnj: 8204,
- AMP: 38,
- Acy: 1040,
- Afr: 120068,
- And: 10835,
- Bcy: 1041,
- Bfr: 120069,
- Cap: 8914,
- Cfr: 8493,
- Chi: 935,
- Cup: 8915,
- Dcy: 1044,
- Del: 8711,
- Dfr: 120071,
- Dot: 168,
- ENG: 330,
- ETH: 208,
- Ecy: 1069,
- Efr: 120072,
- Eta: 919,
- Fcy: 1060,
- Ffr: 120073,
- Gcy: 1043,
- Gfr: 120074,
- Hat: 94,
- Hfr: 8460,
- Icy: 1048,
- Ifr: 8465,
- Int: 8748,
- Jcy: 1049,
- Jfr: 120077,
- Kcy: 1050,
- Kfr: 120078,
- Lcy: 1051,
- Lfr: 120079,
- Lsh: 8624,
- Map: 10501,
- Mcy: 1052,
- Mfr: 120080,
- Ncy: 1053,
- Nfr: 120081,
- Not: 10988,
- Ocy: 1054,
- Ofr: 120082,
- Pcy: 1055,
- Pfr: 120083,
- Phi: 934,
- Psi: 936,
- Qfr: 120084,
- REG: 174,
- Rcy: 1056,
- Rfr: 8476,
- Rho: 929,
- Rsh: 8625,
- Scy: 1057,
- Sfr: 120086,
- Sub: 8912,
- Sum: 8721,
- Sup: 8913,
- Tab: 9,
- Tau: 932,
- Tcy: 1058,
- Tfr: 120087,
- Ucy: 1059,
- Ufr: 120088,
- Vcy: 1042,
- Vee: 8897,
- Vfr: 120089,
- Wfr: 120090,
- Xfr: 120091,
- Ycy: 1067,
- Yfr: 120092,
- Zcy: 1047,
- Zfr: 8488,
- acd: 8767,
- acy: 1072,
- afr: 120094,
- amp: 38,
- and: 8743,
- ang: 8736,
- apE: 10864,
- ape: 8778,
- ast: 42,
- bcy: 1073,
- bfr: 120095,
- bot: 8869,
- cap: 8745,
- cfr: 120096,
- chi: 967,
- cir: 9675,
- cup: 8746,
- dcy: 1076,
- deg: 176,
- dfr: 120097,
- die: 168,
- div: 247,
- dot: 729,
- ecy: 1101,
- efr: 120098,
- egs: 10902,
- ell: 8467,
- els: 10901,
- eng: 331,
- eta: 951,
- eth: 240,
- fcy: 1092,
- ffr: 120099,
- gEl: 10892,
- gap: 10886,
- gcy: 1075,
- gel: 8923,
- geq: 8805,
- ges: 10878,
- gfr: 120100,
- ggg: 8921,
- glE: 10898,
- gla: 10917,
- glj: 10916,
- gnE: 8809,
- gne: 10888,
- hfr: 120101,
- icy: 1080,
- iff: 8660,
- ifr: 120102,
- int: 8747,
- jcy: 1081,
- jfr: 120103,
- kcy: 1082,
- kfr: 120104,
- lEg: 10891,
- lap: 10885,
- lat: 10923,
- lcy: 1083,
- leg: 8922,
- leq: 8804,
- les: 10877,
- lfr: 120105,
- lgE: 10897,
- lnE: 8808,
- lne: 10887,
- loz: 9674,
- lrm: 8206,
- lsh: 8624,
- map: 8614,
- mcy: 1084,
- mfr: 120106,
- mho: 8487,
- mid: 8739,
- nap: 8777,
- ncy: 1085,
- nfr: 120107,
- nge: 8817,
- ngt: 8815,
- nis: 8956,
- niv: 8715,
- nle: 8816,
- nlt: 8814,
- not: 172,
- npr: 8832,
- nsc: 8833,
- num: 35,
- ocy: 1086,
- ofr: 120108,
- ogt: 10689,
- ohm: 8486,
- olt: 10688,
- ord: 10845,
- orv: 10843,
- par: 8741,
- pcy: 1087,
- pfr: 120109,
- phi: 966,
- piv: 982,
- prE: 10931,
- pre: 10927,
- psi: 968,
- qfr: 120110,
- rcy: 1088,
- reg: 174,
- rfr: 120111,
- rho: 961,
- rlm: 8207,
- rsh: 8625,
- scE: 10932,
- sce: 10928,
- scy: 1089,
- sfr: 120112,
- shy: 173,
- sim: 8764,
- smt: 10922,
- sol: 47,
- squ: 9633,
- sub: 8834,
- sum: 8721,
- sup: 8835,
- tau: 964,
- tcy: 1090,
- tfr: 120113,
- top: 8868,
- ucy: 1091,
- ufr: 120114,
- uml: 168,
- vcy: 1074,
- vee: 8744,
- vfr: 120115,
- wfr: 120116,
- xfr: 120117,
- ycy: 1099,
- yen: 165,
- yfr: 120118,
- zcy: 1079,
- zfr: 120119,
- zwj: 8205,
- DD: 8517,
- GT: 62,
- Gg: 8921,
- Gt: 8811,
- Im: 8465,
- LT: 60,
- Ll: 8920,
- Lt: 8810,
- Mu: 924,
- Nu: 925,
- Or: 10836,
- Pi: 928,
- Pr: 10939,
- Re: 8476,
- Sc: 10940,
- Xi: 926,
- ac: 8766,
- af: 8289,
- ap: 8776,
- dd: 8518,
- ee: 8519,
- eg: 10906,
- el: 10905,
- gE: 8807,
- ge: 8805,
- gg: 8811,
- gl: 8823,
- gt: 62,
- ic: 8291,
- ii: 8520,
- in: 8712,
- it: 8290,
- lE: 8806,
- le: 8804,
- lg: 8822,
- ll: 8810,
- lt: 60,
- mp: 8723,
- mu: 956,
- ne: 8800,
- ni: 8715,
- nu: 957,
- oS: 9416,
- or: 8744,
- pi: 960,
- pm: 177,
- pr: 8826,
- rx: 8478,
- sc: 8827,
- wp: 8472,
- wr: 8768,
- xi: 958,
-};
diff --git a/src/parser/parse/utils/html.ts b/src/parser/parse/utils/html.ts
deleted file mode 100644
index 3b406c9cc..000000000
--- a/src/parser/parse/utils/html.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-// @ts-nocheck
-
-import entities from './entities.js';
-
-const windows_1252 = [
- 8364,
- 129,
- 8218,
- 402,
- 8222,
- 8230,
- 8224,
- 8225,
- 710,
- 8240,
- 352,
- 8249,
- 338,
- 141,
- 381,
- 143,
- 144,
- 8216,
- 8217,
- 8220,
- 8221,
- 8226,
- 8211,
- 8212,
- 732,
- 8482,
- 353,
- 8250,
- 339,
- 157,
- 382,
- 376,
-];
-
-const entity_pattern = new RegExp(`&(#?(?:x[\\w\\d]+|\\d+|${Object.keys(entities).join('|')}))(?:;|\\b)`, 'g');
-
-export function decode_character_references(html: string) {
- return html.replace(entity_pattern, (match, entity) => {
- let code;
-
- // Handle named entities
- if (entity[0] !== '#') {
- code = entities[entity];
- } else if (entity[1] === 'x') {
- code = parseInt(entity.substring(2), 16);
- } else {
- code = parseInt(entity.substring(1), 10);
- }
-
- if (!code) {
- return match;
- }
-
- return String.fromCodePoint(validate_code(code));
- });
-}
-
-const NUL = 0;
-
-// some code points are verboten. If we were inserting HTML, the browser would replace the illegal
-// code points with alternatives in some cases - since we're bypassing that mechanism, we need
-// to replace them ourselves
-//
-// Source: http://en.wikipedia.org/wiki/Character_encodings_in_HTML#Illegal_characters
-function validate_code(code: number) {
- // line feed becomes generic whitespace
- if (code === 10) {
- return 32;
- }
-
- // ASCII range. (Why someone would use HTML entities for ASCII characters I don't know, but...)
- if (code < 128) {
- return code;
- }
-
- // code points 128-159 are dealt with leniently by browsers, but they're incorrect. We need
- // to correct the mistake or we'll end up with missing € signs and so on
- if (code <= 159) {
- return windows_1252[code - 128];
- }
-
- // basic multilingual plane
- if (code < 55296) {
- return code;
- }
-
- // UTF-16 surrogate halves
- if (code <= 57343) {
- return NUL;
- }
-
- // rest of the basic multilingual plane
- if (code <= 65535) {
- return code;
- }
-
- // supplementary multilingual plane 0x10000 - 0x1ffff
- if (code >= 65536 && code <= 131071) {
- return code;
- }
-
- // supplementary ideographic plane 0x20000 - 0x2ffff
- if (code >= 131072 && code <= 196607) {
- return code;
- }
-
- return NUL;
-}
-
-// based on http://developers.whatwg.org/syntax.html#syntax-tag-omission
-const disallowed_contents = new Map([
- ['li', new Set(['li'])],
- ['dt', new Set(['dt', 'dd'])],
- ['dd', new Set(['dt', 'dd'])],
- ['p', new Set('address article aside blockquote div dl fieldset footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav ol p pre section table ul'.split(' '))],
- ['rt', new Set(['rt', 'rp'])],
- ['rp', new Set(['rt', 'rp'])],
- ['optgroup', new Set(['optgroup'])],
- ['option', new Set(['option', 'optgroup'])],
- ['thead', new Set(['tbody', 'tfoot'])],
- ['tbody', new Set(['tbody', 'tfoot'])],
- ['tfoot', new Set(['tbody'])],
- ['tr', new Set(['tr', 'tbody'])],
- ['td', new Set(['td', 'th', 'tr'])],
- ['th', new Set(['td', 'th', 'tr'])],
-]);
-
-// can this be a child of the parent element, or does it implicitly
-// close it, like `<li>one<li>two`?
-export function closing_tag_omitted(current: string, next?: string) {
- if (disallowed_contents.has(current)) {
- if (!next || disallowed_contents.get(current).has(next)) {
- return true;
- }
- }
-
- return false;
-}
diff --git a/src/parser/parse/utils/node.ts b/src/parser/parse/utils/node.ts
deleted file mode 100644
index 45769f96e..000000000
--- a/src/parser/parse/utils/node.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { TemplateNode } from '../../interfaces.js';
-
-export function to_string(node: TemplateNode) {
- switch (node.type) {
- case 'IfBlock':
- return '{#if} block';
- case 'ThenBlock':
- return '{:then} block';
- case 'ElseBlock':
- return '{:else} block';
- case 'PendingBlock':
- case 'AwaitBlock':
- return '{#await} block';
- case 'CatchBlock':
- return '{:catch} block';
- case 'EachBlock':
- return '{#each} block';
- case 'RawMustacheTag':
- return '{@html} block';
- case 'DebugTag':
- return '{@debug} block';
- case 'Element':
- case 'InlineComponent':
- case 'Slot':
- case 'Title':
- return `<${node.name}> tag`;
- default:
- return node.type;
- }
-}
diff --git a/src/parser/utils/error.ts b/src/parser/utils/error.ts
deleted file mode 100644
index 8ebb5b093..000000000
--- a/src/parser/utils/error.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-// @ts-nocheck
-
-import { locate } from 'locate-character';
-import get_code_frame from './get_code_frame.js';
-
-export class CompileError extends Error {
- code: string;
- start: { line: number; column: number };
- end: { line: number; column: number };
- pos: number;
- filename: string;
- frame: string;
-
- toString() {
- return `${this.message} (${this.start.line}:${this.start.column})\n${this.frame}`;
- }
-}
-
-/** Throw CompileError */
-export default function error(
- message: string,
- props: {
- name: string;
- code: string;
- source: string;
- filename: string;
- start: number;
- end?: number;
- }
-): never {
- const err = new CompileError(message);
- err.name = props.name;
-
- const start = locate(props.source, props.start, { offsetLine: 1 });
- const end = locate(props.source, props.end || props.start, { offsetLine: 1 });
-
- err.code = props.code;
- err.start = start;
- err.end = end;
- err.pos = props.start;
- err.filename = props.filename;
-
- err.frame = get_code_frame(props.source, start.line - 1, start.column);
-
- throw err;
-}
diff --git a/src/parser/utils/full_char_code_at.ts b/src/parser/utils/full_char_code_at.ts
deleted file mode 100644
index b62b2c77a..000000000
--- a/src/parser/utils/full_char_code_at.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// Adapted from https://github.com/acornjs/acorn/blob/6584815dca7440e00de841d1dad152302fdd7ca5/src/tokenize.js
-// Reproduced under MIT License https://github.com/acornjs/acorn/blob/master/LICENSE
-
-/** @url https://github.com/acornjs/acorn/blob/6584815dca7440e00de841d1dad152302fdd7ca5/src/tokenize.js */
-export default function full_char_code_at(str: string, i: number): number {
- const code = str.charCodeAt(i);
- if (code <= 0xd7ff || code >= 0xe000) return code;
-
- const next = str.charCodeAt(i + 1);
- return (code << 10) + next - 0x35fdc00;
-}
diff --git a/src/parser/utils/fuzzymatch.ts b/src/parser/utils/fuzzymatch.ts
deleted file mode 100644
index 4d17aafdf..000000000
--- a/src/parser/utils/fuzzymatch.ts
+++ /dev/null
@@ -1,233 +0,0 @@
-// @ts-nocheck
-
-/** Utility for accessing FuzzySet */
-export default function fuzzymatch(name: string, names: string[]) {
- const set = new FuzzySet(names);
- const matches = set.get(name);
-
- return matches && matches[0] && matches[0][0] > 0.7 ? matches[0][1] : null;
-}
-
-// adapted from https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js
-// BSD Licensed
-
-const GRAM_SIZE_LOWER = 2;
-const GRAM_SIZE_UPPER = 3;
-
-/** Return an edit distance from 0 to 1 */
-function _distance(str1: string, str2: string) {
- if (str1 === null && str2 === null) {
- throw 'Trying to compare two null values';
- }
- if (str1 === null || str2 === null) return 0;
- str1 = String(str1);
- str2 = String(str2);
-
- const distance = levenshtein(str1, str2);
- if (str1.length > str2.length) {
- return 1 - distance / str1.length;
- } else {
- return 1 - distance / str2.length;
- }
-}
-
-/** @url https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js#L18 */
-function levenshtein(str1: string, str2: string) {
- const current: number[] = [];
- let prev;
- let value;
-
- for (let i = 0; i <= str2.length; i++) {
- for (let j = 0; j <= str1.length; j++) {
- if (i && j) {
- if (str1.charAt(j - 1) === str2.charAt(i - 1)) {
- value = prev;
- } else {
- value = Math.min(current[j], current[j - 1], prev) + 1;
- }
- } else {
- value = i + j;
- }
-
- prev = current[j];
- current[j] = value;
- }
- }
-
- return current.pop();
-}
-
-const non_word_regex = /[^\w, ]+/;
-
-/** @url https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js#L53 */
-function iterate_grams(value: string, gram_size = 2) {
- const simplified = '-' + value.toLowerCase().replace(non_word_regex, '') + '-';
- const len_diff = gram_size - simplified.length;
- const results = [];
-
- if (len_diff > 0) {
- for (let i = 0; i < len_diff; ++i) {
- value += '-';
- }
- }
- for (let i = 0; i < simplified.length - gram_size + 1; ++i) {
- results.push(simplified.slice(i, i + gram_size));
- }
- return results;
-}
-
-/** @url https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js#L69 */
-function gram_counter(value: string, gram_size = 2) {
- // return an object where key=gram, value=number of occurrences
- const result = {};
- const grams = iterate_grams(value, gram_size);
- let i = 0;
-
- for (i; i < grams.length; ++i) {
- if (grams[i] in result) {
- result[grams[i]] += 1;
- } else {
- result[grams[i]] = 1;
- }
- }
- return result;
-}
-
-/** @url https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js#L158 */
-function sort_descending(a, b) {
- return b[0] - a[0];
-}
-
-class FuzzySet {
- exact_set = {};
- match_dict = {};
- items = {};
-
- constructor(arr: string[]) {
- // initialization
- for (let i = GRAM_SIZE_LOWER; i < GRAM_SIZE_UPPER + 1; ++i) {
- this.items[i] = [];
- }
-
- // add all the items to the set
- for (let i = 0; i < arr.length; ++i) {
- this.add(arr[i]);
- }
- }
-
- add(value: string) {
- const normalized_value = value.toLowerCase();
- if (normalized_value in this.exact_set) {
- return false;
- }
-
- let i = GRAM_SIZE_LOWER;
- for (i; i < GRAM_SIZE_UPPER + 1; ++i) {
- this._add(value, i);
- }
- }
-
- _add(value: string, gram_size: number) {
- const normalized_value = value.toLowerCase();
- const items = this.items[gram_size] || [];
- const index = items.length;
-
- items.push(0);
- const gram_counts = gram_counter(normalized_value, gram_size);
- let sum_of_square_gram_counts = 0;
- let gram;
- let gram_count;
-
- for (gram in gram_counts) {
- gram_count = gram_counts[gram];
- sum_of_square_gram_counts += Math.pow(gram_count, 2);
- if (gram in this.match_dict) {
- this.match_dict[gram].push([index, gram_count]);
- } else {
- this.match_dict[gram] = [[index, gram_count]];
- }
- }
- const vector_normal = Math.sqrt(sum_of_square_gram_counts);
- items[index] = [vector_normal, normalized_value];
- this.items[gram_size] = items;
- this.exact_set[normalized_value] = value;
- }
-
- get(value: string) {
- const normalized_value = value.toLowerCase();
- const result = this.exact_set[normalized_value];
-
- if (result) {
- return [[1, result]];
- }
-
- let results = [];
- // start with high gram size and if there are no results, go to lower gram sizes
- for (let gram_size = GRAM_SIZE_UPPER; gram_size >= GRAM_SIZE_LOWER; --gram_size) {
- results = this.__get(value, gram_size);
- if (results) {
- return results;
- }
- }
- return null;
- }
-
- __get(value: string, gram_size: number) {
- const normalized_value = value.toLowerCase();
- const matches = {};
- const gram_counts = gram_counter(normalized_value, gram_size);
- const items = this.items[gram_size];
- let sum_of_square_gram_counts = 0;
- let gram;
- let gram_count;
- let i;
- let index;
- let other_gram_count;
-
- for (gram in gram_counts) {
- gram_count = gram_counts[gram];
- sum_of_square_gram_counts += Math.pow(gram_count, 2);
- if (gram in this.match_dict) {
- for (i = 0; i < this.match_dict[gram].length; ++i) {
- index = this.match_dict[gram][i][0];
- other_gram_count = this.match_dict[gram][i][1];
- if (index in matches) {
- matches[index] += gram_count * other_gram_count;
- } else {
- matches[index] = gram_count * other_gram_count;
- }
- }
- }
- }
-
- const vector_normal = Math.sqrt(sum_of_square_gram_counts);
- let results = [];
- let match_score;
-
- // build a results list of [score, str]
- for (const match_index in matches) {
- match_score = matches[match_index];
- results.push([match_score / (vector_normal * items[match_index][0]), items[match_index][1]]);
- }
-
- results.sort(sort_descending);
-
- let new_results = [];
- const end_index = Math.min(50, results.length);
- // truncate somewhat arbitrarily to 50
- for (let j = 0; j < end_index; ++j) {
- new_results.push([_distance(results[j][1], normalized_value), results[j][1]]);
- }
- results = new_results;
- results.sort(sort_descending);
-
- new_results = [];
- for (let j = 0; j < results.length; ++j) {
- if (results[j][0] == results[0][0]) {
- new_results.push([results[j][0], this.exact_set[results[j][1]]]);
- }
- }
-
- return new_results;
- }
-}
diff --git a/src/parser/utils/get_code_frame.ts b/src/parser/utils/get_code_frame.ts
deleted file mode 100644
index e4f1834fd..000000000
--- a/src/parser/utils/get_code_frame.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/** Die you stupid tabs */
-function tabs_to_spaces(str: string) {
- return str.replace(/^\t+/, (match) => match.split('\t').join(' '));
-}
-
-/** Display syntax error in pretty format in logs */
-export default function get_code_frame(source: string, line: number, column: number) {
- const lines = source.split('\n');
-
- const frame_start = Math.max(0, line - 2);
- const frame_end = Math.min(line + 3, lines.length);
-
- const digits = String(frame_end + 1).length;
-
- return lines
- .slice(frame_start, frame_end)
- .map((str, i) => {
- const isErrorLine = frame_start + i === line;
- const line_num = String(i + frame_start + 1).padStart(digits, ' ');
-
- if (isErrorLine) {
- const indicator = ' '.repeat(digits + 2 + tabs_to_spaces(str.slice(0, column)).length) + '^';
- return `${line_num}: ${tabs_to_spaces(str)}\n${indicator}`;
- }
-
- return `${line_num}: ${tabs_to_spaces(str)}`;
- })
- .join('\n');
-}
diff --git a/src/parser/utils/link.ts b/src/parser/utils/link.ts
deleted file mode 100644
index 4e2ed662f..000000000
--- a/src/parser/utils/link.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-/** Linked list */
-export function link<T extends { next?: T; prev?: T }>(next: T, prev: T) {
- prev.next = next;
- if (next) next.prev = prev;
-}
diff --git a/src/parser/utils/list.ts b/src/parser/utils/list.ts
deleted file mode 100644
index 9388adb14..000000000
--- a/src/parser/utils/list.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-/** Display an array of strings in a human-readable format */
-export default function list(items: string[], conjunction = 'or') {
- if (items.length === 1) return items[0];
- return `${items.slice(0, -1).join(', ')} ${conjunction} ${items[items.length - 1]}`;
-}
diff --git a/src/parser/utils/names.ts b/src/parser/utils/names.ts
deleted file mode 100644
index f041d20ce..000000000
--- a/src/parser/utils/names.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-import { isIdentifierStart, isIdentifierChar } from 'acorn';
-import full_char_code_at from './full_char_code_at.js';
-
-export const globals = new Set([
- 'alert',
- 'Array',
- 'Boolean',
- 'clearInterval',
- 'clearTimeout',
- 'confirm',
- 'console',
- 'Date',
- 'decodeURI',
- 'decodeURIComponent',
- 'document',
- 'Element',
- 'encodeURI',
- 'encodeURIComponent',
- 'Error',
- 'EvalError',
- 'Event',
- 'EventSource',
- 'fetch',
- 'global',
- 'globalThis',
- 'history',
- 'Infinity',
- 'InternalError',
- 'Intl',
- 'isFinite',
- 'isNaN',
- 'JSON',
- 'localStorage',
- 'location',
- 'Map',
- 'Math',
- 'NaN',
- 'navigator',
- 'Number',
- 'Node',
- 'Object',
- 'parseFloat',
- 'parseInt',
- 'process',
- 'Promise',
- 'prompt',
- 'RangeError',
- 'ReferenceError',
- 'RegExp',
- 'sessionStorage',
- 'Set',
- 'setInterval',
- 'setTimeout',
- 'String',
- 'SyntaxError',
- 'TypeError',
- 'undefined',
- 'URIError',
- 'URL',
- 'window',
-]);
-
-export const reserved = new Set([
- 'arguments',
- 'await',
- 'break',
- 'case',
- 'catch',
- 'class',
- 'const',
- 'continue',
- 'debugger',
- 'default',
- 'delete',
- 'do',
- 'else',
- 'enum',
- 'eval',
- 'export',
- 'extends',
- 'false',
- 'finally',
- 'for',
- 'function',
- 'if',
- 'implements',
- 'import',
- 'in',
- 'instanceof',
- 'interface',
- 'let',
- 'new',
- 'null',
- 'package',
- 'private',
- 'protected',
- 'public',
- 'return',
- 'static',
- 'super',
- 'switch',
- 'this',
- 'throw',
- 'true',
- 'try',
- 'typeof',
- 'var',
- 'void',
- 'while',
- 'with',
- 'yield',
-]);
-
-const void_element_names = /^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/;
-
-/** Is this a void HTML element? */
-export function is_void(name: string) {
- return void_element_names.test(name) || name.toLowerCase() === '!doctype';
-}
-
-/** Is this a valid HTML element? */
-export function is_valid(str: string): boolean {
- let i = 0;
-
- while (i < str.length) {
- const code = full_char_code_at(str, i);
- if (!(i === 0 ? isIdentifierStart : isIdentifierChar)(code, true)) return false;
-
- i += code <= 0xffff ? 1 : 2;
- }
-
- return true;
-}
-
-/** Utility to normalize HTML */
-export function sanitize(name: string) {
- return name
- .replace(/[^a-zA-Z0-9_]+/g, '_')
- .replace(/^_/, '')
- .replace(/_$/, '')
- .replace(/^[0-9]/, '_$&');
-}
diff --git a/src/parser/utils/namespaces.ts b/src/parser/utils/namespaces.ts
deleted file mode 100644
index 5f61beff9..000000000
--- a/src/parser/utils/namespaces.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-// The `foreign` namespace covers all DOM implementations that aren't HTML5.
-// It opts out of HTML5-specific a11y checks and case-insensitive attribute names.
-export const foreign = 'https://svelte.dev/docs#svelte_options';
-export const html = 'http://www.w3.org/1999/xhtml';
-export const mathml = 'http://www.w3.org/1998/Math/MathML';
-export const svg = 'http://www.w3.org/2000/svg';
-export const xlink = 'http://www.w3.org/1999/xlink';
-export const xml = 'http://www.w3.org/XML/1998/namespace';
-export const xmlns = 'http://www.w3.org/2000/xmlns';
-
-export const valid_namespaces = ['foreign', 'html', 'mathml', 'svg', 'xlink', 'xml', 'xmlns', foreign, html, mathml, svg, xlink, xml, xmlns];
-
-export const namespaces: Record<string, string> = { foreign, html, mathml, svg, xlink, xml, xmlns };
diff --git a/src/parser/utils/nodes_match.ts b/src/parser/utils/nodes_match.ts
deleted file mode 100644
index 7e4093994..000000000
--- a/src/parser/utils/nodes_match.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-// @ts-nocheck
-
-/** Compare two TemplateNodes to determine if they are equivalent */
-export function nodes_match(a, b) {
- if (!!a !== !!b) return false;
- if (Array.isArray(a) !== Array.isArray(b)) return false;
-
- if (a && typeof a === 'object') {
- if (Array.isArray(a)) {
- if (a.length !== b.length) return false;
- return a.every((child, i) => nodes_match(child, b[i]));
- }
-
- const a_keys = Object.keys(a).sort();
- const b_keys = Object.keys(b).sort();
-
- if (a_keys.length !== b_keys.length) return false;
-
- let i = a_keys.length;
- while (i--) {
- const key = a_keys[i];
- if (b_keys[i] !== key) return false;
-
- if (key === 'start' || key === 'end') continue;
-
- if (!nodes_match(a[key], b[key])) {
- return false;
- }
- }
-
- return true;
- }
-
- return a === b;
-}
diff --git a/src/parser/utils/patterns.ts b/src/parser/utils/patterns.ts
deleted file mode 100644
index 317a7c199..000000000
--- a/src/parser/utils/patterns.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const whitespace = /[ \t\r\n]/;
-
-export const dimensions = /^(?:offset|client)(?:Width|Height)$/;
diff --git a/src/parser/utils/trim.ts b/src/parser/utils/trim.ts
deleted file mode 100644
index 480cc99a8..000000000
--- a/src/parser/utils/trim.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { whitespace } from './patterns.js';
-
-/** Trim whitespace from start of string */
-export function trim_start(str: string) {
- let i = 0;
- while (whitespace.test(str[i])) i += 1;
-
- return str.slice(i);
-}
-
-/** Trim whitespace from end of string */
-export function trim_end(str: string) {
- let i = str.length;
- while (whitespace.test(str[i - 1])) i -= 1;
-
- return str.slice(0, i);
-}
diff --git a/src/runtime.ts b/src/runtime.ts
deleted file mode 100644
index 66a0657cf..000000000
--- a/src/runtime.ts
+++ /dev/null
@@ -1,365 +0,0 @@
-import { fileURLToPath } from 'url';
-import type { SnowpackDevServer, ServerRuntime as SnowpackServerRuntime, SnowpackConfig } from 'snowpack';
-import type { AstroConfig, CollectionResult, CollectionRSS, CreateCollection, Params, RuntimeMode } from './@types/astro';
-import type { LogOptions } from './logger';
-import type { CompileError } from './parser/utils/error.js';
-import { debug, info } from './logger.js';
-import { searchForPage } from './search.js';
-
-import { existsSync } from 'fs';
-import { loadConfiguration, logger as snowpackLogger, startServer as startSnowpackServer } from 'snowpack';
-
-// We need to use require.resolve for snowpack plugins, so create a require function here.
-import { createRequire } from 'module';
-const require = createRequire(import.meta.url);
-
-interface RuntimeConfig {
- astroConfig: AstroConfig;
- logging: LogOptions;
- mode: RuntimeMode;
- backendSnowpack: SnowpackDevServer;
- backendSnowpackRuntime: SnowpackServerRuntime;
- backendSnowpackConfig: SnowpackConfig;
- frontendSnowpack: SnowpackDevServer;
- frontendSnowpackRuntime: SnowpackServerRuntime;
- frontendSnowpackConfig: SnowpackConfig;
-}
-
-// info needed for collection generation
-interface CollectionInfo {
- additionalURLs: Set<string>;
- rss?: { data: any[] & CollectionRSS };
-}
-
-type LoadResultSuccess = {
- statusCode: 200;
- contents: string | Buffer;
- contentType?: string | false;
-};
-type LoadResultNotFound = { statusCode: 404; error: Error; collectionInfo?: CollectionInfo };
-type LoadResultRedirect = { statusCode: 301 | 302; location: string; collectionInfo?: CollectionInfo };
-type LoadResultError = { statusCode: 500 } & ({ type: 'parse-error'; error: CompileError } | { type: 'unknown'; error: Error });
-
-export type LoadResult = (LoadResultSuccess | LoadResultNotFound | LoadResultRedirect | LoadResultError) & { collectionInfo?: CollectionInfo };
-
-// Disable snowpack from writing to stdout/err.
-snowpackLogger.level = 'silent';
-
-/** Pass a URL to Astro to resolve and build */
-async function load(config: RuntimeConfig, rawPathname: string | undefined): Promise<LoadResult> {
- const { logging, backendSnowpackRuntime, frontendSnowpack } = config;
- const { astroRoot } = config.astroConfig;
-
- const fullurl = new URL(rawPathname || '/', 'https://example.org/');
-
- const reqPath = decodeURI(fullurl.pathname);
- info(logging, 'access', reqPath);
-
- const searchResult = searchForPage(fullurl, astroRoot);
- if (searchResult.statusCode === 404) {
- try {
- const result = await frontendSnowpack.loadUrl(reqPath);
- if (!result) throw new Error(`Unable to load ${reqPath}`);
- // success
- return {
- statusCode: 200,
- ...result,
- };
- } catch (err) {
- // build error
- if (err.failed) {
- return { statusCode: 500, type: 'unknown', error: err };
- }
-
- // not found
- return { statusCode: 404, error: err };
- }
- }
-
- if (searchResult.statusCode === 301) {
- return { statusCode: 301, location: searchResult.pathname };
- }
-
- const snowpackURL = searchResult.location.snowpackURL;
- let rss: { data: any[] & CollectionRSS } = {} as any;
-
- try {
- const mod = await backendSnowpackRuntime.importModule(snowpackURL);
- debug(logging, 'resolve', `${reqPath} -> ${snowpackURL}`);
-
- // handle collection
- let collection = {} as CollectionResult;
- let additionalURLs = new Set<string>();
-
- if (mod.exports.createCollection) {
- const createCollection: CreateCollection = await mod.exports.createCollection();
- for (const key of Object.keys(createCollection)) {
- if (key !== 'data' && key !== 'routes' && key !== 'permalink' && key !== 'pageSize' && key !== 'rss') {
- throw new Error(`[createCollection] unknown option: "${key}"`);
- }
- }
- let { data: loadData, routes, permalink, pageSize, rss: createRSS } = createCollection;
- if (!pageSize) pageSize = 25; // can’t be 0
- let currentParams: Params = {};
-
- // params
- if (routes || permalink) {
- if (!routes || !permalink) {
- throw new Error('createCollection() must have both routes and permalink options. Include both together, or omit both.');
- }
- let requestedParams = routes.find((p) => {
- const baseURL = (permalink as any)({ params: p });
- additionalURLs.add(baseURL);
- return baseURL === reqPath || `${baseURL}/${searchResult.currentPage || 1}` === reqPath;
- });
- if (requestedParams) {
- currentParams = requestedParams;
- collection.params = requestedParams;
- }
- }
-
- let data: any[] = await loadData({ params: currentParams });
-
- // handle RSS
- if (createRSS) {
- rss = {
- ...createRSS,
- data: [...data] as any,
- };
- }
-
- collection.start = 0;
- collection.end = data.length - 1;
- collection.total = data.length;
- collection.page = { current: 1, size: pageSize, last: 1 };
- collection.url = { current: reqPath };
-
- // paginate
- if (searchResult.currentPage) {
- const start = (searchResult.currentPage - 1) * pageSize; // currentPage is 1-indexed
- const end = Math.min(start + pageSize, data.length);
-
- collection.start = start;
- collection.end = end - 1;
- collection.page.current = searchResult.currentPage;
- collection.page.last = Math.ceil(data.length / pageSize);
- // TODO: fix the .replace() hack
- if (end < data.length) {
- collection.url.next = collection.url.current.replace(/(\/\d+)?$/, `/${searchResult.currentPage + 1}`);
- }
- if (searchResult.currentPage > 1) {
- collection.url.prev = collection.url.current
- .replace(/\d+$/, `${searchResult.currentPage - 1 || 1}`) // update page #
- .replace(/\/1$/, ''); // if end is `/1`, then just omit
- }
-
- // from page 2 to the end, add all pages as additional URLs (needed for build)
- for (let n = 1; n <= collection.page.last; n++) {
- if (additionalURLs.size) {
- // if this is a param-based collection, paginate all params
- additionalURLs.forEach((url) => {
- additionalURLs.add(url.replace(/(\/\d+)?$/, `/${n}`));
- });
- } else {
- // if this has no params, simply add page
- additionalURLs.add(reqPath.replace(/(\/\d+)?$/, `/${n}`));
- }
- }
-
- data = data.slice(start, end);
- } else if (createCollection.pageSize) {
- // TODO: fix bug where redirect doesn’t happen
- // This happens because a pageSize is set, but the user isn’t on a paginated route. Redirect:
- return {
- statusCode: 301,
- location: reqPath + '/1',
- collectionInfo: {
- additionalURLs,
- rss: rss.data ? rss : undefined,
- },
- };
- }
-
- // if we’ve paginated too far, this is a 404
- if (!data.length) {
- return {
- statusCode: 404,
- error: new Error('Not Found'),
- collectionInfo: {
- additionalURLs,
- rss: rss.data ? rss : undefined,
- },
- };
- }
-
- collection.data = data;
- }
-
- const requestURL = new URL(fullurl.toString());
-
- // For first release query params are not passed to components.
- // An exception is made for dev server specific routes.
- if(reqPath !== '/500') {
- requestURL.search = '';
- }
-
- let html = (await mod.exports.__renderPage({
- request: {
- // params should go here when implemented
- url: requestURL
- },
- children: [],
- props: { collection },
- })) as string;
-
- // inject styles
- // TODO: handle this in compiler
- const styleTags = Array.isArray(mod.css) && mod.css.length ? mod.css.reduce((markup, href) => `${markup}\n<link rel="stylesheet" type="text/css" href="${href}" />`, '') : ``;
- if (html.indexOf('</head>') !== -1) {
- html = html.replace('</head>', `${styleTags}</head>`);
- } else {
- html = styleTags + html;
- }
-
- return {
- statusCode: 200,
- contentType: 'text/html; charset=utf-8',
- contents: html,
- collectionInfo: {
- additionalURLs,
- rss: rss.data ? rss : undefined,
- },
- };
- } catch (err) {
- if (err.code === 'parse-error' || err instanceof SyntaxError) {
- return {
- statusCode: 500,
- type: 'parse-error',
- error: err,
- };
- }
- return {
- statusCode: 500,
- type: 'unknown',
- error: err,
- };
- }
-}
-
-export interface AstroRuntime {
- runtimeConfig: RuntimeConfig;
- load: (rawPathname: string | undefined) => Promise<LoadResult>;
- shutdown: () => Promise<void>;
-}
-
-interface RuntimeOptions {
- mode: RuntimeMode;
- logging: LogOptions;
-}
-
-interface CreateSnowpackOptions {
- env: Record<string, any>;
- mode: RuntimeMode;
- resolvePackageUrl?: (pkgName: string) => Promise<string>;
-}
-
-/** Create a new Snowpack instance to power Astro */
-async function createSnowpack(astroConfig: AstroConfig, options: CreateSnowpackOptions) {
- const { projectRoot, astroRoot, extensions } = astroConfig;
- const { env, mode, resolvePackageUrl } = options;
-
- const internalPath = new URL('./frontend/', import.meta.url);
-
- let snowpack: SnowpackDevServer;
- const astroPlugOptions: {
- resolvePackageUrl?: (s: string) => Promise<string>;
- extensions?: Record<string, string>;
- astroConfig: AstroConfig;
- } = {
- astroConfig,
- extensions,
- resolvePackageUrl,
- };
-
- const mountOptions = {
- [fileURLToPath(astroRoot)]: '/_astro',
- [fileURLToPath(internalPath)]: '/_astro_internal',
- };
-
- if (existsSync(astroConfig.public)) {
- mountOptions[fileURLToPath(astroConfig.public)] = '/';
- }
-
- const snowpackConfig = await loadConfiguration({
- root: fileURLToPath(projectRoot),
- mount: mountOptions,
- mode,
- plugins: [
- [fileURLToPath(new URL('../snowpack-plugin.cjs', import.meta.url)), astroPlugOptions],
- require.resolve('@snowpack/plugin-sass'),
- [require.resolve('@snowpack/plugin-svelte'), { compilerOptions: { hydratable: true } }],
- require.resolve('@snowpack/plugin-vue'),
- ],
- devOptions: {
- open: 'none',
- output: 'stream',
- port: 0,
- },
- buildOptions: {
- out: astroConfig.dist,
- },
- packageOptions: {
- knownEntrypoints: ['preact-render-to-string'],
- external: ['@vue/server-renderer', 'node-fetch', 'prismjs/components/index.js'],
- },
- });
-
- const envConfig = snowpackConfig.env || (snowpackConfig.env = {});
- Object.assign(envConfig, env);
-
- snowpack = await startSnowpackServer({
- config: snowpackConfig,
- lockfile: null,
- });
- const snowpackRuntime = snowpack.getServerRuntime();
-
- return { snowpack, snowpackRuntime, snowpackConfig };
-}
-
-/** Core Astro runtime */
-export async function createRuntime(astroConfig: AstroConfig, { mode, logging }: RuntimeOptions): Promise<AstroRuntime> {
- const resolvePackageUrl = async (pkgName: string) => frontendSnowpack.getUrlForPackage(pkgName);
-
- const { snowpack: backendSnowpack, snowpackRuntime: backendSnowpackRuntime, snowpackConfig: backendSnowpackConfig } = await createSnowpack(astroConfig, {
- env: {
- astro: true,
- },
- mode,
- resolvePackageUrl,
- });
-
- const { snowpack: frontendSnowpack, snowpackRuntime: frontendSnowpackRuntime, snowpackConfig: frontendSnowpackConfig } = await createSnowpack(astroConfig, {
- env: {
- astro: false,
- },
- mode,
- });
-
- const runtimeConfig: RuntimeConfig = {
- astroConfig,
- logging,
- mode,
- backendSnowpack,
- backendSnowpackRuntime,
- backendSnowpackConfig,
- frontendSnowpack,
- frontendSnowpackRuntime,
- frontendSnowpackConfig,
- };
-
- return {
- runtimeConfig,
- load: load.bind(null, runtimeConfig),
- shutdown: () => Promise.all([backendSnowpack.shutdown(), frontendSnowpack.shutdown()]).then(() => void 0),
- };
-}
diff --git a/src/search.ts b/src/search.ts
deleted file mode 100644
index c141e4a77..000000000
--- a/src/search.ts
+++ /dev/null
@@ -1,141 +0,0 @@
-import { existsSync } from 'fs';
-import path from 'path';
-import { fileURLToPath } from 'url';
-import { fdir, PathsOutput } from 'fdir';
-
-interface PageLocation {
- fileURL: URL;
- snowpackURL: string;
-}
-/** findAnyPage and return the _astro candidate for snowpack */
-function findAnyPage(candidates: Array<string>, astroRoot: URL): PageLocation | false {
- for (let candidate of candidates) {
- const url = new URL(`./pages/${candidate}`, astroRoot);
- if (existsSync(url)) {
- return {
- fileURL: url,
- snowpackURL: `/_astro/pages/${candidate}.js`,
- };
- }
- }
- return false;
-}
-
-type SearchResult =
- | {
- statusCode: 200;
- location: PageLocation;
- pathname: string;
- currentPage?: number;
- }
- | {
- statusCode: 301;
- location: null;
- pathname: string;
- }
- | {
- statusCode: 404;
- };
-
-/** Given a URL, attempt to locate its source file (similar to Snowpack’s load()) */
-export function searchForPage(url: URL, astroRoot: URL): SearchResult {
- const reqPath = decodeURI(url.pathname);
- const base = reqPath.substr(1);
-
- // Try to find index.astro/md paths
- if (reqPath.endsWith('/')) {
- const candidates = [`${base}index.astro`, `${base}index.md`];
- const location = findAnyPage(candidates, astroRoot);
- if (location) {
- return {
- statusCode: 200,
- location,
- pathname: reqPath,
- };
- }
- } else {
- // Try to find the page by its name.
- const candidates = [`${base}.astro`, `${base}.md`];
- let location = findAnyPage(candidates, astroRoot);
- if (location) {
- return {
- statusCode: 200,
- location,
- pathname: reqPath,
- };
- }
- }
-
- // Try to find name/index.astro/md
- const candidates = [`${base}/index.astro`, `${base}/index.md`];
- const location = findAnyPage(candidates, astroRoot);
- if (location) {
- return {
- statusCode: 301,
- location: null,
- pathname: reqPath + '/',
- };
- }
-
- // Try and load collections (but only for non-extension files)
- const hasExt = !!path.extname(reqPath);
- if (!location && !hasExt) {
- const collection = loadCollection(reqPath, astroRoot);
- if (collection) {
- return {
- statusCode: 200,
- location: collection.location,
- pathname: reqPath,
- currentPage: collection.currentPage || 1,
- };
- }
- }
-
- if(reqPath === '/500') {
- return {
- statusCode: 200,
- location: {
- fileURL: new URL('./frontend/500.astro', import.meta.url),
- snowpackURL: `/_astro_internal/500.astro.js`
- },
- pathname: reqPath
- };
- }
-
- return {
- statusCode: 404,
- };
-}
-
-const crawler = new fdir();
-
-/** load a collection route */
-function loadCollection(url: string, astroRoot: URL): { currentPage?: number; location: PageLocation } | undefined {
- const pages = (crawler
- .glob('**/*')
- .crawl(path.join(fileURLToPath(astroRoot), 'pages'))
- .sync() as PathsOutput).filter((filepath) => filepath.startsWith('$') || filepath.includes('/$'));
- for (const pageURL of pages) {
- const reqURL = new RegExp('^/' + pageURL.replace(/\$([^/]+)\.astro/, '$1') + '/?(.*)');
- const match = url.match(reqURL);
- if (match) {
- let currentPage: number | undefined;
- if (match[1]) {
- const segments = match[1].split('/').filter((s) => !!s);
- if (segments.length) {
- const last = segments.pop() as string;
- if (parseInt(last, 10)) {
- currentPage = parseInt(last, 10);
- }
- }
- }
- return {
- location: {
- fileURL: new URL(`./pages/${pageURL}`, astroRoot),
- snowpackURL: `/_astro/pages/${pageURL}.js`,
- },
- currentPage,
- };
- }
- }
-}