Home / Function/ tailwindLoader() — tailwindcss Function Reference

tailwindLoader() — tailwindcss Function Reference

Architecture documentation for the tailwindLoader() function in index.ts from the tailwindcss codebase.

Entity Profile

Dependency Diagram

graph TD
  9e631077_170a_b3c6_dd44_1eb8a56fe3eb["tailwindLoader()"]
  6e4ecc38_77b9_08dd_6c29_11df55cb4196["getContextFromCache()"]
  9e631077_170a_b3c6_dd44_1eb8a56fe3eb -->|calls| 6e4ecc38_77b9_08dd_6c29_11df55cb4196
  style 9e631077_170a_b3c6_dd44_1eb8a56fe3eb fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

packages/@tailwindcss-webpack/src/index.ts lines 57–282

export default async function tailwindLoader(
  this: LoaderContext<LoaderOptions>,
  source: string,
): Promise<void> {
  let callback = this.async()
  let options = this.getOptions() ?? {}
  let inputFile = this.resourcePath
  let base = options.base ?? process.cwd()
  let shouldOptimize = options.optimize ?? process.env.NODE_ENV === 'production'
  let isCSSModuleFile = inputFile.endsWith('.module.css')

  using I = new Instrumentation()

  DEBUG && I.start(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)

  // Bail out early if this is guaranteed to be a non-Tailwind CSS file.
  {
    DEBUG && I.start('Quick bail check')
    let canBail = !/@(import|reference|theme|variant|config|plugin|apply|tailwind)\b/.test(source)
    if (canBail) {
      DEBUG && I.end('Quick bail check')
      DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
      callback(null, source)
      return
    }
    DEBUG && I.end('Quick bail check')
  }

  try {
    let context = getContextFromCache(inputFile, options)
    let inputBasePath = path.dirname(path.resolve(inputFile))

    // Whether this is the first build or not
    let isInitialBuild = context.compiler === null

    async function createCompiler() {
      DEBUG && I.start('Setup compiler')
      if (context.fullRebuildPaths.length > 0 && !isInitialBuild) {
        clearRequireCache(context.fullRebuildPaths)
      }

      context.fullRebuildPaths = []

      DEBUG && I.start('Create compiler')
      let compiler = await compile(source, {
        from: inputFile,
        base: inputBasePath,
        shouldRewriteUrls: true,
        onDependency: (depPath) => context.fullRebuildPaths.push(depPath),
        // In CSS Module files, we have to disable the `@property` polyfill since these will
        // emit global `*` rules which are considered to be non-pure and will cause builds
        // to fail.
        polyfills: isCSSModuleFile ? Polyfills.All ^ Polyfills.AtProperty : Polyfills.All,
      })
      DEBUG && I.end('Create compiler')

      DEBUG && I.end('Setup compiler')
      return compiler
    }

    // Setup the compiler if it doesn't exist yet
    context.compiler ??= await createCompiler()

    // Early exit if no Tailwind features are used
    if (context.compiler.features === Features.None) {
      DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
      callback(null, source)
      return
    }

    let rebuildStrategy: 'full' | 'incremental' = 'incremental'

    // Track file modification times to CSS files
    DEBUG && I.start('Register full rebuild paths')
    {
      // Report dependencies for config files, plugins, etc.
      for (let file of context.fullRebuildPaths) {
        this.addDependency(path.resolve(file))
      }

      let files = [...context.fullRebuildPaths, inputFile]

      for (let file of files) {
        let changedTime: number | null = null
        try {
          changedTime = fs.statSync(file)?.mtimeMs ?? null
        } catch {
          // File might not exist
        }

        if (changedTime === null) {
          if (file === inputFile) {
            rebuildStrategy = 'full'
          }
          continue
        }

        let prevTime = context.mtimes.get(file)
        if (prevTime === changedTime) continue

        rebuildStrategy = 'full'
        context.mtimes.set(file, changedTime)
      }
    }
    DEBUG && I.end('Register full rebuild paths')

    if (rebuildStrategy === 'full' && !isInitialBuild) {
      context.compiler = await createCompiler()
    }

    let compiler = context.compiler

    // Check if we need to process this file at all
    if (
      !(
        compiler.features &
        (Features.AtApply | Features.JsPluginCompat | Features.ThemeFunction | Features.Utilities)
      )
    ) {
      DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
      callback(null, source)
      return
    }

    // Setup or update scanner if needed
    if (context.scanner === null || rebuildStrategy === 'full') {
      DEBUG && I.start('Setup scanner')
      let sources = (() => {
        // Disable auto source detection
        if (compiler.root === 'none') {
          return []
        }

        // No root specified, use the base directory
        if (compiler.root === null) {
          return [{ base, pattern: '**/*', negated: false }]
        }

        // Use the specified root
        return [{ ...compiler.root, negated: false }]
      })().concat(compiler.sources)

      context.scanner = new Scanner({ sources })
      DEBUG && I.end('Setup scanner')
    }

    // Scan for candidates if utilities are used
    if (compiler.features & Features.Utilities) {
      DEBUG && I.start('Scan for candidates')
      for (let candidate of context.scanner.scan()) {
        context.candidates.add(candidate)
      }
      DEBUG && I.end('Scan for candidates')

      DEBUG && I.start('Register dependency messages')
      // Add all found files as direct dependencies
      let resolvedInputFile = path.resolve(base, inputFile)
      for (let file of context.scanner.files) {
        let absolutePath = path.resolve(file)
        // The CSS file cannot be a dependency of itself
        if (absolutePath === resolvedInputFile) {
          continue
        }
        this.addDependency(absolutePath)
      }

      // Register context dependencies for glob patterns
      for (let glob of context.scanner.globs) {
        // Skip negated patterns
        if (glob.pattern[0] === '!') continue

        // Avoid adding a dependency on the base directory itself
        if (glob.pattern === '*' && base === glob.base) {
          continue
        }

        this.addContextDependency(path.resolve(glob.base))
      }

      // Validate that source(...) paths are directories
      let root = compiler.root
      if (root !== 'none' && root !== null) {
        let basePath = normalizePath(path.resolve(root.base, root.pattern))
        try {
          let stats = fs.statSync(basePath)
          if (!stats.isDirectory()) {
            throw new Error(
              `The path given to \`source(…)\` must be a directory but got \`source(${basePath})\` instead.`,
            )
          }
        } catch (err) {
          if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
            throw err
          }
          // Directory doesn't exist yet, which is fine
        }
      }
      DEBUG && I.end('Register dependency messages')
    }

    DEBUG && I.start('Build utilities')
    let css = compiler.build([...context.candidates])
    DEBUG && I.end('Build utilities')

    // Optionally optimize the output
    let result = css
    if (shouldOptimize) {
      DEBUG && I.start('Optimization')
      let optimized = optimize(css, {
        minify: typeof shouldOptimize === 'object' ? shouldOptimize.minify : true,
      })
      result = optimized.code
      DEBUG && I.end('Optimization')
    }

    DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
    callback(null, result)
  } catch (error) {
    // Clear the cache entry on error to force a full rebuild next time
    let key = `${inputFile}:${options.base ?? ''}:${JSON.stringify(options.optimize)}`
    cache.delete(key)

    DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
    callback(error as Error)
  }
}

Subdomains

Frequently Asked Questions

What does tailwindLoader() do?
tailwindLoader() is a function in the tailwindcss codebase.
What does tailwindLoader() call?
tailwindLoader() calls 1 function(s): getContextFromCache.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free