diff --git a/src/compiler/build/full-build.ts b/src/compiler/build/full-build.ts index 8765c8a8dd8..7bd957cf172 100644 --- a/src/compiler/build/full-build.ts +++ b/src/compiler/build/full-build.ts @@ -4,7 +4,16 @@ import { BuildContext } from './build-ctx'; import { createTsBuildProgram } from '../transpile/create-build-program'; import ts from 'typescript'; -export const createFullBuild = async (config: d.Config, compilerCtx: d.CompilerCtx) => { +/** + * Build a callable function to perform a full build of a Stencil project + * @param config a Stencil configuration to apply to a full build of a Stencil project + * @param compilerCtx the current Stencil compiler context + * @returns the results of a full build of Stencil + */ +export const createFullBuild = async ( + config: d.Config, + compilerCtx: d.CompilerCtx +): Promise => { return new Promise((resolve) => { let tsWatchProgram: ts.WatchOfConfigFile = null; @@ -13,7 +22,11 @@ export const createFullBuild = async (config: d.Config, compilerCtx: d.CompilerC compilerCtx.fs.clearFileCache(p); }); - const onBuild = async (tsBuilder: ts.BuilderProgram) => { + /** + * A function that kicks off the transpilation process for both the TypeScript and Stencil compilers + * @param tsBuilder the manager of the {@link ts.Program} state + */ + const onBuild = async (tsBuilder: ts.BuilderProgram): Promise => { const buildCtx = new BuildContext(config, compilerCtx); buildCtx.isRebuild = false; buildCtx.requiresFullBuild = true; diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 07754263b3d..c7e3c2ab83c 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -12,7 +12,12 @@ import { resolveModuleIdAsync } from './sys/resolve/resolve-module-async'; import { isFunction } from '@utils'; import ts from 'typescript'; -export const createCompiler = async (config: Config) => { +/** + * Generate a Stencil compiler instance + * @param config a Stencil configuration to apply to the compiler instance + * @returns a new instance of a Stencil compiler + */ +export const createCompiler = async (config: Config): Promise => { // actual compiler code // could be in a web worker on the browser // or the main thread in node diff --git a/src/compiler/transpile/create-build-program.ts b/src/compiler/transpile/create-build-program.ts index 2eca4cc8e7b..cf399434969 100644 --- a/src/compiler/transpile/create-build-program.ts +++ b/src/compiler/transpile/create-build-program.ts @@ -1,67 +1,124 @@ import type * as d from '../../declarations'; import { getTsOptionsToExtend } from './ts-config'; -import { GENERATED_DTS } from '../output-targets/output-utils'; import ts from 'typescript'; +/** + * Create a TypeScript Program ({@link ts.Program}) to perform builds of a Stencil project using the provided + * `buildCallback` entity + * @param config a Stencil configuration to apply to a full build of a Stencil project + * @param buildCallback a callback that invokes the actual transpilation of a Stencil project + * @returns a Program that marries the TypeScript and Stencil compilers together. + */ export const createTsBuildProgram = async ( config: d.Config, buildCallback: (tsBuilder: ts.BuilderProgram) => Promise -) => { - let isRunning = false; - let timeoutId: any; +): Promise> => { + let isBuildRunning = false; + let currentBuildTimeoutId: any; const optionsToExtend = getTsOptionsToExtend(config); + /** + * Create a {@link ts.System}. The System is responsible for handling all interactions between the TypeScript compiler + * and the host operating system. + */ const tsWatchSys: ts.System = { ...ts.sys, - watchFile(path, callback) { - if (path.endsWith(`/${GENERATED_DTS}`)) { - return ts.sys.watchFile(path, callback); - } + /** + * Watch changes in source files, missing files needed to update the program or config file + * @returns a no-op file watcher + */ + watchFile(): ts.FileWatcher { return { close() {}, }; }, - watchDirectory() { + /** + * Watch a resolved module's failed lookup locations, config file specs, type roots where auto type reference + * directives are added + * @returns a no-op file watcher + */ + watchDirectory(): ts.FileWatcher { return { close() {}, }; }, - setTimeout(callback, time) { - timeoutId = setInterval(() => { - if (!isRunning) { + /** + * Set delayed compilation, so that multiple changes in short span are compiled together + * @param callback a callback to invoke upon the completion of compilation. this function is provided to Stencil by + * the TypeScript compiler. + * @param timeoutMs the minimum time to wait (in milliseconds) before checking if compilation is complete or not + * @returns the identifier for the interval that's created + */ + setTimeout(callback: (...args: any[]) => void, timeoutMs: number): any { + currentBuildTimeoutId = setInterval(() => { + if (!isBuildRunning) { callback(); - clearInterval(timeoutId); + clearInterval(currentBuildTimeoutId); } - }, config.sys.watchTimeout || time); - return timeoutId; + }, config.sys.watchTimeout || timeoutMs); + return currentBuildTimeoutId; }, - clearTimeout(id) { - return clearInterval(id); + /** + * Reset existing delayed compilation + * @param timeoutId the current build timeout identifier to clear + */ + clearTimeout(timeoutId: any): void { + clearInterval(timeoutId); }, }; - config.sys.addDestory(() => tsWatchSys.clearTimeout(timeoutId)); + config.sys.addDestory(() => tsWatchSys.clearTimeout(currentBuildTimeoutId)); - const tsWatchHost = ts.createWatchCompilerHost( - config.tsconfig, - optionsToExtend, - tsWatchSys, - ts.createEmitAndSemanticDiagnosticsBuilderProgram, - (reportDiagnostic) => { - config.logger.debug('watch reportDiagnostic:' + reportDiagnostic.messageText); - }, - (reportWatchStatus) => { - config.logger.debug(reportWatchStatus.messageText); - } - ); + /** + * Create a {@link ts.WatchCompilerHost}. A CompilerHost allows a {@link ts.Program} to interact with the + * {@link ts.System}, by acting as an intermediary: + * ``` + * ┌────────────┐ ┌──────────────────────┐ ┌───────────┐ ┌──────────────────┐ + * │ ts.Program │<->│ ts.WatchCompilerHost │<->│ ts.System │<->│ Operating System │ + * └────────────┘ └──────────────────────┘ └───────────┘ └──────────────────┘ + * ``` + * + * Strictly speaking, the created entity is a subclass of a WatchCompilerHost. The + * {@link ts.WatchCompilerHostOfConfigFile} class has the following features that makes it useful to Stencil (even + * when Stencil is performing a single, full build): + * - it provides the opportunity to extend/alter an existing tsconfig file, allowing users to override specific + * configuration options via {@link ts.WatchCompilerHostOfConfigFile#optionsToExtend}, which is a provided as an + * argument in the constructor + * - it includes the {@link ts.WatchCompilerHost#afterProgramCreate} function in its interface, which Stencil + * overrides to invoke a build callback (not as a part of this object's creation) + */ + const tsWatchHost: ts.WatchCompilerHostOfConfigFile = + ts.createWatchCompilerHost( + config.tsconfig, + optionsToExtend, + tsWatchSys, + ts.createEmitAndSemanticDiagnosticsBuilderProgram, + (reportDiagnostic) => { + config.logger.debug('watch reportDiagnostic:' + reportDiagnostic.messageText); + }, + (reportWatchStatus) => { + config.logger.debug(reportWatchStatus.messageText); + } + ); - tsWatchHost.afterProgramCreate = async (tsBuilder) => { - isRunning = true; + /** + * Override {@link ts.WatchCompilerHost#afterProgramCreate} to invoke the build callback that was provided as an + * argument to this function. + * @param tsBuilder a {@link ts.BuilderProgram} to manage the {@link ts.Program} in the provided build context + */ + tsWatchHost.afterProgramCreate = async (tsBuilder: ts.EmitAndSemanticDiagnosticsBuilderProgram): Promise => { + isBuildRunning = true; await buildCallback(tsBuilder); - isRunning = false; + isBuildRunning = false; }; + + /** + * Create the initial {@link ts.Program} using Stencil's custom {@link ts.WatchCompilerHostOfConfigFile}. The Program + * represents the _TypeScript_ compiler context, that will work in tandem with Stencil's compiler context and build + * context + */ return ts.createWatchProgram(tsWatchHost); }; diff --git a/src/compiler/types/generate-app-types.ts b/src/compiler/types/generate-app-types.ts index 11dfe45c282..c1c43bb87f0 100644 --- a/src/compiler/types/generate-app-types.ts +++ b/src/compiler/types/generate-app-types.ts @@ -58,7 +58,7 @@ export const generateAppTypes = async ( }; /** - * Generates a `component.d.ts` file's contents, which contains the typings for all components in a Stencil project + * Generates a `components.d.ts` file's contents, which contains the typings for all components in a Stencil project * @param config the Stencil configuration associated with the project being compiled * @param buildCtx the context associated with the current build * @param areTypesInternal determines if non-exported type definitions are being generated or not