Skip to content

JavaScript heap out of memory when using tailwindCSS.experimental.classRegex #893

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mfb-davidmay opened this issue Dec 24, 2023 · 12 comments · Fixed by #897
Closed

JavaScript heap out of memory when using tailwindCSS.experimental.classRegex #893

mfb-davidmay opened this issue Dec 24, 2023 · 12 comments · Fixed by #897
Assignees

Comments

@mfb-davidmay
Copy link

What version of VS Code are you using?

Version: 1.85.1 (user setup)

What version of Tailwind CSS IntelliSense are you using?

v0.10.4 also tried v0.11.36

What version of Tailwind CSS are you using?

^3.3.6

What package manager are you using?

yarn berry v3.7.0

What operating system are you using?

Windows 10

Tailwind config

import { withTV } from 'tailwind-variants/transformer';

/** @type {import('tailwindcss').Config} */
export default withTV({
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {
      colors: {
        mfb: {
          brand: {
            100: 'rgba(var(--colors-mfb-primary-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-mfb-primary-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-mfb-primary-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-mfb-primary-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-mfb-primary-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-mfb-primary-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-mfb-primary-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-mfb-primary-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-mfb-primary-900-value), <alpha-value>)',
          },
          secondary: {
            100: 'rgba(var(--colors-mfb-secondary-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-mfb-secondary-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-mfb-secondary-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-mfb-secondary-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-mfb-secondary-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-mfb-secondary-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-mfb-secondary-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-mfb-secondary-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-mfb-secondary-900-value), <alpha-value>)',
          },
          support: {
            100: 'rgba(var(--colors-mfb-support-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-mfb-support-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-mfb-support-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-mfb-support-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-mfb-support-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-mfb-support-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-mfb-support-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-mfb-support-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-mfb-support-900-value), <alpha-value>)',
          },
          neutral: {
            100: 'rgba(var(--colors-mfb-neutral-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-mfb-neutral-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-mfb-neutral-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-mfb-neutral-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-mfb-neutral-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-mfb-neutral-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-mfb-neutral-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-mfb-neutral-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-mfb-neutral-900-value), <alpha-value>)',
          }
        },
        bb: {
          brand: {
            100: 'rgba(var(--colors-bb-primary-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-bb-primary-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-bb-primary-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-bb-primary-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-bb-primary-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-bb-primary-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-bb-primary-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-bb-primary-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-bb-primary-900-value), <alpha-value>)',
          },
          secondary: {
            100: 'rgba(var(--colors-bb-secondary-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-bb-secondary-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-bb-secondary-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-bb-secondary-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-bb-secondary-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-bb-secondary-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-bb-secondary-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-bb-secondary-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-bb-secondary-900-value), <alpha-value>)',
          },
          support: {
            100: 'rgba(var(--colors-bb-support-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-bb-support-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-bb-support-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-bb-support-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-bb-support-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-bb-support-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-bb-support-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-bb-support-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-bb-support-900-value), <alpha-value>)',
          },
          neutral: {
            100: 'rgba(var(--colors-bb-neutral-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-bb-neutral-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-bb-neutral-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-bb-neutral-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-bb-neutral-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-bb-neutral-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-bb-neutral-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-bb-neutral-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-bb-neutral-900-value), <alpha-value>)',
          }
        },
        fs: {
          brand: {
            100: 'rgba(var(--colors-fs-primary-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-fs-primary-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-fs-primary-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-fs-primary-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-fs-primary-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-fs-primary-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-fs-primary-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-fs-primary-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-fs-primary-900-value), <alpha-value>)',
          },
          secondary: {
            100: 'rgba(var(--colors-fs-secondary-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-fs-secondary-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-fs-secondary-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-fs-secondary-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-fs-secondary-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-fs-secondary-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-fs-secondary-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-fs-secondary-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-fs-secondary-900-value), <alpha-value>)',
          },
          support: {
            100: 'rgba(var(--colors-fs-support-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-fs-support-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-fs-support-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-fs-support-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-fs-support-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-fs-support-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-fs-support-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-fs-support-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-fs-support-900-value), <alpha-value>)',
          },
          neutral: {
            100: 'rgba(var(--colors-fs-neutral-100-value), <alpha-value>)',
            200: 'rgba(var(--colors-fs-neutral-200-value), <alpha-value>)',
            300: 'rgba(var(--colors-fs-neutral-300-value), <alpha-value>)',
            400: 'rgba(var(--colors-fs-neutral-400-value), <alpha-value>)',
            500: 'rgba(var(--colors-fs-neutral-500-value), <alpha-value>)',
            600: 'rgba(var(--colors-fs-neutral-600-value), <alpha-value>)',
            700: 'rgba(var(--colors-fs-neutral-700-value), <alpha-value>)',
            800: 'rgba(var(--colors-fs-neutral-800-value), <alpha-value>)',
            900: 'rgba(var(--colors-fs-neutral-900-value), <alpha-value>)',
          }
        }
      },
    },
  },
  plugins: [],
  corePlugins: {
    // preflight: true,
  },
  prefix: 'tw-'
});

