From 205e8611ba3f25f66678d4458e88f8db08869032 Mon Sep 17 00:00:00 2001 From: dnalborczyk Date: Wed, 5 Jan 2022 01:20:02 -0500 Subject: [PATCH] refactor: type fixes (#4326) * make queue readonly * extract QueueItem type, use array literal, add function return type * make readqueue readonly, make maxparallel private and pass via constructor * extract and re-use task type, fix load result return type * fix source description condition * make props readonly * remove unknown from generic parameter * convert to async function, make types readonly * fix more queue types * remove test config file * remove unneeded type assertion * add more return types * re-use getexports method * remove type import * more type fixes * more type fixes * fix: use generic constraint * more readonly * simplify type declaration * remove truthy check * clone imports set * remove type assertion * remove type declaration * Improve coverage Co-authored-by: Lukas Taegert-Atkinson Co-authored-by: Lukas Taegert-Atkinson --- browser/resolveId.ts | 4 +- build-plugins/generate-license-file.ts | 8 +-- cli/run/batchWarnings.ts | 19 ++++-- cli/run/commandPlugins.ts | 5 +- cli/run/getConfigPath.ts | 6 +- cli/run/loadConfigFile.ts | 18 +++--- cli/run/resetScreen.ts | 2 +- cli/run/watch-cli.ts | 14 ++-- src/Bundle.ts | 16 ++--- src/Chunk.ts | 60 ++++++++++-------- src/ExternalModule.ts | 14 ++-- src/Graph.ts | 32 +++++----- src/Module.ts | 88 +++++++++++++------------- src/ModuleLoader.ts | 65 +++++++++++-------- src/ast/scopes/ChildScope.ts | 8 +-- src/ast/scopes/ModuleScope.ts | 4 +- src/utils/PluginDriver.ts | 16 ++--- src/utils/collapseSourcemaps.ts | 18 +++--- src/utils/commondir.ts | 6 +- src/utils/deconflictChunk.ts | 18 +++--- src/utils/executionOrder.ts | 4 +- src/utils/exportNames.ts | 4 +- src/utils/getCodeFrame.ts | 4 +- src/utils/getExportMode.ts | 2 +- src/utils/getIndentString.ts | 4 +- src/utils/getOriginalLocation.ts | 2 +- src/utils/getStaticDependencies.ts | 6 +- src/utils/identifierHelpers.ts | 2 +- src/utils/pluginUtils.ts | 5 +- src/utils/printStringList.ts | 5 +- src/utils/pureComments.ts | 8 +-- src/utils/queue.ts | 26 +++++--- src/utils/reassignedExportsMember.ts | 2 +- src/utils/renderHelpers.ts | 4 +- src/utils/resolveId.ts | 4 +- src/utils/resolveIdViaPlugins.ts | 4 +- src/utils/systemJsRendering.ts | 4 +- src/utils/timers.ts | 10 +-- src/watch/fileWatcher.ts | 16 ++--- src/watch/watch.ts | 24 +++---- 40 files changed, 296 insertions(+), 265 deletions(-) diff --git a/browser/resolveId.ts b/browser/resolveId.ts index 945768fb94f..6eccc297b11 100644 --- a/browser/resolveId.ts +++ b/browser/resolveId.ts @@ -13,9 +13,9 @@ export async function resolveId( importer: string | undefined, customOptions: CustomPluginOptions | undefined, isEntry: boolean | undefined, - skip: { importer: string | undefined; plugin: Plugin; source: string }[] | null + skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null ) => Promise, - skip: { importer: string | undefined; plugin: Plugin; source: string }[] | null, + skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null, customOptions: CustomPluginOptions | undefined, isEntry: boolean ): Promise { diff --git a/build-plugins/generate-license-file.ts b/build-plugins/generate-license-file.ts index e64391c9617..42f1e363c38 100644 --- a/build-plugins/generate-license-file.ts +++ b/build-plugins/generate-license-file.ts @@ -1,9 +1,9 @@ -import fs from 'fs'; +import { readFileSync, writeFileSync } from 'fs'; import { PluginImpl } from 'rollup'; import license, { Dependency, Person } from 'rollup-plugin-license'; function generateLicenseFile(dependencies: Dependency[]) { - const coreLicense = fs.readFileSync('LICENSE-CORE.md'); + const coreLicense = readFileSync('LICENSE-CORE.md'); const licenses = new Set(); const dependencyLicenseTexts = dependencies .sort(({ name: nameA }, { name: nameB }) => (nameA! > nameB! ? 1 : -1)) @@ -52,9 +52,9 @@ function generateLicenseFile(dependencies: Dependency[]) { `${Array.from(licenses).join(', ')}\n\n` + `# Bundled dependencies:\n` + dependencyLicenseTexts; - const existingLicenseText = fs.readFileSync('LICENSE.md', 'utf8'); + const existingLicenseText = readFileSync('LICENSE.md', 'utf8'); if (existingLicenseText !== licenseText) { - fs.writeFileSync('LICENSE.md', licenseText); + writeFileSync('LICENSE.md', licenseText); console.warn('LICENSE.md updated. You should commit the updated file.'); } } diff --git a/cli/run/batchWarnings.ts b/cli/run/batchWarnings.ts index a6beab9daa8..d311035fa37 100644 --- a/cli/run/batchWarnings.ts +++ b/cli/run/batchWarnings.ts @@ -248,20 +248,25 @@ const deferredHandlers: { } }; -function title(str: string) { +function title(str: string): void { stderr(bold(yellow(`(!) ${str}`))); } -function info(url: string) { +function info(url: string): void { stderr(gray(url)); } -function nest(array: T[], prop: string) { - const nested: { items: T[]; key: string }[] = []; - const lookup = new Map(); +interface Nested { + items: T[]; + key: string; +} + +function nest>(array: readonly T[], prop: string): Nested[] { + const nested: Nested[] = []; + const lookup = new Map>(); for (const item of array) { - const key = (item as any)[prop]; + const key = item[prop]; getOrCreate(lookup, key, () => { const items = { items: [], @@ -275,7 +280,7 @@ function nest(array: T[], prop: string) { return nested; } -function showTruncatedWarnings(warnings: RollupWarning[]) { +function showTruncatedWarnings(warnings: readonly RollupWarning[]): void { const nestedByModule = nest(warnings, 'id'); const displayedByModule = nestedByModule.length > 5 ? nestedByModule.slice(0, 3) : nestedByModule; diff --git a/cli/run/commandPlugins.ts b/cli/run/commandPlugins.ts index df4bb31d662..67fce28a866 100644 --- a/cli/run/commandPlugins.ts +++ b/cli/run/commandPlugins.ts @@ -38,7 +38,10 @@ export async function addPluginsFromCommandOption( } } -async function loadAndRegisterPlugin(inputOptions: InputOptions, pluginText: string) { +async function loadAndRegisterPlugin( + inputOptions: InputOptions, + pluginText: string +): Promise { let plugin: any = null; let pluginArg: any = undefined; if (pluginText[0] === '{') { diff --git a/cli/run/getConfigPath.ts b/cli/run/getConfigPath.ts index 10623faf256..e47039008fe 100644 --- a/cli/run/getConfigPath.ts +++ b/cli/run/getConfigPath.ts @@ -1,5 +1,5 @@ import { readdirSync } from 'fs'; -import * as path from 'path'; +import { resolve } from 'path'; import relative from 'require-relative'; import { handleError } from '../logging'; @@ -8,7 +8,7 @@ const DEFAULT_CONFIG_BASE = 'rollup.config'; export function getConfigPath(commandConfig: string | true): string { const cwd = process.cwd(); if (commandConfig === true) { - return path.resolve(findConfigFileNameInCwd()); + return resolve(findConfigFileNameInCwd()); } if (commandConfig.slice(0, 5) === 'node:') { const pkgName = commandConfig.slice(5); @@ -28,7 +28,7 @@ export function getConfigPath(commandConfig: string | true): string { } } } - return path.resolve(commandConfig); + return resolve(commandConfig); } function findConfigFileNameInCwd(): string { diff --git a/cli/run/loadConfigFile.ts b/cli/run/loadConfigFile.ts index 1ddba4d142e..3b07543aa6d 100644 --- a/cli/run/loadConfigFile.ts +++ b/cli/run/loadConfigFile.ts @@ -1,5 +1,5 @@ -import * as fs from 'fs'; -import * as path from 'path'; +import { realpathSync } from 'fs'; +import { extname, isAbsolute } from 'path'; import { pathToFileURL } from 'url'; import * as rollup from '../../src/node-entry'; import { MergedRollupOptions } from '../../src/rollup/types'; @@ -12,7 +12,7 @@ import { stderr } from '../logging'; import batchWarnings, { BatchWarnings } from './batchWarnings'; import { addCommandPluginsToInputOptions, addPluginsFromCommandOption } from './commandPlugins'; -function supportsNativeESM() { +function supportsNativeESM(): boolean { return Number(/^v(\d+)/.exec(process.version)![1]) >= 13; } @@ -44,7 +44,7 @@ async function loadConfigFile( fileName: string, commandOptions: Record ): Promise { - const extension = path.extname(fileName); + const extension = extname(fileName); const configFileExport = commandOptions.configPlugin || @@ -68,7 +68,7 @@ async function getDefaultFromTranspiledConfigFile( const warnings = batchWarnings(); const inputOptions = { external: (id: string) => - (id[0] !== '.' && !path.isAbsolute(id)) || id.slice(-5, id.length) === '.json', + (id[0] !== '.' && !isAbsolute(id)) || id.slice(-5, id.length) === '.json', input: fileName, onwarn: warnings.add, plugins: [], @@ -102,9 +102,9 @@ async function getDefaultFromTranspiledConfigFile( return loadConfigFromBundledFile(fileName, code); } -async function loadConfigFromBundledFile(fileName: string, bundledCode: string) { - const resolvedFileName = fs.realpathSync(fileName); - const extension = path.extname(resolvedFileName); +async function loadConfigFromBundledFile(fileName: string, bundledCode: string): Promise { + const resolvedFileName = realpathSync(fileName); + const extension = extname(resolvedFileName); const defaultLoader = require.extensions[extension]; require.extensions[extension] = (module: NodeModule, requiredFileName: string) => { if (requiredFileName === resolvedFileName) { @@ -132,7 +132,7 @@ async function loadConfigFromBundledFile(fileName: string, bundledCode: string) } } -async function getConfigList(configFileExport: any, commandOptions: any) { +async function getConfigList(configFileExport: any, commandOptions: any): Promise { const config = await (typeof configFileExport === 'function' ? configFileExport(commandOptions) : configFileExport); diff --git a/cli/run/resetScreen.ts b/cli/run/resetScreen.ts index 39fc53c70b3..3cf34ee8657 100644 --- a/cli/run/resetScreen.ts +++ b/cli/run/resetScreen.ts @@ -4,7 +4,7 @@ import { stderr } from '../logging'; const CLEAR_SCREEN = '\u001Bc'; export function getResetScreen( - configs: MergedRollupOptions[], + configs: readonly MergedRollupOptions[], allowClearScreen: boolean | undefined ): (heading: string) => void { let clearScreen = allowClearScreen; diff --git a/cli/run/watch-cli.ts b/cli/run/watch-cli.ts index 7baa555d876..9c83fc8265e 100644 --- a/cli/run/watch-cli.ts +++ b/cli/run/watch-cli.ts @@ -1,4 +1,4 @@ -import fs from 'fs'; +import { type FSWatcher, readFileSync } from 'fs'; import chokidar from 'chokidar'; import dateTime from 'date-time'; import ms from 'pretty-ms'; @@ -22,17 +22,17 @@ export async function watch(command: Record): Promise { let configs: MergedRollupOptions[]; let warnings: BatchWarnings; let watcher: RollupWatcher; - let configWatcher: fs.FSWatcher; + let configWatcher: FSWatcher; const configFile = command.config ? getConfigPath(command.config) : null; onExit(close); - process.on('uncaughtException' as any, close); + process.on('uncaughtException', close); if (!process.stdin.isTTY) { process.stdin.on('end', close); process.stdin.resume(); } - async function loadConfigFromFileAndTrack(configFile: string) { + async function loadConfigFromFileAndTrack(configFile: string): Promise { let reloadingConfig = false; let aborted = false; let configFileData: string | null = null; @@ -42,7 +42,7 @@ export async function watch(command: Record): Promise { async function reloadConfigFile() { try { - const newConfigFileData = fs.readFileSync(configFile, 'utf-8'); + const newConfigFileData = readFileSync(configFile, 'utf-8'); if (newConfigFileData === configFileData) { return; } @@ -83,7 +83,7 @@ export async function watch(command: Record): Promise { const resetScreen = getResetScreen(configs!, isTTY); - function start(configs: MergedRollupOptions[]) { + function start(configs: MergedRollupOptions[]): void { try { watcher = rollup.watch(configs as any); } catch (err: any) { @@ -144,7 +144,7 @@ export async function watch(command: Record): Promise { }); } - function close(code: number | null) { + function close(code: number | null): void { process.removeListener('uncaughtException', close); // removing a non-existent listener is a no-op process.stdin.removeListener('end', close); diff --git a/src/Bundle.ts b/src/Bundle.ts index ce26dc85bca..6c23f85d34e 100644 --- a/src/Bundle.ts +++ b/src/Bundle.ts @@ -30,8 +30,8 @@ import { basename, isAbsolute } from './utils/path'; import { timeEnd, timeStart } from './utils/timers'; export default class Bundle { - private facadeChunkByModule = new Map(); - private includedNamespaces = new Set(); + private readonly facadeChunkByModule = new Map(); + private readonly includedNamespaces = new Set(); constructor( private readonly outputOptions: NormalizedOutputOptions, @@ -82,7 +82,7 @@ export default class Bundle { } private async addFinalizedChunksToBundle( - chunks: Chunk[], + chunks: readonly Chunk[], inputBase: string, addons: Addons, outputBundle: OutputBundleWithPlaceholders, @@ -122,11 +122,11 @@ export default class Bundle { } private assignChunkIds( - chunks: Chunk[], + chunks: readonly Chunk[], inputBase: string, addons: Addons, bundle: OutputBundleWithPlaceholders - ) { + ): void { const entryChunks: Chunk[] = []; const otherChunks: Chunk[] = []; for (const chunk of chunks) { @@ -137,7 +137,7 @@ export default class Bundle { } // make sure entry chunk names take precedence with regard to deconflicting - const chunksForNaming: Chunk[] = entryChunks.concat(otherChunks); + const chunksForNaming = entryChunks.concat(otherChunks); for (const chunk of chunksForNaming) { if (this.outputOptions.file) { chunk.id = basename(this.outputOptions.file); @@ -241,7 +241,7 @@ export default class Bundle { } private prerenderChunks( - chunks: Chunk[], + chunks: readonly Chunk[], inputBase: string, snippets: GenerateCodeSnippets ): void { @@ -254,7 +254,7 @@ export default class Bundle { } } -function getAbsoluteEntryModulePaths(chunks: Chunk[]): string[] { +function getAbsoluteEntryModulePaths(chunks: readonly Chunk[]): string[] { const absoluteEntryModulePaths: string[] = []; for (const chunk of chunks) { for (const entryModule of chunk.entryModules) { diff --git a/src/Chunk.ts b/src/Chunk.ts index d72b3fede59..437c7400145 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -108,7 +108,7 @@ function getGlobalName( globals: GlobalsOption, hasExports: boolean, warn: WarningHandler -) { +): string | undefined { const globalName = typeof globals === 'function' ? globals(module.id) : globals[module.id]; if (globalName) { return globalName; @@ -126,7 +126,7 @@ function getGlobalName( } export default class Chunk { - entryModules: Module[] = []; + readonly entryModules: Module[] = []; execIndex: number; exportMode: 'none' | 'named' | 'default' = 'named'; facadeModule: Module | null = null; @@ -136,27 +136,28 @@ export default class Chunk { suggestedVariableName: string; variableName = ''; - private accessedGlobalsByScope = new Map>(); + private readonly accessedGlobalsByScope = new Map>(); private dependencies = new Set(); - private dynamicDependencies = new Set(); - private dynamicEntryModules: Module[] = []; + private readonly dynamicDependencies = new Set(); + private readonly dynamicEntryModules: Module[] = []; private dynamicName: string | null = null; - private exportNamesByVariable = new Map(); - private exports = new Set(); - private exportsByName: Record = Object.create(null); + private readonly exportNamesByVariable = new Map(); + private readonly exports = new Set(); + private readonly exportsByName: Record = Object.create(null); private fileName: string | null = null; private implicitEntryModules: Module[] = []; - private implicitlyLoadedBefore = new Set(); - private imports = new Set(); + private readonly implicitlyLoadedBefore = new Set(); + private readonly imports = new Set(); private indentString: string = undefined as never; + // This may only be updated in the constructor private readonly isEmpty: boolean = true; private name: string | null = null; private renderedDependencies: Map | null = null; private renderedExports: ChunkExports | null = null; - private renderedHash: string = undefined as never; - private renderedModuleSources = new Map(); - private renderedModules: { + private renderedHash: string | undefined = undefined; + private readonly renderedModuleSources = new Map(); + private readonly renderedModules: { [moduleId: string]: RenderedModule; } = Object.create(null); private renderedSource: MagicStringBundle | null = null; @@ -251,7 +252,7 @@ export default class Chunk { return chunk; } - canModuleBeFacade(module: Module, exposedVariables: Set): boolean { + canModuleBeFacade(module: Module, exposedVariables: ReadonlySet): boolean { const moduleExportNamesByVariable = module.getExportNamesByVariable(); for (const exposedVariable of this.exports) { if (!moduleExportNamesByVariable.has(exposedVariable)) { @@ -429,9 +430,9 @@ export default class Chunk { preserveModulesRelativeDir: string, options: NormalizedOutputOptions, existingNames: Record, - unsetOptions: Set + unsetOptions: ReadonlySet ): string { - const id = this.orderedModules[0].id; + const [{ id }] = this.orderedModules; const sanitizedId = this.outputOptions.sanitizeFileName(id); let path: string; @@ -641,7 +642,7 @@ export default class Chunk { this.renderedSource = magicString.trim(); } - this.renderedHash = undefined as never; + this.renderedHash = undefined; if (this.isEmpty && this.getExportNames().length === 0 && this.dependencies.size === 0) { const chunkName = this.getChunkName(); @@ -811,9 +812,9 @@ export default class Chunk { } private addDependenciesToChunk( - moduleDependencies: Set, + moduleDependencies: ReadonlySet, chunkDependencies: Set - ) { + ): void { for (const module of moduleDependencies) { if (module instanceof Module) { const chunk = this.chunkByModule.get(module); @@ -826,7 +827,7 @@ export default class Chunk { } } - private assignFacadeName({ fileName, name }: FacadeName, facadedModule: Module) { + private assignFacadeName({ fileName, name }: FacadeName, facadedModule: Module): void { if (fileName) { this.fileName = fileName; } else { @@ -870,7 +871,7 @@ export default class Chunk { hash.update( [addons.intro, addons.outro, addons.banner, addons.footer].map(addon => addon || '').join(':') ); - hash.update(options.format as string); + hash.update(options.format); const dependenciesForHashing = new Set([this]); for (const current of dependenciesForHashing) { if (current instanceof ExternalModule) { @@ -887,7 +888,7 @@ export default class Chunk { return hash.digest('hex').substr(0, 8); } - private ensureReexportsAreAvailableForModule(module: Module) { + private ensureReexportsAreAvailableForModule(module: Module): void { const map = module.getExportNamesByVariable(); for (const exportedVariable of map.keys()) { const isSynthetic = exportedVariable instanceof SyntheticNamedExportVariable; @@ -910,7 +911,10 @@ export default class Chunk { } } - private finaliseDynamicImports(options: NormalizedOutputOptions, snippets: GenerateCodeSnippets) { + private finaliseDynamicImports( + options: NormalizedOutputOptions, + snippets: GenerateCodeSnippets + ): void { const stripKnownJsExtensions = options.format === 'amd'; for (const [module, code] of this.renderedModuleSources) { for (const { node, resolution } of module.dynamicImports) { @@ -1218,7 +1222,7 @@ export default class Chunk { return relativePath.startsWith('../') ? relativePath : './' + relativePath; } - private inlineChunkDependencies(chunk: Chunk) { + private inlineChunkDependencies(chunk: Chunk): void { for (const dep of chunk.dependencies) { if (this.dependencies.has(dep)) continue; this.dependencies.add(dep); @@ -1228,7 +1232,7 @@ export default class Chunk { } } - private prepareModulesForRendering(snippets: GenerateCodeSnippets) { + private prepareModulesForRendering(snippets: GenerateCodeSnippets): void { const accessedGlobalsByScope = this.accessedGlobalsByScope; for (const module of this.orderedModules) { for (const { node, resolution } of module.dynamicImports) { @@ -1268,7 +1272,7 @@ export default class Chunk { } } - private setExternalRenderPaths(options: NormalizedOutputOptions, inputBase: string) { + private setExternalRenderPaths(options: NormalizedOutputOptions, inputBase: string): void { for (const dependency of [...this.dependencies, ...this.dynamicDependencies]) { if (dependency instanceof ExternalModule) { dependency.setRenderPath(options, inputBase); @@ -1303,7 +1307,7 @@ export default class Chunk { break; } } - const usedNames = new Set(['Object', 'Promise']); + const usedNames = new Set(['Object', 'Promise']); if (this.needsExportsShim) { usedNames.add(MISSING_EXPORT_SHIM_VARIABLE); } @@ -1347,7 +1351,7 @@ export default class Chunk { ); } - private setUpChunkImportsAndExportsForModule(module: Module) { + private setUpChunkImportsAndExportsForModule(module: Module): void { const moduleImports = new Set(module.imports); // when we are not preserving modules, we need to make all namespace variables available for // rendering the namespace object diff --git a/src/ExternalModule.ts b/src/ExternalModule.ts index 8a395b827df..97a66c36e67 100644 --- a/src/ExternalModule.ts +++ b/src/ExternalModule.ts @@ -12,15 +12,15 @@ import { printQuotedStringList } from './utils/printStringList'; import relativeId from './utils/relativeId'; export default class ExternalModule { - declarations: { [name: string]: ExternalVariable } = Object.create(null); + readonly declarations: { [name: string]: ExternalVariable } = Object.create(null); defaultVariableName = ''; - dynamicImporters: string[] = []; + readonly dynamicImporters: string[] = []; execIndex = Infinity; - exportedVariables = new Map(); - importers: string[] = []; - info: ModuleInfo; + readonly exportedVariables = new Map(); + readonly importers: string[] = []; + readonly info: ModuleInfo; mostCommonSuggestion = 0; - nameSuggestions: { [name: string]: number } = Object.create(null); + readonly nameSuggestions: { [name: string]: number } = Object.create(null); namespaceVariableName = ''; reexported = false; renderPath: string = undefined as never; @@ -33,7 +33,7 @@ export default class ExternalModule { public readonly id: string, hasModuleSideEffects: boolean | 'no-treeshake', meta: CustomPluginOptions, - public renormalizeRenderPath: boolean + public readonly renormalizeRenderPath: boolean ) { this.suggestedVariableName = makeLegal(id.split(/[\\/]/).pop()!); diff --git a/src/Graph.ts b/src/Graph.ts index 9ed4bbdb4d9..38fc7a0d7cc 100644 --- a/src/Graph.ts +++ b/src/Graph.ts @@ -23,7 +23,7 @@ import { timeEnd, timeStart } from './utils/timers'; import { markModuleAndImpureDependenciesAsExecuted } from './utils/traverseStaticDependencies'; function normalizeEntryModules( - entryModules: string[] | Record + entryModules: readonly string[] | Record ): UnresolvedModule[] { if (Array.isArray(entryModules)) { return entryModules.map(id => ({ @@ -44,20 +44,20 @@ function normalizeEntryModules( } export default class Graph { - acornParser: typeof acorn.Parser; - cachedModules = new Map(); - deoptimizationTracker = new PathTracker(); + readonly acornParser: typeof acorn.Parser; + readonly cachedModules = new Map(); + readonly deoptimizationTracker = new PathTracker(); entryModules: Module[] = []; - moduleLoader: ModuleLoader; - modulesById = new Map(); + readonly moduleLoader: ModuleLoader; + readonly modulesById = new Map(); needsTreeshakingPass = false; phase: BuildPhase = BuildPhase.LOAD_AND_PARSE; - pluginDriver: PluginDriver; - scope = new GlobalScope(); - watchFiles: Record = Object.create(null); + readonly pluginDriver: PluginDriver; + readonly scope = new GlobalScope(); + readonly watchFiles: Record = Object.create(null); watchMode = false; - private externalModules: ExternalModule[] = []; + private readonly externalModules: ExternalModule[] = []; private implicitEntryModules: Module[] = []; private modules: Module[] = []; private declare pluginCache?: Record; @@ -178,7 +178,7 @@ export default class Graph { } } - private includeStatements() { + private includeStatements(): void { for (const module of [...this.entryModules, ...this.implicitEntryModules]) { markModuleAndImpureDependenciesAsExecuted(module); } @@ -221,7 +221,7 @@ export default class Graph { } } - private sortModules() { + private sortModules(): void { const { orderedModules, cyclePaths } = analyseModuleExecution(this.entryModules); for (const cyclePath of cyclePaths) { this.options.onwarn({ @@ -238,21 +238,21 @@ export default class Graph { this.warnForMissingExports(); } - private warnForMissingExports() { + private warnForMissingExports(): void { for (const module of this.modules) { for (const importDescription of Object.values(module.importDescriptions)) { if ( importDescription.name !== '*' && - !(importDescription.module as Module).getVariableForExportName(importDescription.name) + !importDescription.module.getVariableForExportName(importDescription.name) ) { module.warn( { code: 'NON_EXISTENT_EXPORT', message: `Non-existent export '${ importDescription.name - }' is imported from ${relativeId((importDescription.module as Module).id)}`, + }' is imported from ${relativeId(importDescription.module.id)}`, name: importDescription.name, - source: (importDescription.module as Module).id + source: importDescription.module.id }, importDescription.start ); diff --git a/src/Module.ts b/src/Module.ts index 361b9300f51..dc9094bef55 100644 --- a/src/Module.ts +++ b/src/Module.ts @@ -11,7 +11,6 @@ import ExportNamedDeclaration from './ast/nodes/ExportNamedDeclaration'; import Identifier from './ast/nodes/Identifier'; import ImportDeclaration from './ast/nodes/ImportDeclaration'; import ImportExpression from './ast/nodes/ImportExpression'; -import ImportSpecifier from './ast/nodes/ImportSpecifier'; import Literal from './ast/nodes/Literal'; import MetaProperty from './ast/nodes/MetaProperty'; import * as NodeType from './ast/nodes/NodeType'; @@ -187,29 +186,29 @@ function getAndExtendSideEffectModules(variable: Variable, module: Module): Set< } export default class Module { - alternativeReexportModules = new Map(); + readonly alternativeReexportModules = new Map(); ast: Program | null = null; - chunkFileNames = new Set(); + readonly chunkFileNames = new Set(); chunkName: string | null = null; - cycles = new Set(); - dependencies = new Set(); - dynamicDependencies = new Set(); - dynamicImporters: string[] = []; - dynamicImports: DynamicImport[] = []; + readonly cycles = new Set(); + readonly dependencies = new Set(); + readonly dynamicDependencies = new Set(); + readonly dynamicImporters: string[] = []; + readonly dynamicImports: DynamicImport[] = []; excludeFromSourcemap: boolean; execIndex = Infinity; - exportAllSources = new Set(); - exports: { [name: string]: ExportDescription } = Object.create(null); - exportsAll: { [name: string]: string } = Object.create(null); - implicitlyLoadedAfter = new Set(); - implicitlyLoadedBefore = new Set(); - importDescriptions: { [name: string]: ImportDescription } = Object.create(null); - importMetas: MetaProperty[] = []; + readonly exportAllSources = new Set(); + readonly exports: { [name: string]: ExportDescription } = Object.create(null); + readonly exportsAll: { [name: string]: string } = Object.create(null); + readonly implicitlyLoadedAfter = new Set(); + readonly implicitlyLoadedBefore = new Set(); + readonly importDescriptions: { [name: string]: ImportDescription } = Object.create(null); + readonly importMetas: MetaProperty[] = []; importedFromNotTreeshaken = false; - importers: string[] = []; - imports = new Set(); - includedDynamicImporters: Module[] = []; - info: ModuleInfo; + readonly importers: string[] = []; + readonly imports = new Set(); + readonly includedDynamicImporters: Module[] = []; + readonly info: ModuleInfo; isExecuted = false; isUserDefinedEntryPoint = false; declare namespace: NamespaceVariable; @@ -217,27 +216,27 @@ export default class Module { declare originalCode: string; declare originalSourcemap: ExistingDecodedSourceMap | null; preserveSignature: PreserveEntrySignaturesOption; - reexportDescriptions: { [name: string]: ReexportDescription } = Object.create(null); + readonly reexportDescriptions: { [name: string]: ReexportDescription } = Object.create(null); declare resolvedIds: ResolvedIdMap; declare scope: ModuleScope; - sideEffectDependenciesByVariable = new Map>(); + readonly sideEffectDependenciesByVariable = new Map>(); declare sourcemapChain: DecodedSourceMapOrMissing[]; - sources = new Set(); + readonly sources = new Set(); declare transformFiles?: EmittedFile[]; - userChunkNames = new Set(); + readonly userChunkNames = new Set(); usesTopLevelAwait = false; private allExportNames: Set | null = null; private declare astContext: AstContext; private readonly context: string; private declare customTransformCache: boolean; - private exportAllModules: (Module | ExternalModule)[] = []; + private readonly exportAllModules: (Module | ExternalModule)[] = []; private exportNamesByVariable: Map | null = null; - private exportShimVariable: ExportShimVariable = new ExportShimVariable(this); + private readonly exportShimVariable: ExportShimVariable = new ExportShimVariable(this); private declare magicString: MagicString; private namespaceReexportsByName: Record = Object.create(null); private relevantDependencies: Set | null = null; - private syntheticExports = new Map(); + private readonly syntheticExports = new Map(); private syntheticNamespace: Variable | null | undefined = null; private transformDependencies: string[] = []; private transitiveReexports: string[] | null = null; @@ -320,7 +319,7 @@ export default class Module { return this.allExportNames; } const allExportNames = (this.allExportNames = new Set()); - for (const name of Object.keys(this.exports)) { + for (const name of this.getExports()) { allExportNames.add(name); } for (const name of Object.keys(this.reexportDescriptions)) { @@ -345,15 +344,14 @@ export default class Module { const relevantDependencies = new Set(); const necessaryDependencies = new Set(); const alwaysCheckedDependencies = new Set(); + const dependencyVariables = new Set(this.imports); - let dependencyVariables: Set | IterableIterator = this.imports.keys(); if ( this.info.isEntry || this.includedDynamicImporters.length > 0 || this.namespace.included || this.implicitlyLoadedAfter.size > 0 ) { - dependencyVariables = new Set(dependencyVariables); for (const exportName of [...this.getReexports(), ...this.getExports()]) { const exportedVariable = this.getVariableForExportName(exportName); if (exportedVariable) { @@ -396,7 +394,7 @@ export default class Module { if (this.exportNamesByVariable) { return this.exportNamesByVariable; } - const exportNamesByVariable: Map = new Map(); + const exportNamesByVariable = new Map(); for (const exportName of this.getAllExportNames()) { if (exportName === this.info.syntheticNamedExports) continue; let tracedVariable = this.getVariableForExportName(exportName); @@ -863,7 +861,7 @@ export default class Module { private addExport( node: ExportAllDeclaration | ExportNamedDeclaration | ExportDefaultDeclaration - ) { + ): void { if (node instanceof ExportDefaultDeclaration) { // export default foo; @@ -931,18 +929,14 @@ export default class Module { } } - private addImport(node: ImportDeclaration) { + private addImport(node: ImportDeclaration): void { const source = node.source.value; this.sources.add(source); for (const specifier of node.specifiers) { const isDefault = specifier.type === NodeType.ImportDefaultSpecifier; const isNamespace = specifier.type === NodeType.ImportNamespaceSpecifier; - const name = isDefault - ? 'default' - : isNamespace - ? '*' - : (specifier as ImportSpecifier).imported.name; + const name = isDefault ? 'default' : isNamespace ? '*' : specifier.imported.name; this.importDescriptions[specifier.local.name] = { module: null as never, // filled in later name, @@ -952,7 +946,7 @@ export default class Module { } } - private addImportMeta(node: MetaProperty) { + private addImportMeta(node: MetaProperty): void { this.importMetas.push(node); } @@ -985,7 +979,7 @@ export default class Module { private addModulesToImportDescriptions(importDescription: { [name: string]: ImportDescription | ReexportDescription; - }) { + }): void { for (const specifier of Object.values(importDescription)) { const id = this.resolvedIds[specifier.source].id; specifier.module = this.graph.modulesById.get(id)!; @@ -996,7 +990,7 @@ export default class Module { relevantDependencies: Set, necessaryDependencies: Set, alwaysCheckedDependencies: Set - ) { + ): void { const handledDependencies = new Set(); const addSideEffectDependencies = (possibleDependencies: Set) => { @@ -1109,7 +1103,7 @@ export default class Module { return [...syntheticNamespaces, ...externalNamespaces]; } - private includeDynamicImport(node: ImportExpression) { + private includeDynamicImport(node: ImportExpression): void { const resolution = ( this.dynamicImports.find(dynamicImport => dynamicImport.node === node) as { resolution: string | Module | ExternalModule | undefined; @@ -1121,12 +1115,12 @@ export default class Module { } } - private includeVariable(variable: Variable) { + private includeVariable(variable: Variable): void { if (!variable.included) { variable.include(); this.graph.needsTreeshakingPass = true; const variableModule = variable.module; - if (variableModule && variableModule instanceof Module) { + if (variableModule instanceof Module) { if (!variableModule.isExecuted) { markModuleAndImpureDependenciesAsExecuted(variableModule); } @@ -1142,7 +1136,7 @@ export default class Module { } } - private includeVariableInModule(variable: Variable) { + private includeVariableInModule(variable: Variable): void { this.includeVariable(variable); const variableModule = variable.module; if (variableModule && variableModule !== this) { @@ -1164,7 +1158,11 @@ export default class Module { // if there is a cyclic import in the reexport chain, we should not // import from the original module but from the cyclic module to not // mess up execution order. -function setAlternativeExporterIfCyclic(variable: Variable, importer: Module, reexporter: Module) { +function setAlternativeExporterIfCyclic( + variable: Variable, + importer: Module, + reexporter: Module +): void { if (variable.module instanceof Module && variable.module !== reexporter) { const exporterCycles = variable.module.cycles; if (exporterCycles.size > 0) { diff --git a/src/ModuleLoader.ts b/src/ModuleLoader.ts index 515ebf233ad..ede3546b14f 100644 --- a/src/ModuleLoader.ts +++ b/src/ModuleLoader.ts @@ -2,18 +2,18 @@ import * as acorn from 'acorn'; import ExternalModule from './ExternalModule'; import Graph from './Graph'; import Module, { DynamicImport } from './Module'; -import { +import type { CustomPluginOptions, EmittedChunk, HasModuleSideEffects, + LoadResult, ModuleInfo, ModuleOptions, NormalizedInputOptions, PartialNull, Plugin, ResolvedId, - ResolveIdResult, - SourceDescription + ResolveIdResult } from './rollup/types'; import { PluginDriver } from './utils/PluginDriver'; import { EMPTY_OBJECT } from './utils/blank'; @@ -66,10 +66,10 @@ export class ModuleLoader { private readonly implicitEntryModules = new Set(); private readonly indexedEntryModules: { index: number; module: Module }[] = []; private latestLoadModulesPromise: Promise = Promise.resolve(); - private moduleLoadPromises = new Map(); - private modulesWithLoadedDependencies = new Set(); + private readonly moduleLoadPromises = new Map(); + private readonly modulesWithLoadedDependencies = new Set(); private nextEntryModuleIndex = 0; - private readQueue = new Queue(); + private readonly readQueue: Queue; constructor( private readonly graph: Graph, @@ -80,10 +80,11 @@ export class ModuleLoader { this.hasModuleSideEffects = options.treeshake ? options.treeshake.moduleSideEffects : () => true; - this.readQueue.maxParallel = options.maxParallelFileReads; + + this.readQueue = new Queue(options.maxParallelFileReads); } - async addAdditionalModules(unresolvedModules: string[]): Promise { + async addAdditionalModules(unresolvedModules: readonly string[]): Promise { const result = this.extendLoadModulesPromise( Promise.all(unresolvedModules.map(id => this.loadEntryModule(id, false, undefined, null))) ); @@ -92,7 +93,7 @@ export class ModuleLoader { } async addEntryModules( - unresolvedEntryModules: UnresolvedModule[], + unresolvedEntryModules: readonly UnresolvedModule[], isUserDefined: boolean ): Promise<{ entryModules: Module[]; @@ -103,8 +104,8 @@ export class ModuleLoader { this.nextEntryModuleIndex += unresolvedEntryModules.length; const newEntryModules = await this.extendLoadModulesPromise( Promise.all( - unresolvedEntryModules.map( - ({ id, importer }): Promise => this.loadEntryModule(id, true, importer, null) + unresolvedEntryModules.map(({ id, importer }) => + this.loadEntryModule(id, true, importer, null) ) ).then(entryModules => { let moduleIndex = firstEntryModuleIndex; @@ -160,10 +161,14 @@ export class ModuleLoader { return module; } - public preloadModule(resolvedId: NormalizedResolveIdWithoutDefaults): Promise { - return this.fetchModule(this.addDefaultsToResolvedId(resolvedId)!, undefined, false, true).then( - module => module.info + public async preloadModule(resolvedId: NormalizedResolveIdWithoutDefaults): Promise { + const module = await this.fetchModule( + this.addDefaultsToResolvedId(resolvedId)!, + undefined, + false, + true ); + return module.info; } resolveId = async ( @@ -171,7 +176,7 @@ export class ModuleLoader { importer: string | undefined, customOptions: CustomPluginOptions | undefined, isEntry: boolean | undefined, - skip: { importer: string | undefined; plugin: Plugin; source: string }[] | null = null + skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null = null ): Promise => { return this.addDefaultsToResolvedId( this.getNormalizedResolvedIdWithoutDefaults( @@ -213,7 +218,7 @@ export class ModuleLoader { private addEntryWithImplicitDependants( unresolvedModule: UnresolvedModule, - implicitlyLoadedAfter: string[] + implicitlyLoadedAfter: readonly string[] ): Promise { return this.extendLoadModulesPromise( this.loadEntryModule(unresolvedModule.id, false, unresolvedModule.importer, null).then( @@ -239,9 +244,13 @@ export class ModuleLoader { ); } - private async addModuleSource(id: string, importer: string | undefined, module: Module) { + private async addModuleSource( + id: string, + importer: string | undefined, + module: Module + ): Promise { timeStart('load modules', 3); - let source: string | SourceDescription; + let source: LoadResult; try { source = await this.readQueue.run( async () => (await this.pluginDriver.hookFirst('load', [id])) ?? (await readFile(id)) @@ -258,7 +267,7 @@ export class ModuleLoader { const sourceDescription = typeof source === 'string' ? { code: source } - : typeof source === 'object' && typeof source.code === 'string' + : source != null && typeof source === 'object' && typeof source.code === 'string' ? source : error(errBadLoader(id)); const cachedModule = this.graph.cachedModules.get(id); @@ -301,7 +310,7 @@ export class ModuleLoader { private async fetchDynamicDependencies( module: Module, - resolveDynamicImportPromises: ResolveDynamicDependencyPromise[] + resolveDynamicImportPromises: readonly ResolveDynamicDependencyPromise[] ): Promise { const dependencies = await Promise.all( resolveDynamicImportPromises.map(resolveDynamicImportPromise => @@ -342,7 +351,7 @@ export class ModuleLoader { return existingModule; } - const module: Module = new Module( + const module = new Module( this.graph, id, this.options, @@ -360,7 +369,7 @@ export class ModuleLoader { ]); const loadAndResolveDependenciesPromise = loadPromise .then(([resolveStaticDependencyPromises, resolveDynamicImportPromises]) => - Promise.all([...resolveStaticDependencyPromises, ...resolveDynamicImportPromises]) + Promise.all([...resolveStaticDependencyPromises, ...resolveDynamicImportPromises]) ) .then(() => this.pluginDriver.hookParallel('moduleParsed', [module.info])); loadAndResolveDependenciesPromise.catch(() => { @@ -376,10 +385,10 @@ export class ModuleLoader { private async fetchModuleDependencies( module: Module, - resolveStaticDependencyPromises: ResolveStaticDependencyPromise[], - resolveDynamicDependencyPromises: ResolveDynamicDependencyPromise[], + resolveStaticDependencyPromises: readonly ResolveStaticDependencyPromise[], + resolveDynamicDependencyPromises: readonly ResolveDynamicDependencyPromise[], loadAndResolveDependenciesPromise: Promise - ) { + ): Promise { if (this.modulesWithLoadedDependencies.has(module)) { return; } @@ -425,7 +434,7 @@ export class ModuleLoader { private async fetchStaticDependencies( module: Module, - resolveStaticDependencyPromises: ResolveStaticDependencyPromise[] + resolveStaticDependencyPromises: readonly ResolveStaticDependencyPromise[] ): Promise { for (const dependency of await Promise.all( resolveStaticDependencyPromises.map(resolveStaticDependencyPromise => @@ -665,7 +674,7 @@ function addChunkNamesToModule( module: Module, { fileName, name }: UnresolvedModule, isUserDefined: boolean -) { +): void { if (fileName !== null) { module.chunkFileNames.add(fileName); } else if (name !== null) { @@ -682,7 +691,7 @@ function isNotAbsoluteExternal( id: string, source: string, makeAbsoluteExternalsRelative: boolean | 'ifRelativeSource' -) { +): boolean { return ( makeAbsoluteExternalsRelative === true || (makeAbsoluteExternalsRelative === 'ifRelativeSource' && isRelative(source)) || diff --git a/src/ast/scopes/ChildScope.ts b/src/ast/scopes/ChildScope.ts index 6b7acfbbf08..96ca9d95c67 100644 --- a/src/ast/scopes/ChildScope.ts +++ b/src/ast/scopes/ChildScope.ts @@ -51,8 +51,8 @@ export default class ChildScope extends Scope { addUsedOutsideNames( usedNames: Set, format: InternalModuleFormat, - exportNamesByVariable: Map, - accessedGlobalsByScope: Map> + exportNamesByVariable: ReadonlyMap, + accessedGlobalsByScope: ReadonlyMap> ): void { for (const variable of this.accessedOutsideVariables.values()) { if (variable.included) { @@ -76,8 +76,8 @@ export default class ChildScope extends Scope { deconflict( format: InternalModuleFormat, - exportNamesByVariable: Map, - accessedGlobalsByScope: Map> + exportNamesByVariable: ReadonlyMap, + accessedGlobalsByScope: ReadonlyMap> ): void { const usedNames = new Set(); this.addUsedOutsideNames(usedNames, format, exportNamesByVariable, accessedGlobalsByScope); diff --git a/src/ast/scopes/ModuleScope.ts b/src/ast/scopes/ModuleScope.ts index ab0c5696211..e6e328a7b89 100644 --- a/src/ast/scopes/ModuleScope.ts +++ b/src/ast/scopes/ModuleScope.ts @@ -33,8 +33,8 @@ export default class ModuleScope extends ChildScope { deconflict( format: InternalModuleFormat, - exportNamesByVariable: Map, - accessedGlobalsByScope: Map> + exportNamesByVariable: ReadonlyMap, + accessedGlobalsByScope: ReadonlyMap> ): void { // all module level variables are already deconflicted when deconflicting the chunk for (const scope of this.children) diff --git a/src/utils/PluginDriver.ts b/src/utils/PluginDriver.ts index 5913fbeed04..1a77fe57851 100644 --- a/src/utils/PluginDriver.ts +++ b/src/utils/PluginDriver.ts @@ -70,24 +70,24 @@ function throwInvalidHookError(hookName: string, pluginName: string) { } export class PluginDriver { - public emitFile: EmitFile; + public readonly emitFile: EmitFile; public finaliseAssets: () => void; public getFileName: (fileReferenceId: string) => string; - public setOutputBundle: ( + public readonly setOutputBundle: ( outputBundle: OutputBundleWithPlaceholders, outputOptions: NormalizedOutputOptions, facadeChunkByModule: Map ) => void; - private fileEmitter: FileEmitter; - private pluginCache: Record | undefined; - private pluginContexts = new Map(); - private plugins: Plugin[]; + private readonly fileEmitter: FileEmitter; + private readonly pluginCache: Record | undefined; + private readonly pluginContexts = new Map(); + private readonly plugins: Plugin[]; constructor( private readonly graph: Graph, private readonly options: NormalizedInputOptions, - userPlugins: Plugin[], + userPlugins: readonly Plugin[], pluginCache: Record | undefined, basePluginDriver?: PluginDriver ) { @@ -121,7 +121,7 @@ export class PluginDriver { } } - public createOutputPluginDriver(plugins: Plugin[]): PluginDriver { + public createOutputPluginDriver(plugins: readonly Plugin[]): PluginDriver { return new PluginDriver(this.graph, this.options, plugins, this.pluginCache, this); } diff --git a/src/utils/collapseSourcemaps.ts b/src/utils/collapseSourcemaps.ts index 784eadf6696..5842126e0e8 100644 --- a/src/utils/collapseSourcemaps.ts +++ b/src/utils/collapseSourcemaps.ts @@ -10,8 +10,8 @@ import { error } from './error'; import { basename, dirname, relative, resolve } from './path'; class Source { - content: string; - filename: string; + readonly content: string; + readonly filename: string; isOriginal = true; constructor(filename: string, content: string) { @@ -32,9 +32,9 @@ interface SourceMapSegmentObject { } class Link { - mappings: SourceMapSegment[][]; - names: string[]; - sources: (Source | Link)[]; + readonly mappings: SourceMapSegment[][]; + readonly names: string[]; + readonly sources: (Source | Link)[]; constructor( map: { mappings: SourceMapSegment[][]; names: string[] }, @@ -176,7 +176,7 @@ function getCollapsedSourcemap( id: string, originalCode: string, originalSourcemap: ExistingDecodedSourceMap | null, - sourcemapChain: DecodedSourceMapOrMissing[], + sourcemapChain: readonly DecodedSourceMapOrMissing[], linkMap: (source: Source | Link, map: DecodedSourceMapOrMissing) => Link ): Source | Link { let source: Source | Link; @@ -200,8 +200,8 @@ function getCollapsedSourcemap( export function collapseSourcemaps( file: string, map: DecodedSourceMap, - modules: Module[], - bundleSourcemapChain: DecodedSourceMapOrMissing[], + modules: readonly Module[], + bundleSourcemapChain: readonly DecodedSourceMapOrMissing[], excludeContent: boolean | undefined, warn: WarningHandler ): SourceMap { @@ -241,7 +241,7 @@ export function collapseSourcemap( id: string, originalCode: string, originalSourcemap: ExistingDecodedSourceMap | null, - sourcemapChain: DecodedSourceMapOrMissing[], + sourcemapChain: readonly DecodedSourceMapOrMissing[], warn: WarningHandler ): ExistingDecodedSourceMap | null { if (!sourcemapChain.length) { diff --git a/src/utils/commondir.ts b/src/utils/commondir.ts index fa3bb0a29e9..95a705d2e4f 100644 --- a/src/utils/commondir.ts +++ b/src/utils/commondir.ts @@ -1,9 +1,9 @@ -import * as path from './path'; +import { dirname } from './path'; // ported from https://github.com/substack/node-commondir -export default function commondir(files: string[]): string { +export default function commondir(files: readonly string[]): string { if (files.length === 0) return '/'; - if (files.length === 1) return path.dirname(files[0]); + if (files.length === 1) return dirname(files[0]); const commonSegments = files.slice(1).reduce((commonSegments, file) => { const pathSegements = file.split(/\/+|\\+/); let i; diff --git a/src/utils/deconflictChunk.ts b/src/utils/deconflictChunk.ts index a344d0ca9f8..d19c41dc2a3 100644 --- a/src/utils/deconflictChunk.ts +++ b/src/utils/deconflictChunk.ts @@ -41,7 +41,7 @@ const DECONFLICT_IMPORTED_VARIABLES_BY_FORMAT: { }; export function deconflictChunk( - modules: Module[], + modules: readonly Module[], dependenciesToBeDeconflicted: DependenciesToBeDeconflicted, imports: Set, usedNames: Set, @@ -51,9 +51,9 @@ export function deconflictChunk( externalLiveBindings: boolean, chunkByModule: Map, syntheticExports: Set, - exportNamesByVariable: Map, - accessedGlobalsByScope: Map>, - includedNamespaces: Set + exportNamesByVariable: ReadonlyMap, + accessedGlobalsByScope: ReadonlyMap>, + includedNamespaces: ReadonlySet ): void { const reversedModules = modules.slice().reverse(); for (const module of reversedModules) { @@ -83,7 +83,7 @@ export function deconflictChunk( function deconflictImportsEsmOrSystem( usedNames: Set, - imports: Set, + imports: ReadonlySet, dependenciesToBeDeconflicted: DependenciesToBeDeconflicted, _interop: GetInterop, preserveModules: boolean, @@ -134,7 +134,7 @@ function deconflictImportsOther( preserveModules: boolean, externalLiveBindings: boolean, chunkByModule: Map -) { +): void { for (const chunkOrExternalModule of dependencies) { chunkOrExternalModule.variableName = getSafeName( chunkOrExternalModule.suggestedVariableName, @@ -206,9 +206,9 @@ function deconflictImportsOther( function deconflictTopLevelVariables( usedNames: Set, - modules: Module[], - includedNamespaces: Set -) { + modules: readonly Module[], + includedNamespaces: ReadonlySet +): void { for (const module of modules) { for (const variable of module.scope.variables.values()) { if ( diff --git a/src/utils/executionOrder.ts b/src/utils/executionOrder.ts index 6d8430dec4f..d966f993ba7 100644 --- a/src/utils/executionOrder.ts +++ b/src/utils/executionOrder.ts @@ -13,7 +13,7 @@ export function sortByExecutionOrder(units: OrderedExecutionUnit[]): void { units.sort(compareExecIndex); } -export function analyseModuleExecution(entryModules: Module[]): { +export function analyseModuleExecution(entryModules: readonly Module[]): { cyclePaths: string[][]; orderedModules: Module[]; } { @@ -72,7 +72,7 @@ function getCyclePath( module: Module, parent: Module, parents: Map -) { +): string[] { const cycleSymbol = Symbol(module.id); const path = [relativeId(module.id)]; let nextModule = parent; diff --git a/src/utils/exportNames.ts b/src/utils/exportNames.ts index 33a59384689..dcb297edc63 100644 --- a/src/utils/exportNames.ts +++ b/src/utils/exportNames.ts @@ -3,7 +3,7 @@ import RESERVED_NAMES from './RESERVED_NAMES'; import { toBase64 } from './base64'; export function assignExportsToMangledNames( - exports: Set, + exports: ReadonlySet, exportsByName: Record, exportNamesByVariable: Map ): void { @@ -26,7 +26,7 @@ export function assignExportsToMangledNames( } export function assignExportsToNames( - exports: Set, + exports: ReadonlySet, exportsByName: Record, exportNamesByVariable: Map ): void { diff --git a/src/utils/getCodeFrame.ts b/src/utils/getCodeFrame.ts index 612f2530fdf..0b1858e8b47 100644 --- a/src/utils/getCodeFrame.ts +++ b/src/utils/getCodeFrame.ts @@ -1,10 +1,10 @@ -function spaces(i: number) { +function spaces(i: number): string { let result = ''; while (i--) result += ' '; return result; } -function tabsToSpaces(str: string) { +function tabsToSpaces(str: string): string { return str.replace(/^\t+/, match => match.split('\t').join(' ')); } diff --git a/src/utils/getExportMode.ts b/src/utils/getExportMode.ts index f5f3a060911..d0854ef9959 100644 --- a/src/utils/getExportMode.ts +++ b/src/utils/getExportMode.ts @@ -10,7 +10,7 @@ import { export default function getExportMode( chunk: Chunk, { exports: exportMode, name, format }: NormalizedOutputOptions, - unsetOptions: Set, + unsetOptions: ReadonlySet, facadeModuleId: string, warn: WarningHandler ): 'default' | 'named' | 'none' { diff --git a/src/utils/getIndentString.ts b/src/utils/getIndentString.ts index 183d708c030..b7cd960ba44 100644 --- a/src/utils/getIndentString.ts +++ b/src/utils/getIndentString.ts @@ -1,6 +1,6 @@ import Module from '../Module'; -function guessIndentString(code: string) { +function guessIndentString(code: string): string | null { const lines = code.split('\n'); const tabbed = lines.filter(line => /^\t+/.test(line)); @@ -27,7 +27,7 @@ function guessIndentString(code: string) { } export default function getIndentString( - modules: Module[], + modules: readonly Module[], options: { indent: true | string } ): string { if (options.indent !== true) return options.indent; diff --git a/src/utils/getOriginalLocation.ts b/src/utils/getOriginalLocation.ts index 692888dd472..fb615b0a397 100644 --- a/src/utils/getOriginalLocation.ts +++ b/src/utils/getOriginalLocation.ts @@ -1,7 +1,7 @@ import { DecodedSourceMapOrMissing, ExistingDecodedSourceMap } from '../rollup/types'; export function getOriginalLocation( - sourcemapChain: DecodedSourceMapOrMissing[], + sourcemapChain: readonly DecodedSourceMapOrMissing[], location: { column: number; line: number; name?: string; source?: string } ): { column: number; line: number } { const filteredSourcemapChain = sourcemapChain.filter( diff --git a/src/utils/getStaticDependencies.ts b/src/utils/getStaticDependencies.ts index 5d3a447c9f5..173206e1a78 100644 --- a/src/utils/getStaticDependencies.ts +++ b/src/utils/getStaticDependencies.ts @@ -4,8 +4,8 @@ import Module from '../Module'; export function getStaticDependencies( chunk: Chunk, - orderedModules: Module[], - chunkByModule: Map + orderedModules: readonly Module[], + chunkByModule: ReadonlyMap ): Set { const staticDependencyBlocks: (Chunk | ExternalModule)[][] = []; const handledDependencies = new Set(); @@ -31,7 +31,7 @@ function addStaticDependencies( staticDependencies: (Chunk | ExternalModule)[], handledModules: Set, chunk: Chunk, - chunkByModule: Map + chunkByModule: ReadonlyMap ): void { const dependencies = module.getDependenciesToBeIncluded(); for (const dependency of dependencies) { diff --git a/src/utils/identifierHelpers.ts b/src/utils/identifierHelpers.ts index b67952d14c7..0dbbde65c74 100644 --- a/src/utils/identifierHelpers.ts +++ b/src/utils/identifierHelpers.ts @@ -2,7 +2,7 @@ import RESERVED_NAMES from './RESERVED_NAMES'; const illegalCharacters = /[^$_a-zA-Z0-9]/g; -const startsWithDigit = (str: string) => /\d/.test(str[0]); +const startsWithDigit = (str: string): boolean => /\d/.test(str[0]); export function isLegal(str: string): boolean { if (startsWithDigit(str) || RESERVED_NAMES.has(str)) { diff --git a/src/utils/pluginUtils.ts b/src/utils/pluginUtils.ts index af027c59f01..1bac7a304c5 100644 --- a/src/utils/pluginUtils.ts +++ b/src/utils/pluginUtils.ts @@ -28,7 +28,10 @@ export const deprecatedHooks: { active: boolean; deprecated: string; replacement { active: true, deprecated: 'resolveAssetUrl', replacement: 'resolveFileUrl' } ]; -export function warnDeprecatedHooks(plugins: Plugin[], options: NormalizedInputOptions): void { +export function warnDeprecatedHooks( + plugins: readonly Plugin[], + options: NormalizedInputOptions +): void { for (const { active, deprecated, replacement } of deprecatedHooks) { for (const plugin of plugins) { if (deprecated in plugin) { diff --git a/src/utils/printStringList.ts b/src/utils/printStringList.ts index 19c80ce5e60..80445ebb8fa 100644 --- a/src/utils/printStringList.ts +++ b/src/utils/printStringList.ts @@ -1,4 +1,7 @@ -export function printQuotedStringList(list: string[], verbs?: [string, string]): string { +export function printQuotedStringList( + list: readonly string[], + verbs?: readonly [string, string] +): string { const isSingleItem = list.length <= 1; const quotedList = list.map(item => `"${item}"`); let output = isSingleItem diff --git a/src/utils/pureComments.ts b/src/utils/pureComments.ts index 29cefc6faaa..2177df22e4e 100644 --- a/src/utils/pureComments.ts +++ b/src/utils/pureComments.ts @@ -13,7 +13,7 @@ import { import { SOURCEMAPPING_URL_RE } from './sourceMappingURL'; // patch up acorn-walk until class-fields are officially supported -basicWalker.PropertyDefinition = function (node: any, st: any, c: any) { +basicWalker.PropertyDefinition = function (node: any, st: any, c: any): void { if (node.computed) { c(node.key, st, 'Expression'); } @@ -40,7 +40,7 @@ function handlePureAnnotationsOfNode( node: acorn.Node, state: CommentState, type: string = node.type -) { +): void { const { annotations } = state; let comment = annotations[state.annotationIndex]; while (comment && node.start >= comment.end) { @@ -59,7 +59,7 @@ function handlePureAnnotationsOfNode( const neitherWithespaceNorBrackets = /[^\s(]/g; const noWhitespace = /\S/g; -function markPureNode(node: NodeWithComments, comment: acorn.Comment, code: string) { +function markPureNode(node: NodeWithComments, comment: acorn.Comment, code: string): void { const annotatedNodes = []; let invalidAnnotation: boolean | undefined; const codeInBetween = code.slice(comment.end, node.start); @@ -162,7 +162,7 @@ export function addAnnotations( }); } -function annotateNode(node: NodeWithComments, comment: acorn.Comment, valid: boolean) { +function annotateNode(node: NodeWithComments, comment: acorn.Comment, valid: boolean): void { const key = valid ? ANNOTATION_KEY : INVALID_COMMENT_KEY; const property = node[key]; if (property) { diff --git a/src/utils/queue.ts b/src/utils/queue.ts index 8be2d4826d8..18e056ee72d 100644 --- a/src/utils/queue.ts +++ b/src/utils/queue.ts @@ -1,25 +1,31 @@ -export class Queue { - private queue = new Array<{ - reject: (reason?: any) => void; - resolve: (value: any) => void; - task: () => any; - }>(); +interface Task { + (): T | Promise; +} + +interface QueueItem { + reject: (reason?: unknown) => void; + resolve: (value: T) => void; + task: Task; +} + +export class Queue { + private readonly queue: QueueItem[] = []; private workerCount = 0; - constructor(public maxParallel = 1) {} + constructor(private maxParallel: number) {} - run(task: () => T | Promise): Promise { + run(task: Task): Promise { return new Promise((resolve, reject) => { this.queue.push({ reject, resolve, task }); this.work(); }); } - private async work() { + private async work(): Promise { if (this.workerCount >= this.maxParallel) return; this.workerCount++; - let entry; + let entry: QueueItem | undefined; while ((entry = this.queue.shift())) { const { reject, resolve, task } = entry; diff --git a/src/utils/reassignedExportsMember.ts b/src/utils/reassignedExportsMember.ts index c51081a5bc4..c7ac51146f1 100644 --- a/src/utils/reassignedExportsMember.ts +++ b/src/utils/reassignedExportsMember.ts @@ -2,7 +2,7 @@ import Variable from '../ast/variables/Variable'; export function isReassignedExportsMember( variable: Variable, - exportNamesByVariable: Map + exportNamesByVariable: ReadonlyMap ): boolean { return ( variable.renderBaseName !== null && exportNamesByVariable.has(variable) && variable.isReassigned diff --git a/src/utils/renderHelpers.ts b/src/utils/renderHelpers.ts index 14ed47f8f90..6e9c6c2d209 100644 --- a/src/utils/renderHelpers.ts +++ b/src/utils/renderHelpers.ts @@ -89,7 +89,7 @@ function findFirstLineBreakOutsideComment(code: string): [number, number] { } export function renderStatementList( - statements: StatementNode[], + statements: readonly StatementNode[], code: MagicString, start: number, end: number, @@ -134,7 +134,7 @@ export function renderStatementList( // This assumes that the first character is not part of the first node export function getCommaSeparatedNodesWithBoundaries( - nodes: N[], + nodes: readonly N[], code: MagicString, start: number, end: number diff --git a/src/utils/resolveId.ts b/src/utils/resolveId.ts index c6a908f926f..221cab3f79b 100644 --- a/src/utils/resolveId.ts +++ b/src/utils/resolveId.ts @@ -14,9 +14,9 @@ export async function resolveId( importer: string | undefined, customOptions: CustomPluginOptions | undefined, isEntry: boolean | undefined, - skip: { importer: string | undefined; plugin: Plugin; source: string }[] | null + skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null ) => Promise, - skip: { importer: string | undefined; plugin: Plugin; source: string }[] | null, + skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null, customOptions: CustomPluginOptions | undefined, isEntry: boolean ): Promise { diff --git a/src/utils/resolveIdViaPlugins.ts b/src/utils/resolveIdViaPlugins.ts index 23a14a332d6..20d177e6bc2 100644 --- a/src/utils/resolveIdViaPlugins.ts +++ b/src/utils/resolveIdViaPlugins.ts @@ -17,9 +17,9 @@ export function resolveIdViaPlugins( importer: string | undefined, customOptions: CustomPluginOptions | undefined, isEntry: boolean | undefined, - skip: { importer: string | undefined; plugin: Plugin; source: string }[] | null + skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null ) => Promise, - skip: { importer: string | undefined; plugin: Plugin; source: string }[] | null, + skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null, customOptions: CustomPluginOptions | undefined, isEntry: boolean ): Promise { diff --git a/src/utils/systemJsRendering.ts b/src/utils/systemJsRendering.ts index e5e29f479c6..6d8e9fed0a9 100644 --- a/src/utils/systemJsRendering.ts +++ b/src/utils/systemJsRendering.ts @@ -3,7 +3,7 @@ import Variable from '../ast/variables/Variable'; import { RenderOptions } from './renderHelpers'; export function getSystemExportStatement( - exportedVariables: Variable[], + exportedVariables: readonly Variable[], { exportNamesByVariable, snippets: { _, getObject, getPropertyAccess } }: RenderOptions, modifier = '' ): string { @@ -41,7 +41,7 @@ export function renderSystemExportExpression( } export function renderSystemExportFunction( - exportedVariables: Variable[], + exportedVariables: readonly Variable[], expressionStart: number, expressionEnd: number, needsParens: boolean | undefined, diff --git a/src/utils/timers.ts b/src/utils/timers.ts index cc5c02bb128..6ecb47e94bb 100644 --- a/src/utils/timers.ts +++ b/src/utils/timers.ts @@ -20,9 +20,9 @@ let getElapsedTime: (previous: StartTime) => number = () => 0; let getMemory: () => number = () => 0; let timers: Timers = {}; -const normalizeHrTime = (time: [number, number]) => time[0] * 1e3 + time[1] / 1e6; +const normalizeHrTime = (time: [number, number]): number => time[0] * 1e3 + time[1] / 1e6; -function setTimeHelpers() { +function setTimeHelpers(): void { if (typeof process !== 'undefined' && typeof process.hrtime === 'function') { getStartTime = process.hrtime.bind(process); getElapsedTime = previous => normalizeHrTime(process.hrtime(previous)); @@ -35,7 +35,7 @@ function setTimeHelpers() { } } -function getPersistedLabel(label: string, level: number) { +function getPersistedLabel(label: string, level: number): string { switch (level) { case 1: return `# ${label}`; @@ -48,7 +48,7 @@ function getPersistedLabel(label: string, level: number) { } } -function timeStartImpl(label: string, level = 3) { +function timeStartImpl(label: string, level = 3): void { label = getPersistedLabel(label, level); if (!timers.hasOwnProperty(label)) { timers[label] = { @@ -64,7 +64,7 @@ function timeStartImpl(label: string, level = 3) { timers[label].startMemory = currentMemory; } -function timeEndImpl(label: string, level = 3) { +function timeEndImpl(label: string, level = 3): void { label = getPersistedLabel(label, level); if (timers.hasOwnProperty(label)) { const currentMemory = getMemory(); diff --git a/src/watch/fileWatcher.ts b/src/watch/fileWatcher.ts index d774b74b04d..f586ae7f1f5 100644 --- a/src/watch/fileWatcher.ts +++ b/src/watch/fileWatcher.ts @@ -1,13 +1,13 @@ import { platform } from 'os'; -import chokidar, { FSWatcher } from 'chokidar'; -import { ChangeEvent, ChokidarOptions } from '../rollup/types'; -import { Task } from './watch'; +import chokidar, { type FSWatcher } from 'chokidar'; +import type { ChangeEvent, ChokidarOptions } from '../rollup/types'; +import type { Task } from './watch'; export class FileWatcher { - private chokidarOptions: ChokidarOptions; - private task: Task; - private transformWatchers = new Map(); - private watcher: FSWatcher; + private readonly chokidarOptions: ChokidarOptions; + private readonly task: Task; + private readonly transformWatchers = new Map(); + private readonly watcher: FSWatcher; constructor(task: Task, chokidarOptions: ChokidarOptions) { this.chokidarOptions = chokidarOptions; @@ -33,7 +33,7 @@ export class FileWatcher { watch(id: string, isTransformDependency: boolean): void { if (isTransformDependency) { - const watcher = this.transformWatchers.get(id) || this.createWatcher(id); + const watcher = this.transformWatchers.get(id) ?? this.createWatcher(id); watcher.add(id); this.transformWatchers.set(id, watcher); } else { diff --git a/src/watch/watch.ts b/src/watch/watch.ts index 9ed301f729e..134124cc7b1 100644 --- a/src/watch/watch.ts +++ b/src/watch/watch.ts @@ -1,4 +1,4 @@ -import * as path from 'path'; +import { resolve } from 'path'; import { createFilter } from '@rollup/pluginutils'; import { rollupInternal } from '../rollup/rollup'; import { @@ -33,16 +33,16 @@ const eventsRewrites: Record(); + private readonly invalidatedIds = new Map(); private rerun = false; private running = true; - private tasks: Task[]; + private readonly tasks: Task[]; - constructor(configs: GenericConfigObject[], emitter: RollupWatcher) { + constructor(configs: readonly GenericConfigObject[], emitter: RollupWatcher) { this.emitter = emitter; emitter.close = this.close.bind(this); this.tasks = configs.map(config => new Task(this, config)); @@ -97,7 +97,7 @@ export class Watcher { }, this.buildDelay); } - private async run() { + private async run(): Promise { this.running = true; this.emitter.emit('event', { code: 'START' @@ -123,15 +123,15 @@ export class Task { watchFiles: string[] = []; private closed = false; - private fileWatcher: FileWatcher; + private readonly fileWatcher: FileWatcher; private filter: (id: string) => boolean; private invalidated = true; - private options: MergedRollupOptions; - private outputFiles: string[]; - private outputs: OutputOptions[]; + private readonly options: MergedRollupOptions; + private readonly outputFiles: string[]; + private readonly outputs: OutputOptions[]; private skipWrite: boolean; private watched = new Set(); - private watcher: Watcher; + private readonly watcher: Watcher; constructor(watcher: Watcher, config: GenericConfigObject) { this.watcher = watcher; @@ -140,7 +140,7 @@ export class Task { this.options = mergeOptions(config); this.outputs = this.options.output; this.outputFiles = this.outputs.map(output => { - if (output.file || output.dir) return path.resolve(output.file || output.dir!); + if (output.file || output.dir) return resolve(output.file || output.dir!); return undefined as never; });