refactor: prefer native methods to lodash where possible (#328)

- _.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
This commit is contained in:
Anton Gilgur 2022-05-16 18:30:48 -04:00 committed by GitHub
parent 529c8530f5
commit 197061bf92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 51 additions and 74 deletions

View File

@ -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)}`);

View File

@ -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;

View File

@ -46,9 +46,18 @@ const typescript: PluginImpl<RPT2Options> = (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<RPT2Options> = (options) =>
tsconfigDefaults: {},
objectHashIgnoreUnknownHack: false,
cwd: process.cwd(),
});
}, options as IOptions);
if (!pluginOptions.typescript) {
pluginOptions.typescript = require("typescript");
@ -87,7 +96,7 @@ const typescript: PluginImpl<RPT2Options> = (options) =>
if (generateRound === 0)
{
parsedConfig.fileNames.forEach((fileName) => { allImportedFiles.add(fileName); });
parsedConfig.fileNames.map(allImportedFiles.add, allImportedFiles);
context.info(`typescript version: ${tsModule.version}`);
context.info(`tslib version: ${tslibVersion}`);
@ -151,7 +160,7 @@ const typescript: PluginImpl<RPT2Options> = (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<RPT2Options> = (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<RPT2Options> = (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<RPT2Options> = (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<RPT2Options> = (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<RPT2Options> = (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);
});

View File

@ -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)
{

View File

@ -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;

View File

@ -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<IDiagnostics[]>, 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;