VS Code settings

{
  "files.associations": {
    "plyconfig.json": "jsonc"
  },

  // Yarn 3.8+ Setup
  "search.exclude": {
    "**/.yarn": true,
    "**/.pnp.*": true
  },
  "eslint.nodePath": ".yarn/sdks",

  // Typescript Setup
  // "typescript.tsdk": ".yarn/sdks/typescript/lib",
  "typescript.tsdk": "node_modules\\typescript\\lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,

  // Tailwind Setup
  "css.customData": [".vscode/tailwind.json"],
  "scss.lint.unknownAtRules": "ignore",
  "tailwindCSS.experimental.classRegex": [
    ["(?<=tv\\()[^]*?(?=}\\))", "(?<=['\"])[\\w\\-\\s:\\[\\]\\.]*?(?=['\"])"]
  ],
  "editor.quickSuggestions": {
    "strings": "on"
  }
}

Reproduction URL

https://github.com/mfb-davidmay/tw-intellisense-timeout/blob/main/src/components/Button/index.tsx

Describe your issue

When attempting to load Intellisense for the aforementioned file, Visual Studio will consume large amounts of memory and CPU to the point that the Tailwind Extension runs out of memory.

I've pinpointed the issue to be an experimental regex that I've added which was recommended by the author of the tailwind-variants library. The regex shown here is my attempt to improve the one suggested to simplify the matching.

I believe the memory exception might be because there's too many classes matched within the container? but unsure as the logging of the extension isn't telling me much.

Removing the classRegex returns the extension back to normal, and I can use it with standard out of the box support. Just wanting some help to understand what I can do to enable support for the tv(...) functions in my components.

Here's the extension output.

[Global] Creating projects: [{"folder":"c:/dev/Experiments/onionui","configPath":"c:/dev/Experiments/onionui/tailwind.config.js","isUserConfigured":false,"documentSelector":[{"pattern":"c:/dev/Experiments/onionui/tailwind.config.js","priority":0},{"pattern":"c:/dev/Experiments/onionui/**","priority":3}]}]
[Global] Adding watch patterns: c:/dev/Experiments/onionui/tailwind.config.js, c:/dev/Experiments/onionui, c:/dev/Experiments/onionui/tailwind.config.js, c:/dev/Experiments/onionui
[tailwind.config.js] Initializing...
[tailwind.config.js] Loaded Tailwind CSS config file: c:/dev/Experiments/onionui/tailwind.config.js
[tailwind.config.js] Loaded postcss v8.4.32: c:\dev\Experiments\onionui\node_modules\postcss
[tailwind.config.js] Loaded tailwindcss v3.4.0: c:\dev\Experiments\onionui\node_modules\tailwindcss
[tailwind.config.js] Building...

<--- Last few GCs --->

[24556:0000128C00328000]    41079 ms: Mark-Compact 3963.9 (4094.9) -> 3961.6 (4094.9) MB, 2705.69 / 0.00 ms  (average mu = 0.116, current mu = 0.001) allocation failure; scavenge might not succeed
[24556:0000128C00328000]    43728 ms: Mark-Compact 3964.2 (4095.4) -> 3962.1 (4095.4) MB, 2646.47 / 0.00 ms  (average mu = 0.062, current mu = 0.001) allocation failure; scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 00007FF7A1873376 node::SetTracingController+80086
 2: 00007FF7A1873603 node::OnFatalError+595
 3: 00007FF7A4562D33 v8::Function::NewInstance+835
 4: 00007FF7A4562CC3 v8::Function::NewInstance+723
 5: 00007FF7A462B6E7 v8::CppHeap::CollectGarbageInYoungGenerationForTesting+74791
 6: 00007FF7A06433EE v8::CppHeap::Terminate+89774
 7: 00007FF7A064000F v8::CppHeap::Terminate+76495
 8: 00007FF7A3171A99 v8::MicrotasksScope::PerformCheckpoint+541961
 9: 00007FF7A316D4DD v8::MicrotasksScope::PerformCheckpoint+524109
10: 00007FF7A315D8D7 v8::MicrotasksScope::PerformCheckpoint+459591
11: 00007FF7A33689C3 v8::MicrotasksScope::PerformCheckpoint+2602035
12: 00007FF7A36860BA v8_inspector::String16::String16+2535018
13: 00007FF7E038F8CA 
[Info  - 12:34:52 PM] Connection to server got closed. Server will restart.
@mfb-davidmay
Copy link
Author

