From 77e890fd335b38427cd892aea42ecfdb722cb33b Mon Sep 17 00:00:00 2001 From: Jakub Nowak Date: Tue, 31 Mar 2020 18:13:44 +0200 Subject: [PATCH 1/2] Allow non-module resolves --- src/mapPath.js | 4 +-- src/normalizeOptions.js | 50 ++++++++++++++++++++++++++------------ src/resolvePath.js | 28 ++++++++++++++------- src/transformers/call.js | 10 +++++--- src/transformers/import.js | 2 +- test/custom-call.test.js | 33 ++++++++++++++++++------- test/index.test.js | 22 +++++++++++++++++ 7 files changed, 109 insertions(+), 40 deletions(-) diff --git a/src/mapPath.js b/src/mapPath.js index 43ce277..b293145 100644 --- a/src/mapPath.js +++ b/src/mapPath.js @@ -1,6 +1,6 @@ import defaultResolvePath from './resolvePath'; -export default function mapPathString(nodePath, state) { +export default function mapPathString(nodePath, state, resolveOpts) { if (!state.types.isStringLiteral(nodePath)) { return; } @@ -9,7 +9,7 @@ export default function mapPathString(nodePath, state) { const currentFile = state.file.opts.filename; const resolvePath = state.normalizedOpts.customResolvePath || defaultResolvePath; - const modulePath = resolvePath(sourcePath, currentFile, state.opts); + const modulePath = resolvePath(sourcePath, currentFile, state.opts, resolveOpts); if (modulePath) { if (nodePath.node.pathResolved) { return; diff --git a/src/normalizeOptions.js b/src/normalizeOptions.js index 297a847..a1288dc 100644 --- a/src/normalizeOptions.js +++ b/src/normalizeOptions.js @@ -9,20 +9,20 @@ import pkgUp from 'pkg-up'; import { escapeRegExp } from './utils'; const defaultExtensions = ['.js', '.jsx', '.es', '.es6', '.mjs']; -const defaultTransformedFunctions = [ - 'require', - 'require.resolve', - 'System.import', +const defaultTransformFunctions = [ + { pattern: 'require', isModulePath: true }, + { pattern: 'require.resolve', isModulePath: true }, + { pattern: 'System.import', isModulePath: true }, // Jest methods - 'jest.genMockFromModule', - 'jest.mock', - 'jest.unmock', - 'jest.doMock', - 'jest.dontMock', - 'jest.setMock', - 'require.requireActual', - 'require.requireMock', + { pattern: 'jest.genMockFromModule', isModulePath: true }, + { pattern: 'jest.mock', isModulePath: true }, + { pattern: 'jest.unmock', isModulePath: true }, + { pattern: 'jest.doMock', isModulePath: true }, + { pattern: 'jest.dontMock', isModulePath: true }, + { pattern: 'jest.setMock', isModulePath: true }, + { pattern: 'require.requireActual', isModulePath: true }, + { pattern: 'require.requireMock', isModulePath: true }, ]; function isRegExp(string) { @@ -122,12 +122,30 @@ function normalizeAlias(optsAlias) { }, []); } -function normalizeTransformedFunctions(optsTransformFunctions) { +function normalizeTransformFunctionsElement(optsTransformFunction) { + let pattern; + let opts; + if (Array.isArray(optsTransformFunction)) { + [pattern, opts] = optsTransformFunction; + } else { + [pattern, opts] = [optsTransformFunction, {}]; + } + + const finalOpts = { pattern, isModulePath: true }; + if ('isModulePath' in opts) { + finalOpts.isModulePath = opts.isModulePath; + } + + return finalOpts; +} + +function normalizeTransformFunctions(optsTransformFunctions) { if (!optsTransformFunctions) { - return defaultTransformedFunctions; + return defaultTransformFunctions; } - return [...defaultTransformedFunctions, ...optsTransformFunctions]; + return [...defaultTransformFunctions, + ...optsTransformFunctions.map(normalizeTransformFunctionsElement)]; } function normalizeLoglevel(optsLoglevel) { @@ -143,7 +161,7 @@ export default createSelector( const root = normalizeRoot(opts.root, cwd); const alias = normalizeAlias(opts.alias); const loglevel = normalizeLoglevel(opts.loglevel); - const transformFunctions = normalizeTransformedFunctions(opts.transformFunctions); + const transformFunctions = normalizeTransformFunctions(opts.transformFunctions); const extensions = opts.extensions || defaultExtensions; const stripExtensions = opts.stripExtensions || extensions; diff --git a/src/resolvePath.js b/src/resolvePath.js index fcb3818..91f8242 100644 --- a/src/resolvePath.js +++ b/src/resolvePath.js @@ -17,20 +17,24 @@ function getRelativePath(sourcePath, currentFile, absFileInRoot, opts) { return toLocalPath(toPosixPath(relativePath)); } -function findPathInRoots(sourcePath, { extensions, root }) { +function findPathInRoots(sourcePath, { extensions, root }, { isModulePath }) { // Search the source path inside every custom root directory let resolvedSourceFile; root.some((basedir) => { - resolvedSourceFile = nodeResolvePath(`./${sourcePath}`, basedir, extensions); + if (isModulePath) { + resolvedSourceFile = nodeResolvePath(`./${sourcePath}`, basedir, extensions); + } else { + resolvedSourceFile = path.resolve(basedir, `./${sourcePath}`); + } return resolvedSourceFile !== null; }); return resolvedSourceFile; } -function resolvePathFromRootConfig(sourcePath, currentFile, opts) { - const absFileInRoot = findPathInRoots(sourcePath, opts); +function resolvePathFromRootConfig(sourcePath, currentFile, opts, resolveOpts) { + const absFileInRoot = findPathInRoots(sourcePath, opts, resolveOpts); if (!absFileInRoot) { return null; @@ -46,7 +50,7 @@ function checkIfPackageExists(modulePath, currentFile, extensions, loglevel) { } } -function resolvePathFromAliasConfig(sourcePath, currentFile, opts) { +function resolvePathFromAliasConfig(sourcePath, currentFile, opts, { isModulePath = true } = {}) { let aliasedSourceFile; opts.alias.find(([regExp, substitute]) => { @@ -73,7 +77,10 @@ function resolvePathFromAliasConfig(sourcePath, currentFile, opts) { } return asf; }) - .find(src => nodeResolvePath(src, path.dirname(currentFile), opts.extensions)); + .find(src => { + return !isModulePath + || nodeResolvePath(src, path.dirname(currentFile), opts.extensions); + }); } if (isRelativePath(aliasedSourceFile)) { @@ -82,7 +89,7 @@ function resolvePathFromAliasConfig(sourcePath, currentFile, opts) { ); } - if (process.env.NODE_ENV !== 'production') { + if (isModulePath && process.env.NODE_ENV !== 'production') { checkIfPackageExists(aliasedSourceFile, currentFile, opts.extensions, opts.loglevel); } @@ -94,7 +101,9 @@ const resolvers = [ resolvePathFromRootConfig, ]; -export default function resolvePath(sourcePath, currentFile, opts) { +export default function resolvePath( + sourcePath, currentFile, opts, { isModulePath = true } = {}, +) { if (isRelativePath(sourcePath)) { return sourcePath; } @@ -107,7 +116,8 @@ export default function resolvePath(sourcePath, currentFile, opts) { let resolvedPath = null; resolvers.some((resolver) => { - resolvedPath = resolver(sourcePath, absoluteCurrentFile, normalizedOpts); + resolvedPath = resolver( + sourcePath, absoluteCurrentFile, normalizedOpts, { isModulePath }); return resolvedPath !== null; }); diff --git a/src/transformers/call.js b/src/transformers/call.js index 16e040d..218dbfd 100644 --- a/src/transformers/call.js +++ b/src/transformers/call.js @@ -7,12 +7,16 @@ export default function transformCall(nodePath, state) { } const calleePath = nodePath.get('callee'); - const isNormalCall = state.normalizedOpts.transformFunctions.some(pattern => + const transformFunction = state.normalizedOpts.transformFunctions.find(({ pattern }) => matchesPattern(state.types, calleePath, pattern) ); - if (isNormalCall || isImportCall(state.types, nodePath)) { + if (transformFunction) { state.moduleResolverVisited.add(nodePath); - mapPathString(nodePath.get('arguments.0'), state); + const { isModulePath } = transformFunction; + mapPathString(nodePath.get('arguments.0'), state, { isModulePath }); + } else if (isImportCall(state.types, nodePath)) { + state.moduleResolverVisited.add(nodePath); + mapPathString(nodePath.get('arguments.0'), state, { isModulePath: true }); } } diff --git a/src/transformers/import.js b/src/transformers/import.js index 4a884c8..c46f934 100644 --- a/src/transformers/import.js +++ b/src/transformers/import.js @@ -6,5 +6,5 @@ export default function transformImport(nodePath, state) { } state.moduleResolverVisited.add(nodePath); - mapPathString(nodePath.get('source'), state); + mapPathString(nodePath.get('source'), state, { isModulePath: true }); } diff --git a/test/custom-call.test.js b/test/custom-call.test.js index bd2d9df..8786c2c 100644 --- a/test/custom-call.test.js +++ b/test/custom-call.test.js @@ -3,7 +3,8 @@ import plugin from '../src'; const calls = [ - 'customMethod.something', + 'customMethod.Module', + 'customMethod.NonModule', ]; describe('custom calls', () => { @@ -16,7 +17,8 @@ describe('custom calls', () => { test: './test/testproject/test', }, transformFunctions: [ - 'customMethod.something', + 'customMethod.Module', + ['customMethod.NonModule', { isModulePath: false }], ], }], ], @@ -59,13 +61,6 @@ describe('custom calls', () => { expect(result.code).toBe(`${name}(path, ...args);`); }); - it('should handle an empty path', () => { - const code = `${name}('', ...args);`; - const result = transform(code, transformerOpts); - - expect(result.code).toBe(`${name}('', ...args);`); - }); - it('should ignore the call if the method name is not fully matched (suffix)', () => { const code = `${name}.after("components/Sidebar/Footer", ...args);`; const result = transform(code, transformerOpts); @@ -81,4 +76,24 @@ describe('custom calls', () => { }); }); }); + + describe('customMethod.Module', () => { + const name = 'customMethod.Module'; + it('should handle an empty path', () => { + const code = `${name}('', ...args);`; + const result = transform(code, transformerOpts); + + expect(result.code).toBe(`${name}('', ...args);`); + }); + }); + + describe('customMethod.NonModule', () => { + const name = 'customMethod.NonModule'; + it('should handle an empty path', () => { + const code = `${name}('', ...args);`; + const result = transform(code, transformerOpts); + + expect(result.code).toBe(`${name}("./test/testproject/src", ...args);`); + }); + }); }); diff --git a/test/index.test.js b/test/index.test.js index 384f545..8de79fd 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -12,6 +12,13 @@ describe('module-resolver', () => { expect(result.code).toBe(`import something from "${output}";`); } + function testWithFunction(functionName, source, output, transformerOpts) { + const code = `${functionName}("${source}");`; + const result = transform(code, transformerOpts); + + expect(result.code).toBe(`${functionName}("${output}");`); + } + describe('exports', () => { describe('resolvePath', () => { it('should be a function', () => { @@ -636,9 +643,13 @@ describe('module-resolver', () => { plugins: [ [pluginWithMock, { alias: { + '~': './test/testproject/src', legacy: 'npm:legacy', 'non-existing': 'this-package-does-not-exist', }, + transformFunctions: [ + ['customResolvePath', { isModulePath: false }], + ], }], ], }; @@ -670,6 +681,17 @@ describe('module-resolver', () => { expect(mockWarn).toBeCalledWith(`Could not resolve "this-package-does-not-exist/lib" in file ${fileName}.`); }); + it("shouldn't print a warning for a non-module resolve", () => { + testWithFunction( + 'customResolvePath', + '~/libs', + './test/testproject/src/libs', + missingAliasTransformerOpts, + ); + + expect(mockWarn.mock.calls.length).toBe(0); + }); + describe('production environment', () => { beforeEach(() => { process.env.NODE_ENV = 'production'; From a38b7375dd3b03b593dc1b3b5841b29c52d42f23 Mon Sep 17 00:00:00 2001 From: Jakub Nowak Date: Thu, 2 Apr 2020 21:49:32 +0200 Subject: [PATCH 2/2] Convert `in` to `!== undefined` --- src/normalizeOptions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/normalizeOptions.js b/src/normalizeOptions.js index a1288dc..09491a5 100644 --- a/src/normalizeOptions.js +++ b/src/normalizeOptions.js @@ -132,7 +132,7 @@ function normalizeTransformFunctionsElement(optsTransformFunction) { } const finalOpts = { pattern, isModulePath: true }; - if ('isModulePath' in opts) { + if (opts.isModulePath !== undefined) { finalOpts.isModulePath = opts.isModulePath; }