From ad80ded69df6cfdc249f854e54ce9f211da5ef31 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 2 Oct 2026 12:28:02 +0200 Subject: [PATCH] Drop the backward-compat gates on v2-dev and fix lint and bundlewatch Remove check:compat and check:compat-fixture with their files and workflow steps, since 2.0 breaks the 1.x API on purpose. Format the mixins test and raise the tabler-props budget to 3.8 kB. --- .build/check-compat-fixture.ts | 172 ------------------- .build/check-compat.ts | 267 ----------------------------- .build/compat-baseline.txt | 14 -- .build/compat-fixture/README.md | 14 -- .build/compat-fixture/page.html | 51 ------ .build/compat-fixture/project.scss | 31 ---- .build/released-package.ts | 36 ---- .github/workflows/build.yml | 8 - .github/workflows/html-checks.yml | 5 - .github/workflows/release.yml | 9 - core/package.json | 4 +- core/scss/tests/_mixins.test.scss | 1 - package.json | 2 - 13 files changed, 2 insertions(+), 612 deletions(-) delete mode 100644 .build/check-compat-fixture.ts delete mode 100644 .build/check-compat.ts delete mode 100644 .build/compat-baseline.txt delete mode 100644 .build/compat-fixture/README.md delete mode 100644 .build/compat-fixture/page.html delete mode 100644 .build/compat-fixture/project.scss delete mode 100644 .build/released-package.ts diff --git a/.build/check-compat-fixture.ts b/.build/check-compat-fixture.ts deleted file mode 100644 index a216ce9dd..000000000 --- a/.build/check-compat-fixture.ts +++ /dev/null @@ -1,172 +0,0 @@ -// Runs a project written for the last release against that release and against -// the working tree, and fails when the two disagree. -// -// `check-compat` compares names: it knows that `--tblr-primary-rgb` still -// exists, not that `rgba(var(--tblr-primary-rgb), 0.5)` still paints. This check -// covers behaviour: the project in `.build/compat-fixture/` compiles its Sass -// with 1.5 variables and helpers, and its page uses 1.5 markup, the libraries in -// `dist/libs` and the old JavaScript helpers. Every assertion has to give the -// same answer on both packages. -// -// The release is checked too, on purpose: an assertion that fails there is a -// mistake in the fixture, not a break, and would otherwise hide one. -// -// Needs `core/dist`, so run it after the core build. -// -// Usage: tsx .build/check-compat-fixture.ts [--package ] -// -// `--package` points at another unpacked or built `@tabler/core` instead of -// `core/`, for example an older checkout. Without a `dist/` in it only the Sass -// half runs. -import { createReadStream, existsSync, statSync } from 'node:fs' -import { createServer, type Server } from 'node:http' -import { extname, join, resolve } from 'node:path' -import { chromium, type Page } from 'playwright' -import { compile as compileSass } from 'sass' -import { pkgName, releasedPackage } from './released-package' - -const fixtureDir = '.build/compat-fixture' -const packageArg = process.argv.indexOf('--package') -const coreDir = packageArg > -1 ? process.argv[packageArg + 1] : 'core' - -const MIME: Record = { '.html': 'text/html', '.css': 'text/css', '.js': 'text/javascript', '.svg': 'image/svg+xml', '.map': 'application/json' } - -type Result = Record - -// --- sass ------------------------------------------------------------------ - -function sassResults(pkg: string): Result { - const results: Result = {} - try { - const { css } = compileSass(join(fixtureDir, 'project.scss'), { loadPaths: [pkg, 'node_modules'], style: 'expanded', quietDeps: true, logger: { warn() {}, debug() {} } }) - results['sass: the project compiles'] = 'yes' - - const fixture = /\.fixture \{([^}]*)\}/.exec(css)?.[1] ?? '' - const value = (name: string): string => new RegExp(`${name}:\\s*([^;]+);`).exec(fixture)?.[1]?.trim() ?? '(missing)' - results['sass: $input-focus-width follows $input-btn-focus-width'] = value('--fixture-width') - results['sass: $theme-colors-rgb has the primary colour'] = value('--fixture-has-rgb-map') - results['sass: theme-color-darker() returns a colour'] = /^(#|rgb|oklch|color)/.test(value('color')) ? 'yes' : value('color') - results['sass: theme-color-lighter() takes the $primary variable'] = /^(#|rgb|oklch|color)/.test(value('border-color')) ? 'yes' : value('border-color') - results['sass: url-svg() builds a data uri'] = value('background-image').startsWith('url(') ? 'yes' : value('background-image') - results['sass: varify() wraps names in var()'] = value('transition-property') - results['sass: media-breakpoint-down-than(md) emits a media query'] = /@media \(max-width: 575\.98px\)\s*\{\s*\.fixture/.test(css) ? 'yes' : 'no' - results['sass: focus-ring($show-border: true) emits a focus style'] = /\.fixture:focus \{[^}]*(outline|box-shadow)/.test(css) ? 'yes' : 'no' - } catch (error) { - results['sass: the project compiles'] = `no — ${String((error as Error).message).split('\n')[0]}` - } - return results -} - -// --- page ------------------------------------------------------------------ - -function serve(pkg: string): Promise { - const server = createServer((request, response) => { - const path = decodeURIComponent((request.url ?? '/').split('?')[0]) - const file = path === '/' ? resolve(fixtureDir, 'page.html') : resolve(pkg, `.${path}`) - if (!existsSync(file) || !statSync(file).isFile()) { - response.writeHead(404).end() - return - } - response.writeHead(200, { 'content-type': MIME[extname(file)] ?? 'application/octet-stream' }) - createReadStream(file).pipe(response) - }) - return new Promise((done) => server.listen(0, '127.0.0.1', () => done(server))) -} - -async function pageResults(pkg: string, page: Page): Promise { - const results: Result = {} - const problems: string[] = [] - page.on('pageerror', (error) => problems.push(error.message)) - page.on('response', (response) => { - if (response.status() >= 400) problems.push(`${response.status()} ${new URL(response.url()).pathname}`) - }) - - const server = await serve(pkg) - const { port } = server.address() as { port: number } - await page.goto(`http://127.0.0.1:${port}/`, { waitUntil: 'networkidle' }) - await page.addStyleTag({ content: '*, *::before, *::after { transition: none !important; }' }) - - const style = (selector: string, property: string): Promise => page.evaluate(([s, p]) => getComputedStyle(document.querySelector(s)!).getPropertyValue(p), [selector, property]) - - results['page: no script error and no 404'] = problems.length === 0 ? 'yes' : problems.join('; ') - results['page: rgba(var(--tblr-primary-rgb), .5) paints'] = await style('#highlight', 'background-color') - results['page: an empty .legend is a visible dot'] = (await page.evaluate(() => document.querySelector('#legend')!.getBoundingClientRect().width)) > 0 ? 'yes' : 'no' - results['page: .form-hint is a block'] = await style('#hint', 'display') - results['page: .form-hint sits below the control'] = (await page.evaluate(() => document.querySelector('#hint')!.getBoundingClientRect().top >= document.querySelector('#email')!.getBoundingClientRect().bottom)) ? 'yes' : 'no' - - await page.focus('#button') - await page.keyboard.press('Shift+Tab') - await page.keyboard.press('Tab') - results['page: --tblr-btn-focus-box-shadow is drawn on focus'] = (await style('#button', 'box-shadow')).includes('rgb(255, 0, 0)') ? 'yes' : await style('#button', 'box-shadow') - - results['page: window.tabler.Modal exists'] = await page.evaluate(() => typeof (window as any).tabler?.Modal) - results['page: tabler.tabler.prefix'] = await page.evaluate(() => String((window as any).fixture?.prefix)) - results['page: tabler.tabler.getColor() returns a colour'] = (await page.evaluate(() => Boolean((window as any).fixture?.color))) ? 'yes' : 'no' - results['page: tabler.tabler.getColor(name, 0.5) returns a colour'] = (await page.evaluate(() => Boolean((window as any).fixture?.colorHalf))) ? 'yes' : 'no' - - for (const name of ['autosize', 'countUp', 'ClipboardJS', 'Litepicker']) { - results[`page: dist/libs provides window.${name}`] = await page.evaluate((n) => typeof (window as any)[n] !== 'undefined', name).then((ok) => (ok ? 'yes' : 'no')) - } - results['page: Litepicker renders with Tabler styles'] = (await page.evaluate(() => { - const picker = document.querySelector('.litepicker') - return picker ? getComputedStyle(picker).getPropertyValue('--litepicker-day-color').trim() !== '' : false - })) - ? 'yes' - : 'no' - - await page.fill('#message', 'one\ntwo\nthree\nfour\nfive\nsix') - await page.dispatchEvent('#message', 'input') - results['page: data-bs-toggle="autosize" grows the textarea'] = (await page.evaluate(() => (document.querySelector('#message') as HTMLElement).offsetHeight)) > 60 ? 'yes' : 'no' - - await page.waitForTimeout(600) - results['page: data-countup ends on the number'] = (await page.textContent('#total'))?.replace(/\D/g, '') ?? '' - - await page.click('#open-modal') - await page.waitForTimeout(100) - results['page: data-bs-toggle="modal" opens the modal'] = (await page.evaluate(() => document.querySelector('#modal')!.classList.contains('show'))) ? 'yes' : 'no' - - server.close() - return results -} - -// --- main ------------------------------------------------------------------ - -async function main() { - const hasDist = existsSync(join(coreDir, 'dist/css/tabler.css')) - if (!hasDist && packageArg === -1) { - throw new Error('core/dist is missing — build core first: pnpm --filter @tabler/core build') - } - - const { dir: released, version } = await releasedPackage() - const browser = await chromium.launch() - const run = async (pkg: string): Promise => { - const page = await browser.newPage() - const results = { ...sassResults(pkg), ...(hasDist ? await pageResults(pkg, page) : {}) } - await page.close() - return results - } - const before = await run(released) - const after = await run(coreDir) - await browser.close() - - const failed: string[] = [] - for (const [name, expected] of Object.entries(before)) { - const actual = after[name] ?? '(not run)' - const ok = actual === expected - console.log(`${ok ? '✓' : '✗'} ${name}${ok ? '' : `\n ${pkgName}@${version}: ${expected}\n working tree: ${actual}`}`) - if (!ok) failed.push(name) - } - - // The fixture is only worth something if the release passes it. - const broken = Object.entries(before).filter(([, value]) => value.startsWith('no') || value === '(missing)' || value === 'undefined') - for (const [name, value] of broken) console.error(`\n! ${name} fails on ${pkgName}@${version} itself (${value}) — fix the fixture.`) - - if (failed.length > 0) console.error(`\n${failed.length} of ${Object.keys(before).length} checks behave differently from ${pkgName}@${version}. See the backward-compat skill.`) - if (failed.length > 0 || broken.length > 0) process.exit(1) - console.log(`\nOK — a ${version} project compiles and behaves the same on the working tree (${Object.keys(before).length} checks).`) -} - -main().catch((error) => { - console.error(error) - process.exit(1) -}) diff --git a/.build/check-compat.ts b/.build/check-compat.ts deleted file mode 100644 index 7401c97d9..000000000 --- a/.build/check-compat.ts +++ /dev/null @@ -1,267 +0,0 @@ -// Fails when the build drops something the last release shipped. -// -// The README promises semver: a project on `^1.0.0` updates without touching its -// own code. A removed class, custom property, Sass variable or file breaks that -// promise, and nothing else notices — the build is green, the tests pass, and -// the first report comes from a user (#3108). This check downloads the last -// release of `@tabler/core` from npm and compares its public surface with the -// working tree: -// -// css classes and custom properties of every dist/css/*.css -// sass `$variables`, and `@function` / `@mixin` names and parameters, in scss/ -// sass-type a variable that was a Sass colour and is now a string: `darken($x, 5%)` -// in a project stops compiling, and no alias can fix a type -// js top-level exports of dist/js/tabler.esm.js -// file every path under dist/ (libs, images, types), and scss/ partials -// -// Known breaks are listed in compat-baseline.txt. A line ending in -// `# accepted: ` is a decision; a line without it is a break still to fix. -// The check fails on a break that is not listed, and on a listed one that no -// longer breaks, so fixing one means deleting its line. `--strict` also fails -// while any unaccepted line is left: the release runs it that way. -// -// The baseline names the release it was written against (`# against: 1.6.0`). -// Once a newer release is on npm, every listed break shipped in it, so none of -// them can break against it: the check ignores the stale list and only warns. -// `--reset` empties the list and points it at the current release. -// -// Needs `core/dist`, so run it after the core build. See the `backward-compat` -// skill for how to fix what it reports. -// -// Usage: tsx .build/check-compat.ts [--strict | --reset] -import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs' -import { join, relative } from 'node:path' -import { compileString } from 'sass' -import { pkgName, releasedPackage } from './released-package' - -const coreDir = 'core' -const baselineFile = '.build/compat-baseline.txt' -const strict = process.argv.includes('--strict') -const reset = process.argv.includes('--reset') -const againstPattern = /^#\s*against:\s*(\S+)\s*$/ - -function walk(root: string, dir = root): string[] { - if (!existsSync(dir)) return [] - return readdirSync(dir).flatMap((name) => { - const path = join(dir, name) - return statSync(path).isDirectory() ? walk(root, path) : [relative(root, path)] - }) -} - -const matches = (source: string, pattern: RegExp): Set => new Set([...source.matchAll(pattern)].map((match) => match[1])) - -// --- css ------------------------------------------------------------------- - -// A class is a dot followed by an identifier; `1.5rem` and `url(a.svg)` are not. -const cssClass = /(? css.replace(/\/\*[\s\S]*?\*\//g, '').replace(/url\([^)]*\)/g, 'url()') - -function cssBreaks(released: string, current: string): string[] { - const breaks: string[] = [] - const sheets = walk(join(released, 'dist/css')).filter((file) => file.endsWith('.css') && !file.includes('.min.') && !file.includes('.rtl.')) - - for (const sheet of sheets) { - const currentPath = join(current, 'dist/css', sheet) - if (!existsSync(currentPath)) continue // reported by fileBreaks - const before = stripCssNoise(readFileSync(join(released, 'dist/css', sheet), 'utf8')) - const after = stripCssNoise(readFileSync(currentPath, 'utf8')) - - const classesAfter = matches(after, cssClass) - for (const name of matches(before, cssClass)) { - if (!classesAfter.has(name)) breaks.push(`css:${sheet}:.${name}`) - } - const propertiesAfter = matches(after, cssProperty) - for (const name of matches(before, cssProperty)) { - if (!propertiesAfter.has(name)) breaks.push(`css:${sheet}:${name}`) - } - } - return breaks -} - -// --- sass ------------------------------------------------------------------ - -type Callable = { kind: string; name: string; params: string[] } - -const sassVariable = /^\$([\w-]+)\s*:/gm -const sassCallable = /^@(function|mixin)\s+([\w-]+)\s*(?:\(([\s\S]*?)\))?\s*\{/gm - -function splitParams(source: string | undefined): string[] { - if (!source) return [] - const params: string[] = [] - let depth = 0 - let current = '' - for (const char of source) { - if (char === '(') depth++ - if (char === ')') depth-- - if (char === ',' && depth === 0) { - params.push(current) - current = '' - } else { - current += char - } - } - params.push(current) - // `$color: rgba($white, 0.15)` → `$color`; `$args...` → `$args...` - return params.map((param) => param.trim().split(':')[0].trim()).filter(Boolean) -} - -function sassSurface(root: string): { variables: Set; callables: Map } { - const variables = new Set() - const callables = new Map() - for (const file of walk(join(root, 'scss'))) { - if (!file.endsWith('.scss') || file.startsWith('tests/')) continue - const source = readFileSync(join(root, 'scss', file), 'utf8').replace(/\/\/.*$/gm, '') - for (const name of matches(source, sassVariable)) variables.add(name) - for (const [, kind, name, params] of source.matchAll(sassCallable)) { - if (name.startsWith('_') || name.startsWith('-')) continue // private by convention - callables.set(`${kind} ${name}`, { kind: kind, name: name, params: splitParams(params) }) - } - } - return { variables, callables } -} - -function sassBreaks(released: string, current: string): string[] { - const breaks: string[] = [] - const before = sassSurface(released) - const after = sassSurface(current) - - for (const name of before.variables) { - if (!after.variables.has(name)) breaks.push(`sass:$${name}`) - } - for (const [key, callable] of before.callables) { - const now = after.callables.get(key) - if (!now) { - breaks.push(`sass:@${callable.kind} ${callable.name}`) - continue - } - // A caller passes arguments by position or by keyword: an old parameter has - // to keep both its name and its place. New ones go after it. - callable.params.forEach((param, index) => { - if (now.params[index] !== param) breaks.push(`sass:@${callable.kind} ${callable.name}(${param})`) - }) - } - return breaks -} - -// --- sass types ------------------------------------------------------------ - -// The type of every variable the config module exposes, read by Sass itself. -function sassTypes(root: string): Map { - const source = ` - @use 'sass:meta'; - @use 'scss/config' as config; - a { - @each $name, $value in meta.module-variables('config') { - --#{$name}: #{meta.type-of($value)}; - } - }` - const { css } = compileString(source, { loadPaths: [root, 'node_modules'], logger: { warn() {}, debug() {} } }) - return new Map([...css.matchAll(/--([\w-]+): (\w+);/g)].map((match) => [match[1], match[2]])) -} - -function sassTypeBreaks(released: string, current: string): string[] { - const after = sassTypes(current) - return [...sassTypes(released)].filter(([name, type]) => type === 'color' && after.has(name) && after.get(name) !== 'color').map(([name]) => `sass-type:$${name}`) -} - -// --- js -------------------------------------------------------------------- - -function esmExports(file: string): Set { - if (!existsSync(file)) return new Set() - const source = readFileSync(file, 'utf8') - const names = new Set() - for (const [, list] of source.matchAll(/^export\s*\{([^}]*)\}/gm)) { - for (const entry of list.split(',')) { - const name = entry - .trim() - .split(/\s+as\s+/) - .pop() - if (name) names.add(name) - } - } - for (const name of matches(source, /^export\s+(?:const|let|var|function|class)\s+([\w$]+)/gm)) names.add(name) - if (/^export\s+default\b/m.test(source)) names.add('default') - return names -} - -function jsBreaks(released: string, current: string): string[] { - const breaks: string[] = [] - for (const bundle of ['tabler.esm.js', 'tabler-theme.esm.js']) { - const after = esmExports(join(current, 'dist/js', bundle)) - for (const name of esmExports(join(released, 'dist/js', bundle))) { - if (!after.has(name)) breaks.push(`js:${bundle}:${name}`) - } - } - return breaks -} - -// --- files ----------------------------------------------------------------- - -function fileBreaks(released: string, current: string): string[] { - const now = new Set([...walk(join(current, 'dist')).map((file) => `dist/${file}`), ...walk(join(current, 'scss')).map((file) => `scss/${file}`)]) - const before = [...walk(join(released, 'dist')).map((file) => `dist/${file}`), ...walk(join(released, 'scss')).map((file) => `scss/${file}`)] - return before.filter((file) => !file.endsWith('.map') && !file.startsWith('scss/tests/') && !now.has(file)).map((file) => `file:${file}`) -} - -// --- main ------------------------------------------------------------------ - -async function main() { - if (!existsSync(join(coreDir, 'dist/css/tabler.css'))) { - throw new Error('core/dist is missing — build core first: pnpm --filter @tabler/core build') - } - - const { dir: released, version } = await releasedPackage() - const breaks = new Set([...cssBreaks(released, coreDir), ...sassBreaks(released, coreDir), ...sassTypeBreaks(released, coreDir), ...jsBreaks(released, coreDir), ...fileBreaks(released, coreDir)]) - - const lines = readFileSync(baselineFile, 'utf8').split('\n') - const againstIndex = lines.findIndex((line) => againstPattern.test(line)) - if (againstIndex === -1) throw new Error(`${baselineFile} has no \`# against: \` line`) - const against = againstPattern.exec(lines[againstIndex])![1] - - if (reset) { - writeFileSync(baselineFile, [...lines.slice(0, againstIndex), `# against: ${version}`, ''].join('\n')) - console.log(`${baselineFile} emptied and pointed at ${pkgName}@${version}.`) - return - } - - // Written against an older release: every listed break shipped in the current one. - const stale = against !== version - const baseline = new Map() // key → accepted - for (const line of stale ? [] : lines) { - const [entry, comment = ''] = line.split(' # ') - const key = entry.trim() - if (!key || key.startsWith('#')) continue - baseline.set(key, comment.trim().startsWith('accepted:')) - } - if (stale) { - console.warn(`${baselineFile} was written against ${pkgName}@${against}, npm has ${version}: its entries no longer apply. Run \`pnpm run check:compat --reset\` and commit the file.\n`) - } - - const added = [...breaks].filter((key) => !baseline.has(key)).sort() - const fixed = [...baseline.keys()].filter((key) => !breaks.has(key)) - const todo = [...baseline].filter(([key, accepted]) => !accepted && breaks.has(key)).map(([key]) => key) - - for (const key of added) console.error(`✗ ${key}`) - if (added.length > 0) { - console.error(`\n${added.length} new ${added.length === 1 ? 'break' : 'breaks'} against ${pkgName}@${version}. Keep the old name working as a deprecated alias — see the backward-compat skill.`) - } - if (fixed.length > 0) { - console.error(`\nNo longer ${fixed.length === 1 ? 'a break' : 'breaks'} — delete from ${baselineFile}:\n${fixed.map((key) => ` ${key}`).join('\n')}`) - } - if (strict && todo.length > 0) { - console.error(`\n--strict: ${todo.length} listed ${todo.length === 1 ? 'break is' : 'breaks are'} neither fixed nor accepted:\n${todo.map((key) => ` ${key}`).join('\n')}`) - } - if (added.length > 0 || fixed.length > 0 || (strict && todo.length > 0)) process.exit(1) - - const accepted = baseline.size - todo.length - console.log(`OK — nothing new is missing compared with ${pkgName}@${version} (${todo.length} known breaks still to fix, ${accepted} accepted, in ${baselineFile}).`) -} - -main().catch((error) => { - console.error(error) - process.exit(1) -}) diff --git a/.build/compat-baseline.txt b/.build/compat-baseline.txt deleted file mode 100644 index d954ebfe5..000000000 --- a/.build/compat-baseline.txt +++ /dev/null @@ -1,14 +0,0 @@ -# What the working tree no longer ships compared with the last release of -# @tabler/core - see check-compat.ts. The check fails on any break that is not -# listed here, and on a listed one that no longer breaks, so fixing one means -# deleting its line. -# -# A plain line is a break that still has to be fixed before the release: -# `check-compat --strict` fails while one is left. Add ` # accepted: ` -# to a line only after the maintainers decided to ship the break, for example -# an override for a class that a third-party library dropped. -# -# The line below names the release this list is written against. Once a newer -# release is on npm the list no longer applies: the check only warns, and -# `pnpm run check:compat --reset` empties it and moves the line to that release. -# against: 1.6.0 diff --git a/.build/compat-fixture/README.md b/.build/compat-fixture/README.md deleted file mode 100644 index 272e5ed39..000000000 --- a/.build/compat-fixture/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Compatibility fixture - -A small project written the way a Tabler **1.5** user would write it. It must -compile and behave the same on the last release from npm and on the working -tree. `pnpm run check:compat-fixture` runs it against both. - -- `project.scss` configures and reads 1.5 Sass variables, and calls 1.5 - functions and mixins. -- `page.html` loads the compiled files from `dist/` with 1.5 markup, the - libraries from `dist/libs`, and the old JavaScript helpers. - -Do not update this project when Tabler changes. When a check fails, fix Tabler: -see the `backward-compat` skill. Add to it only when a new kind of break shows -that it missed something a real project does. diff --git a/.build/compat-fixture/page.html b/.build/compat-fixture/page.html deleted file mode 100644 index e6f8a4750..000000000 --- a/.build/compat-fixture/page.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - Tabler 1.5 project - - - - - -
-
rgba() from the RGB triplet
- - - - We'll never share your email. - - -

