Skip to content

Commit

Permalink
chore: move heavy populateMap function to utils (#637)
Browse files Browse the repository at this point in the history
* chore: move heavy populateMap function to utils

* chore: update
  • Loading branch information
ineshbose committed Mar 21, 2023
1 parent 98dcd41 commit 41691d1
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 73 deletions.
74 changes: 3 additions & 71 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { existsSync } from 'fs'
import { join, relative, dirname } from 'pathe'
import { join, relative } from 'pathe'
import { defuArrayFn } from 'defu'
import { watch } from 'chokidar'
import { underline, yellow } from 'colorette'
Expand All @@ -21,7 +21,7 @@ import { eventHandler, sendRedirect } from 'h3'
import { name, version } from '../package.json'
import vitePlugin from './hmr'
import defaultTailwindConfig from './tailwind.config'
import { InjectPosition, resolveInjectPosition } from './utils'
import { createTemplates, InjectPosition, resolveInjectPosition } from './utils'

const logger = useLogger('nuxt:tailwindcss')

Expand Down Expand Up @@ -173,75 +173,7 @@ export default defineNuxtModule<ModuleOptions>({
// Expose resolved tailwind config as an alias
// https://tailwindcss.com/docs/configuration/#referencing-in-javascript
if (moduleOptions.exposeConfig) {
const dtsContent: string[] = []

/**
* Creates MJS exports for properties of the config
*
* @param obj config
* @param path parent properties trace
* @param level level of object depth
* @param maxLevel maximum level of depth
*/
const populateMap = (obj: any, path: string[] = [], level = 1, maxLevel = moduleOptions.exposeLevel) => {
Object.entries(obj).forEach(([key, value = {} as any]) => {
const subpath = path.concat(key).join('/')

if (
level >= maxLevel || // if recursive call is more than desired
typeof value !== 'object' || // if its not an object, no more recursion required
Array.isArray(value) || // arrays are objects in JS, but we can't break it down
Object.keys(value).find(k => !k.match(/^[0-9a-z]+$/i)) // object has non-alphanumeric property (unsafe var name)
) {
if (typeof value === 'object' && !Array.isArray(value)) {
const validKeys: string[] = []
const invalidKeys: string[] = []
Object.keys(value).forEach(i => (/^[0-9a-z]+$/i.test(i) ? validKeys : invalidKeys).push(i))

addTemplate({
filename: `tailwind.config/${subpath}.mjs`,
getContents: () => `${validKeys.map(i => `const _${i} = ${JSON.stringify(value[i])}`).join('\n')}\nconst config = { ${validKeys.map(i => `"${i}": _${i}, `).join('')}${invalidKeys.map(i => `"${i}": ${JSON.stringify(value[i])}, `).join('')} }\nexport { config as default${validKeys.length > 0 ? ', _' : ''}${validKeys.join(', _')} }`
})
dtsContent.push(`declare module "#tailwind-config/${subpath}" { ${validKeys.map(i => `export const _${i}: ${JSON.stringify(value[i])};`).join('')} const defaultExport: { ${validKeys.map(i => `"${i}": typeof _${i}, `).join('')}${invalidKeys.map(i => `"${i}": ${JSON.stringify(value[i])}, `).join('')} }; export default defaultExport; }`)
} else {
addTemplate({
filename: `tailwind.config/${subpath}.mjs`,
getContents: () => `export default ${JSON.stringify(value, null, 2)}`
})
dtsContent.push(`declare module "#tailwind-config/${subpath}" { const defaultExport: ${JSON.stringify(value)}; export default defaultExport; }`)
}
} else {
// recurse through nested objects
populateMap(value, path.concat(key), level + 1, maxLevel)

const values = Object.keys(value)
addTemplate({
filename: `tailwind.config/${subpath}.mjs`,
getContents: () => `${values.map(v => `import _${v} from "./${key}/${v}.mjs"`).join('\n')}\nconst config = { ${values.map(k => `"${k}": _${k}`).join(', ')} }\nexport { config as default${values.length > 0 ? ', _' : ''}${values.join(', _')} }`
})
dtsContent.push(`declare module "#tailwind-config/${subpath}" {${Object.keys(value).map(v => ` export const _${v}: typeof import("#tailwind-config/${join(`${key}/${subpath}`, `../${v}`)}");`).join('')} const defaultExport: { ${values.map(k => `"${k}": typeof _${k}`).join(', ')} }; export default defaultExport; }`)
}
})
}

populateMap(resolvedConfig)
const configOptions = Object.keys(resolvedConfig)

const template = addTemplate({
filename: 'tailwind.config/index.mjs',
getContents: () => `${configOptions.map(v => `import ${v} from "#build/tailwind.config/${v}.mjs"`).join('\n')}\nconst config = { ${configOptions.join(', ')} }\nexport { config as default, ${configOptions.join(', ')} }`,
write: true
})
dtsContent.push(`declare module "#tailwind-config" {${configOptions.map(v => ` export const ${v}: typeof import("${join('#tailwind-config', v)}");`).join('')} const defaultExport: { ${configOptions.map(v => `"${v}": typeof ${v}`)} }; export default defaultExport; }`)
const typesTemplate = addTemplate({
filename: 'tailwind.config.d.ts',
getContents: () => dtsContent.join('\n'),
write: true
})
nuxt.options.alias['#tailwind-config'] = dirname(template.dst)
nuxt.hook('prepare:types', (opts) => {
opts.references.push({ path: typesTemplate.dst })
})
createTemplates(resolvedConfig, moduleOptions.exposeLevel, nuxt)
}

// Allow extending tailwindcss config by other modules
Expand Down
89 changes: 87 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import { dirname, join } from 'pathe'
import { useNuxt, addTemplate } from '@nuxt/kit'
import type { Config } from 'tailwindcss'

const NON_ALPHANUMERIC_RE = /^[0-9a-z]+$/i
const isJSObject = (value: any) => typeof value === 'object' && !Array.isArray(value)

export type InjectPosition = 'first' | 'last' | number | { after: string };

/** Resolve human-readable inject position specification into absolute index in the array */
export function resolveInjectPosition (css: string[], position: InjectPosition): number {
/**
* Resolve human-readable inject position specification into absolute index in the array
*
* @param css nuxt css config
* @param position position to inject
*
* @returns index in the css array
*/
export function resolveInjectPosition (css: string[], position: InjectPosition) {
if (typeof (position) === 'number') {
return ~~Math.min(position, css.length + 1)
}
Expand All @@ -25,3 +39,74 @@ export function resolveInjectPosition (css: string[], position: InjectPosition):

throw new Error('invalid position: ' + JSON.stringify(position))
}

/**
* Creates MJS exports for properties of the config
*
* @param resolvedConfig tailwind config
* @param maxLevel maximum level of depth
* @param nuxt nuxt app
*/
export const createTemplates = (resolvedConfig: Config, maxLevel: number, nuxt = useNuxt()) => {
const dtsContent: string[] = []

const populateMap = (obj: any, path: string[] = [], level = 1) => {
Object.entries(obj).forEach(([key, value = {} as any]) => {
const subpath = path.concat(key).join('/')

if (
level >= maxLevel || // if recursive call is more than desired
!isJSObject(value) || // if its not an object, no more recursion required
Object.keys(value).find(k => !k.match(NON_ALPHANUMERIC_RE)) // object has non-alphanumeric property (unsafe var name)
) {
if (isJSObject(value)) {
const [validKeys, invalidKeys]: [string[], string[]] = [[], []]
Object.keys(value).forEach(i => (NON_ALPHANUMERIC_RE.test(i) ? validKeys : invalidKeys).push(i))

addTemplate({
filename: `tailwind.config/${subpath}.mjs`,
getContents: () => `${validKeys.map(i => `const _${i} = ${JSON.stringify(value[i])}`).join('\n')}\nconst config = { ${validKeys.map(i => `"${i}": _${i}, `).join('')}${invalidKeys.map(i => `"${i}": ${JSON.stringify(value[i])}, `).join('')} }\nexport { config as default${validKeys.length > 0 ? ', _' : ''}${validKeys.join(', _')} }`
})
dtsContent.push(`declare module "#tailwind-config/${subpath}" { ${validKeys.map(i => `export const _${i}: ${JSON.stringify(value[i])};`).join('')} const defaultExport: { ${validKeys.map(i => `"${i}": typeof _${i}, `).join('')}${invalidKeys.map(i => `"${i}": ${JSON.stringify(value[i])}, `).join('')} }; export default defaultExport; }`)
} else {
addTemplate({
filename: `tailwind.config/${subpath}.mjs`,
getContents: () => `export default ${JSON.stringify(value, null, 2)}`
})
dtsContent.push(`declare module "#tailwind-config/${subpath}" { const defaultExport: ${JSON.stringify(value)}; export default defaultExport; }`)
}
} else {
// recurse through nested objects
populateMap(value, path.concat(key), level + 1)

const values = Object.keys(value)
addTemplate({
filename: `tailwind.config/${subpath}.mjs`,
getContents: () => `${values.map(v => `import _${v} from "./${key}/${v}.mjs"`).join('\n')}\nconst config = { ${values.map(k => `"${k}": _${k}`).join(', ')} }\nexport { config as default${values.length > 0 ? ', _' : ''}${values.join(', _')} }`
})
dtsContent.push(`declare module "#tailwind-config/${subpath}" {${Object.keys(value).map(v => ` export const _${v}: typeof import("#tailwind-config/${join(`${key}/${subpath}`, `../${v}`)}");`).join('')} const defaultExport: { ${values.map(k => `"${k}": typeof _${k}`).join(', ')} }; export default defaultExport; }`)
}
})
}

populateMap(resolvedConfig)
const configOptions = Object.keys(resolvedConfig)

const template = addTemplate({
filename: 'tailwind.config/index.mjs',
getContents: () => `${configOptions.map(v => `import ${v} from "#build/tailwind.config/${v}.mjs"`).join('\n')}\nconst config = { ${configOptions.join(', ')} }\nexport { config as default, ${configOptions.join(', ')} }`,
write: true
})

dtsContent.push(`declare module "#tailwind-config" {${configOptions.map(v => ` export const ${v}: typeof import("${join('#tailwind-config', v)}");`).join('')} const defaultExport: { ${configOptions.map(v => `"${v}": typeof ${v}`)} }; export default defaultExport; }`)
const typesTemplate = addTemplate({
filename: 'tailwind.config.d.ts',
getContents: () => dtsContent.join('\n'),
write: true
})

nuxt.options.alias['#tailwind-config'] = dirname(template.dst)
nuxt.hook('prepare:types', (opts) => {
opts.references.push({ path: typesTemplate.dst })
})
}

0 comments on commit 41691d1

Please sign in to comment.