diff options
Diffstat (limited to 'src')
30 files changed, 151 insertions, 46 deletions
diff --git a/src/ast.ts b/src/ast.ts index 6c0bd7bd2..88178d399 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -2,11 +2,13 @@ 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) { @@ -14,6 +16,7 @@ export function getAttrValue(attributes: Attribute[], name: string): string | un } } +/** Set TemplateNode attribute value */ export function setAttrValue(attributes: Attribute[], name: string, value: string): void { const attr = attributes.find((a) => a.name === name); if (attr) { diff --git a/src/build.ts b/src/build.ts index 39b778600..831d0be0e 100644 --- a/src/build.ts +++ b/src/build.ts @@ -17,6 +17,7 @@ const logging: LogOptions = { dest: defaultLogDestination, }; +/** Return contents of astro/pages */ async function allPages(root: URL) { const api = new fdir() .filter((p) => /\.(astro|md)$/.test(p)) @@ -26,6 +27,7 @@ async function allPages(root: URL) { 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); @@ -33,12 +35,14 @@ function mergeSet(a: Set<string>, b: Set<string>) { return a; } +/** Utility for writing to file (async) */ async function writeFilep(outPath: URL, bytes: string | Buffer, encoding: 'utf-8' | 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 | 'utf-8') { if (result.statusCode !== 200) { error(logging, 'build', result.error || result.statusCode); @@ -49,6 +53,7 @@ async function writeResult(result: LoadResult, outPath: URL, encoding: null | 'u } } +/** The primary build action */ export async function build(astroConfig: AstroConfig): Promise<0 | 1> { const { projectRoot, astroRoot } = astroConfig; const pageRoot = new URL('./pages/', astroRoot); diff --git a/src/build/bundle.ts b/src/build/bundle.ts index 143be0521..ba1b8f2c2 100644 --- a/src/build/bundle.ts +++ b/src/build/bundle.ts @@ -8,7 +8,7 @@ import esbuild from 'esbuild'; import { promises as fsPromises } from 'fs'; import { parse } from '../parser/index.js'; import { optimize } from '../compiler/optimize/index.js'; -import { getAttrValue, setAttrValue } from '../ast.js'; +import { getAttrValue } from '../ast.js'; import { walk } from 'estree-walker'; import babelParser from '@babel/parser'; import path from 'path'; @@ -20,6 +20,7 @@ const { readFile } = fsPromises; type DynamicImportMap = Map<'vue' | 'react' | 'react-dom' | 'preact', string>; +/** Add framework runtimes when needed */ async function acquireDynamicComponentImports(plugins: Set<ValidExtensionPlugins>, resolve: (s: string) => Promise<string>): Promise<DynamicImportMap> { const importMap: DynamicImportMap = new Map(); for (let plugin of plugins) { @@ -42,6 +43,7 @@ async function acquireDynamicComponentImports(plugins: Set<ValidExtensionPlugins return importMap; } +/** Evaluate mustache expression (safely) */ function compileExpressionSafe(raw: string): string { let { code } = transformSync(raw, { loader: 'tsx', @@ -65,6 +67,7 @@ interface CollectDynamic { mode: RuntimeMode; } +/** Gather necessary framework runtimes for dynamic components */ export async function collectDynamicImports(filename: URL, { astroConfig, logging, resolve, mode }: CollectDynamic) { const imports = new Set<string>(); @@ -127,7 +130,8 @@ export async function collectDynamicImports(filename: URL, { astroConfig, loggin const dynamic = await acquireDynamicComponentImports(plugins, resolve); - function appendImports(rawName: string, filename: URL, astroConfig: AstroConfig) { + /** Add dynamic component runtimes to imports */ + function appendImports(rawName: string, importUrl: URL) { const [componentName, componentType] = rawName.split(':'); if (!componentType) { return; @@ -138,7 +142,7 @@ export async function collectDynamicImports(filename: URL, { astroConfig, loggin } const defn = components[componentName]; - const fileUrl = new URL(defn.specifier, filename); + const fileUrl = new URL(defn.specifier, importUrl); let rel = path.posix.relative(astroConfig.astroRoot.pathname, fileUrl.pathname); switch (defn.plugin) { @@ -193,15 +197,15 @@ export async function collectDynamicImports(filename: URL, { astroConfig, loggin while ((match = regex.exec(code))) { matches.push(match); } - for (const match of matches.reverse()) { - const name = match[1]; - appendImports(name, filename, astroConfig); + 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, astroConfig); + appendImports(node.name, filename); return; } @@ -220,6 +224,7 @@ interface BundleOptions { astroConfig: AstroConfig; } +/** The primary bundling/optimization action */ export async function bundle(imports: Set<string>, { runtime, dist }: BundleOptions) { const ROOT = 'astro:root'; const root = ` diff --git a/src/build/static.ts b/src/build/static.ts index a10f4c33f..b24ce7729 100644 --- a/src/build/static.ts +++ b/src/build/static.ts @@ -1,6 +1,7 @@ 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>(); diff --git a/src/cli.ts b/src/cli.ts index d74656615..e0f0c0dc3 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -17,6 +17,7 @@ const buildAndExit = async (...args: Parameters<typeof build>) => { type Arguments = yargs.Arguments; type cliState = 'help' | 'version' | 'dev' | 'build'; +/** Determine which action the user requested */ function resolveArgs(flags: Arguments): cliState { if (flags.version) { return 'version'; @@ -35,6 +36,7 @@ function resolveArgs(flags: Arguments): cliState { } } +/** Display --help flag */ function printHelp() { console.error(` ${colors.bold('astro')} - Futuristic web development tool. @@ -48,11 +50,13 @@ function printHelp() { `); } +/** 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); } +/** Handle `astro run` command */ async function runCommand(rawRoot: string, cmd: (a: AstroConfig) => Promise<void>) { const astroConfig = await loadConfig(rawRoot); if (typeof astroConfig === 'undefined') { @@ -69,6 +73,7 @@ const cmdMap = new Map([ ['dev', devServer], ]); +/** The primary CLI action */ export async function cli(args: string[]) { const flags = yargs(args); const state = resolveArgs(flags); diff --git a/src/compiler/codegen.ts b/src/compiler/codegen.ts index 63fc44dfb..2486ef92f 100644 --- a/src/compiler/codegen.ts +++ b/src/compiler/codegen.ts @@ -30,10 +30,12 @@ interface CodeGenOptions { 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) { @@ -79,6 +81,7 @@ function getAttributes(attrs: Attribute[]): Record<string, string> { return result; } +/** Get value from a TemplateNode Attribute (text attributes only!) */ function getTextFromAttribute(attr: any): string { if (attr.raw !== undefined) { return attr.raw; @@ -89,6 +92,7 @@ function getTextFromAttribute(attr: any): string { throw new Error('UNKNOWN attr'); } +/** Convert TemplateNode attributes to string */ function generateAttributes(attrs: Record<string, string>): string { let result = '{'; for (const [key, val] of Object.entries(attrs)) { @@ -117,6 +121,8 @@ interface GetComponentWrapperOptions { 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; @@ -222,6 +228,7 @@ function getComponentWrapper(_name: string, { type, plugin, url }: ComponentInfo } } +/** Evaluate mustache expression (safely) */ function compileExpressionSafe(raw: string): string { let { code } = transformSync(raw, { loader: 'tsx', @@ -232,6 +239,7 @@ function compileExpressionSafe(raw: string): string { return code; } +/** Build dependency map of dynamic component runtime frameworks */ async function acquireDynamicComponentImports(plugins: Set<ValidExtensionPlugins>, resolve: (s: string) => Promise<string>): Promise<DynamicImportMap> { const importMap: DynamicImportMap = new Map(); for (let plugin of plugins) { @@ -254,7 +262,15 @@ async function acquireDynamicComponentImports(plugins: Set<ValidExtensionPlugins return importMap; } -export async function codegen(ast: Ast, { compileOptions, filename, fileID }: CodeGenOptions): Promise<TransformResult> { +/** + * 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> { const { extensions = defaultExtensions, astroConfig } = compileOptions; await eslexer.init; @@ -364,8 +380,8 @@ export async function codegen(ast: Ast, { compileOptions, filename, fileID }: Co while ((match = regex.exec(code))) { matches.push(match); } - for (const match of matches.reverse()) { - const name = match[1]; + for (const astroComponent of matches.reverse()) { + const name = astroComponent[1]; const [componentName, componentKind] = name.split(':'); if (!components[componentName]) { throw new Error(`Unknown Component: ${componentName}`); @@ -375,7 +391,7 @@ export async function codegen(ast: Ast, { compileOptions, filename, fileID }: Co importExportStatements.add(wrapperImport); } if (wrapper !== name) { - code = code.slice(0, match.index + 2) + wrapper + code.slice(match.index + match[0].length - 1); + code = code.slice(0, astroComponent.index + 2) + wrapper + code.slice(astroComponent.index + astroComponent[0].length - 1); } } collectionItem!.jsx += `,(${code.trim().replace(/\;$/, '')})`; diff --git a/src/compiler/index.ts b/src/compiler/index.ts index 88ea5caf9..541bae21e 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -13,6 +13,7 @@ import { encodeMarkdown } from '../micromark-encode.js'; import { optimize } from './optimize/index.js'; import { codegen } from './codegen.js'; +/** Return Astro internal import URL */ function internalImport(internalPath: string) { return `/_astro_internal/${internalPath}`; } @@ -23,6 +24,13 @@ interface ConvertAstroOptions { fileID: string; } +/** + * .astro -> .jsx + * Core function processing .astro files. Initiates all 3 phases of compilation: + * 1. Parse + * 2. Optimize + * 3. Codegen + */ async function convertAstroToJsx(template: string, opts: ConvertAstroOptions): Promise<TransformResult> { const { filename } = opts; @@ -34,10 +42,14 @@ async function convertAstroToJsx(template: string, opts: ConvertAstroOptions): P // 2. Optimize the AST await optimize(ast, opts); - // Turn AST into JSX + // 3. Turn AST into JSX return await codegen(ast, opts); } +/** + * .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 } @@ -80,6 +92,7 @@ async function convertMdToJsx( 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 } @@ -95,6 +108,7 @@ async function transformFromSource( } } +/** Return internal code that gets processed in Snowpack */ export async function compileComponent( source: string, { compileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string } diff --git a/src/compiler/optimize/doctype.ts b/src/compiler/optimize/doctype.ts index fdf6c4078..176880c08 100644 --- a/src/compiler/optimize/doctype.ts +++ b/src/compiler/optimize/doctype.ts @@ -1,5 +1,6 @@ import { Optimizer } from '../../@types/optimizer'; +/** Optimize <!doctype> tg */ export default function (_opts: { filename: string; fileID: string }): Optimizer { let hasDoctype = false; diff --git a/src/compiler/optimize/index.ts b/src/compiler/optimize/index.ts index 53dd3f2d6..a7bf828e0 100644 --- a/src/compiler/optimize/index.ts +++ b/src/compiler/optimize/index.ts @@ -13,6 +13,7 @@ interface VisitorCollection { 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[]>(); @@ -22,6 +23,7 @@ function addVisitor(visitor: NodeVisitor, collection: VisitorCollection, nodeNam collection[event].set(nodeName, visitors); } +/** Compile visitor actions from optimizer */ function collectVisitors(optimizer: Optimizer, htmlVisitors: VisitorCollection, cssVisitors: VisitorCollection, finalizers: Array<() => Promise<void>>) { if (optimizer.visitors) { if (optimizer.visitors.html) { @@ -40,6 +42,7 @@ function collectVisitors(optimizer: Optimizer, htmlVisitors: VisitorCollection, finalizers.push(optimizer.finalize); } +/** Utility for formatting visitors */ function createVisitorCollection() { return { enter: new Map<string, VisitorFn[]>(), @@ -47,6 +50,7 @@ function createVisitorCollection() { }; } +/** Walk AST with collected visitors */ function walkAstWithVisitors(tmpl: TemplateNode, collection: VisitorCollection) { walk(tmpl, { enter(node, parent, key, index) { @@ -68,6 +72,12 @@ function walkAstWithVisitors(tmpl: TemplateNode, collection: VisitorCollection) }); } +/** + * Optimize + * Step 2/3 in Astro SSR. + * Optimize is the point at which we mutate the AST before sending off to + * Codegen, and then to Snowpack. In some ways, it‘s a preprocessor. + */ export async function optimize(ast: Ast, opts: OptimizeOptions) { const htmlVisitors = createVisitorCollection(); const cssVisitors = createVisitorCollection(); diff --git a/src/compiler/optimize/module-scripts.ts b/src/compiler/optimize/module-scripts.ts index 713747fcb..9d4949215 100644 --- a/src/compiler/optimize/module-scripts.ts +++ b/src/compiler/optimize/module-scripts.ts @@ -4,6 +4,7 @@ import type { CompileOptions } from '../../@types/compiler'; import path from 'path'; import { getAttrValue, setAttrValue } from '../../ast.js'; +/** Optimize <script type="module"> */ export default function ({ compileOptions, filename }: { compileOptions: CompileOptions; filename: string; fileID: string }): Optimizer { const { astroConfig } = compileOptions; const { astroRoot } = astroConfig; diff --git a/src/compiler/optimize/postcss-scoped-styles/index.ts b/src/compiler/optimize/postcss-scoped-styles/index.ts index a4afd99aa..0d1253350 100644 --- a/src/compiler/optimize/postcss-scoped-styles/index.ts +++ b/src/compiler/optimize/postcss-scoped-styles/index.ts @@ -24,15 +24,17 @@ export function scopeSelectors(selector: string, className: string) { let ss = selector; // final output // Pass 1: parse selector string; extract top-level selectors - let start = 0; - let lastValue = ''; - for (let n = 0; n < ss.length; n++) { - const isEnd = n === selector.length - 1; - if (isEnd || 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; + { + let start = 0; + let lastValue = ''; + for (let n = 0; n < ss.length; n++) { + const isEnd = n === selector.length - 1; + if (isEnd || 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; + } } } diff --git a/src/compiler/optimize/styles.ts b/src/compiler/optimize/styles.ts index b613e4845..36e6f1d7d 100644 --- a/src/compiler/optimize/styles.ts +++ b/src/compiler/optimize/styles.ts @@ -106,7 +106,7 @@ async function transformStyle(code: string, { type, filename, scopedClass, mode return { css, type: styleType }; } -/** Style optimizer */ +/** Optimize <style> tags */ export default function optimizeStyles({ compileOptions, filename, fileID }: OptimizeOptions): Optimizer { const styleNodes: TemplateNode[] = []; // <style> tags to be updated const styleTransformPromises: Promise<StyleTransformResult>[] = []; // async style transform results to be finished in finalize(); diff --git a/src/config.ts b/src/config.ts index 96c4e92d4..72583df5a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -2,6 +2,7 @@ import type { AstroConfig } from './@types/astro'; import { join as pathJoin, resolve as pathResolve } from 'path'; import { existsSync } from 'fs'; +/** Attempt to load an `astro.config.mjs` file */ export async function loadConfig(rawRoot: string | undefined): Promise<AstroConfig | undefined> { if (typeof rawRoot === 'undefined') { rawRoot = process.cwd(); diff --git a/src/dev.ts b/src/dev.ts index f2cd6a80d..851379d7b 100644 --- a/src/dev.ts +++ b/src/dev.ts @@ -18,7 +18,8 @@ const logging: LogOptions = { dest: defaultLogDestination, }; -export default async function (astroConfig: AstroConfig) { +/** The primary dev action */ +export default async function dev(astroConfig: AstroConfig) { const { projectRoot } = astroConfig; const runtime = await createRuntime(astroConfig, { mode: 'development', logging }); @@ -69,7 +70,8 @@ export default async function (astroConfig: AstroConfig) { }); } -function formatErrorForBrowser(error: Error) { +/** Format error message */ +function formatErrorForBrowser(err: Error) { // TODO make this pretty. - return error.toString(); + return err.toString(); } diff --git a/src/frontend/h.ts b/src/frontend/h.ts index 7d26d21d2..c1e21dc95 100644 --- a/src/frontend/h.ts +++ b/src/frontend/h.ts @@ -5,6 +5,7 @@ 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 '; @@ -47,6 +48,7 @@ function* _h(tag: string, attrs: HProps, children: Array<HChild>) { 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') { @@ -57,6 +59,7 @@ export async function h(tag: HTag, attrs: HProps, ...pChildren: Array<Promise<HC 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/renderer.ts b/src/frontend/render/renderer.ts index 9589cef85..272f4ef5c 100644 --- a/src/frontend/render/renderer.ts +++ b/src/frontend/render/renderer.ts @@ -10,12 +10,13 @@ export interface Renderer { imports?: Record<string, string[]>; } +/** Initialize Astro Component renderer for Static and Dynamic components */ export function createRenderer(renderer: Renderer) { const _static: Renderer['renderStatic'] = (Component: any) => renderer.renderStatic(Component); const _imports = (context: DynamicRenderContext) => { const values = Object.values(renderer.imports ?? {}) - .reduce((acc, values) => { - return [...acc, `{ ${values.join(', ')} }`]; + .reduce((acc, v) => { + return [...acc, `{ ${v.join(', ')} }`]; }, []) .join(', '); const libs = Object.keys(renderer.imports ?? {}) diff --git a/src/logger.ts b/src/logger.ts index 6634c5092..7ffc2da5a 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -61,6 +61,7 @@ const levels: Record<LoggerLevel, number> = { silent: 90, }; +/** Full logging API */ export function log(opts: LogOptions = defaultLogOptions, level: LoggerLevel, type: string, ...args: Array<any>) { const event: LogMessage = { type, @@ -77,22 +78,27 @@ export function log(opts: LogOptions = defaultLogOptions, level: LoggerLevel, ty opts.dest.write(event); } +/** Emit a message only shown in debug mode */ export function debug(opts: LogOptions, type: string, ...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, ...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, ...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, ...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 @@ -108,7 +114,7 @@ export function parseError(opts: LogOptions, err: CompileError) { ` ${underline(bold(grey(`${err.filename}:${err.start.line}:${err.start.column}`)))} - + ${bold(red(`𝘅 ${err.message}`))} ${frame} diff --git a/src/micromark-collect-headers.ts b/src/micromark-collect-headers.ts index 78567699c..69781231a 100644 --- a/src/micromark-collect-headers.ts +++ b/src/micromark-collect-headers.ts @@ -1,7 +1,10 @@ import slugger from 'github-slugger'; -// 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". +/** + * 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; diff --git a/src/micromark-encode.ts b/src/micromark-encode.ts index e3b328224..f9e863fdd 100644 --- a/src/micromark-encode.ts +++ b/src/micromark-encode.ts @@ -11,12 +11,14 @@ const characterReferences = { 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: Record<string, () => void>) { const token: Token = arguments[0]; const serialize = (this.sliceSerialize as unknown) as (t: Token) => string; diff --git a/src/parser/Stats.ts b/src/parser/Stats.ts index 33802a42b..5f7f991f8 100644 --- a/src/parser/Stats.ts +++ b/src/parser/Stats.ts @@ -15,6 +15,7 @@ interface Timing { children: Timing[]; } +/** Format benchmarks */ function collapse_timings(timings) { const result = {}; timings.forEach((timing) => { diff --git a/src/parser/parse/index.ts b/src/parser/parse/index.ts index 052cf0317..124e125ef 100644 --- a/src/parser/parse/index.ts +++ b/src/parser/parse/index.ts @@ -217,6 +217,11 @@ export class Parser { } } +/** + * 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); diff --git a/src/parser/utils/error.ts b/src/parser/utils/error.ts index 3c1b23e4c..8ebb5b093 100644 --- a/src/parser/utils/error.ts +++ b/src/parser/utils/error.ts @@ -16,6 +16,7 @@ export class CompileError extends Error { } } +/** Throw CompileError */ export default function error( message: string, props: { @@ -27,19 +28,19 @@ export default function error( end?: number; } ): never { - const error = new CompileError(message); - error.name = props.name; + 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 }); - error.code = props.code; - error.start = start; - error.end = end; - error.pos = props.start; - error.filename = props.filename; + err.code = props.code; + err.start = start; + err.end = end; + err.pos = props.start; + err.filename = props.filename; - error.frame = get_code_frame(props.source, start.line - 1, start.column); + err.frame = get_code_frame(props.source, start.line - 1, start.column); - throw error; + throw err; } diff --git a/src/parser/utils/full_char_code_at.ts b/src/parser/utils/full_char_code_at.ts index fea5151b6..b62b2c77a 100644 --- a/src/parser/utils/full_char_code_at.ts +++ b/src/parser/utils/full_char_code_at.ts @@ -1,6 +1,7 @@ // 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; diff --git a/src/parser/utils/fuzzymatch.ts b/src/parser/utils/fuzzymatch.ts index d24d0fd0a..4d17aafdf 100644 --- a/src/parser/utils/fuzzymatch.ts +++ b/src/parser/utils/fuzzymatch.ts @@ -1,5 +1,6 @@ // @ts-nocheck +/** Utility for accessing FuzzySet */ export default function fuzzymatch(name: string, names: string[]) { const set = new FuzzySet(names); const matches = set.get(name); @@ -13,7 +14,7 @@ export default function fuzzymatch(name: string, names: string[]) { const GRAM_SIZE_LOWER = 2; const GRAM_SIZE_UPPER = 3; -// return an edit distance from 0 to 1 +/** 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'; @@ -30,7 +31,7 @@ function _distance(str1: string, str2: string) { } } -// helper functions +/** @url https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js#L18 */ function levenshtein(str1: string, str2: string) { const current: number[] = []; let prev; @@ -58,6 +59,7 @@ function levenshtein(str1: string, str2: string) { 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; @@ -74,6 +76,7 @@ function iterate_grams(value: string, gram_size = 2) { 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 = {}; @@ -90,6 +93,7 @@ function gram_counter(value: string, gram_size = 2) { 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]; } @@ -211,16 +215,16 @@ class FuzzySet { let new_results = []; const end_index = Math.min(50, results.length); // truncate somewhat arbitrarily to 50 - for (let i = 0; i < end_index; ++i) { - new_results.push([_distance(results[i][1], normalized_value), results[i][1]]); + 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 i = 0; i < results.length; ++i) { - if (results[i][0] == results[0][0]) { - new_results.push([results[i][0], this.exact_set[results[i][1]]]); + 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]]]); } } diff --git a/src/parser/utils/get_code_frame.ts b/src/parser/utils/get_code_frame.ts index a0c296672..e4f1834fd 100644 --- a/src/parser/utils/get_code_frame.ts +++ b/src/parser/utils/get_code_frame.ts @@ -1,7 +1,9 @@ +/** 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'); diff --git a/src/parser/utils/list.ts b/src/parser/utils/list.ts index ba1ef9f4c..9388adb14 100644 --- a/src/parser/utils/list.ts +++ b/src/parser/utils/list.ts @@ -1,3 +1,4 @@ +/** 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 index f2e1dfc8e..f041d20ce 100644 --- a/src/parser/utils/names.ts +++ b/src/parser/utils/names.ts @@ -113,10 +113,12 @@ export const reserved = new Set([ 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; @@ -130,6 +132,7 @@ export function is_valid(str: string): boolean { return true; } +/** Utility to normalize HTML */ export function sanitize(name: string) { return name .replace(/[^a-zA-Z0-9_]+/g, '_') diff --git a/src/parser/utils/nodes_match.ts b/src/parser/utils/nodes_match.ts index 563742635..7e4093994 100644 --- a/src/parser/utils/nodes_match.ts +++ b/src/parser/utils/nodes_match.ts @@ -1,5 +1,6 @@ // @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; diff --git a/src/parser/utils/trim.ts b/src/parser/utils/trim.ts index 406a8c97f..480cc99a8 100644 --- a/src/parser/utils/trim.ts +++ b/src/parser/utils/trim.ts @@ -1,5 +1,6 @@ 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; @@ -7,6 +8,7 @@ export function trim_start(str: string) { 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; diff --git a/src/runtime.ts b/src/runtime.ts index 0d8a34035..d7524433d 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -32,6 +32,7 @@ export type LoadResult = LoadResultSuccess | LoadResultNotFound | LoadResultErro // 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; @@ -134,6 +135,7 @@ interface RuntimeOptions { logging: LogOptions; } +/** Create a new Snowpack instance to power Astro */ async function createSnowpack(astroConfig: AstroConfig, env: Record<string, any>) { const { projectRoot, astroRoot, extensions } = astroConfig; @@ -189,6 +191,7 @@ async function createSnowpack(astroConfig: AstroConfig, env: Record<string, any> return { snowpack, snowpackRuntime, snowpackConfig }; } +/** Core Astro runtime */ export async function createRuntime(astroConfig: AstroConfig, { mode, logging }: RuntimeOptions): Promise<AstroRuntime> { const { snowpack: backendSnowpack, snowpackRuntime: backendSnowpackRuntime, snowpackConfig: backendSnowpackConfig } = await createSnowpack(astroConfig, { astro: true, |