1500

- - - - - -
- - - - - - - - - diff --git a/.build/compat-fixture/project.scss b/.build/compat-fixture/project.scss deleted file mode 100644 index b3df3819e..000000000 --- a/.build/compat-fixture/project.scss +++ /dev/null @@ -1,31 +0,0 @@ -// A 1.5 project that compiles Tabler from source. Every variable configured -// here existed in 1.5; configuring one that is gone is a Sass compile error. -@use 'scss/tabler' as tabler with ( - $input-btn-focus-width: 0.125rem, - $input-btn-focus-color-opacity: 0.4, - $btn-focus-box-shadow: 0 0 0 3px red, - $form-hint-margin-top: 0.5rem, - $form-switch-checked-bg-position: right center, - $pagination-focus-outline: 0, - $enable-deprecation-messages: false -); - -// Reading old variables and calling old helpers, as a project's own partials do. -.fixture { - --fixture-width: #{tabler.$input-focus-width}; - --fixture-primary-rgb: #{tabler.to-rgb(tabler.$primary)}; - --fixture-has-rgb-map: #{map-has-key(tabler.$theme-colors-rgb, 'primary')}; - color: tabler.theme-color-darker(#066fd1); - background: tabler.theme-color-lighter(#066fd1); - border-color: tabler.theme-color-lighter(tabler.$primary); - background-image: tabler.url-svg(''); - transition-property: tabler.varify((a, b)); - - &:focus { - @include tabler.focus-ring($show-border: true); - } - - @include tabler.media-breakpoint-down-than(md) { - display: none; - } -} diff --git a/.build/released-package.ts b/.build/released-package.ts deleted file mode 100644 index 32aa17363..000000000 --- a/.build/released-package.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Downloads the last release of `@tabler/core` from npm, once, and returns where -// it was unpacked. `check-compat` compares its public surface with the working -// tree; `check-compat-fixture` runs a 1.5-style project against both. -import { execFileSync } from 'node:child_process' -import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' -import { join } from 'node:path' - -export const pkgName = '@tabler/core' -const coreDir = 'core' -const cacheDir = 'node_modules/.cache/check-compat' - -const registry = 'https://registry.npmjs.org' - -export async function releasedPackage(): Promise<{ dir: string; version: string }> { - const local = JSON.parse(readFileSync(join(coreDir, 'package.json'), 'utf8')).version as string - // On the versions PR the local version is not published yet: compare with `latest`. - let version = local - let response = await fetch(`${registry}/${pkgName}/${version}`) - if (response.status === 404) { - response = await fetch(`${registry}/${pkgName}/latest`) - } - if (!response.ok) throw new Error(`npm registry answered ${response.status} for ${pkgName}`) - const manifest = (await response.json()) as { version: string; dist: { tarball: string } } - version = manifest.version - - const dir = join(cacheDir, version) - if (!existsSync(join(dir, 'package', 'package.json'))) { - mkdirSync(dir, { recursive: true }) - const tarball = await fetch(manifest.dist.tarball) - if (!tarball.ok) throw new Error(`could not download ${manifest.dist.tarball}: ${tarball.status}`) - const archive = join(dir, 'package.tgz') - writeFileSync(archive, Buffer.from(await tarball.arrayBuffer())) - execFileSync('tar', ['-xzf', archive, '-C', dir]) - } - return { dir: join(dir, 'package'), version } -} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 425e6682b..ab9dcf360 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,11 +29,3 @@ jobs: # turbo directly: `pnpm run build` also zips the package, which only the release needs - name: Build run: pnpm exec turbo build - - # Nothing the last release shipped may disappear in a minor release. On the - # versions pull request it runs with --strict, but workflows there wait for - # a manual approval, so the Release workflow runs --strict too. - - name: Check backward compatibility - env: - STRICT: ${{ github.head_ref == 'changeset-release/dev' && '--strict' || '' }} - run: pnpm run check:compat $STRICT diff --git a/.github/workflows/html-checks.yml b/.github/workflows/html-checks.yml index 3d4a8a43d..551a2ae0b 100644 --- a/.github/workflows/html-checks.yml +++ b/.github/workflows/html-checks.yml @@ -38,8 +38,3 @@ jobs: - name: Check markup classes run: pnpm run check:markup-classes --html preview/dist --html docs/dist --css preview/dist/dist/css --css preview/dist/dist/libs --css preview/dist/preview/css --css docs/dist/css --css docs/dist/preview/css - - # A project written for the last release has to compile and behave the - # same on this build. Runs here because it needs the core build and a browser. - - name: Check a project written for the last release - run: pnpm run check:compat-fixture diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 13d073d42..3a41c7be7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,15 +35,6 @@ jobs: - name: Build run: pnpm run build - # The last gate before npm: nothing the previous release shipped may be - # missing, unless compat-baseline.txt marks it as accepted. It runs here and - # not only on the versions pull request, because workflows on a pull request - # opened by the bot wait for a manual approval and may never run. On the - # push that publishes, the local version is not on npm yet, so the check - # compares with `latest`, which is the release users update from. - - name: Check backward compatibility - run: pnpm run check:compat --strict - - name: Create release Pull Request or publish to NPM id: changesets uses: changesets/action@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2 diff --git a/core/package.json b/core/package.json index 49efa76b8..0498e590c 100644 --- a/core/package.json +++ b/core/package.json @@ -171,7 +171,7 @@ }, { "path": "./dist/css/tabler-props.min.css", - "maxSize": "3.7 kB" + "maxSize": "3.8 kB" }, { "path": "./dist/css/tabler-props.rtl.css", @@ -179,7 +179,7 @@ }, { "path": "./dist/css/tabler-props.rtl.min.css", - "maxSize": "3.7 kB" + "maxSize": "3.8 kB" }, { "path": "./dist/css/tabler-socials.css", diff --git a/core/scss/tests/_mixins.test.scss b/core/scss/tests/_mixins.test.scss index 9d70904ba..8a93e349b 100644 --- a/core/scss/tests/_mixins.test.scss +++ b/core/scss/tests/_mixins.test.scss @@ -149,7 +149,6 @@ } } } - } @include describe('media-print mixin') { diff --git a/package.json b/package.json index f5dd6c512..1186dca73 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,6 @@ "check:sri": "tsx .build/generate-sri.ts check", "check:docs-links": "tsx .build/check-docs-links.ts", "check:css-vars": "tsx .build/check-css-vars.ts", - "check:compat": "tsx .build/check-compat.ts", - "check:compat-fixture": "tsx .build/check-compat-fixture.ts", "check:open-source": "tsx .build/check-open-source.ts", "check:html": "tsx .build/check-html.ts", "check:markup-classes": "tsx .build/check-markup-classes.ts",