Some additional information from my machine that might help.

Version: 1.85.1 (user setup)
Commit: 0ee08df0cf4527e40edc9aa28f4b5bd38bbff2b2
Date: 2023-12-13T09:49:37.021Z
Electron: 25.9.7
ElectronBuildId: 25551756
Chromium: 114.0.5735.289
Node.js: 18.15.0
V8: 11.4.183.29-electron.0
OS: Windows_NT x64 10.0.19045

@mfb-davidmay
Copy link
Author

Just following up with some notes from tinkering around with the regex container, I believe the issue lies with the amount of data captured with the container regex (?<=tv\\()[^]*?(?=}\\)) which is is a capture group of 3,558 characters.

When I change the capture group to something much smaller, and general (it's basically matching any, and every string in the file now) the extension is working as expected, and not timing out anymore. This change results in container capture groups of 200-400 characters.

Here's what I changed it to.

{
  "tailwindCSS.experimental.classRegex": [
    ["([\"'`][^\"'`]*.*?[\"'`])", "[\"'`]([^\"'`]*).*?[\"'`]"]
  ]
}

@mfb-davidmay
Copy link
Author

mfb-davidmay commented Dec 25, 2023

Ideally, having the ability to specify (?<=tv\([^]*?)([\"'`][^\"'`]*.*?[\"'`])(?=[^]*?}\);?$) which contains both a positive lookbehind, and positive lookahead to scope matches within the tv function would be the ultimate setup. But I think the regex library you're using doesn't support these regex features, or maybe I'm doing something wrong because intellisense is turned off with this configuration.

I experimented with the becke-ch--regex--s0-0-v1--base--pl--lib using this regex and it returns matches with gm flags so unsure what's going on.

@bitabs
Copy link

bitabs commented Dec 31, 2023

I had the same issue. I was surprised considering its a Macbook pro 16" M3 Max with 64GB memory 🤦🏻

@mfb-davidmay
Copy link
Author

mfb-davidmay commented Jan 1, 2024

I had the same issue. I was surprised considering its a Macbook pro 16" M3 Max with 64GB memory 🤦🏻

Ha, not surprised tbh. There's a lot of code that gets executed with the container match regex.

"([\"'`][^\"'`]*.*?[\"'`])", "[\"'`]([^\"'`]*).*?[\"'`]" while overly general in terms of what it matches in a file will at least stop turning your laptop into a portable heater.

@thecrypticace thecrypticace self-assigned this Jan 3, 2024
@thecrypticace
Copy link
Contributor

The problem is with (?<=['\"])[\\w\\-\\s:\\[\\]\\.]*?(?=['\"]). It matches a zero-length string at a specific position.

Here, a very simplified, non-escaped form: /(?<=')\w*?(?=')/ — this regex basically says match anything zero characters or more between single quotes but not the quotes themselves. Given the string let x = '' it'll match the position between the quotes. And because it's zero length, the exec call in becke-ch--regex--s0-0-v1--base--pl--lib appears to never advance an internal pointer or something resulting in an infinite loop which ends up allocating more and more memory.

I have a fix for this but I'm not yet sure if it's the right fix. I need to do some more testing today.

@thecrypticace
Copy link
Contributor

Turns out my fix wasn't that good so I ended up rewriting things, discovered new JS APIs in the process, and rewrote things some more.

I have a little bit more testing to do but I think things are in a better spot for this now. Should have at least a fix in the pre-release version of the extension tomorrow.

@thecrypticace
Copy link
Contributor

Was hoping to get a pre-release out today but I've run into something odd — I think it's a v8 bug but I'm not 100% sure yet.

@thecrypticace
Copy link
Contributor

I just merged #897 which fixes this. It'll be available in the pre-release version of the extension. If you could give it a test and report back to me that would be 💯

@dgknca
Copy link

dgknca commented May 9, 2024

I'm using pre-release version but I'm suffering from the same issue.

@thecrypticace
Copy link
Contributor

@dgknca can you open a separate issue with a reproduction that I can take a look at?

@dgknca
Copy link

dgknca commented May 16, 2024

@dgknca can you open a separate issue with a reproduction that I can take a look at?

I was using this regex https://www.tailwind-variants.org/docs/getting-started#intellisense-setup-optional

{
  "tailwindCSS.experimental.classRegex": [
    ["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
  ]
}

replaced it with:

["([\"'`][^\"'`]*.*?[\"'`])", "[\"'`]([^\"'`]*).*?[\"'`]"]

works fine now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants