From 99b88030854087ff3f0cbc79c4c5f3ab61dbeae2 Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Sat, 14 May 2022 12:05:15 -0400 Subject: [PATCH] refactor: prefer native methods to lodash where possible - _.endsWith -> String.endsWith - _.concat -> Array.concat - _.each -> Array.forEach - _.filter -> Array.filter - _.map -> Array.map - _.some -> Array.some - _.has -> `key in Object` - _.defaults -> Object.assign - _.get -> `?.` and `??` (optional chaining and nullish coalescing) - refactor: replace fairly complicated `expandIncludeWithDirs` func to just use a few simple `forEach`s - not as FP anymore, more imperative, but much simpler to read IMO - refactor: add a `getDiagnostics` helper to DRY up some code - also aids readability IMO - a few places are still using lodash, but this paves the way toward removing it or replacing it with much smaller individual deps - _.compact still used because Array.filter heavily complicates the type-checking currently - _.isFunction still used because while it's a one-liner natively, need to import a function in several places - also the package `lodash.isFunction` is lodash v3 and quite different from the v4 implementation, so couldn't replace with it unfortunately - _.merge is a deep merge, so there's no native version of this - but we may remove deep merges entirely in the future (as tsconfig doesn't quite perform a deep merge), or could replace this with a smaller `lodash.merge` package or similar - see also https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore --- src/get-options-overrides.ts | 25 +++++++-------- src/host.ts | 9 +++--- src/index.ts | 61 +++++++++++++----------------------- src/parse-tsconfig.ts | 2 +- src/print-diagnostics.ts | 3 +- src/tscache.ts | 25 ++++++++------- 6 files changed, 51 insertions(+), 74 deletions(-) diff --git a/src/get-options-overrides.ts b/src/get-options-overrides.ts index a12f7090..56574a25 100644 --- a/src/get-options-overrides.ts +++ b/src/get-options-overrides.ts @@ -2,7 +2,6 @@ import { createFilter as createRollupFilter} from "@rollup/pluginutils"; import { tsModule } from "./tsproxy"; import * as tsTypes from "typescript"; import { IOptions } from "./ioptions"; -import * as _ from "lodash"; import { join } from "path"; import { IContext } from "./context"; @@ -39,17 +38,15 @@ export function getOptionsOverrides({ useTsconfigDeclarationDir, cacheRoot }: IO function expandIncludeWithDirs(include: string | string[], dirs: string[]) { - return _ - .chain(dirs) - .flatMap((root) => - { - if (include instanceof Array) - return include.map((x) => join(root, x)); - else - return join(root, include); - }) - .uniq() - .value(); + const newDirs: string[] = []; + + dirs.forEach(root => { + if (include instanceof Array) + include.forEach(x => newDirs.push(join(root, x))); + else + newDirs.push(join(root, include)); + }); + return newDirs; } export function createFilter(context: IContext, pluginOptions: IOptions, parsedConfig: tsTypes.ParsedCommandLine) @@ -65,8 +62,8 @@ export function createFilter(context: IContext, pluginOptions: IOptions, parsedC if (parsedConfig.projectReferences) { - included = _.concat(included, expandIncludeWithDirs(included, parsedConfig.projectReferences.map((x) => x.path))); - excluded = _.concat(excluded, expandIncludeWithDirs(excluded, parsedConfig.projectReferences.map((x) => x.path))); + included = expandIncludeWithDirs(included, parsedConfig.projectReferences.map((x) => x.path)).concat(included); + excluded = expandIncludeWithDirs(excluded, parsedConfig.projectReferences.map((x) => x.path)).concat(excluded); } context.debug(() => `included:\n${JSON.stringify(included, undefined, 4)}`); diff --git a/src/host.ts b/src/host.ts index 0ee68e19..6c1d4c55 100644 --- a/src/host.ts +++ b/src/host.ts @@ -1,6 +1,5 @@ import { tsModule } from "./tsproxy"; import * as tsTypes from "typescript"; -import * as _ from "lodash"; import { normalizePath as normalize } from "@rollup/pluginutils"; import { TransformerFactoryCreator } from "./ioptions"; @@ -44,7 +43,7 @@ export class LanguageServiceHost implements tsTypes.LanguageServiceHost { fileName = normalize(fileName); - if (_.has(this.snapshots, fileName)) + if (fileName in this.snapshots) return this.snapshots[fileName]; const source = tsModule.sys.readFile(fileName); @@ -136,11 +135,11 @@ export class LanguageServiceHost implements tsTypes.LanguageServiceHost { const factory = creator(this.service); if (factory.before) - transformer.before = _.concat(transformer.before!, factory.before); + transformer.before = transformer.before!.concat(factory.before); if (factory.after) - transformer.after = _.concat(transformer.after!, factory.after); + transformer.after = transformer.after!.concat(factory.after); if (factory.afterDeclarations) - transformer.afterDeclarations = _.concat(transformer.afterDeclarations!, factory.afterDeclarations); + transformer.afterDeclarations = transformer.afterDeclarations!.concat(factory.afterDeclarations); } return transformer; diff --git a/src/index.ts b/src/index.ts index 57eb9f96..574c2569 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,9 +46,18 @@ const typescript: PluginImpl = (options) => return _cache; }; - const pluginOptions = { ...options } as IOptions; + const getDiagnostics = (id: string, snapshot: tsTypes.IScriptSnapshot) => + { + return cache().getSyntacticDiagnostics(id, snapshot, () => + { + return service.getSyntacticDiagnostics(id); + }).concat(cache().getSemanticDiagnostics(id, snapshot, () => + { + return service.getSemanticDiagnostics(id); + })); + } - _.defaults(pluginOptions, + const pluginOptions: IOptions = Object.assign({}, { check: true, verbosity: VerbosityLevel.Warning, @@ -65,7 +74,7 @@ const typescript: PluginImpl = (options) => tsconfigDefaults: {}, objectHashIgnoreUnknownHack: false, cwd: process.cwd(), - }); + }, options as IOptions); if (!pluginOptions.typescript) { pluginOptions.typescript = require("typescript"); @@ -87,7 +96,7 @@ const typescript: PluginImpl = (options) => if (generateRound === 0) { - parsedConfig.fileNames.forEach((fileName) => { allImportedFiles.add(fileName); }); + parsedConfig.fileNames.map(allImportedFiles.add); context.info(`typescript version: ${tsModule.version}`); context.info(`tslib version: ${tslibVersion}`); @@ -151,7 +160,7 @@ const typescript: PluginImpl = (options) => if (filter(result.resolvedModule.resolvedFileName)) cache().setDependency(result.resolvedModule.resolvedFileName, importer); - if (_.endsWith(result.resolvedModule.resolvedFileName, ".d.ts")) + if (result.resolvedModule.resolvedFileName.endsWith(".d.ts")) return; const resolved = pluginOptions.rollupCommonJSResolveHack @@ -198,16 +207,7 @@ const typescript: PluginImpl = (options) => noErrors = false; // always checking on fatal errors, even if options.check is set to false - const diagnostics = _.concat( - cache().getSyntacticDiagnostics(id, snapshot, () => - { - return service.getSyntacticDiagnostics(id); - }), - cache().getSemanticDiagnostics(id, snapshot, () => - { - return service.getSemanticDiagnostics(id); - }), - ); + const diagnostics = getDiagnostics(id, snapshot); printDiagnostics(contextWrapper, diagnostics, parsedConfig.options.pretty === true); // since no output was generated, aborting compilation @@ -222,17 +222,7 @@ const typescript: PluginImpl = (options) => if (pluginOptions.check) { - const diagnostics = _.concat( - cache().getSyntacticDiagnostics(id, snapshot, () => - { - return service.getSyntacticDiagnostics(id); - }), - cache().getSemanticDiagnostics(id, snapshot, () => - { - return service.getSemanticDiagnostics(id); - }), - ); - + const diagnostics = getDiagnostics(id, snapshot); if (diagnostics.length > 0) noErrors = false; @@ -295,17 +285,7 @@ const typescript: PluginImpl = (options) => if (!snapshot) return; - const diagnostics = _.concat( - cache().getSyntacticDiagnostics(id, snapshot, () => - { - return service.getSyntacticDiagnostics(id); - }), - cache().getSemanticDiagnostics(id, snapshot, () => - { - return service.getSemanticDiagnostics(id); - }), - ); - + const diagnostics = getDiagnostics(id, snapshot); printDiagnostics(context, diagnostics, parsedConfig.options.pretty === true); }); } @@ -323,10 +303,10 @@ const typescript: PluginImpl = (options) => if (!parsedConfig.options.declaration) return; - _.each(parsedConfig.fileNames, (name) => + parsedConfig.fileNames.forEach((name) => { const key = normalize(name); - if (_.has(declarations, key)) + if (key in declarations) return; if (!allImportedFiles.has(key)) { @@ -390,8 +370,9 @@ const typescript: PluginImpl = (options) => } }; - _.each(declarations, ({ type, map }, key) => + Object.keys(declarations).forEach((key) => { + const { type, map } = declarations[key]; emitDeclaration(key, ".d.ts", type); emitDeclaration(key, ".d.ts.map", map); }); diff --git a/src/parse-tsconfig.ts b/src/parse-tsconfig.ts index d0f06de1..f2152a8f 100644 --- a/src/parse-tsconfig.ts +++ b/src/parse-tsconfig.ts @@ -27,7 +27,7 @@ export function parseTsConfig(context: IContext, pluginOptions: IOptions) throw new Error(`rpt2: failed to read '${fileName}'`); const result = tsModule.parseConfigFileTextToJson(fileName, text); - pretty = _.get(result.config, "pretty", pretty); + pretty = result.config?.pretty ?? pretty; if (result.error !== undefined) { diff --git a/src/print-diagnostics.ts b/src/print-diagnostics.ts index bad4480b..9185ca39 100644 --- a/src/print-diagnostics.ts +++ b/src/print-diagnostics.ts @@ -2,11 +2,10 @@ import { tsModule } from "./tsproxy"; import { red, white, yellow } from "colors/safe"; import { IContext } from "./context"; import { IDiagnostics } from "./tscache"; -import * as _ from "lodash"; export function printDiagnostics(context: IContext, diagnostics: IDiagnostics[], pretty: boolean): void { - _.each(diagnostics, (diagnostic) => + diagnostics.forEach((diagnostic) => { let print; let color; diff --git a/src/tscache.ts b/src/tscache.ts index cadb3271..fd069407 100644 --- a/src/tscache.ts +++ b/src/tscache.ts @@ -47,11 +47,11 @@ export function convertEmitOutput(output: tsTypes.EmitOutput, references?: strin output.outputFiles.forEach((e) => { - if (_.endsWith(e.name, ".d.ts")) + if (e.name.endsWith(".d.ts")) out.dts = e; - else if (_.endsWith(e.name, ".d.ts.map")) + else if (e.name.endsWith(".d.ts.map")) out.dtsmap = e; - else if (_.endsWith(e.name, ".map")) + else if (e.name.endsWith(".map")) out.map = e.text; else out.code = e.text; @@ -67,7 +67,7 @@ export function getAllReferences(importer: string, snapshot: tsTypes.IScriptSnap const info = tsModule.preProcessFile(snapshot.getText(0, snapshot.getLength()), true, true); - return _.compact(_.concat(info.referencedFiles, info.importedFiles).map((reference) => + return _.compact(info.referencedFiles.concat(info.importedFiles).map((reference) => { const resolved = tsModule.nodeModuleNameResolver(reference.fileName, importer, options, tsModule.sys); return resolved.resolvedModule ? resolved.resolvedModule.resolvedFileName : undefined; @@ -76,7 +76,7 @@ export function getAllReferences(importer: string, snapshot: tsTypes.IScriptSnap export function convertDiagnostic(type: string, data: tsTypes.Diagnostic[]): IDiagnostics[] { - return _.map(data, (diagnostic) => + return data.map((diagnostic) => { const entry: IDiagnostics = { @@ -131,11 +131,11 @@ export class TsCache this.dependencyTree = new Graph({ directed: true }); this.dependencyTree.setDefaultNodeLabel((_node: string) => ({ dirty: false })); - const automaticTypes = _.map(tsModule.getAutomaticTypeDirectiveNames(options, tsModule.sys), (entry) => tsModule.resolveTypeReferenceDirective(entry, undefined, options, tsModule.sys)) + const automaticTypes = tsModule.getAutomaticTypeDirectiveNames(options, tsModule.sys).map((entry) => tsModule.resolveTypeReferenceDirective(entry, undefined, options, tsModule.sys)) .filter((entry) => entry.resolvedTypeReferenceDirective && entry.resolvedTypeReferenceDirective.resolvedFileName) .map((entry) => entry.resolvedTypeReferenceDirective!.resolvedFileName!); - this.ambientTypes = _.filter(rootFilenames, (file) => _.endsWith(file, ".d.ts")) + this.ambientTypes = rootFilenames.filter(file => file.endsWith(".d.ts")) .concat(automaticTypes) .map((id) => ({ id, snapshot: this.host.getScriptSnapshot(id) })); @@ -180,13 +180,13 @@ export class TsCache if (acyclic) { - _.each(alg.topsort(this.dependencyTree), (id: string) => cb(id)); + alg.topsort(this.dependencyTree).forEach(id => cb(id)); return; } this.context.info(yellow("import tree has cycles")); - _.each(this.dependencyTree.nodes(), (id: string) => cb(id)); + this.dependencyTree.nodes().forEach(id => cb(id)); } public done() @@ -252,7 +252,7 @@ export class TsCache } this.context.debug(blue("Ambient types:")); - const typeNames = _.filter(this.ambientTypes, (snapshot) => snapshot.snapshot !== undefined) + const typeNames = this.ambientTypes.filter((snapshot) => snapshot.snapshot !== undefined) .map((snapshot) => { this.context.debug(` ${snapshot.id}`); @@ -264,7 +264,7 @@ export class TsCache if (this.ambientTypesDirty) this.context.info(yellow("ambient types changed, redoing all semantic diagnostics")); - _.each(typeNames, (name) => this.typesCache.touch(name)); + typeNames.forEach(this.typesCache.touch, this); } private getDiagnostics(type: string, cache: ICache, id: string, snapshot: tsTypes.IScriptSnapshot, check: () => tsTypes.Diagnostic[]): IDiagnostics[] @@ -342,8 +342,9 @@ export class TsCache const dependencies = alg.dijkstra(this.dependencyTree, id); - return _.some(dependencies, (dependency, node) => + return Object.keys(dependencies).some(node => { + const dependency = dependencies[node]; if (!node || dependency.distance === Infinity) return false;