From 6552700ffcdcc5fab6ea3fb1a5007c93788ce5ec Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Sat, 15 Oct 2022 17:00:21 +0100 Subject: [PATCH] fix: improve typescript defs --- lib/Resolver.js | 8 +++- lib/index.js | 106 +++++++++++++++++++++++++++++----------- test/resolve.js | 53 +++++++++++++------- test/simple.js | 6 ++- test/symlink.js | 12 +++-- types.d.ts | 125 +++++++++++++++++++++++++++++++++++++++--------- 6 files changed, 235 insertions(+), 75 deletions(-) diff --git a/lib/Resolver.js b/lib/Resolver.js index 875b435f..dd413350 100644 --- a/lib/Resolver.js +++ b/lib/Resolver.js @@ -17,6 +17,10 @@ const { /** @typedef {import("./ResolverFactory").ResolveOptions} ResolveOptions */ +/** @typedef {Error & {details?: string}} ErrorWithDetail */ + +/** @typedef {(err: ErrorWithDetail|null, res?: string|false, req?: ResolveRequest) => void} ResolveCallback */ + /** * @typedef {Object} FileSystemStats * @property {function(): boolean} isDirectory @@ -254,7 +258,7 @@ class Resolver { * @param {string} path context path * @param {string} request request string * @param {ResolveContext} resolveContext resolve context - * @param {function(Error | null, (string|false)=, ResolveRequest=): void} callback callback function + * @param {ResolveCallback} callback callback function * @returns {void} */ resolve(context, path, request, resolveContext, callback) { @@ -304,7 +308,7 @@ class Resolver { const finishWithoutResolve = log => { /** - * @type {Error & {details?: string}} + * @type {ErrorWithDetail} */ const error = new Error("Can't " + message); error.details = log.join("\n"); diff --git a/lib/index.js b/lib/index.js index 0a6f4372..8fe7b04b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -12,6 +12,7 @@ const ResolverFactory = require("./ResolverFactory"); /** @typedef {import("./PnpPlugin").PnpApiImpl} PnpApi */ /** @typedef {import("./Resolver")} Resolver */ /** @typedef {import("./Resolver").FileSystem} FileSystem */ +/** @typedef {import("./Resolver").ResolveCallback} ResolveCallback */ /** @typedef {import("./Resolver").ResolveContext} ResolveContext */ /** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */ /** @typedef {import("./ResolverFactory").Plugin} Plugin */ @@ -28,19 +29,42 @@ const asyncResolver = ResolverFactory.createResolver({ extensions: [".js", ".json", ".node"], fileSystem: nodeFileSystem }); -function resolve(context, path, request, resolveContext, callback) { - if (typeof context === "string") { - callback = resolveContext; - resolveContext = request; - request = path; - path = context; - context = nodeContext; - } - if (typeof callback !== "function") { - callback = resolveContext; - } - asyncResolver.resolve(context, path, request, resolveContext, callback); -} + +/** + * @type {{ + * (context: object, path: string, request: string, resolveContext: ResolveContext, callback: ResolveCallback): void; + * (context: object, path: string, request: string, callback: ResolveCallback): void; + * (path: string, request: string, resolveContext: ResolveContext, callback: ResolveCallback): void; + * (path: string, request: string, callback: ResolveCallback): void; + * }} + */ +const resolve = + /** + * @param {object|string} context + * @param {string} path + * @param {string|ResolveContext|ResolveCallback} request + * @param {ResolveContext|ResolveCallback=} resolveContext + * @param {ResolveCallback=} callback + */ + (context, path, request, resolveContext, callback) => { + if (typeof context === "string") { + callback = /** @type {ResolveCallback} */ (resolveContext); + resolveContext = /** @type {ResolveContext} */ (request); + request = path; + path = context; + context = nodeContext; + } + if (typeof callback !== "function") { + callback = /** @type {ResolveCallback} */ (resolveContext); + } + asyncResolver.resolve( + context, + path, + /** @type {string} */ (request), + /** @type {ResolveContext} */ (resolveContext), + /** @type {ResolveCallback} */ (callback) + ); + }; const syncResolver = ResolverFactory.createResolver({ conditionNames: ["node"], @@ -48,21 +72,43 @@ const syncResolver = ResolverFactory.createResolver({ useSyncFileSystemCalls: true, fileSystem: nodeFileSystem }); -function resolveSync(context, path, request) { - if (typeof context === "string") { - request = path; - path = context; - context = nodeContext; - } - return syncResolver.resolveSync(context, path, request); -} +/** + * @type {{ + * (context: object, path: string, request: string): string|false; + * (path: string, request: string): string|false; + * }} + */ +const resolveSync = + /** + * @param {object|string} context + * @param {string} path + * @param {string=} request + */ + (context, path, request) => { + if (typeof context === "string") { + request = path; + path = context; + context = nodeContext; + } + return syncResolver.resolveSync( + context, + path, + /** @type {string} */ (request) + ); + }; + +/** @typedef {Omit & Partial>} ResolveOptionsOptionalFS */ + +/** + * @param {ResolveOptionsOptionalFS} options Resolver options + * @returns {typeof resolve} Resolver function + */ function create(options) { - options = { + const resolver = ResolverFactory.createResolver({ fileSystem: nodeFileSystem, ...options - }; - const resolver = ResolverFactory.createResolver(options); + }); return function (context, path, request, resolveContext, callback) { if (typeof context === "string") { callback = resolveContext; @@ -78,13 +124,19 @@ function create(options) { }; } +/** + * @param {ResolveOptionsOptionalFS} options Resolver options + * @returns {{ + * (context: object, path: string, request: string): string|false; + * (path: string, request: string): string|false; + * }} Resolver function + */ function createSync(options) { - options = { + const resolver = ResolverFactory.createResolver({ useSyncFileSystemCalls: true, fileSystem: nodeFileSystem, ...options - }; - const resolver = ResolverFactory.createResolver(options); + }); return function (context, path, request) { if (typeof context === "string") { request = path; diff --git a/test/resolve.js b/test/resolve.js index 7a04de96..055bad4d 100644 --- a/test/resolve.js +++ b/test/resolve.js @@ -35,7 +35,7 @@ function testResolve(name, context, moduleName, result) { resolve(context, moduleName, function (err, filename) { if (err) return done(err); should.exist(filename); - filename.should.equal(result); + /** @type {string} */ (filename).should.equal(result); done(); }); }); @@ -48,7 +48,7 @@ function testResolveContext(name, context, moduleName, result) { asyncContextResolve(context, moduleName, function (err, filename) { if (err) done(err); should.exist(filename); - filename.should.equal(result); + /** @type {string} */ (filename).should.equal(result); done(); }); }); @@ -269,7 +269,7 @@ describe("resolve", function () { function (err, filename) { if (err) done(err); should.exist(filename); - filename.should.equal( + /** @type {string} */ (filename).should.equal( path.resolve(issue238, "./src/common/config/myObjectFile.js") ); done(); @@ -281,7 +281,9 @@ describe("resolve", function () { preferRelativeResolve(fixtures, "main1.js", function (err, filename) { if (err) done(err); should.exist(filename); - filename.should.equal(path.join(fixtures, "main1.js")); + /** @type {string} */ (filename).should.equal( + path.join(fixtures, "main1.js") + ); done(); }); }); @@ -290,29 +292,46 @@ describe("resolve", function () { preferRelativeResolve(fixtures, "m1/a.js", function (err, filename) { if (err) done(err); should.exist(filename); - filename.should.equal(path.join(fixtures, "node_modules", "m1", "a.js")); + /** @type {string} */ (filename).should.equal( + path.join(fixtures, "node_modules", "m1", "a.js") + ); done(); }); }); it("should not crash when passing undefined as path", done => { - resolve(fixtures, undefined, err => { - err.should.be.instanceof(Error); - done(); - }); + resolve( + fixtures, + /** @type {string} */ (/** @type {unknown} */ (undefined)), + err => { + /** @type {Error} */ (err).should.be.instanceof(Error); + done(); + } + ); }); it("should not crash when passing undefined as context", done => { - resolve({}, undefined, "./test/resolve.js", err => { - err.should.be.instanceof(Error); - done(); - }); + resolve( + {}, + /** @type {string} */ (/** @type {unknown} */ (undefined)), + "./test/resolve.js", + err => { + /** @type {Error} */ (err).should.be.instanceof(Error); + done(); + } + ); }); it("should not crash when passing undefined everywhere", done => { - resolve(undefined, undefined, undefined, undefined, err => { - err.should.be.instanceof(Error); - done(); - }); + resolve( + /** @type {object} */ (undefined), + /** @type {string} */ (/** @type {unknown} */ (undefined)), + /** @type {string} */ (/** @type {unknown} */ (undefined)), + /** @type {object} */ (/** @type {unknown} */ (undefined)), + err => { + /** @type {Error} */ (err).should.be.instanceof(Error); + done(); + } + ); }); }); diff --git a/test/simple.js b/test/simple.js index a2d8f541..4ea0de0b 100644 --- a/test/simple.js +++ b/test/simple.js @@ -22,8 +22,10 @@ describe("simple", function () { new Error([err.message, err.stack, err.details].join("\n")) ); should.exist(filename); - filename.should.have.type("string"); - filename.should.be.eql(path.join(__dirname, "..", "lib", "index.js")); + /** @type {string} */ (filename).should.have.type("string"); + /** @type {string} */ (filename).should.be.eql( + path.join(__dirname, "..", "lib", "index.js") + ); done(); }); }); diff --git a/test/symlink.js b/test/symlink.js index 278028a3..ad451c26 100644 --- a/test/symlink.js +++ b/test/symlink.js @@ -214,15 +214,19 @@ describe("symlink", function () { resolve(pathToIt[0], pathToIt[1], function (err, filename) { if (err) return done(err); should.exist(filename); - filename.should.have.type("string"); - filename.should.be.eql(path.join(__dirname, "..", "lib", "index.js")); + /** @type {string} */ (filename).should.have.type("string"); + /** @type {string} */ (filename).should.be.eql( + path.join(__dirname, "..", "lib", "index.js") + ); resolveWithoutSymlinks(pathToIt[0], pathToIt[1], function ( err, filename ) { if (err) return done(err); - filename.should.have.type("string"); - filename.should.be.eql(path.resolve(pathToIt[0], pathToIt[1])); + /** @type {string} */ (filename).should.have.type("string"); + /** @type {string} */ (filename).should.be.eql( + path.resolve(pathToIt[0], pathToIt[1]) + ); done(); }); }); diff --git a/types.d.ts b/types.d.ts index 01436533..49f72295 100644 --- a/types.d.ts +++ b/types.d.ts @@ -91,6 +91,7 @@ declare class CloneBasenamePlugin { target: any; apply(resolver: Resolver): void; } +type ErrorWithDetail = Error & { details?: string }; declare interface ExtensionAliasOption { alias: string | string[]; extension: string; @@ -250,6 +251,8 @@ declare interface ResolveOptions { preferRelative: boolean; preferAbsolute: boolean; } +type ResolveOptionsOptionalFS = Omit & + Partial>; type ResolveRequest = BaseResolveRequest & Partial; declare abstract class Resolver { fileSystem: FileSystem; @@ -300,9 +303,9 @@ declare abstract class Resolver { request: string, resolveContext: ResolveContext, callback: ( - arg0: null | Error, - arg1?: string | false, - arg2?: ResolveRequest + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest ) => void ): void; doResolve( @@ -468,31 +471,101 @@ declare interface WriteOnlySet { add: (T?: any) => void; } declare function exports( - context?: any, - path?: any, - request?: any, - resolveContext?: any, - callback?: any + context: object, + path: string, + request: string, + resolveContext: ResolveContext, + callback: ( + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest + ) => void +): void; +declare function exports( + context: object, + path: string, + request: string, + callback: ( + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest + ) => void +): void; +declare function exports( + path: string, + request: string, + resolveContext: ResolveContext, + callback: ( + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest + ) => void +): void; +declare function exports( + path: string, + request: string, + callback: ( + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest + ) => void ): void; declare namespace exports { - export const sync: ( - context?: any, - path?: any, - request?: any - ) => string | false; + export const sync: { + (context: object, path: string, request: string): string | false; + (path: string, request: string): string | false; + }; export function create( - options?: any - ): ( - context?: any, - path?: any, - request?: any, - resolveContext?: any, - callback?: any - ) => void; + options: ResolveOptionsOptionalFS + ): { + ( + context: object, + path: string, + request: string, + resolveContext: ResolveContext, + callback: ( + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest + ) => void + ): void; + ( + context: object, + path: string, + request: string, + callback: ( + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest + ) => void + ): void; + ( + path: string, + request: string, + resolveContext: ResolveContext, + callback: ( + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest + ) => void + ): void; + ( + path: string, + request: string, + callback: ( + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest + ) => void + ): void; + }; export namespace create { export const sync: ( - options?: any - ) => (context?: any, path?: any, request?: any) => string | false; + options: ResolveOptionsOptionalFS + ) => { + (context: object, path: string, request: string): string | false; + (path: string, request: string): string | false; + }; } export namespace ResolverFactory { export let createResolver: (options: UserResolveOptions) => Resolver; @@ -502,10 +575,16 @@ declare namespace exports { iterator?: any, callback?: any ) => any; + export type ResolveCallback = ( + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest + ) => void; export { CachedInputFileSystem, CloneBasenamePlugin, LogInfoPlugin, + ResolveOptionsOptionalFS, PnpApiImpl as PnpApi, Resolver, FileSystem,