Ship @tabler/core as ESM only, drop the UMD bundle (#2983)

This commit is contained in:
Bartosz-Do authored and Bartek committed 2026-10-02 12:03:12 +02:00
1 parent f98f27228d
commit d0ddf0a96f
12 files changed
+69 -48

No files matched your search

+12 -10
View File
@@ -11,10 +11,12 @@ description: >-
## 1. Two entry points
| Entry | Ships as | Holds |
| -------------------- | ---------------------------------- | ---------------------------------------------------------------------------- |
| `js/tabler.ts` | `tabler.js` / `.esm.js` (+ `.min`) | plugin initialisers, the Bootstrap components, the `tabler` helper namespace |
| `js/tabler-theme.ts` | `tabler-theme.js` (+ variants) | the colour-mode/theme switcher only |
| Entry | Ships as | Holds |
| --- | --- | --- |
| `js/tabler.ts` | `tabler.js` (+ `.min`) | plugin initialisers, the Bootstrap components, the `tabler` helper exports |
| `js/tabler-theme.ts` | `tabler-theme.js` (+ `.min`) | the colour-mode/theme switcher only |
Both bundles are **ESM only** (Tabler 2.0, like Bootstrap v6): no UMD, no `window.tabler` global. `tabler.js` is loaded with `<script type="module">`; `tabler-theme.js` has no imports or exports once bundled, so it stays a classic blocking script.
`tabler-theme.js` is loaded right after `<body>` and **not deferred**, so the chosen theme applies before the first paint. It stays tiny on purpose (bundlewatch: 1 kB raw, 800 B minified) — do not add anything to it that is not needed before paint.
@@ -68,13 +70,13 @@ pnpm --filter @tabler/core test:js:coverage
## 6. Build and budget
`vite` builds each entry as a library in `es` + `umd` (`BASE_NAME` selects the entry), then terser produces the `.min` variants with source maps. Sizes are enforced:
`vite` builds each entry as a library in `es` only (`BASE_NAME` selects the entry), then terser produces the `.min` variants with source maps. `core/dist/js/` holds 4 files + maps. Sizes are enforced:
| File | Limit |
| ------------------------- | ----- |
| `dist/js/tabler.js` | 64 kB |
| `dist/js/tabler.min.js` | 48 kB |
| `dist/js/tabler-theme.js` | 1 kB |
| File | Limit |
| --- | --- |
| `dist/js/tabler.js` | 64 kB |
| `dist/js/tabler.min.js` | 48 kB |
| `dist/js/tabler-theme.js` | 1.3 kB |
```bash
pnpm --filter @tabler/core build
+7
View File
@@ -0,0 +1,7 @@
---
"@tabler/core": major
"@tabler/preview": patch
"@tabler/docs": patch
---
Removed the UMD bundle and `window.tabler` global; `@tabler/core` ships ESM only, loaded with `<script type="module">`.
+2 -2
View File
@@ -75,7 +75,7 @@ Load Tabler from the CDN and start building:
<body>
<h1>Hello, Tabler!</h1>
<button class="btn btn-primary">Primary button</button>
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@latest/dist/js/tabler.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@tabler/core@latest/dist/js/tabler.min.js"></script>
</body>
</html>
```
@@ -106,7 +106,7 @@ The `@tabler/core` package contains compiled and minified CSS and JavaScript, th
├── dist/
│ ├── css/ tabler.css and the optional stylesheets (flags, marketing, payments, socials,
│ │ themes, vendors), each with .min and .rtl versions
│ ├── js/ tabler.js and tabler.esm.js, plus the standalone tabler-theme.js
│ ├── js/ tabler.js (ESM, load with <script type="module">), plus the standalone tabler-theme.js
│ ├── libs/ bundled plugins: ApexCharts, Tom Select, Vanilla Calendar Pro, FullCalendar and others
│ ├── types/ TypeScript declarations
│ ├── fonts/
+3 -5
View File
@@ -18,11 +18,9 @@ const entry = `${entryPath}.ts`
export default createViteConfig({
entry: entry,
name: libraryName,
fileName: (format) => {
const esmSuffix = format === 'es' ? '.esm' : ''
return `${baseName}${esmSuffix}.js`
},
formats: ['es', 'umd'],
// ESM only (Tabler 2.0, like Bootstrap v6): one artifact per entry, no `.esm` suffix.
fileName: () => `${baseName}.js`,
formats: ['es'],
outDir: path.resolve(__dirname, '../dist/js'),
banner: bannerText,
minify: false,
+4 -3
View File
@@ -39,7 +39,7 @@ All files in the package are also available over a CDN:
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@latest/dist/css/tabler.min.css" />
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@latest/dist/js/tabler.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@tabler/core@latest/dist/js/tabler.min.js"></script>
```
If you don't use a package manager, [download the latest release](https://github.com/tabler/tabler/releases) as a ZIP archive.
@@ -55,7 +55,8 @@ import '@tabler/core/dist/css/tabler.min.css'
import '@tabler/core/dist/js/tabler.min.js'
```
There's also an ES module build with named exports, which is handy when you create components from code:
The bundle is ESM only — in a plain HTML page load it with `<script type="module">`. It also has
named exports, which is handy when you create components from code:
```js
import { Tooltip } from '@tabler/core'
@@ -104,7 +105,7 @@ The third-party libraries used by the demo pages (ApexCharts, Tom Select, Vanill
@tabler/core/
├── dist/
│ ├── css/ tabler.css and the optional stylesheets, each with .min and .rtl versions
│ ├── js/ tabler.js and tabler.esm.js, plus the standalone tabler-theme.js
│ ├── js/ tabler.js (ESM, load with <script type="module">), plus the standalone tabler-theme.js
│ ├── libs/ bundled third-party plugins
│ ├── types/ TypeScript declarations
│ ├── fonts/
+2 -2
View File
@@ -32,5 +32,5 @@ export { default as Sparkline } from './src/sparkline'
export { default as Strength } from './src/strength'
export { default as SwitchIcon } from './src/switch-icon'
// deprecated(2.0): the `tabler` namespace with `getColor()`, `hexToRgba()` and `prefix`
export * as tabler from './src/deprecated'
// Tabler's own helpers as named exports (prefix, hexToRgba, getColor)
export * from './src/tabler'
+24 -21
View File
@@ -3,6 +3,7 @@
"version": "1.6.1",
"description": "Premium and Open Source dashboard template with responsive and high quality UI.",
"homepage": "https://tabler.io",
"type": "module",
"scripts": {
"dev": "pnpm run watch",
"dev:prepare": "pnpm run clean && pnpm run copy && concurrently \"pnpm run css:dev\" \"pnpm run js:build\"",
@@ -21,8 +22,8 @@
"js:build:theme": "cross-env BASE_NAME=tabler-theme vite build --config .build/vite.config.mts",
"js:build:standalone": "cross-env BASE_NAME=tabler vite build --config .build/vite.config.mts",
"js:min": "concurrently \"pnpm run js:min:standalone\" \"pnpm run js:min:theme\"",
"js:min:standalone": "concurrently \"terser dist/js/tabler.js --compress passes=2,ecma=2020 --ecma 2020 --mangle --source-map \\\"content=dist/js/tabler.js.map,filename=dist/js/tabler.min.js.map,url=tabler.min.js.map\\\" -o dist/js/tabler.min.js\" \"terser dist/js/tabler.esm.js --module --compress passes=2,ecma=2020 --ecma 2020 --mangle --source-map \\\"content=dist/js/tabler.esm.js.map,filename=dist/js/tabler.esm.min.js.map,url=tabler.esm.min.js.map\\\" -o dist/js/tabler.esm.min.js\"",
"js:min:theme": "concurrently \"terser dist/js/tabler-theme.js --compress passes=2,ecma=2020 --ecma 2020 --mangle --source-map \\\"content=dist/js/tabler-theme.js.map,filename=dist/js/tabler-theme.min.js.map,url=tabler-theme.min.js.map\\\" -o dist/js/tabler-theme.min.js\" \"terser dist/js/tabler-theme.esm.js --module --compress passes=2,ecma=2020 --ecma 2020 --mangle --source-map \\\"content=dist/js/tabler-theme.esm.js.map,filename=dist/js/tabler-theme.esm.min.js.map,url=tabler-theme.esm.min.js.map\\\" -o dist/js/tabler-theme.esm.min.js\"",
"js:min:standalone": "terser dist/js/tabler.js --module --compress passes=2,ecma=2020 --ecma 2020 --mangle --source-map \"content=dist/js/tabler.js.map,filename=dist/js/tabler.min.js.map,url=tabler.min.js.map\" -o dist/js/tabler.min.js",
"js:min:theme": "terser dist/js/tabler-theme.js --module --compress passes=2,ecma=2020 --ecma 2020 --mangle --source-map \"content=dist/js/tabler-theme.js.map,filename=dist/js/tabler-theme.min.js.map,url=tabler-theme.min.js.map\" -o dist/js/tabler-theme.min.js",
"copy": "concurrently \"pnpm run copy:img\" \"pnpm run copy:libs\"",
"copy:img": "tsx .build/copy-img.ts",
"copy:libs": "tsx .build/copy-libs.ts",
@@ -75,11 +76,29 @@
],
"style": "dist/css/tabler.css",
"sass": "scss/tabler.scss",
"unpkg": "dist/js/tabler.min.js",
"umd:main": "dist/js/tabler.min.js",
"module": "dist/js/tabler.esm.js",
"main": "dist/js/tabler.js",
"module": "dist/js/tabler.js",
"types": "dist/types/tabler.d.ts",
"unpkg": "dist/js/tabler.min.js",
"jsdelivr": "dist/js/tabler.min.js",
"sideEffects": [
"./js/**",
"./dist/js/tabler.js",
"./dist/js/tabler-theme.js"
],
"exports": {
".": {
"types": "./dist/types/tabler.d.ts",
"import": "./dist/js/tabler.js"
},
"./theme": "./dist/js/tabler-theme.js",
"./package.json": "./package.json",
"./libs.json": "./libs.json",
"./scss/*": "./scss/*",
"./js/*.ts": "./js/*.ts",
"./js/*": "./js/*.ts",
"./dist/*": "./dist/*"
},
"bundlewatch": {
"files": [
{
@@ -218,29 +237,13 @@
"path": "./dist/js/tabler.min.js",
"maxSize": "40 kB"
},
{
"path": "./dist/js/tabler.esm.js",
"maxSize": "58 kB"
},
{
"path": "./dist/js/tabler.esm.min.js",
"maxSize": "40 kB"
},
{
"path": "./dist/js/tabler-theme.js",
"maxSize": "1.3 kB"
},
{
"path": "./dist/js/tabler-theme.esm.js",
"maxSize": "1.3 kB"
},
{
"path": "./dist/js/tabler-theme.min.js",
"maxSize": "800 B"
},
{
"path": "./dist/js/tabler-theme.esm.min.js",
"maxSize": "800 B"
}
]
},
+1 -1
View File
@@ -520,7 +520,7 @@ const relatedPages = await Promise.all(
<!-- BEGIN GLOBAL MANDATORY SCRIPTS -->
<script is:inline src="/dist/js/tabler.js" defer></script>
<script is:inline type="module" src="/dist/js/tabler.js"></script>
<!-- END GLOBAL MANDATORY SCRIPTS -->
<PageScripts />
@@ -177,7 +177,11 @@ const libJsFiles = pageLibEntries.flatMap(([, lib]) => (lib.js ?? []).map((file)
</nav>
{libJsFiles.map((src) => <script is:inline src={src} defer />)}
<script is:inline src={`/dist/js/tabler${min}.js`} defer></script>
<!-- ESM-only build: load as a module and re-expose the namespace as a local
shim so the capture pages' window.tabler.Dropdown/Offcanvas calls keep working.
A static import (not import()) so window.tabler is set before DOMContentLoaded,
the way the old classic script guaranteed. -->
<script is:inline type="module" set:html={`import * as tabler from '/dist/js/tabler${min}.js';\nwindow.tabler = tabler;`} />
<PageScripts />
+5 -1
View File
@@ -168,7 +168,11 @@ const libJsFiles = pageLibEntries.flatMap(([, lib]) => (lib.js ?? []).map((file)
</div>
{libJsFiles.map((src) => <script is:inline src={src} defer />)}
<script is:inline src={`/dist/js/tabler${min}.js`} defer></script>
<!-- ESM-only build: load as a module and re-expose the namespace as a local
shim so the capture pages' window.tabler.Dropdown/Offcanvas calls keep working.
A static import (not import()) so window.tabler is set before DOMContentLoaded,
the way the old classic script guaranteed. -->
<script is:inline type="module" set:html={`import * as tabler from '/dist/js/tabler${min}.js';\nwindow.tabler = tabler;`} />
<!-- components that render via CaptureScript (e.g. Chart) queue their init script
into shared/lib/page-scripts instead of inlining it where used; this drains it. -->
@@ -104,7 +104,9 @@ import CaptureScript from '../CaptureScript.astro'
for (const close of document.querySelectorAll('[data-navbar-notifications-close]')) {
close.onclick = () => {
const toggle = close.closest('.dropdown').querySelector('[data-bs-toggle="dropdown"]')
window.tabler.bootstrap.Dropdown.getOrCreateInstance(toggle).hide()
// ESM-only build: no window.tabler global. Close via Bootstrap's own
// data-API — clicking an open toggle dismisses the dropdown.
if (toggle && toggle.getAttribute('aria-expanded') === 'true') toggle.click()
}
}
</script>
+1 -1
View File
@@ -135,7 +135,7 @@ const libJsFiles = (head: boolean) => pageLibEntries.filter(([, lib]) => Boolean
}
<!-- BEGIN GLOBAL MANDATORY SCRIPTS -->
<script is:inline src={`${base}/dist/js/tabler${min}.js`} defer></script>
<script is:inline type="module" src={`${base}/dist/js/tabler${min}.js`}></script>
<!-- END GLOBAL MANDATORY SCRIPTS -->
<PageScripts />