From 5e91d0cf0b38d9b910d02b4acad8aedd167773d3 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 24 Aug 2022 19:48:46 +1000 Subject: [PATCH 1/2] fix: resolve config paths fully and warn on config errors Fixes #515 --- src/module.ts | 34 +++++++++-------- test/basic.test.ts | 8 +++- test/configs.test.ts | 45 +++++++++++++++++++++++ test/fixture/basic/alt-tailwind.config.js | 12 ++++++ test/fixture/basic/tailwind.config.js | 9 ++++- test/fixture/basic/ts-tailwind.config.ts | 14 +++++++ 6 files changed, 105 insertions(+), 17 deletions(-) create mode 100644 test/configs.test.ts create mode 100644 test/fixture/basic/alt-tailwind.config.js create mode 100644 test/fixture/basic/ts-tailwind.config.ts diff --git a/src/module.ts b/src/module.ts index d980fa86..6a294c95 100644 --- a/src/module.ts +++ b/src/module.ts @@ -12,8 +12,7 @@ import { createResolver, resolvePath, addVitePlugin, - tryRequireModule, - isNuxt3 + isNuxt3, findPath, requireModule } from '@nuxt/kit' import { Config } from 'tailwindcss' import { name, version } from '../package.json' @@ -39,8 +38,10 @@ export interface ModuleHooks { 'tailwindcss:config': (tailwindConfig: any) => void } +type Arrayable = T | T[] + export interface ModuleOptions { - configPath: string; + configPath: Arrayable; cssPath: string; config: Config; viewer: boolean; @@ -76,17 +77,15 @@ export default defineNuxtModule({ * Push config paths into `configPaths` without extension. * Allows next steps of processing to try both .js / .ts when resolving the config. */ - const addConfigPath = async (path: string | string[]) => { - if (typeof path === 'string') { - path = (await resolvePath(path, { extensions: ['.js', '.ts'] })).split('.').slice(0, -1).join('.') - configPaths.push(path) - return - } - - if (Array.isArray(path)) { - for (let _path of path) { - _path = (await resolvePath(_path, { extensions: ['.js', '.ts'] })).split('.').slice(0, -1).join('.') - configPaths.push() + const addConfigPath = async (path: Arrayable) => { + // filter in case an empty path is provided + const paths = (Array.isArray(path) ? path : [path]).filter(Boolean) + for (const path of paths) { + const resolvedPath = (await findPath(path, { extensions: ['.js', '.mjs', '.ts'] }, 'file')) + // note if the path does not exist, it doesn't get added to the configPaths + if (resolvedPath) { + // remove extension + configPaths.push(resolvedPath) } } } @@ -116,7 +115,12 @@ export default defineNuxtModule({ // Recursively resolve each config and merge tailwind configs together. let tailwindConfig: any = {} for (const configPath of configPaths) { - const _tailwindConfig = tryRequireModule(configPath, { clearCache: true }) + let _tailwindConfig + try { + _tailwindConfig = requireModule(configPath, { clearCache: true }) + } catch (e) { + logger.warn(`Failed to load Tailwind config at: \`./${relative(nuxt.options.rootDir, configPath)}\``, e) + } // Transform purge option from Array to object with { content } if (_tailwindConfig && Array.isArray(_tailwindConfig.purge)) { diff --git a/test/basic.test.ts b/test/basic.test.ts index 3c36a7c8..28c7720a 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -13,7 +13,6 @@ describe('tailwindcss module', async () => { await setupNuxtTailwind({ exposeConfig: true, - configPath: r('tailwind.config.js'), cssPath: r('tailwind.css') }) @@ -39,6 +38,13 @@ describe('tailwindcss module', async () => { expect(nuxt.options.css[0]).toEqual(nuxt.options.tailwindcss.cssPath) }) + test('default js config is merged in', () => { + const nuxt = useTestContext().nuxt + const vfsKey = Object.keys(nuxt.vfs).find(k => k.includes('tailwind.config.')) + // set from ts-tailwind.config.ts + expect(nuxt.vfs[vfsKey]).contains('"primary": "#f1e05a"') + }) + // @todo re-implement // test('custom paths', () => { // const ctx = useTestContext() diff --git a/test/configs.test.ts b/test/configs.test.ts new file mode 100644 index 00000000..f29edbb1 --- /dev/null +++ b/test/configs.test.ts @@ -0,0 +1,45 @@ +import { describe, test, expect, vi } from 'vitest' +import { mockedWarn } from 'consola' +import { useTestContext } from '@nuxt/test-utils' +import { setupNuxtTailwind } from './util' + +describe('tailwindcss module configs', async () => { + vi.mock('consola', async () => { + const { default: mod } = (await vi.importActual('consola')) + mod.withScope = () => mod + mod.info = vi.fn() + mod.warn = vi.fn() + return { default: mod, info: mod.info, mockedWarn: mod.warn } + }) + + await setupNuxtTailwind({ + exposeConfig: true, + configPath: [ + 'alt-tailwind.config.js', + 'malformed-tailwind.config', + 'ts-tailwind.config' + ], + cssPath: 'tailwind.css' + }) + + test('throws error about malformed config', () => { + expect(mockedWarn.mock.calls[0][0]).toMatchInlineSnapshot('"Failed to load Tailwind config at: `./malformed-tailwind.config.js`"') + expect(mockedWarn.mock.calls[0][0]).contains('Failed to load Tailwind config at: `./malformed-tailwind.config.js`') + + expect(mockedWarn.mock.calls[0].length).toBe(2) + }) + + test('ts config file is loaded and merged', () => { + const nuxt = useTestContext().nuxt + const vfsKey = Object.keys(nuxt.vfs).find(k => k.includes('tailwind.config.')) + // set from ts-tailwind.config.ts + expect(nuxt.vfs[vfsKey]).contains('"typescriptBlue": "#007acc"') + }) + + test('js config file is loaded and merged', () => { + const nuxt = useTestContext().nuxt + const vfsKey = Object.keys(nuxt.vfs).find(k => k.includes('tailwind.config.')) + // set from ts-tailwind.config.ts + expect(nuxt.vfs[vfsKey]).contains('"javascriptYellow": "#f1e05a"') + }) +}) diff --git a/test/fixture/basic/alt-tailwind.config.js b/test/fixture/basic/alt-tailwind.config.js new file mode 100644 index 00000000..4af03f47 --- /dev/null +++ b/test/fixture/basic/alt-tailwind.config.js @@ -0,0 +1,12 @@ +module.exports = { + theme: { + extend: { + colors: { + javascriptYellow: '#f1e05a' + } + } + }, + content: [ + 'content/**/*.md' + ] +} diff --git a/test/fixture/basic/tailwind.config.js b/test/fixture/basic/tailwind.config.js index 7b0a6b42..dd9834a5 100644 --- a/test/fixture/basic/tailwind.config.js +++ b/test/fixture/basic/tailwind.config.js @@ -1,5 +1,12 @@ module.exports = { content: [ 'content/**/*.md' - ] + ], + theme: { + extend: { + colors: { + primary: '#f1e05a' + } + } + } } diff --git a/test/fixture/basic/ts-tailwind.config.ts b/test/fixture/basic/ts-tailwind.config.ts new file mode 100644 index 00000000..095bf3a2 --- /dev/null +++ b/test/fixture/basic/ts-tailwind.config.ts @@ -0,0 +1,14 @@ +import { Config } from 'tailwindcss' + +export default { + content: [ + 'content/**/*.md' + ], + theme: { + extend: { + colors: { + typescriptBlue: '#007acc' + } + } + } +} From 2f5094685a1744b52b8d7c7d634ce33b7f77c23c Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 24 Aug 2022 19:55:27 +1000 Subject: [PATCH 2/2] chore: code doc clean up --- src/module.ts | 3 +-- test/basic.test.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/module.ts b/src/module.ts index 6a294c95..09b22d4a 100644 --- a/src/module.ts +++ b/src/module.ts @@ -82,9 +82,8 @@ export default defineNuxtModule({ const paths = (Array.isArray(path) ? path : [path]).filter(Boolean) for (const path of paths) { const resolvedPath = (await findPath(path, { extensions: ['.js', '.mjs', '.ts'] }, 'file')) - // note if the path does not exist, it doesn't get added to the configPaths + // only if the path is found if (resolvedPath) { - // remove extension configPaths.push(resolvedPath) } } diff --git a/test/basic.test.ts b/test/basic.test.ts index 28c7720a..2f65ceae 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -41,7 +41,7 @@ describe('tailwindcss module', async () => { test('default js config is merged in', () => { const nuxt = useTestContext().nuxt const vfsKey = Object.keys(nuxt.vfs).find(k => k.includes('tailwind.config.')) - // set from ts-tailwind.config.ts + // set from default config tailwind.config.js expect(nuxt.vfs[vfsKey]).contains('"primary": "#f1e05a"') })