From bdf2009a5e1e5eb63863231d74511af267411153 Mon Sep 17 00:00:00 2001 From: Jeremy Albright Date: Fri, 1 May 2020 21:45:37 -1000 Subject: [PATCH 1/7] fix regexp for paths with backslashes --- src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index 11ea5f0..e71f1d6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -28,7 +28,7 @@ export const isDir = async (pathname: string): Promise => { export const walkBack = async (startPath: string): Promise => { const procPath = path.resolve(startPath); - const sep = '[\\/]'; + const sep = '[\\\\/]'; const matches = new RegExp(`(.*${sep}node_modules)(?:${sep}.+?$|${sep}?$)`, 'i').exec(procPath); if (matches && matches[1]) return matches[1]; return ''; From b9ea65f110a7afe7544b70aea84f4ae842c4dffc Mon Sep 17 00:00:00 2001 From: Jeremy Albright Date: Fri, 1 May 2020 21:45:49 -1000 Subject: [PATCH 2/7] only add unique module paths --- src/gatsby-node.ts | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/gatsby-node.ts b/src/gatsby-node.ts index 3b7f39c..d3bb647 100644 --- a/src/gatsby-node.ts +++ b/src/gatsby-node.ts @@ -1,7 +1,8 @@ import * as path from 'path'; import { Configuration } from 'webpack'; import { GatsbyNode, CreateWebpackConfigArgs, PluginOptions } from 'gatsby'; -import { realpath, isDir, getPkgNodeModules } from './utils'; +import uniq from 'lodash.uniq'; +import { isDir, getPkgNodeModules } from './utils'; export interface IPluginOptions extends Omit { include?: string[]; @@ -24,8 +25,16 @@ export interface IPluginOptions extends Omit { * | projectPath | **OPTIONAL**: The path to your project; i.e. the folder containing your `package.json`. This will be used when locating package names included in `include`, and for resolving your project's `node_modules` directory | * | strict | **OPTIONAL**: Defaults to true. `true` = Resolve modules using the `pnpm` philosophy of limiting the module scope of your project. `false` = Use `node`'s module resolution, which looks in every `node_modules` walking up your directory tree. | */ -export const onCreateWebpackConfig: GatsbyNode['onCreateWebpackConfig'] = async ({ actions, reporter }: CreateWebpackConfigArgs, options: IPluginOptions = {} as IPluginOptions): Promise => { - const { setWebpackConfig } = actions; +export const onCreateWebpackConfig: GatsbyNode['onCreateWebpackConfig'] = async ( + { + actions, + reporter, + getConfig, + }: CreateWebpackConfigArgs, + options: IPluginOptions = {} as IPluginOptions, +): Promise => { + const webpackConfig: Configuration = getConfig(); + const { replaceWebpackConfig } = actions; const { include, projectPath = process.cwd(), @@ -76,14 +85,15 @@ export const onCreateWebpackConfig: GatsbyNode['onCreateWebpackConfig'] = async } } - const config: Configuration = { - resolve: { - modules: modulePaths, - }, - resolveLoader: { - modules: modulePaths, - }, - }; + if (!webpackConfig.resolve) webpackConfig.resolve = {}; + if (!webpackConfig.resolveLoader) webpackConfig.resolveLoader = {}; - setWebpackConfig(config); + const compareResolvePaths = webpackConfig.resolve.modules || []; + const compareResolveLoaderPaths = webpackConfig.resolveLoader.modules || []; + + webpackConfig.resolve.modules = uniq([...modulePaths, ...compareResolvePaths]); + webpackConfig.resolveLoader.modules = uniq([...modulePaths, ...compareResolveLoaderPaths]); + + console.log(webpackConfig.resolve.modules); + replaceWebpackConfig(webpackConfig); }; \ No newline at end of file From 1eeddcfeb0d9ccb221c94a8bfdfcf7bf0feb3495 Mon Sep 17 00:00:00 2001 From: Jeremy Albright Date: Fri, 1 May 2020 21:46:22 -1000 Subject: [PATCH 3/7] * mock getConfig() * change getWebpackConfig() to replaceWebpackConfig() --- tests/gatsby-node.test.ts | 38 +++++++++++++++++------------- tests/silo/tests/silo-node.test.ts | 30 +++++++++++++---------- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/tests/gatsby-node.test.ts b/tests/gatsby-node.test.ts index dafaff5..ac362ff 100644 --- a/tests/gatsby-node.test.ts +++ b/tests/gatsby-node.test.ts @@ -1,6 +1,8 @@ import * as path from 'path'; -import { realpath, walkBack } from '../src/utils'; +import uniq from 'lodash.uniq'; +import { CreateWebpackConfigArgs as _CreateWebpackConfigArgs } from 'gatsby'; import { Configuration as WebpackConfig } from 'webpack'; +import { realpath, walkBack } from '../src/utils'; import { onCreateWebpackConfig as _onCreateWebpackConfig, IPluginOptions } from '../src/gatsby-node'; const reporter = { @@ -10,19 +12,20 @@ const reporter = { interface CreateWebpackConfigArgs { actions: { - setWebpackConfig: jest.Mock; + replaceWebpackConfig: jest.Mock; }; reporter: typeof reporter; + getConfig: _CreateWebpackConfigArgs['getConfig']; } type IOnCreateWebpackConfig = (actions: CreateWebpackConfigArgs, options?: IPluginOptions) => Promise; const getConfigResults = (resolutions: string[]): WebpackConfig => { return { resolve: { - modules: resolutions, + modules: uniq(resolutions), }, resolveLoader: { - modules: resolutions, + modules: uniq(resolutions), }, }; }; @@ -30,20 +33,21 @@ const getConfigResults = (resolutions: string[]): WebpackConfig => { describe('Defining module/loader resolutions', () => { const onCreateWebpackConfig = _onCreateWebpackConfig as unknown as IOnCreateWebpackConfig; - const setWebpackConfig: CreateWebpackConfigArgs['actions']['setWebpackConfig'] = jest.fn((config) => config); + const replaceWebpackConfig: CreateWebpackConfigArgs['actions']['replaceWebpackConfig'] = jest.fn((config) => config); const actions: CreateWebpackConfigArgs['actions'] = { - setWebpackConfig, + replaceWebpackConfig, }; const args: CreateWebpackConfigArgs = { actions, reporter, + getConfig: () => ({}), }; const curDir = process.cwd(); describe('Resolves with default options accurately', () => { beforeEach(() => { - setWebpackConfig.mockReset(); + replaceWebpackConfig.mockReset(); }); it('With default options', async () => { const resolutions = [ @@ -54,13 +58,13 @@ describe('Defining module/loader resolutions', () => { ]; const shouldEqual = getConfigResults(resolutions); await onCreateWebpackConfig(args); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); }); describe('Resolves with include options accurately', () => { beforeEach(() => { - setWebpackConfig.mockReset(); + replaceWebpackConfig.mockReset(); Object.entries(reporter).forEach(([key, fn]) => { fn.mockReset(); }); @@ -80,7 +84,7 @@ describe('Defining module/loader resolutions', () => { 'jest', ], }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('With directory', async () => { @@ -99,7 +103,7 @@ describe('Defining module/loader resolutions', () => { './node_modules', ], }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('Warns with non-existant package', async () => { @@ -124,7 +128,7 @@ describe('Defining module/loader resolutions', () => { describe('Resolves with strict mode correctly', () => { beforeEach(() => { - setWebpackConfig.mockReset(); + replaceWebpackConfig.mockReset(); Object.entries(reporter).forEach(([key, fn]) => { fn.mockReset(); }); @@ -142,7 +146,7 @@ describe('Defining module/loader resolutions', () => { await onCreateWebpackConfig(args, { strict: true, }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('With strict off', async () => { process.chdir(__dirname); @@ -156,7 +160,7 @@ describe('Defining module/loader resolutions', () => { await onCreateWebpackConfig(args, { strict: false, }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('Panics with strict on, and no Gatsby', async () => { process.chdir(__dirname); @@ -169,7 +173,7 @@ describe('Defining module/loader resolutions', () => { describe('Resolves with projectPath correctly', () => { beforeEach(() => { - setWebpackConfig.mockReset(); + replaceWebpackConfig.mockReset(); Object.entries(reporter).forEach(([key, fn]) => { fn.mockReset(); }); @@ -195,7 +199,7 @@ describe('Defining module/loader resolutions', () => { ], projectPath: __dirname, }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('With strict off, and package and directory includes', async () => { @@ -220,7 +224,7 @@ describe('Defining module/loader resolutions', () => { ], projectPath: __dirname, }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); }); diff --git a/tests/silo/tests/silo-node.test.ts b/tests/silo/tests/silo-node.test.ts index b2c5f10..d61ff48 100644 --- a/tests/silo/tests/silo-node.test.ts +++ b/tests/silo/tests/silo-node.test.ts @@ -1,6 +1,8 @@ import * as path from 'path'; -import { realpath, walkBack } from '../../../src/utils'; +import uniq from 'lodash.uniq'; import { Configuration as WebpackConfig } from 'webpack'; +import { CreateWebpackConfigArgs as _CreateWebpackConfigArgs } from 'gatsby'; +import { realpath, walkBack } from '../../../src/utils'; import { onCreateWebpackConfig as _onCreateWebpackConfig, IPluginOptions } from '../../../src/gatsby-node'; const reporter = { @@ -10,19 +12,20 @@ const reporter = { interface CreateWebpackConfigArgs { actions: { - setWebpackConfig: jest.Mock; + replaceWebpackConfig: jest.Mock; }; reporter: typeof reporter; + getConfig: _CreateWebpackConfigArgs['getConfig']; } type IOnCreateWebpackConfig = (actions: CreateWebpackConfigArgs, options?: IPluginOptions) => Promise; const getConfigResults = (resolutions: string[]): WebpackConfig => { return { resolve: { - modules: resolutions, + modules: uniq(resolutions), }, resolveLoader: { - modules: resolutions, + modules: uniq(resolutions), }, }; }; @@ -30,13 +33,14 @@ const getConfigResults = (resolutions: string[]): WebpackConfig => { describe('Defining module/loader resolutions in silo', () => { const onCreateWebpackConfig = _onCreateWebpackConfig as unknown as IOnCreateWebpackConfig; - const setWebpackConfig: CreateWebpackConfigArgs['actions']['setWebpackConfig'] = jest.fn((config) => config); + const replaceWebpackConfig: CreateWebpackConfigArgs['actions']['replaceWebpackConfig'] = jest.fn((config) => config); const actions: CreateWebpackConfigArgs['actions'] = { - setWebpackConfig, + replaceWebpackConfig, }; const args: CreateWebpackConfigArgs = { actions, reporter, + getConfig: () => ({}), }; const curDir = process.cwd(); @@ -47,7 +51,7 @@ describe('Defining module/loader resolutions in silo', () => { describe('Resolves with strict mode correctly', () => { beforeEach(() => { - setWebpackConfig.mockReset(); + replaceWebpackConfig.mockReset(); Object.entries(reporter).forEach(([key, fn]) => { fn.mockReset(); }); @@ -65,7 +69,7 @@ describe('Defining module/loader resolutions in silo', () => { await onCreateWebpackConfig(args, { strict: false, }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('With strict off, include package name', async () => { const resolutions = [ @@ -82,7 +86,7 @@ describe('Defining module/loader resolutions in silo', () => { 'jest', ], }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('With strict off, include package name and directory', async () => { const resolutions = [ @@ -101,7 +105,7 @@ describe('Defining module/loader resolutions in silo', () => { '../../../node_modules', ], }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('Panics with strict on, and no Gatsby', async () => { @@ -115,7 +119,7 @@ describe('Defining module/loader resolutions in silo', () => { describe('Resolves with projectPath correctly', () => { beforeEach(() => { - setWebpackConfig.mockReset(); + replaceWebpackConfig.mockReset(); Object.entries(reporter).forEach(([key, fn]) => { fn.mockReset(); }); @@ -139,7 +143,7 @@ describe('Defining module/loader resolutions in silo', () => { ], projectPath: testsDir, }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('With strict on, and package and directory includes', async () => { @@ -162,7 +166,7 @@ describe('Defining module/loader resolutions in silo', () => { ], projectPath: rootDir, }); - expect(setWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); + expect(replaceWebpackConfig).toHaveBeenLastCalledWith(shouldEqual); }); it('With strict on, and panic with no Gatsby', async () => { From 7708524f347adcef3e4df04a8502b28b4ac46262 Mon Sep 17 00:00:00 2001 From: Jeremy Albright Date: Fri, 1 May 2020 21:46:37 -1000 Subject: [PATCH 4/7] mock path module, for backslash testing --- tests/utils.test.ts | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 36ec649..684e5eb 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -1,10 +1,20 @@ import * as path from 'path'; +import { mocked } from 'ts-jest/utils'; import { isDir, fileExists, walkBack, realpath, getPkgNodeModules } from '../src/utils'; +jest.mock('path'); +const { resolve, join } = jest.requireActual('path'); + describe('Utility function tests', () => { + beforeEach(() => { + jest.clearAllMocks(); + mocked(path.resolve).mockImplementation((str) => resolve(str)); + mocked(path.join).mockImplementation((...str) => join(...str)); + }); + const curDir = process.cwd(); - const curFile = path.join(curDir, 'package.json'); - const projectNodeModules = path.join(curDir, 'node_modules'); + const curFile = join(curDir, 'package.json'); + const projectNodeModules = join(curDir, 'node_modules'); it('isDir() is accurate', async () => { expect(await isDir(curDir)).toBe(true); @@ -12,17 +22,25 @@ describe('Utility function tests', () => { }); it('fileExists() is accurate', async () => { expect(await fileExists(curFile)).not.toBe(void 0); - expect(await fileExists(path.join(curDir, 'asdf'))).toBe(void 0); + expect(await fileExists(join(curDir, 'asdf'))).toBe(void 0); }); describe('walkBack() is accurate', () => { + beforeEach(() => { + mocked(path.resolve).mockImplementation((str) => str); + }); + it('With node_modules as start, and with nested paths', async () => { - const shouldBe = path.join(curDir, 'node_modules'); + const shouldBe = join(curDir, 'node_modules'); expect(await walkBack(shouldBe)).toBe(shouldBe); - expect(await walkBack(path.join(shouldBe, 'gatsby', 'dist'))).toBe(shouldBe); + expect(await walkBack(join(shouldBe, 'gatsby', 'dist'))).toBe(shouldBe); + }); + it('With backslashes in path', async () => { + const shouldBe = join(curDir, 'node_modules').replace(/\//g, '\\'); + expect(await walkBack(join(shouldBe, 'test', 'dist').replace(/\//g, '\\'))).toBe(shouldBe); }); it('Returns 0-length string at beginning of tree', async () => { const shouldBe = ''; - expect(await walkBack(path.resolve('/'))).toBe(shouldBe); + expect(await walkBack(resolve('/'))).toBe(shouldBe); }); it('Returns 0-length string when no "node_modules" exists in path', async () => { const shouldBe = ''; @@ -33,8 +51,9 @@ describe('Utility function tests', () => { beforeEach(() => { process.chdir(curDir); }); + it('Resolves Gatsby with strict mode correctly', async () => { - const shouldBe = await walkBack(await realpath(path.join(curDir, 'node_modules', 'gatsby'))); + const shouldBe = await walkBack(await realpath(join(curDir, 'node_modules', 'gatsby'))); expect(await getPkgNodeModules({ pkgName: 'gatsby', nodeModules: projectNodeModules, @@ -46,7 +65,7 @@ describe('Utility function tests', () => { process.chdir(__dirname); expect(await getPkgNodeModules({ pkgName: 'gatsby', - nodeModules: path.join(__dirname, 'node_modules'), + nodeModules: join(__dirname, 'node_modules'), strict: true, })).toBe(shouldBe); }); From 3fca4a082a0f25ed1f9d646a9e89262a679b5d0b Mon Sep 17 00:00:00 2001 From: Jeremy Albright Date: Fri, 1 May 2020 21:47:04 -1000 Subject: [PATCH 5/7] lodash.uniq --- package.json | 4 ++++ pnpm-lock.yaml | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b75543..382abe3 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "devDependencies": { "@jtechsvcs/eslint-config-typescript": "^2.0.3", "@types/jest": "^25.1.2", + "@types/lodash.uniq": "^4.5.6", "@types/node": "^12.12.26", "@types/webpack": "^4.41.5", "@typescript-eslint/eslint-plugin": "^2.19.0", @@ -60,5 +61,8 @@ "type-fest": "^0.10.0", "typedoc": "^0.15.8", "typescript": "^3.7.5" + }, + "dependencies": { + "lodash.uniq": "^4.5.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ebd692..0e57788 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,6 +1,9 @@ +dependencies: + lodash.uniq: 4.5.0 devDependencies: '@jtechsvcs/eslint-config-typescript': 2.0.3_0e623a8db94d98d9fac7066b18561e21 '@types/jest': 25.1.2 + '@types/lodash.uniq': 4.5.6 '@types/node': 12.12.26 '@types/webpack': 4.41.5 '@typescript-eslint/eslint-plugin': 2.19.0_25db45452788e415134bad7c0cac009a @@ -1375,6 +1378,16 @@ packages: dev: true resolution: integrity: sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + /@types/lodash.uniq/4.5.6: + dependencies: + '@types/lodash': 4.14.150 + dev: true + resolution: + integrity: sha512-XHNMXBtiwsWZstZMyxOYjr0e8YYWv0RgPlzIHblTuwBBiWo2MzWVaTBihtBpslb5BglgAWIeBv69qt1+RTRW1A== + /@types/lodash/4.14.150: + dev: true + resolution: + integrity: sha512-kMNLM5JBcasgYscD9x/Gvr6lTAv2NVgsKtet/hm93qMyf/D1pt+7jeEZklKJKxMVmXjxbRVQQGfqDSfipYCO6w== /@types/minimatch/3.0.3: dev: true resolution: @@ -7748,7 +7761,6 @@ packages: resolution: integrity: sha1-JMS/zWsvuji/0FlNsRedjptlZWE= /lodash.uniq/4.5.0: - dev: true resolution: integrity: sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= /lodash/4.17.15: @@ -12631,6 +12643,7 @@ packages: specifiers: '@jtechsvcs/eslint-config-typescript': ^2.0.3 '@types/jest': ^25.1.2 + '@types/lodash.uniq': ^4.5.6 '@types/node': ^12.12.26 '@types/webpack': ^4.41.5 '@typescript-eslint/eslint-plugin': ^2.19.0 @@ -12640,6 +12653,7 @@ specifiers: eslint: ^6.8.0 gatsby: ^2.19.12 jest: ^25.1.0 + lodash.uniq: ^4.5.0 npm-run-all: ^4.1.5 rimraf: ^3.0.1 ts-jest: ^25.2.0 From 5b5cb4d80e06eee383e65b973fca6c3505a904a3 Mon Sep 17 00:00:00 2001 From: Jeremy Albright Date: Fri, 1 May 2020 21:47:10 -1000 Subject: [PATCH 6/7] v1.2.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 382abe3..96c1af8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gatsby-plugin-pnpm", - "version": "1.2.3", + "version": "1.2.5", "description": "Easily add pnpm support to your Gatsby project", "main": "index.js", "types": "./dist/gatsby-node.js", From a2586bbd3d44c3ef1b277bc4c4198dff5b7daa24 Mon Sep 17 00:00:00 2001 From: Jeremy Albright Date: Fri, 1 May 2020 21:47:41 -1000 Subject: [PATCH 7/7] remove debug logging --- src/gatsby-node.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gatsby-node.ts b/src/gatsby-node.ts index d3bb647..6e75648 100644 --- a/src/gatsby-node.ts +++ b/src/gatsby-node.ts @@ -94,6 +94,5 @@ export const onCreateWebpackConfig: GatsbyNode['onCreateWebpackConfig'] = async webpackConfig.resolve.modules = uniq([...modulePaths, ...compareResolvePaths]); webpackConfig.resolveLoader.modules = uniq([...modulePaths, ...compareResolveLoaderPaths]); - console.log(webpackConfig.resolve.modules); replaceWebpackConfig(webpackConfig); }; \ No newline at end of file