Set up comprehensive frontend testing infrastructure

- Install Jest for unit testing with React Testing Library
- Install Playwright for end-to-end testing
- Configure Jest with proper TypeScript support and module mapping
- Create test setup files and utilities for both unit and e2e tests

Components:
* Jest configuration with coverage thresholds
* Playwright configuration with browser automation
* Unit tests for LoginForm, AuthContext, and useSocketIO hook
* E2E tests for authentication, dashboard, and agents workflows
* GitHub Actions workflow for automated testing
* Mock data and API utilities for consistent testing
* Test documentation with best practices

Testing features:
- Unit tests with 70% coverage threshold
- E2E tests with API mocking and user journey testing
- CI/CD integration for automated test runs
- Cross-browser testing support with Playwright
- Authentication system testing end-to-end

🚀 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-07-11 14:06:34 +10:00
parent c6d69695a8
commit aacb45156b
6109 changed files with 777927 additions and 1 deletions

96
.github/workflows/frontend-tests.yml vendored Normal file
View File

@@ -0,0 +1,96 @@
name: Frontend Tests
on:
push:
branches: [ master, main, develop ]
paths:
- 'frontend/**'
- '.github/workflows/frontend-tests.yml'
pull_request:
branches: [ master, main, develop ]
paths:
- 'frontend/**'
- '.github/workflows/frontend-tests.yml'
jobs:
unit-tests:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run type checking
run: npm run type-check
- name: Run linting
run: npm run lint
- name: Run unit tests
run: npm run test:coverage
- name: Upload coverage reports
uses: codecov/codecov-action@v4
with:
flags: frontend
directory: ./frontend/coverage
e2e-tests:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
- name: Build frontend
run: npm run build
- name: Run Playwright tests
run: npm run test:e2e
env:
CI: true
- name: Upload Playwright Report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: frontend/playwright-report/
retention-days: 30
- name: Upload E2E Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: e2e-results
path: frontend/e2e-results.xml
retention-days: 30

207
frontend/TESTING.md Normal file
View File

@@ -0,0 +1,207 @@
# Frontend Testing Infrastructure
This document describes the testing setup for the Hive frontend application.
## Overview
The testing infrastructure includes:
- **Unit Tests**: Jest + React Testing Library for component and hook testing
- **End-to-End Tests**: Playwright for full user journey testing
- **Type Checking**: TypeScript compiler for type safety
- **Linting**: ESLint for code quality
## Test Commands
### Unit Tests
```bash
# Run all unit tests
npm run test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage report
npm run test:coverage
```
### End-to-End Tests
```bash
# Run e2e tests headlessly
npm run test:e2e
# Run e2e tests with UI
npm run test:e2e:ui
# Debug e2e tests
npm run test:e2e:debug
```
### All Tests
```bash
# Run both unit and e2e tests
npm run test:all
```
## Test Structure
### Unit Tests
- Location: `src/**/__tests__/` or `src/**/*.test.tsx`
- Framework: Jest + React Testing Library
- Configuration: `jest.config.js`
- Setup: `src/test/setup.ts`
- Utilities: `src/test/utils.tsx`
Example test file structure:
```
src/
├── components/
│ └── auth/
│ ├── LoginForm.tsx
│ └── __tests__/
│ └── LoginForm.test.tsx
├── hooks/
│ └── __tests__/
│ └── useSocketIO.test.ts
└── test/
├── setup.ts
└── utils.tsx
```
### End-to-End Tests
- Location: `e2e/`
- Framework: Playwright
- Configuration: `playwright.config.ts`
- Global Setup: `e2e/global-setup.ts`
Example e2e test structure:
```
e2e/
├── auth.spec.ts
├── dashboard.spec.ts
├── agents.spec.ts
└── global-setup.ts
```
## Testing Best Practices
### Unit Testing
1. **Test user interactions, not implementation details**
2. **Use semantic queries** (getByRole, getByLabelText)
3. **Mock external dependencies** (APIs, WebSocket)
4. **Test error states and loading states**
5. **Maintain high coverage** (70%+ threshold)
### E2E Testing
1. **Test critical user journeys**
2. **Mock API responses** for consistent testing
3. **Use Page Object Model** for complex interactions
4. **Test responsive design** across viewports
5. **Include accessibility checks**
### Mocking Strategies
#### API Mocking (Unit Tests)
```typescript
jest.mock('../../api/auth');
const mockAuthApi = authApi as jest.Mocked<typeof authApi>;
mockAuthApi.login.mockResolvedValue(mockApiResponses.auth.login);
```
#### API Mocking (E2E Tests)
```typescript
await page.route('**/api/auth/login', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(mockResponse),
});
});
```
## Coverage Requirements
- **Statements**: 70%
- **Branches**: 70%
- **Functions**: 70%
- **Lines**: 70%
## CI/CD Integration
Tests run automatically on:
- Push to main branches
- Pull requests
- Frontend code changes
GitHub Actions workflow: `.github/workflows/frontend-tests.yml`
## Debugging Tests
### Unit Tests
```bash
# Debug specific test
npm run test -- --testNamePattern="LoginForm"
# Debug with Node.js inspector
node --inspect-brk node_modules/.bin/jest --runInBand
```
### E2E Tests
```bash
# Run in headed mode
npm run test:e2e:debug
# Run specific test
npx playwright test auth.spec.ts
# Open test results
npx playwright show-report
```
## Test Data
Mock data is centralized in `src/test/utils.tsx`:
- `mockUser`: Test user data
- `mockAgent`: Test agent data
- `mockTask`: Test task data
- `mockApiResponses`: API response templates
## Environment Setup
### Prerequisites
- Node.js 18+
- Chrome/Chromium (for Playwright)
### Installation
```bash
cd frontend
npm install
npx playwright install chromium
```
### Configuration Files
- `jest.config.js`: Jest configuration
- `playwright.config.ts`: Playwright configuration
- `src/test/setup.ts`: Jest test setup
- `e2e/global-setup.ts`: Playwright global setup
## Common Issues
### Jest Issues
- **Module resolution**: Check `moduleNameMapping` in jest.config.js
- **Async tests**: Use `act()` for async operations
- **React warnings**: Mock console methods in setup
### Playwright Issues
- **Timeouts**: Increase timeout in playwright.config.ts
- **Flaky tests**: Add proper wait conditions
- **Browser not found**: Run `npx playwright install`
## Contributing
When adding new features:
1. Write unit tests for components/hooks
2. Add e2e tests for new user flows
3. Update mock data if needed
4. Ensure coverage thresholds are met
5. Test both success and error scenarios

212
frontend/e2e/agents.spec.ts Normal file
View File

@@ -0,0 +1,212 @@
import { test, expect } from '@playwright/test';
test.describe('Agents Management', () => {
test.beforeEach(async ({ page }) => {
// Mock authentication
await page.addInitScript(() => {
localStorage.setItem('token', 'mock-jwt-token');
});
await page.route('**/api/auth/me', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
id: '123e4567-e89b-12d3-a456-426614174000',
username: 'testuser',
email: 'test@example.com',
full_name: 'Test User',
role: 'admin',
is_active: true,
is_superuser: true,
is_verified: true,
}),
});
});
// Mock agents API
await page.route('**/api/agents', async route => {
if (route.request().method() === 'GET') {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{
id: 'agent-1',
name: 'Walnut CodeLlama',
endpoint: 'http://walnut.local:11434',
model: 'codellama:34b',
specialty: 'kernel_dev',
status: 'online',
current_tasks: 1,
max_concurrent: 2,
agent_type: 'ollama',
last_heartbeat: Date.now(),
},
{
id: 'agent-2',
name: 'Oak Gemma',
endpoint: 'http://oak.local:11434',
model: 'gemma2:27b',
specialty: 'pytorch_dev',
status: 'offline',
current_tasks: 0,
max_concurrent: 2,
agent_type: 'ollama',
last_heartbeat: Date.now() - 300000, // 5 minutes ago
},
]),
});
}
});
});
test('should display agents list', async ({ page }) => {
await page.goto('/agents');
// Check page title
await expect(page.getByRole('heading', { name: /agents/i })).toBeVisible();
// Check agent cards
await expect(page.getByText('Walnut CodeLlama')).toBeVisible();
await expect(page.getByText('Oak Gemma')).toBeVisible();
// Check agent details
await expect(page.getByText('codellama:34b')).toBeVisible();
await expect(page.getByText('gemma2:27b')).toBeVisible();
await expect(page.getByText('kernel_dev')).toBeVisible();
await expect(page.getByText('pytorch_dev')).toBeVisible();
});
test('should show agent status indicators', async ({ page }) => {
await page.goto('/agents');
// Check online status
await expect(page.getByText('online')).toBeVisible();
await expect(page.getByText('offline')).toBeVisible();
});
test('should display agent utilization', async ({ page }) => {
await page.goto('/agents');
// Check task counts
await expect(page.getByText('1/2')).toBeVisible(); // Current/max tasks for agent-1
await expect(page.getByText('0/2')).toBeVisible(); // Current/max tasks for agent-2
});
test('should filter agents by status', async ({ page }) => {
await page.goto('/agents');
// Apply online filter
await page.getByRole('button', { name: /filter/i }).click();
await page.getByRole('option', { name: /online/i }).click();
// Should show only online agents
await expect(page.getByText('Walnut CodeLlama')).toBeVisible();
// Offline agent might be hidden depending on implementation
});
test('should search agents by name', async ({ page }) => {
await page.goto('/agents');
// Search for specific agent
const searchInput = page.getByPlaceholder(/search agents/i);
await searchInput.fill('Walnut');
// Should show filtered results
await expect(page.getByText('Walnut CodeLlama')).toBeVisible();
});
test('should show agent details in modal/popup', async ({ page }) => {
await page.goto('/agents');
// Click on agent to view details
await page.getByText('Walnut CodeLlama').click();
// Check that detail view opens
await expect(page.getByText('http://walnut.local:11434')).toBeVisible();
await expect(page.getByText('codellama:34b')).toBeVisible();
});
test('should handle agent creation for admin users', async ({ page }) => {
await page.goto('/agents');
// Mock agent creation
await page.route('**/api/agents', async route => {
if (route.request().method() === 'POST') {
await route.fulfill({
status: 201,
contentType: 'application/json',
body: JSON.stringify({
id: 'agent-3',
name: 'New Agent',
endpoint: 'http://localhost:11434',
model: 'llama3.1:8b',
specialty: 'general_ai',
status: 'online',
current_tasks: 0,
max_concurrent: 2,
agent_type: 'ollama',
}),
});
}
});
// Look for add agent button (admin only)
const addButton = page.getByRole('button', { name: /add agent/i });
await expect(addButton).toBeVisible();
// Click add agent
await addButton.click();
// Fill agent form
await page.getByLabel(/name/i).fill('New Agent');
await page.getByLabel(/endpoint/i).fill('http://localhost:11434');
await page.getByLabel(/model/i).fill('llama3.1:8b');
// Submit form
await page.getByRole('button', { name: /create/i }).click();
// Should show success message or new agent in list
await expect(page.getByText('New Agent')).toBeVisible();
});
test('should handle agent health checks', async ({ page }) => {
await page.goto('/agents');
// Mock health check endpoint
await page.route('**/api/agents/agent-1/health', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
status: 'healthy',
response_time: 125,
last_check: new Date().toISOString(),
}),
});
});
// Trigger health check
await page.getByRole('button', { name: /check health/i }).first().click();
// Should show health status
await expect(page.getByText(/healthy/i)).toBeVisible();
});
test('should handle agent errors gracefully', async ({ page }) => {
// Mock API error
await page.route('**/api/agents', async route => {
await route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ detail: 'Internal server error' }),
});
});
await page.goto('/agents');
// Should show error message
await expect(page.getByText(/error loading agents/i)).toBeVisible();
});
});

175
frontend/e2e/auth.spec.ts Normal file
View File

@@ -0,0 +1,175 @@
import { test, expect } from '@playwright/test';
test.describe('Authentication Flow', () => {
test.beforeEach(async ({ page }) => {
// Start fresh for each test
await page.goto('/');
});
test('should display login form on unauthenticated access', async ({ page }) => {
// Should redirect to login page
await expect(page).toHaveURL(/.*\/login/);
// Check login form elements
await expect(page.getByRole('heading', { name: /sign in/i })).toBeVisible();
await expect(page.getByLabel(/email/i)).toBeVisible();
await expect(page.getByLabel(/password/i)).toBeVisible();
await expect(page.getByRole('button', { name: /sign in/i })).toBeVisible();
});
test('should show validation errors for empty login form', async ({ page }) => {
await page.goto('/login');
// Try to submit empty form
await page.getByRole('button', { name: /sign in/i }).click();
// Check for validation errors
await expect(page.getByText(/email is required/i)).toBeVisible();
await expect(page.getByText(/password is required/i)).toBeVisible();
});
test('should show error for invalid email format', async ({ page }) => {
await page.goto('/login');
// Enter invalid email
await page.getByLabel(/email/i).fill('invalid-email');
await page.getByLabel(/password/i).fill('password123');
await page.getByRole('button', { name: /sign in/i }).click();
// Check for validation error
await expect(page.getByText(/invalid email format/i)).toBeVisible();
});
test('should handle login failure gracefully', async ({ page }) => {
await page.goto('/login');
// Mock failed login response
await page.route('**/api/auth/login', async route => {
await route.fulfill({
status: 401,
contentType: 'application/json',
body: JSON.stringify({ detail: 'Invalid credentials' }),
});
});
// Fill and submit form
await page.getByLabel(/email/i).fill('test@example.com');
await page.getByLabel(/password/i).fill('wrongpassword');
await page.getByRole('button', { name: /sign in/i }).click();
// Check for error message
await expect(page.getByText(/invalid credentials/i)).toBeVisible();
// Should still be on login page
await expect(page).toHaveURL(/.*\/login/);
});
test('should successfully login with valid credentials', async ({ page }) => {
await page.goto('/login');
// Mock successful login response
await page.route('**/api/auth/login', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
access_token: 'mock-jwt-token',
token_type: 'bearer',
user: {
id: '123e4567-e89b-12d3-a456-426614174000',
username: 'testuser',
email: 'test@example.com',
full_name: 'Test User',
role: 'user',
is_active: true,
is_superuser: false,
is_verified: true,
},
}),
});
});
// Mock the /me endpoint for authenticated user
await page.route('**/api/auth/me', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
id: '123e4567-e89b-12d3-a456-426614174000',
username: 'testuser',
email: 'test@example.com',
full_name: 'Test User',
role: 'user',
is_active: true,
is_superuser: false,
is_verified: true,
}),
});
});
// Fill and submit form
await page.getByLabel(/email/i).fill('test@example.com');
await page.getByLabel(/password/i).fill('password123');
await page.getByRole('button', { name: /sign in/i }).click();
// Should redirect to dashboard
await expect(page).toHaveURL(/.*\/dashboard/);
// Check that user is logged in (user menu should be visible)
await expect(page.getByText('Test User')).toBeVisible();
});
test('should toggle password visibility', async ({ page }) => {
await page.goto('/login');
const passwordInput = page.getByLabel(/password/i);
const toggleButton = page.getByRole('button', { name: /toggle password visibility/i });
// Initially password should be hidden
await expect(passwordInput).toHaveAttribute('type', 'password');
// Click toggle to show password
await toggleButton.click();
await expect(passwordInput).toHaveAttribute('type', 'text');
// Click toggle again to hide password
await toggleButton.click();
await expect(passwordInput).toHaveAttribute('type', 'password');
});
test('should logout successfully', async ({ page }) => {
// First login
await page.goto('/login');
// Mock successful login
await page.route('**/api/auth/login', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
access_token: 'mock-jwt-token',
token_type: 'bearer',
user: {
id: '123e4567-e89b-12d3-a456-426614174000',
username: 'testuser',
email: 'test@example.com',
full_name: 'Test User',
},
}),
});
});
await page.getByLabel(/email/i).fill('test@example.com');
await page.getByLabel(/password/i).fill('password123');
await page.getByRole('button', { name: /sign in/i }).click();
// Wait for dashboard
await expect(page).toHaveURL(/.*\/dashboard/);
// Click logout
await page.getByRole('button', { name: /logout/i }).click();
// Should redirect to login page
await expect(page).toHaveURL(/.*\/login/);
});
});

View File

@@ -0,0 +1,155 @@
import { test, expect } from '@playwright/test';
test.describe('Dashboard', () => {
test.beforeEach(async ({ page }) => {
// Mock authentication
await page.addInitScript(() => {
localStorage.setItem('token', 'mock-jwt-token');
});
// Mock authenticated user endpoint
await page.route('**/api/auth/me', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
id: '123e4567-e89b-12d3-a456-426614174000',
username: 'testuser',
email: 'test@example.com',
full_name: 'Test User',
role: 'user',
is_active: true,
is_superuser: false,
is_verified: true,
}),
});
});
// Mock dashboard data endpoints
await page.route('**/api/agents', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{
id: 'agent-1',
name: 'Test Agent',
endpoint: 'http://localhost:11434',
model: 'llama3.1:8b',
specialty: 'general_ai',
status: 'online',
current_tasks: 1,
max_concurrent: 2,
},
]),
});
});
await page.route('**/api/tasks', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{
id: 'task-1',
title: 'Test Task',
type: 'general_ai',
status: 'pending',
priority: 3,
created_at: new Date().toISOString(),
},
]),
});
});
await page.route('**/api/health', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
status: 'operational',
agents: {},
total_agents: 1,
active_tasks: 1,
pending_tasks: 0,
completed_tasks: 5,
}),
});
});
});
test('should display dashboard content when authenticated', async ({ page }) => {
await page.goto('/dashboard');
// Check dashboard elements
await expect(page.getByRole('heading', { name: /dashboard/i })).toBeVisible();
await expect(page.getByText('Test User')).toBeVisible();
// Check for system status cards
await expect(page.getByText(/agents/i)).toBeVisible();
await expect(page.getByText(/tasks/i)).toBeVisible();
});
test('should display agent status', async ({ page }) => {
await page.goto('/dashboard');
// Wait for agents to load
await expect(page.getByText('Test Agent')).toBeVisible();
await expect(page.getByText(/online/i)).toBeVisible();
});
test('should display task information', async ({ page }) => {
await page.goto('/dashboard');
// Wait for tasks to load
await expect(page.getByText('Test Task')).toBeVisible();
await expect(page.getByText(/pending/i)).toBeVisible();
});
test('should navigate to different sections', async ({ page }) => {
await page.goto('/dashboard');
// Test navigation to agents page
await page.getByRole('link', { name: /agents/i }).click();
await expect(page).toHaveURL(/.*\/agents/);
// Navigate back to dashboard
await page.getByRole('link', { name: /dashboard/i }).click();
await expect(page).toHaveURL(/.*\/dashboard/);
// Test navigation to tasks page
await page.getByRole('link', { name: /tasks/i }).click();
await expect(page).toHaveURL(/.*\/tasks/);
});
test('should display system metrics', async ({ page }) => {
await page.goto('/dashboard');
// Check for metric cards
await expect(page.getByText('1')).toBeVisible(); // Total agents
await expect(page.getByText('5')).toBeVisible(); // Completed tasks
});
test('should handle real-time updates via WebSocket', async ({ page }) => {
await page.goto('/dashboard');
// Mock WebSocket connection
await page.evaluate(() => {
// Simulate WebSocket message
window.dispatchEvent(new CustomEvent('socket-message', {
detail: {
type: 'task_update',
data: {
id: 'task-1',
status: 'completed'
}
}
}));
});
// Check that the task status updates
// Note: This would require the actual WebSocket implementation
// For now, we just verify the dashboard loads properly
await expect(page.getByRole('heading', { name: /dashboard/i })).toBeVisible();
});
});

View File

@@ -0,0 +1,31 @@
import { chromium, FullConfig } from '@playwright/test';
async function globalSetup(config: FullConfig) {
console.log('🚀 Starting global setup for Playwright tests...');
// Check if backend is running
try {
const browser = await chromium.launch();
const page = await browser.newPage();
// Try to reach the backend health endpoint
try {
const response = await page.request.get('http://localhost:8087/health');
if (!response.ok()) {
console.warn('⚠️ Backend health check failed. Some tests may fail.');
} else {
console.log('✅ Backend is running and healthy');
}
} catch (error) {
console.warn('⚠️ Could not reach backend. Some tests may fail:', error);
}
await browser.close();
} catch (error) {
console.error('❌ Global setup failed:', error);
}
console.log('✅ Global setup completed');
}
export default globalSetup;

40
frontend/jest.config.js Normal file
View File

@@ -0,0 +1,40 @@
/** @type {import('jest').Config} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
roots: ['<rootDir>/src'],
testMatch: [
'**/__tests__/**/*.(ts|tsx|js)',
'**/*.(test|spec).(ts|tsx|js)'
],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
},
collectCoverageFrom: [
'src/**/*.(ts|tsx)',
'!src/**/*.d.ts',
'!src/main.tsx',
'!src/vite-env.d.ts'
],
moduleNameMapping: {
'^@/(.*)$': '<rootDir>/src/$1',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'jest-transform-stub'
},
setupFilesAfterEnv: ['<rootDir>/src/test/setup.ts'],
testPathIgnorePatterns: [
'<rootDir>/node_modules/',
'<rootDir>/dist/',
'<rootDir>/e2e/'
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70
}
},
testTimeout: 10000
};

1
frontend/node_modules/.bin/ejs generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../ejs/bin/cli.js

1
frontend/node_modules/.bin/esparse generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../esprima/bin/esparse.js

1
frontend/node_modules/.bin/esvalidate generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../esprima/bin/esvalidate.js

1
frontend/node_modules/.bin/import-local-fixture generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../import-local/fixtures/cli.js

1
frontend/node_modules/.bin/jake generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../jake/bin/cli.js

1
frontend/node_modules/.bin/jest generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../jest/bin/jest.js

1
frontend/node_modules/.bin/lz-string generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../lz-string/bin/bin.js

1
frontend/node_modules/.bin/napi-postinstall generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../napi-postinstall/lib/cli.js

1
frontend/node_modules/.bin/playwright generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../@playwright/test/cli.js

1
frontend/node_modules/.bin/playwright-core generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../playwright-core/cli.js

1
frontend/node_modules/.bin/tldts generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../tldts/bin/cli.js

1
frontend/node_modules/.bin/ts-jest generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../ts-jest/cli.js

File diff suppressed because it is too large Load Diff

10
frontend/node_modules/@adobe/css-tools/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,10 @@
(The MIT License)
Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
Copyright (c) 2022 Jean-Philippe Zolesio <holblin@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

327
frontend/node_modules/@adobe/css-tools/Readme.md generated vendored Normal file
View File

@@ -0,0 +1,327 @@
# @adobe/css-tools
> This is a fork of the npm css package due to low maintenance
CSS parser / stringifier.
## Installation
$ npm install @adobe/css-tools
## Usage
```js
import { parse, stringify } from '@adobe/css-tools'
let obj = parse('body { font-size: 12px; }', options);
let css = stringify(obj, options);
```
## API
### parse(code, [options])
Accepts a CSS string and returns an AST `object`.
`options`:
- silent: silently fail on parse errors.
- source: the path to the file containing `css`. Makes errors and source
maps more helpful, by letting them know where code comes from.
### stringify(object, [options])
Accepts an AST `object` (as `css.parse` produces) and returns a CSS string.
`options`:
- indent: the string used to indent the output. Defaults to two spaces.
- compress: omit comments and extraneous whitespace.
### Example
```js
var ast = parse('body { font-size: 12px; }', { source: 'source.css' });
var css = stringify(ast);
```
### Errors
Errors thrown during parsing have the following properties:
- message: `String`. The full error message with the source position.
- reason: `String`. The error message without position.
- filename: `String` or `undefined`. The value of `options.source` if
passed to `css.parse`. Otherwise `undefined`.
- line: `Integer`.
- column: `Integer`.
- source: `String`. The portion of code that couldn't be parsed.
When parsing with the `silent` option, errors are listed in the
`parsingErrors` property of the [`stylesheet`](#stylesheet) node instead
of being thrown.
If you create any errors in plugins such as in
[rework](https://github.com/reworkcss/rework), you __must__ set the same
properties for consistency.
## AST
Interactively explore the AST with <http://iamdustan.com/reworkcss_ast_explorer/>.
### Common properties
All nodes have the following properties.
#### position
Information about the position in the source string that corresponds to
the node.
`Object`:
- start: `Object`:
- line: `Number`.
- column: `Number`.
- end: `Object`:
- line: `Number`.
- column: `Number`.
- source: `String` or `undefined`. The value of `options.source` if passed to
`css.parse`. Otherwise `undefined`.
- content: `String`. The full source string passed to `css.parse`.
The line and column numbers are 1-based: The first line is 1 and the first
column of a line is 1 (not 0).
The `position` property lets you know from which source file the node comes
from (if available), what that file contains, and what part of that file was
parsed into the node.
#### type
`String`. The possible values are the ones listed in the Types section below.
#### parent
A reference to the parent node, or `null` if the node has no parent.
### Types
The available values of `node.type` are listed below, as well as the available
properties of each node (other than the common properties listed above.)
#### stylesheet
The root node returned by `css.parse`.
- stylesheet: `Object`:
- rules: `Array` of nodes with the types `rule`, `comment` and any of the
at-rule types.
- parsingErrors: `Array` of `Error`s. Errors collected during parsing when
option `silent` is true.
#### rule
- selectors: `Array` of `String`s. The list of selectors of the rule, split
on commas. Each selector is trimmed from whitespace and comments.
- declarations: `Array` of nodes with the types `declaration` and `comment`.
#### declaration
- property: `String`. The property name, trimmed from whitespace and
comments. May not be empty.
- value: `String`. The value of the property, trimmed from whitespace and
comments. Empty values are allowed.
#### comment
A rule-level or declaration-level comment. Comments inside selectors,
properties and values etc. are lost.
- comment: `String`. The part between the starting `/*` and the ending `*/`
of the comment, including whitespace.
#### charset
The `@charset` at-rule.
- charset: `String`. The part following `@charset `.
#### custom-media
The `@custom-media` at-rule.
- name: `String`. The `--`-prefixed name.
- media: `String`. The part following the name.
#### document
The `@document` at-rule.
- document: `String`. The part following `@document `.
- vendor: `String` or `undefined`. The vendor prefix in `@document`, or
`undefined` if there is none.
- rules: `Array` of nodes with the types `rule`, `comment` and any of the
at-rule types.
#### font-face
The `@font-face` at-rule.
- declarations: `Array` of nodes with the types `declaration` and `comment`.
#### host
The `@host` at-rule.
- rules: `Array` of nodes with the types `rule`, `comment` and any of the
at-rule types.
#### import
The `@import` at-rule.
- import: `String`. The part following `@import `.
#### keyframes
The `@keyframes` at-rule.
- name: `String`. The name of the keyframes rule.
- vendor: `String` or `undefined`. The vendor prefix in `@keyframes`, or
`undefined` if there is none.
- keyframes: `Array` of nodes with the types `keyframe` and `comment`.
#### keyframe
- values: `Array` of `String`s. The list of “selectors” of the keyframe rule,
split on commas. Each “selector” is trimmed from whitespace.
- declarations: `Array` of nodes with the types `declaration` and `comment`.
#### media
The `@media` at-rule.
- media: `String`. The part following `@media `.
- rules: `Array` of nodes with the types `rule`, `comment` and any of the
at-rule types.
#### namespace
The `@namespace` at-rule.
- namespace: `String`. The part following `@namespace `.
#### page
The `@page` at-rule.
- selectors: `Array` of `String`s. The list of selectors of the rule, split
on commas. Each selector is trimmed from whitespace and comments.
- declarations: `Array` of nodes with the types `declaration` and `comment`.
#### supports
The `@supports` at-rule.
- supports: `String`. The part following `@supports `.
- rules: `Array` of nodes with the types `rule`, `comment` and any of the
at-rule types.
### container
The `@container` at-rule.
- conatiner: `String`. The part following `@container `.
- rules: `Array` of nodes with the types `rule`, `comment` and any of the
at-rule types.
### layer
The `@layer` at-rule.
- layer: `String`. The part following `@layer `.
- rules: `Array` of nodes with the types `rule`, `comment` and any of the
at-rule types. This may be null, if the rule did not contain any.
### starting-style
The `@starting-style` at-rule.
- rules: `Array` of nodes with the types `rule`, `comment` and any of the
at-rule types.
### Example
CSS:
```css
body {
background: #eee;
color: #888;
}
```
Parse tree:
```json
{
"type": "stylesheet",
"stylesheet": {
"rules": [
{
"type": "rule",
"selectors": [
"body"
],
"declarations": [
{
"type": "declaration",
"property": "background",
"value": "#eee",
"position": {
"start": {
"line": 2,
"column": 3
},
"end": {
"line": 2,
"column": 19
}
}
},
{
"type": "declaration",
"property": "color",
"value": "#888",
"position": {
"start": {
"line": 3,
"column": 3
},
"end": {
"line": 3,
"column": 14
}
}
}
],
"position": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 4,
"column": 2
}
}
}
]
}
}
```
## License
MIT

886
frontend/node_modules/@adobe/css-tools/dist/index.cjs generated vendored Normal file
View File

@@ -0,0 +1,886 @@
function $parcel$defineInteropFlag(a) {
Object.defineProperty(a, '__esModule', {value: true, configurable: true});
}
function $parcel$exportWildcard(dest, source) {
Object.keys(source).forEach(function(key) {
if (key === 'default' || key === '__esModule' || Object.prototype.hasOwnProperty.call(dest, key)) {
return;
}
Object.defineProperty(dest, key, {
enumerable: true,
get: function get() {
return source[key];
}
});
});
return dest;
}
function $parcel$export(e, n, v, s) {
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
}
$parcel$defineInteropFlag(module.exports);
$parcel$export(module.exports, "parse", () => $882b6d93070905b3$export$98e6a39c04603d36);
$parcel$export(module.exports, "stringify", () => $882b6d93070905b3$export$fac44ee5b035f737);
$parcel$export(module.exports, "default", () => $882b6d93070905b3$export$2e2bcd8739ae039);
var $cb508b9219b02820$exports = {};
$parcel$defineInteropFlag($cb508b9219b02820$exports);
$parcel$export($cb508b9219b02820$exports, "default", () => $cb508b9219b02820$export$2e2bcd8739ae039);
class $cb508b9219b02820$export$2e2bcd8739ae039 extends Error {
constructor(filename, msg, lineno, column, css){
super(filename + ':' + lineno + ':' + column + ': ' + msg);
this.reason = msg;
this.filename = filename;
this.line = lineno;
this.column = column;
this.source = css;
}
}
var $4bafb28828007b46$exports = {};
$parcel$defineInteropFlag($4bafb28828007b46$exports);
$parcel$export($4bafb28828007b46$exports, "default", () => $4bafb28828007b46$export$2e2bcd8739ae039);
/**
* Store position information for a node
*/ class $4bafb28828007b46$export$2e2bcd8739ae039 {
constructor(start, end, source){
this.start = start;
this.end = end;
this.source = source;
}
}
var $d103407e81c97042$exports = {};
$parcel$export($d103407e81c97042$exports, "CssTypes", () => $d103407e81c97042$export$9be5dd6e61d5d73a);
var $d103407e81c97042$export$9be5dd6e61d5d73a = /*#__PURE__*/ function(CssTypes) {
CssTypes["stylesheet"] = "stylesheet";
CssTypes["rule"] = "rule";
CssTypes["declaration"] = "declaration";
CssTypes["comment"] = "comment";
CssTypes["container"] = "container";
CssTypes["charset"] = "charset";
CssTypes["document"] = "document";
CssTypes["customMedia"] = "custom-media";
CssTypes["fontFace"] = "font-face";
CssTypes["host"] = "host";
CssTypes["import"] = "import";
CssTypes["keyframes"] = "keyframes";
CssTypes["keyframe"] = "keyframe";
CssTypes["layer"] = "layer";
CssTypes["media"] = "media";
CssTypes["namespace"] = "namespace";
CssTypes["page"] = "page";
CssTypes["startingStyle"] = "starting-style";
CssTypes["supports"] = "supports";
return CssTypes;
}({});
const $6fedb2016a78880b$export$82eb5486fce3d340 = 10000;
const $6fedb2016a78880b$export$c8b95ffeec50f24a = (string, search, position)=>{
let currentPosition = position;
let maxLoop = $6fedb2016a78880b$export$82eb5486fce3d340;
do {
const all = search.map((v)=>string.indexOf(v, currentPosition));
all.push(string.indexOf('\\', currentPosition));
const foundAll = all.filter((v)=>v !== -1);
if (foundAll.length === 0) return -1;
const found = Math.min(...foundAll);
if (string[found] === '\\') {
currentPosition = found + 2;
maxLoop--;
} else return found;
}while (maxLoop > 0);
throw new Error('Too many escaping');
};
const $6fedb2016a78880b$export$b8d42a8583d2e477 = (string, search, position)=>{
let currentSearchPosition = position;
let maxLoop = $6fedb2016a78880b$export$82eb5486fce3d340;
do {
const all = search.map((v)=>string.indexOf(v, currentSearchPosition));
all.push(string.indexOf('(', currentSearchPosition));
all.push(string.indexOf('"', currentSearchPosition));
all.push(string.indexOf("'", currentSearchPosition));
all.push(string.indexOf('\\', currentSearchPosition));
const foundAll = all.filter((v)=>v !== -1);
if (foundAll.length === 0) return -1;
const firstMatchPos = Math.min(...foundAll);
const char = string[firstMatchPos];
switch(char){
case '\\':
currentSearchPosition = firstMatchPos + 2;
break;
case '(':
{
const endPosition = $6fedb2016a78880b$export$b8d42a8583d2e477(string, [
')'
], firstMatchPos + 1);
if (endPosition === -1) return -1;
currentSearchPosition = endPosition + 1;
}
break;
case '"':
{
const endQuotePosition = $6fedb2016a78880b$export$c8b95ffeec50f24a(string, [
'"'
], firstMatchPos + 1);
if (endQuotePosition === -1) return -1;
currentSearchPosition = endQuotePosition + 1;
}
break;
case "'":
{
const endQuotePosition = $6fedb2016a78880b$export$c8b95ffeec50f24a(string, [
"'"
], firstMatchPos + 1);
if (endQuotePosition === -1) return -1;
currentSearchPosition = endQuotePosition + 1;
}
break;
default:
return firstMatchPos;
}
maxLoop--;
}while (maxLoop > 0);
throw new Error('Too many escaping');
};
const $6fedb2016a78880b$export$801dd37ac183521b = (string, search)=>{
const result = [];
let currentPosition = 0;
while(currentPosition < string.length){
const index = $6fedb2016a78880b$export$b8d42a8583d2e477(string, search, currentPosition);
if (index === -1) {
result.push(string.substring(currentPosition));
return result;
}
result.push(string.substring(currentPosition, index));
currentPosition = index + 1;
}
return result;
};
// http://www.w3.org/TR/CSS21/grammar.html
// https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027
// New rule => https://www.w3.org/TR/CSS22/syndata.html#comments
// [^] is equivalent to [.\n\r]
const $b499486c7f02abe7$var$commentre = /\/\*[^]*?(?:\*\/|$)/g;
const $b499486c7f02abe7$export$98e6a39c04603d36 = (css, options)=>{
options = options || {};
/**
* Positional.
*/ let lineno = 1;
let column = 1;
/**
* Update lineno and column based on `str`.
*/ function updatePosition(str) {
const lines = str.match(/\n/g);
if (lines) lineno += lines.length;
const i = str.lastIndexOf('\n');
column = ~i ? str.length - i : column + str.length;
}
/**
* Mark position and patch `node.position`.
*/ function position() {
const start = {
line: lineno,
column: column
};
return function(node) {
node.position = new (0, $4bafb28828007b46$export$2e2bcd8739ae039)(start, {
line: lineno,
column: column
}, options?.source || '');
whitespace();
return node;
};
}
/**
* Error `msg`.
*/ const errorsList = [];
function error(msg) {
const err = new (0, $cb508b9219b02820$export$2e2bcd8739ae039)(options?.source || '', msg, lineno, column, css);
if (options?.silent) errorsList.push(err);
else throw err;
}
/**
* Parse stylesheet.
*/ function stylesheet() {
const rulesList = rules();
const result = {
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).stylesheet,
stylesheet: {
source: options?.source,
rules: rulesList,
parsingErrors: errorsList
}
};
return result;
}
/**
* Opening brace.
*/ function open() {
const openMatch = /^{\s*/.exec(css);
if (openMatch) {
processMatch(openMatch);
return true;
}
return false;
}
/**
* Closing brace.
*/ function close() {
const closeMatch = /^}/.exec(css);
if (closeMatch) {
processMatch(closeMatch);
return true;
}
return false;
}
/**
* Parse ruleset.
*/ function rules() {
let node;
const rules = [];
whitespace();
comments(rules);
while(css.length && css.charAt(0) !== '}' && (node = atrule() || rule()))if (node) {
rules.push(node);
comments(rules);
}
return rules;
}
/**
* Update position and css string. Return the matches
*/ function processMatch(m) {
const str = m[0];
updatePosition(str);
css = css.slice(str.length);
return m;
}
/**
* Parse whitespace.
*/ function whitespace() {
const m = /^\s*/.exec(css);
if (m) processMatch(m);
}
/**
* Parse comments;
*/ function comments(rules) {
let c;
rules = rules || [];
while(c = comment())if (c) rules.push(c);
return rules;
}
/**
* Parse comment.
*/ function comment() {
const pos = position();
if ('/' !== css.charAt(0) || '*' !== css.charAt(1)) return;
const m = /^\/\*[^]*?\*\//.exec(css);
if (!m) return error('End of comment missing');
processMatch(m);
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).comment,
comment: m[0].slice(2, -2)
});
}
/**
* Parse selector.
*/ function selector() {
const m = /^([^{]+)/.exec(css);
if (!m) return;
processMatch(m);
// remove comment in selector;
const res = $b499486c7f02abe7$var$trim(m[0]).replace($b499486c7f02abe7$var$commentre, '');
return (0, $6fedb2016a78880b$export$801dd37ac183521b)(res, [
','
]).map((v)=>$b499486c7f02abe7$var$trim(v));
}
/**
* Parse declaration.
*/ function declaration() {
const pos = position();
// prop
const propMatch = /^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/.exec(css);
if (!propMatch) return;
processMatch(propMatch);
const propValue = $b499486c7f02abe7$var$trim(propMatch[0]);
// :
const sepratotorMatch = /^:\s*/.exec(css);
if (!sepratotorMatch) return error("property missing ':'");
processMatch(sepratotorMatch);
// val
let value = '';
const endValuePosition = (0, $6fedb2016a78880b$export$b8d42a8583d2e477)(css, [
';',
'}'
]);
if (endValuePosition !== -1) {
value = css.substring(0, endValuePosition);
const fakeMatch = [
value
];
processMatch(fakeMatch);
value = $b499486c7f02abe7$var$trim(value).replace($b499486c7f02abe7$var$commentre, '');
}
const ret = pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).declaration,
property: propValue.replace($b499486c7f02abe7$var$commentre, ''),
value: value
});
// ;
const endMatch = /^[;\s]*/.exec(css);
if (endMatch) processMatch(endMatch);
return ret;
}
/**
* Parse declarations.
*/ function declarations() {
const decls = [];
if (!open()) return error("missing '{'");
comments(decls);
// declarations
let decl;
while(decl = declaration())if (decl) {
decls.push(decl);
comments(decls);
}
if (!close()) return error("missing '}'");
return decls;
}
/**
* Parse keyframe.
*/ function keyframe() {
let m;
const vals = [];
const pos = position();
while(m = /^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/.exec(css)){
const res = processMatch(m);
vals.push(res[1]);
const spacesMatch = /^,\s*/.exec(css);
if (spacesMatch) processMatch(spacesMatch);
}
if (!vals.length) return;
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).keyframe,
values: vals,
declarations: declarations() || []
});
}
/**
* Parse keyframes.
*/ function atkeyframes() {
const pos = position();
const m1 = /^@([-\w]+)?keyframes\s*/.exec(css);
if (!m1) return;
const vendor = processMatch(m1)[1];
// identifier
const m2 = /^([-\w]+)\s*/.exec(css);
if (!m2) return error('@keyframes missing name');
const name = processMatch(m2)[1];
if (!open()) return error("@keyframes missing '{'");
let frame;
let frames = comments();
while(frame = keyframe()){
frames.push(frame);
frames = frames.concat(comments());
}
if (!close()) return error("@keyframes missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).keyframes,
name: name,
vendor: vendor,
keyframes: frames
});
}
/**
* Parse supports.
*/ function atsupports() {
const pos = position();
const m = /^@supports *([^{]+)/.exec(css);
if (!m) return;
const supports = $b499486c7f02abe7$var$trim(processMatch(m)[1]);
if (!open()) return error("@supports missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@supports missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).supports,
supports: supports,
rules: style
});
}
/**
* Parse host.
*/ function athost() {
const pos = position();
const m = /^@host\s*/.exec(css);
if (!m) return;
processMatch(m);
if (!open()) return error("@host missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@host missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).host,
rules: style
});
}
/**
* Parse container.
*/ function atcontainer() {
const pos = position();
const m = /^@container *([^{]+)/.exec(css);
if (!m) return;
const container = $b499486c7f02abe7$var$trim(processMatch(m)[1]);
if (!open()) return error("@container missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@container missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).container,
container: container,
rules: style
});
}
/**
* Parse container.
*/ function atlayer() {
const pos = position();
const m = /^@layer *([^{;@]+)/.exec(css);
if (!m) return;
const layer = $b499486c7f02abe7$var$trim(processMatch(m)[1]);
if (!open()) {
const m2 = /^[;\s]*/.exec(css);
if (m2) processMatch(m2);
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).layer,
layer: layer
});
}
const style = comments().concat(rules());
if (!close()) return error("@layer missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).layer,
layer: layer,
rules: style
});
}
/**
* Parse media.
*/ function atmedia() {
const pos = position();
const m = /^@media *([^{]+)/.exec(css);
if (!m) return;
const media = $b499486c7f02abe7$var$trim(processMatch(m)[1]);
if (!open()) return error("@media missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@media missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).media,
media: media,
rules: style
});
}
/**
* Parse custom-media.
*/ function atcustommedia() {
const pos = position();
const m = /^@custom-media\s+(--\S+)\s+([^{;\s][^{;]*);/.exec(css);
if (!m) return;
const res = processMatch(m);
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).customMedia,
name: $b499486c7f02abe7$var$trim(res[1]),
media: $b499486c7f02abe7$var$trim(res[2])
});
}
/**
* Parse paged media.
*/ function atpage() {
const pos = position();
const m = /^@page */.exec(css);
if (!m) return;
processMatch(m);
const sel = selector() || [];
if (!open()) return error("@page missing '{'");
let decls = comments();
// declarations
let decl;
while(decl = declaration()){
decls.push(decl);
decls = decls.concat(comments());
}
if (!close()) return error("@page missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).page,
selectors: sel,
declarations: decls
});
}
/**
* Parse document.
*/ function atdocument() {
const pos = position();
const m = /^@([-\w]+)?document *([^{]+)/.exec(css);
if (!m) return;
const res = processMatch(m);
const vendor = $b499486c7f02abe7$var$trim(res[1]);
const doc = $b499486c7f02abe7$var$trim(res[2]);
if (!open()) return error("@document missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@document missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).document,
document: doc,
vendor: vendor,
rules: style
});
}
/**
* Parse font-face.
*/ function atfontface() {
const pos = position();
const m = /^@font-face\s*/.exec(css);
if (!m) return;
processMatch(m);
if (!open()) return error("@font-face missing '{'");
let decls = comments();
// declarations
let decl;
while(decl = declaration()){
decls.push(decl);
decls = decls.concat(comments());
}
if (!close()) return error("@font-face missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).fontFace,
declarations: decls
});
}
/**
* Parse starting style.
*/ function atstartingstyle() {
const pos = position();
const m = /^@starting-style\s*/.exec(css);
if (!m) return;
processMatch(m);
if (!open()) return error("@starting-style missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@starting-style missing '}'");
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).startingStyle,
rules: style
});
}
/**
* Parse import
*/ const atimport = _compileAtrule('import');
/**
* Parse charset
*/ const atcharset = _compileAtrule('charset');
/**
* Parse namespace
*/ const atnamespace = _compileAtrule('namespace');
/**
* Parse non-block at-rules
*/ function _compileAtrule(name) {
const re = new RegExp('^@' + name + '\\s*((?::?[^;\'"]|"(?:\\\\"|[^"])*?"|\'(?:\\\\\'|[^\'])*?\')+)(?:;|$)');
// ^@import\s*([^;"']|("|')(?:\\\2|.)*?\2)+(;|$)
return function() {
const pos = position();
const m = re.exec(css);
if (!m) return;
const res = processMatch(m);
const ret = {
type: name
};
ret[name] = res[1].trim();
return pos(ret);
};
}
/**
* Parse at rule.
*/ function atrule() {
if (css[0] !== '@') return;
return atkeyframes() || atmedia() || atcustommedia() || atsupports() || atimport() || atcharset() || atnamespace() || atdocument() || atpage() || athost() || atfontface() || atcontainer() || atstartingstyle() || atlayer();
}
/**
* Parse rule.
*/ function rule() {
const pos = position();
const sel = selector();
if (!sel) return error('selector missing');
comments();
return pos({
type: (0, $d103407e81c97042$export$9be5dd6e61d5d73a).rule,
selectors: sel,
declarations: declarations() || []
});
}
return $b499486c7f02abe7$var$addParent(stylesheet());
};
/**
* Trim `str`.
*/ function $b499486c7f02abe7$var$trim(str) {
return str ? str.trim() : '';
}
/**
* Adds non-enumerable parent node reference to each node.
*/ function $b499486c7f02abe7$var$addParent(obj, parent) {
const isNode = obj && typeof obj.type === 'string';
const childParent = isNode ? obj : parent;
for(const k in obj){
const value = obj[k];
if (Array.isArray(value)) value.forEach((v)=>{
$b499486c7f02abe7$var$addParent(v, childParent);
});
else if (value && typeof value === 'object') $b499486c7f02abe7$var$addParent(value, childParent);
}
if (isNode) Object.defineProperty(obj, 'parent', {
configurable: true,
writable: true,
enumerable: false,
value: parent || null
});
return obj;
}
var $b499486c7f02abe7$export$2e2bcd8739ae039 = $b499486c7f02abe7$export$98e6a39c04603d36;
class $24dc7e49cb76910e$var$Compiler {
constructor(options){
this.level = 0;
this.indentation = ' ';
this.compress = false;
if (typeof options?.indent === 'string') this.indentation = options?.indent;
if (options?.compress) this.compress = true;
}
// We disable no-unused-vars for _position. We keep position for potential reintroduction of source-map
// eslint-disable-next-line @typescript-eslint/no-unused-vars
emit(str, _position) {
return str;
}
/**
* Increase, decrease or return current indentation.
*/ indent(level) {
this.level = this.level || 1;
if (level) {
this.level += level;
return '';
}
return Array(this.level).join(this.indentation);
}
visit(node) {
switch(node.type){
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).stylesheet:
return this.stylesheet(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).rule:
return this.rule(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).declaration:
return this.declaration(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).comment:
return this.comment(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).container:
return this.container(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).charset:
return this.charset(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).document:
return this.document(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).customMedia:
return this.customMedia(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).fontFace:
return this.fontFace(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).host:
return this.host(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).import:
return this.import(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).keyframes:
return this.keyframes(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).keyframe:
return this.keyframe(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).layer:
return this.layer(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).media:
return this.media(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).namespace:
return this.namespace(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).page:
return this.page(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).startingStyle:
return this.startingStyle(node);
case (0, $d103407e81c97042$export$9be5dd6e61d5d73a).supports:
return this.supports(node);
}
}
mapVisit(nodes, delim) {
let buf = '';
delim = delim || '';
for(let i = 0, length = nodes.length; i < length; i++){
buf += this.visit(nodes[i]);
if (delim && i < length - 1) buf += this.emit(delim);
}
return buf;
}
compile(node) {
if (this.compress) return node.stylesheet.rules.map(this.visit, this).join('');
return this.stylesheet(node);
}
/**
* Visit stylesheet node.
*/ stylesheet(node) {
return this.mapVisit(node.stylesheet.rules, '\n\n');
}
/**
* Visit comment node.
*/ comment(node) {
if (this.compress) return this.emit('', node.position);
return this.emit(this.indent() + '/*' + node.comment + '*/', node.position);
}
/**
* Visit container node.
*/ container(node) {
if (this.compress) return this.emit('@container ' + node.container, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(this.indent() + '@container ' + node.container, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}');
}
/**
* Visit container node.
*/ layer(node) {
if (this.compress) return this.emit('@layer ' + node.layer, node.position) + (node.rules ? this.emit('{') + this.mapVisit(node.rules) + this.emit('}') : ';');
return this.emit(this.indent() + '@layer ' + node.layer, node.position) + (node.rules ? this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}') : ';');
}
/**
* Visit import node.
*/ import(node) {
return this.emit('@import ' + node.import + ';', node.position);
}
/**
* Visit media node.
*/ media(node) {
if (this.compress) return this.emit('@media ' + node.media, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(this.indent() + '@media ' + node.media, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}');
}
/**
* Visit document node.
*/ document(node) {
const doc = '@' + (node.vendor || '') + 'document ' + node.document;
if (this.compress) return this.emit(doc, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(doc, node.position) + this.emit(" {\n" + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit(this.indent(-1) + '\n}');
}
/**
* Visit charset node.
*/ charset(node) {
return this.emit('@charset ' + node.charset + ';', node.position);
}
/**
* Visit namespace node.
*/ namespace(node) {
return this.emit('@namespace ' + node.namespace + ';', node.position);
}
/**
* Visit container node.
*/ startingStyle(node) {
if (this.compress) return this.emit('@starting-style', node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(this.indent() + '@starting-style', node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}');
}
/**
* Visit supports node.
*/ supports(node) {
if (this.compress) return this.emit('@supports ' + node.supports, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(this.indent() + '@supports ' + node.supports, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}');
}
/**
* Visit keyframes node.
*/ keyframes(node) {
if (this.compress) return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position) + this.emit('{') + this.mapVisit(node.keyframes) + this.emit('}');
return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.keyframes, '\n') + this.emit(this.indent(-1) + '}');
}
/**
* Visit keyframe node.
*/ keyframe(node) {
const decls = node.declarations;
if (this.compress) return this.emit(node.values.join(','), node.position) + this.emit('{') + this.mapVisit(decls) + this.emit('}');
return this.emit(this.indent()) + this.emit(node.values.join(', '), node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(decls, '\n') + this.emit(this.indent(-1) + '\n' + this.indent() + '}\n');
}
/**
* Visit page node.
*/ page(node) {
if (this.compress) {
const sel = node.selectors.length ? node.selectors.join(', ') : '';
return this.emit('@page ' + sel, node.position) + this.emit('{') + this.mapVisit(node.declarations) + this.emit('}');
}
const sel = node.selectors.length ? node.selectors.join(', ') + ' ' : '';
return this.emit('@page ' + sel, node.position) + this.emit('{\n') + this.emit(this.indent(1)) + this.mapVisit(node.declarations, '\n') + this.emit(this.indent(-1)) + this.emit('\n}');
}
/**
* Visit font-face node.
*/ fontFace(node) {
if (this.compress) return this.emit('@font-face', node.position) + this.emit('{') + this.mapVisit(node.declarations) + this.emit('}');
return this.emit('@font-face ', node.position) + this.emit('{\n') + this.emit(this.indent(1)) + this.mapVisit(node.declarations, '\n') + this.emit(this.indent(-1)) + this.emit('\n}');
}
/**
* Visit host node.
*/ host(node) {
if (this.compress) return this.emit('@host', node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit('@host', node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit(this.indent(-1) + '\n}');
}
/**
* Visit custom-media node.
*/ customMedia(node) {
return this.emit('@custom-media ' + node.name + ' ' + node.media + ';', node.position);
}
/**
* Visit rule node.
*/ rule(node) {
const decls = node.declarations;
if (!decls.length) return '';
if (this.compress) return this.emit(node.selectors.join(','), node.position) + this.emit('{') + this.mapVisit(decls) + this.emit('}');
const indent = this.indent();
return this.emit(node.selectors.map((s)=>{
return indent + s;
}).join(',\n'), node.position) + this.emit(' {\n') + this.emit(this.indent(1)) + this.mapVisit(decls, '\n') + this.emit(this.indent(-1)) + this.emit('\n' + this.indent() + '}');
}
/**
* Visit declaration node.
*/ declaration(node) {
if (this.compress) return this.emit(node.property + ':' + node.value, node.position) + this.emit(';');
if (node.property === 'grid-template-areas') return this.emit(this.indent()) + this.emit(node.property + ': ' + node.value.split('\n').join('\n'.padEnd(22) + this.indent()), node.position) + this.emit(';');
return this.emit(this.indent()) + this.emit(node.property + ': ' + node.value, node.position) + this.emit(';');
}
}
var $24dc7e49cb76910e$export$2e2bcd8739ae039 = $24dc7e49cb76910e$var$Compiler;
var $fd680ce0c35731f5$export$2e2bcd8739ae039 = (node, options)=>{
const compiler = new (0, $24dc7e49cb76910e$export$2e2bcd8739ae039)(options || {});
return compiler.compile(node);
};
const $882b6d93070905b3$export$98e6a39c04603d36 = (0, $b499486c7f02abe7$export$2e2bcd8739ae039);
const $882b6d93070905b3$export$fac44ee5b035f737 = (0, $fd680ce0c35731f5$export$2e2bcd8739ae039);
var $882b6d93070905b3$export$2e2bcd8739ae039 = {
parse: $882b6d93070905b3$export$98e6a39c04603d36,
stringify: $882b6d93070905b3$export$fac44ee5b035f737
};
$parcel$exportWildcard(module.exports, $d103407e81c97042$exports);
$parcel$exportWildcard(module.exports, $cb508b9219b02820$exports);
$parcel$exportWildcard(module.exports, $4bafb28828007b46$exports);
//# sourceMappingURL=index.cjs.map

File diff suppressed because one or more lines are too long

861
frontend/node_modules/@adobe/css-tools/dist/index.mjs generated vendored Normal file
View File

@@ -0,0 +1,861 @@
function $parcel$defineInteropFlag(a) {
Object.defineProperty(a, '__esModule', {value: true, configurable: true});
}
function $parcel$export(e, n, v, s) {
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
}
var $009ddb00d3ec72b8$exports = {};
$parcel$defineInteropFlag($009ddb00d3ec72b8$exports);
$parcel$export($009ddb00d3ec72b8$exports, "default", () => $009ddb00d3ec72b8$export$2e2bcd8739ae039);
class $009ddb00d3ec72b8$export$2e2bcd8739ae039 extends Error {
constructor(filename, msg, lineno, column, css){
super(filename + ':' + lineno + ':' + column + ': ' + msg);
this.reason = msg;
this.filename = filename;
this.line = lineno;
this.column = column;
this.source = css;
}
}
var $0865a9fb4cc365fe$exports = {};
$parcel$defineInteropFlag($0865a9fb4cc365fe$exports);
$parcel$export($0865a9fb4cc365fe$exports, "default", () => $0865a9fb4cc365fe$export$2e2bcd8739ae039);
/**
* Store position information for a node
*/ class $0865a9fb4cc365fe$export$2e2bcd8739ae039 {
constructor(start, end, source){
this.start = start;
this.end = end;
this.source = source;
}
}
var $b2e137848b48cf4f$exports = {};
$parcel$export($b2e137848b48cf4f$exports, "CssTypes", () => $b2e137848b48cf4f$export$9be5dd6e61d5d73a);
var $b2e137848b48cf4f$export$9be5dd6e61d5d73a = /*#__PURE__*/ function(CssTypes) {
CssTypes["stylesheet"] = "stylesheet";
CssTypes["rule"] = "rule";
CssTypes["declaration"] = "declaration";
CssTypes["comment"] = "comment";
CssTypes["container"] = "container";
CssTypes["charset"] = "charset";
CssTypes["document"] = "document";
CssTypes["customMedia"] = "custom-media";
CssTypes["fontFace"] = "font-face";
CssTypes["host"] = "host";
CssTypes["import"] = "import";
CssTypes["keyframes"] = "keyframes";
CssTypes["keyframe"] = "keyframe";
CssTypes["layer"] = "layer";
CssTypes["media"] = "media";
CssTypes["namespace"] = "namespace";
CssTypes["page"] = "page";
CssTypes["startingStyle"] = "starting-style";
CssTypes["supports"] = "supports";
return CssTypes;
}({});
const $6d129ebf064c486f$export$82eb5486fce3d340 = 10000;
const $6d129ebf064c486f$export$c8b95ffeec50f24a = (string, search, position)=>{
let currentPosition = position;
let maxLoop = $6d129ebf064c486f$export$82eb5486fce3d340;
do {
const all = search.map((v)=>string.indexOf(v, currentPosition));
all.push(string.indexOf('\\', currentPosition));
const foundAll = all.filter((v)=>v !== -1);
if (foundAll.length === 0) return -1;
const found = Math.min(...foundAll);
if (string[found] === '\\') {
currentPosition = found + 2;
maxLoop--;
} else return found;
}while (maxLoop > 0);
throw new Error('Too many escaping');
};
const $6d129ebf064c486f$export$b8d42a8583d2e477 = (string, search, position)=>{
let currentSearchPosition = position;
let maxLoop = $6d129ebf064c486f$export$82eb5486fce3d340;
do {
const all = search.map((v)=>string.indexOf(v, currentSearchPosition));
all.push(string.indexOf('(', currentSearchPosition));
all.push(string.indexOf('"', currentSearchPosition));
all.push(string.indexOf("'", currentSearchPosition));
all.push(string.indexOf('\\', currentSearchPosition));
const foundAll = all.filter((v)=>v !== -1);
if (foundAll.length === 0) return -1;
const firstMatchPos = Math.min(...foundAll);
const char = string[firstMatchPos];
switch(char){
case '\\':
currentSearchPosition = firstMatchPos + 2;
break;
case '(':
{
const endPosition = $6d129ebf064c486f$export$b8d42a8583d2e477(string, [
')'
], firstMatchPos + 1);
if (endPosition === -1) return -1;
currentSearchPosition = endPosition + 1;
}
break;
case '"':
{
const endQuotePosition = $6d129ebf064c486f$export$c8b95ffeec50f24a(string, [
'"'
], firstMatchPos + 1);
if (endQuotePosition === -1) return -1;
currentSearchPosition = endQuotePosition + 1;
}
break;
case "'":
{
const endQuotePosition = $6d129ebf064c486f$export$c8b95ffeec50f24a(string, [
"'"
], firstMatchPos + 1);
if (endQuotePosition === -1) return -1;
currentSearchPosition = endQuotePosition + 1;
}
break;
default:
return firstMatchPos;
}
maxLoop--;
}while (maxLoop > 0);
throw new Error('Too many escaping');
};
const $6d129ebf064c486f$export$801dd37ac183521b = (string, search)=>{
const result = [];
let currentPosition = 0;
while(currentPosition < string.length){
const index = $6d129ebf064c486f$export$b8d42a8583d2e477(string, search, currentPosition);
if (index === -1) {
result.push(string.substring(currentPosition));
return result;
}
result.push(string.substring(currentPosition, index));
currentPosition = index + 1;
}
return result;
};
// http://www.w3.org/TR/CSS21/grammar.html
// https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027
// New rule => https://www.w3.org/TR/CSS22/syndata.html#comments
// [^] is equivalent to [.\n\r]
const $d708735ed1303b43$var$commentre = /\/\*[^]*?(?:\*\/|$)/g;
const $d708735ed1303b43$export$98e6a39c04603d36 = (css, options)=>{
options = options || {};
/**
* Positional.
*/ let lineno = 1;
let column = 1;
/**
* Update lineno and column based on `str`.
*/ function updatePosition(str) {
const lines = str.match(/\n/g);
if (lines) lineno += lines.length;
const i = str.lastIndexOf('\n');
column = ~i ? str.length - i : column + str.length;
}
/**
* Mark position and patch `node.position`.
*/ function position() {
const start = {
line: lineno,
column: column
};
return function(node) {
node.position = new (0, $0865a9fb4cc365fe$export$2e2bcd8739ae039)(start, {
line: lineno,
column: column
}, options?.source || '');
whitespace();
return node;
};
}
/**
* Error `msg`.
*/ const errorsList = [];
function error(msg) {
const err = new (0, $009ddb00d3ec72b8$export$2e2bcd8739ae039)(options?.source || '', msg, lineno, column, css);
if (options?.silent) errorsList.push(err);
else throw err;
}
/**
* Parse stylesheet.
*/ function stylesheet() {
const rulesList = rules();
const result = {
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).stylesheet,
stylesheet: {
source: options?.source,
rules: rulesList,
parsingErrors: errorsList
}
};
return result;
}
/**
* Opening brace.
*/ function open() {
const openMatch = /^{\s*/.exec(css);
if (openMatch) {
processMatch(openMatch);
return true;
}
return false;
}
/**
* Closing brace.
*/ function close() {
const closeMatch = /^}/.exec(css);
if (closeMatch) {
processMatch(closeMatch);
return true;
}
return false;
}
/**
* Parse ruleset.
*/ function rules() {
let node;
const rules = [];
whitespace();
comments(rules);
while(css.length && css.charAt(0) !== '}' && (node = atrule() || rule()))if (node) {
rules.push(node);
comments(rules);
}
return rules;
}
/**
* Update position and css string. Return the matches
*/ function processMatch(m) {
const str = m[0];
updatePosition(str);
css = css.slice(str.length);
return m;
}
/**
* Parse whitespace.
*/ function whitespace() {
const m = /^\s*/.exec(css);
if (m) processMatch(m);
}
/**
* Parse comments;
*/ function comments(rules) {
let c;
rules = rules || [];
while(c = comment())if (c) rules.push(c);
return rules;
}
/**
* Parse comment.
*/ function comment() {
const pos = position();
if ('/' !== css.charAt(0) || '*' !== css.charAt(1)) return;
const m = /^\/\*[^]*?\*\//.exec(css);
if (!m) return error('End of comment missing');
processMatch(m);
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).comment,
comment: m[0].slice(2, -2)
});
}
/**
* Parse selector.
*/ function selector() {
const m = /^([^{]+)/.exec(css);
if (!m) return;
processMatch(m);
// remove comment in selector;
const res = $d708735ed1303b43$var$trim(m[0]).replace($d708735ed1303b43$var$commentre, '');
return (0, $6d129ebf064c486f$export$801dd37ac183521b)(res, [
','
]).map((v)=>$d708735ed1303b43$var$trim(v));
}
/**
* Parse declaration.
*/ function declaration() {
const pos = position();
// prop
const propMatch = /^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/.exec(css);
if (!propMatch) return;
processMatch(propMatch);
const propValue = $d708735ed1303b43$var$trim(propMatch[0]);
// :
const sepratotorMatch = /^:\s*/.exec(css);
if (!sepratotorMatch) return error("property missing ':'");
processMatch(sepratotorMatch);
// val
let value = '';
const endValuePosition = (0, $6d129ebf064c486f$export$b8d42a8583d2e477)(css, [
';',
'}'
]);
if (endValuePosition !== -1) {
value = css.substring(0, endValuePosition);
const fakeMatch = [
value
];
processMatch(fakeMatch);
value = $d708735ed1303b43$var$trim(value).replace($d708735ed1303b43$var$commentre, '');
}
const ret = pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).declaration,
property: propValue.replace($d708735ed1303b43$var$commentre, ''),
value: value
});
// ;
const endMatch = /^[;\s]*/.exec(css);
if (endMatch) processMatch(endMatch);
return ret;
}
/**
* Parse declarations.
*/ function declarations() {
const decls = [];
if (!open()) return error("missing '{'");
comments(decls);
// declarations
let decl;
while(decl = declaration())if (decl) {
decls.push(decl);
comments(decls);
}
if (!close()) return error("missing '}'");
return decls;
}
/**
* Parse keyframe.
*/ function keyframe() {
let m;
const vals = [];
const pos = position();
while(m = /^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/.exec(css)){
const res = processMatch(m);
vals.push(res[1]);
const spacesMatch = /^,\s*/.exec(css);
if (spacesMatch) processMatch(spacesMatch);
}
if (!vals.length) return;
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).keyframe,
values: vals,
declarations: declarations() || []
});
}
/**
* Parse keyframes.
*/ function atkeyframes() {
const pos = position();
const m1 = /^@([-\w]+)?keyframes\s*/.exec(css);
if (!m1) return;
const vendor = processMatch(m1)[1];
// identifier
const m2 = /^([-\w]+)\s*/.exec(css);
if (!m2) return error('@keyframes missing name');
const name = processMatch(m2)[1];
if (!open()) return error("@keyframes missing '{'");
let frame;
let frames = comments();
while(frame = keyframe()){
frames.push(frame);
frames = frames.concat(comments());
}
if (!close()) return error("@keyframes missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).keyframes,
name: name,
vendor: vendor,
keyframes: frames
});
}
/**
* Parse supports.
*/ function atsupports() {
const pos = position();
const m = /^@supports *([^{]+)/.exec(css);
if (!m) return;
const supports = $d708735ed1303b43$var$trim(processMatch(m)[1]);
if (!open()) return error("@supports missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@supports missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).supports,
supports: supports,
rules: style
});
}
/**
* Parse host.
*/ function athost() {
const pos = position();
const m = /^@host\s*/.exec(css);
if (!m) return;
processMatch(m);
if (!open()) return error("@host missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@host missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).host,
rules: style
});
}
/**
* Parse container.
*/ function atcontainer() {
const pos = position();
const m = /^@container *([^{]+)/.exec(css);
if (!m) return;
const container = $d708735ed1303b43$var$trim(processMatch(m)[1]);
if (!open()) return error("@container missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@container missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).container,
container: container,
rules: style
});
}
/**
* Parse container.
*/ function atlayer() {
const pos = position();
const m = /^@layer *([^{;@]+)/.exec(css);
if (!m) return;
const layer = $d708735ed1303b43$var$trim(processMatch(m)[1]);
if (!open()) {
const m2 = /^[;\s]*/.exec(css);
if (m2) processMatch(m2);
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).layer,
layer: layer
});
}
const style = comments().concat(rules());
if (!close()) return error("@layer missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).layer,
layer: layer,
rules: style
});
}
/**
* Parse media.
*/ function atmedia() {
const pos = position();
const m = /^@media *([^{]+)/.exec(css);
if (!m) return;
const media = $d708735ed1303b43$var$trim(processMatch(m)[1]);
if (!open()) return error("@media missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@media missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).media,
media: media,
rules: style
});
}
/**
* Parse custom-media.
*/ function atcustommedia() {
const pos = position();
const m = /^@custom-media\s+(--\S+)\s+([^{;\s][^{;]*);/.exec(css);
if (!m) return;
const res = processMatch(m);
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).customMedia,
name: $d708735ed1303b43$var$trim(res[1]),
media: $d708735ed1303b43$var$trim(res[2])
});
}
/**
* Parse paged media.
*/ function atpage() {
const pos = position();
const m = /^@page */.exec(css);
if (!m) return;
processMatch(m);
const sel = selector() || [];
if (!open()) return error("@page missing '{'");
let decls = comments();
// declarations
let decl;
while(decl = declaration()){
decls.push(decl);
decls = decls.concat(comments());
}
if (!close()) return error("@page missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).page,
selectors: sel,
declarations: decls
});
}
/**
* Parse document.
*/ function atdocument() {
const pos = position();
const m = /^@([-\w]+)?document *([^{]+)/.exec(css);
if (!m) return;
const res = processMatch(m);
const vendor = $d708735ed1303b43$var$trim(res[1]);
const doc = $d708735ed1303b43$var$trim(res[2]);
if (!open()) return error("@document missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@document missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).document,
document: doc,
vendor: vendor,
rules: style
});
}
/**
* Parse font-face.
*/ function atfontface() {
const pos = position();
const m = /^@font-face\s*/.exec(css);
if (!m) return;
processMatch(m);
if (!open()) return error("@font-face missing '{'");
let decls = comments();
// declarations
let decl;
while(decl = declaration()){
decls.push(decl);
decls = decls.concat(comments());
}
if (!close()) return error("@font-face missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).fontFace,
declarations: decls
});
}
/**
* Parse starting style.
*/ function atstartingstyle() {
const pos = position();
const m = /^@starting-style\s*/.exec(css);
if (!m) return;
processMatch(m);
if (!open()) return error("@starting-style missing '{'");
const style = comments().concat(rules());
if (!close()) return error("@starting-style missing '}'");
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).startingStyle,
rules: style
});
}
/**
* Parse import
*/ const atimport = _compileAtrule('import');
/**
* Parse charset
*/ const atcharset = _compileAtrule('charset');
/**
* Parse namespace
*/ const atnamespace = _compileAtrule('namespace');
/**
* Parse non-block at-rules
*/ function _compileAtrule(name) {
const re = new RegExp('^@' + name + '\\s*((?::?[^;\'"]|"(?:\\\\"|[^"])*?"|\'(?:\\\\\'|[^\'])*?\')+)(?:;|$)');
// ^@import\s*([^;"']|("|')(?:\\\2|.)*?\2)+(;|$)
return function() {
const pos = position();
const m = re.exec(css);
if (!m) return;
const res = processMatch(m);
const ret = {
type: name
};
ret[name] = res[1].trim();
return pos(ret);
};
}
/**
* Parse at rule.
*/ function atrule() {
if (css[0] !== '@') return;
return atkeyframes() || atmedia() || atcustommedia() || atsupports() || atimport() || atcharset() || atnamespace() || atdocument() || atpage() || athost() || atfontface() || atcontainer() || atstartingstyle() || atlayer();
}
/**
* Parse rule.
*/ function rule() {
const pos = position();
const sel = selector();
if (!sel) return error('selector missing');
comments();
return pos({
type: (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).rule,
selectors: sel,
declarations: declarations() || []
});
}
return $d708735ed1303b43$var$addParent(stylesheet());
};
/**
* Trim `str`.
*/ function $d708735ed1303b43$var$trim(str) {
return str ? str.trim() : '';
}
/**
* Adds non-enumerable parent node reference to each node.
*/ function $d708735ed1303b43$var$addParent(obj, parent) {
const isNode = obj && typeof obj.type === 'string';
const childParent = isNode ? obj : parent;
for(const k in obj){
const value = obj[k];
if (Array.isArray(value)) value.forEach((v)=>{
$d708735ed1303b43$var$addParent(v, childParent);
});
else if (value && typeof value === 'object') $d708735ed1303b43$var$addParent(value, childParent);
}
if (isNode) Object.defineProperty(obj, 'parent', {
configurable: true,
writable: true,
enumerable: false,
value: parent || null
});
return obj;
}
var $d708735ed1303b43$export$2e2bcd8739ae039 = $d708735ed1303b43$export$98e6a39c04603d36;
class $de9540138ed1fd01$var$Compiler {
constructor(options){
this.level = 0;
this.indentation = ' ';
this.compress = false;
if (typeof options?.indent === 'string') this.indentation = options?.indent;
if (options?.compress) this.compress = true;
}
// We disable no-unused-vars for _position. We keep position for potential reintroduction of source-map
// eslint-disable-next-line @typescript-eslint/no-unused-vars
emit(str, _position) {
return str;
}
/**
* Increase, decrease or return current indentation.
*/ indent(level) {
this.level = this.level || 1;
if (level) {
this.level += level;
return '';
}
return Array(this.level).join(this.indentation);
}
visit(node) {
switch(node.type){
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).stylesheet:
return this.stylesheet(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).rule:
return this.rule(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).declaration:
return this.declaration(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).comment:
return this.comment(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).container:
return this.container(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).charset:
return this.charset(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).document:
return this.document(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).customMedia:
return this.customMedia(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).fontFace:
return this.fontFace(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).host:
return this.host(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).import:
return this.import(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).keyframes:
return this.keyframes(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).keyframe:
return this.keyframe(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).layer:
return this.layer(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).media:
return this.media(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).namespace:
return this.namespace(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).page:
return this.page(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).startingStyle:
return this.startingStyle(node);
case (0, $b2e137848b48cf4f$export$9be5dd6e61d5d73a).supports:
return this.supports(node);
}
}
mapVisit(nodes, delim) {
let buf = '';
delim = delim || '';
for(let i = 0, length = nodes.length; i < length; i++){
buf += this.visit(nodes[i]);
if (delim && i < length - 1) buf += this.emit(delim);
}
return buf;
}
compile(node) {
if (this.compress) return node.stylesheet.rules.map(this.visit, this).join('');
return this.stylesheet(node);
}
/**
* Visit stylesheet node.
*/ stylesheet(node) {
return this.mapVisit(node.stylesheet.rules, '\n\n');
}
/**
* Visit comment node.
*/ comment(node) {
if (this.compress) return this.emit('', node.position);
return this.emit(this.indent() + '/*' + node.comment + '*/', node.position);
}
/**
* Visit container node.
*/ container(node) {
if (this.compress) return this.emit('@container ' + node.container, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(this.indent() + '@container ' + node.container, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}');
}
/**
* Visit container node.
*/ layer(node) {
if (this.compress) return this.emit('@layer ' + node.layer, node.position) + (node.rules ? this.emit('{') + this.mapVisit(node.rules) + this.emit('}') : ';');
return this.emit(this.indent() + '@layer ' + node.layer, node.position) + (node.rules ? this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}') : ';');
}
/**
* Visit import node.
*/ import(node) {
return this.emit('@import ' + node.import + ';', node.position);
}
/**
* Visit media node.
*/ media(node) {
if (this.compress) return this.emit('@media ' + node.media, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(this.indent() + '@media ' + node.media, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}');
}
/**
* Visit document node.
*/ document(node) {
const doc = '@' + (node.vendor || '') + 'document ' + node.document;
if (this.compress) return this.emit(doc, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(doc, node.position) + this.emit(" {\n" + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit(this.indent(-1) + '\n}');
}
/**
* Visit charset node.
*/ charset(node) {
return this.emit('@charset ' + node.charset + ';', node.position);
}
/**
* Visit namespace node.
*/ namespace(node) {
return this.emit('@namespace ' + node.namespace + ';', node.position);
}
/**
* Visit container node.
*/ startingStyle(node) {
if (this.compress) return this.emit('@starting-style', node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(this.indent() + '@starting-style', node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}');
}
/**
* Visit supports node.
*/ supports(node) {
if (this.compress) return this.emit('@supports ' + node.supports, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit(this.indent() + '@supports ' + node.supports, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit('\n' + this.indent(-1) + this.indent() + '}');
}
/**
* Visit keyframes node.
*/ keyframes(node) {
if (this.compress) return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position) + this.emit('{') + this.mapVisit(node.keyframes) + this.emit('}');
return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.keyframes, '\n') + this.emit(this.indent(-1) + '}');
}
/**
* Visit keyframe node.
*/ keyframe(node) {
const decls = node.declarations;
if (this.compress) return this.emit(node.values.join(','), node.position) + this.emit('{') + this.mapVisit(decls) + this.emit('}');
return this.emit(this.indent()) + this.emit(node.values.join(', '), node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(decls, '\n') + this.emit(this.indent(-1) + '\n' + this.indent() + '}\n');
}
/**
* Visit page node.
*/ page(node) {
if (this.compress) {
const sel = node.selectors.length ? node.selectors.join(', ') : '';
return this.emit('@page ' + sel, node.position) + this.emit('{') + this.mapVisit(node.declarations) + this.emit('}');
}
const sel = node.selectors.length ? node.selectors.join(', ') + ' ' : '';
return this.emit('@page ' + sel, node.position) + this.emit('{\n') + this.emit(this.indent(1)) + this.mapVisit(node.declarations, '\n') + this.emit(this.indent(-1)) + this.emit('\n}');
}
/**
* Visit font-face node.
*/ fontFace(node) {
if (this.compress) return this.emit('@font-face', node.position) + this.emit('{') + this.mapVisit(node.declarations) + this.emit('}');
return this.emit('@font-face ', node.position) + this.emit('{\n') + this.emit(this.indent(1)) + this.mapVisit(node.declarations, '\n') + this.emit(this.indent(-1)) + this.emit('\n}');
}
/**
* Visit host node.
*/ host(node) {
if (this.compress) return this.emit('@host', node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}');
return this.emit('@host', node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit(this.indent(-1) + '\n}');
}
/**
* Visit custom-media node.
*/ customMedia(node) {
return this.emit('@custom-media ' + node.name + ' ' + node.media + ';', node.position);
}
/**
* Visit rule node.
*/ rule(node) {
const decls = node.declarations;
if (!decls.length) return '';
if (this.compress) return this.emit(node.selectors.join(','), node.position) + this.emit('{') + this.mapVisit(decls) + this.emit('}');
const indent = this.indent();
return this.emit(node.selectors.map((s)=>{
return indent + s;
}).join(',\n'), node.position) + this.emit(' {\n') + this.emit(this.indent(1)) + this.mapVisit(decls, '\n') + this.emit(this.indent(-1)) + this.emit('\n' + this.indent() + '}');
}
/**
* Visit declaration node.
*/ declaration(node) {
if (this.compress) return this.emit(node.property + ':' + node.value, node.position) + this.emit(';');
if (node.property === 'grid-template-areas') return this.emit(this.indent()) + this.emit(node.property + ': ' + node.value.split('\n').join('\n'.padEnd(22) + this.indent()), node.position) + this.emit(';');
return this.emit(this.indent()) + this.emit(node.property + ': ' + node.value, node.position) + this.emit(';');
}
}
var $de9540138ed1fd01$export$2e2bcd8739ae039 = $de9540138ed1fd01$var$Compiler;
var $fdf773ab87e20450$export$2e2bcd8739ae039 = (node, options)=>{
const compiler = new (0, $de9540138ed1fd01$export$2e2bcd8739ae039)(options || {});
return compiler.compile(node);
};
const $149c1bd638913645$export$98e6a39c04603d36 = (0, $d708735ed1303b43$export$2e2bcd8739ae039);
const $149c1bd638913645$export$fac44ee5b035f737 = (0, $fdf773ab87e20450$export$2e2bcd8739ae039);
var $149c1bd638913645$export$2e2bcd8739ae039 = {
parse: $149c1bd638913645$export$98e6a39c04603d36,
stringify: $149c1bd638913645$export$fac44ee5b035f737
};
export {$149c1bd638913645$export$98e6a39c04603d36 as parse, $149c1bd638913645$export$fac44ee5b035f737 as stringify, $149c1bd638913645$export$2e2bcd8739ae039 as default, $b2e137848b48cf4f$export$9be5dd6e61d5d73a as CssTypes};
//# sourceMappingURL=index.mjs.map

File diff suppressed because one or more lines are too long

171
frontend/node_modules/@adobe/css-tools/dist/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,171 @@
declare class CssParseError extends Error {
readonly reason: string;
readonly filename?: string;
readonly line: number;
readonly column: number;
readonly source: string;
constructor(filename: string, msg: string, lineno: number, column: number, css: string);
}
/**
* Store position information for a node
*/
declare class Position {
start: {
line: number;
column: number;
};
end: {
line: number;
column: number;
};
source?: string;
constructor(start: {
line: number;
column: number;
}, end: {
line: number;
column: number;
}, source: string);
}
export enum CssTypes {
stylesheet = "stylesheet",
rule = "rule",
declaration = "declaration",
comment = "comment",
container = "container",
charset = "charset",
document = "document",
customMedia = "custom-media",
fontFace = "font-face",
host = "host",
import = "import",
keyframes = "keyframes",
keyframe = "keyframe",
layer = "layer",
media = "media",
namespace = "namespace",
page = "page",
startingStyle = "starting-style",
supports = "supports"
}
export type CssCommonAST = {
type: CssTypes;
};
export type CssCommonPositionAST = CssCommonAST & {
position?: Position;
parent?: unknown;
};
export type CssStylesheetAST = CssCommonAST & {
type: CssTypes.stylesheet;
stylesheet: {
source?: string;
rules: Array<CssAtRuleAST>;
parsingErrors?: Array<CssParseError>;
};
};
export type CssRuleAST = CssCommonPositionAST & {
type: CssTypes.rule;
selectors: Array<string>;
declarations: Array<CssDeclarationAST | CssCommentAST>;
};
export type CssDeclarationAST = CssCommonPositionAST & {
type: CssTypes.declaration;
property: string;
value: string;
};
export type CssCommentAST = CssCommonPositionAST & {
type: CssTypes.comment;
comment: string;
};
export type CssContainerAST = CssCommonPositionAST & {
type: CssTypes.container;
container: string;
rules: Array<CssAtRuleAST>;
};
export type CssCharsetAST = CssCommonPositionAST & {
type: CssTypes.charset;
charset: string;
};
export type CssCustomMediaAST = CssCommonPositionAST & {
type: CssTypes.customMedia;
name: string;
media: string;
};
export type CssDocumentAST = CssCommonPositionAST & {
type: CssTypes.document;
document: string;
vendor?: string;
rules: Array<CssAtRuleAST>;
};
export type CssFontFaceAST = CssCommonPositionAST & {
type: CssTypes.fontFace;
declarations: Array<CssDeclarationAST | CssCommentAST>;
};
export type CssHostAST = CssCommonPositionAST & {
type: CssTypes.host;
rules: Array<CssAtRuleAST>;
};
export type CssImportAST = CssCommonPositionAST & {
type: CssTypes.import;
import: string;
};
export type CssKeyframesAST = CssCommonPositionAST & {
type: CssTypes.keyframes;
name: string;
vendor?: string;
keyframes: Array<CssKeyframeAST | CssCommentAST>;
};
export type CssKeyframeAST = CssCommonPositionAST & {
type: CssTypes.keyframe;
values: Array<string>;
declarations: Array<CssDeclarationAST | CssCommentAST>;
};
export type CssLayerAST = CssCommonPositionAST & {
type: CssTypes.layer;
layer: string;
rules?: Array<CssAtRuleAST>;
};
export type CssMediaAST = CssCommonPositionAST & {
type: CssTypes.media;
media: string;
rules: Array<CssAtRuleAST>;
};
export type CssNamespaceAST = CssCommonPositionAST & {
type: CssTypes.namespace;
namespace: string;
};
export type CssPageAST = CssCommonPositionAST & {
type: CssTypes.page;
selectors: Array<string>;
declarations: Array<CssDeclarationAST | CssCommentAST>;
};
export type CssSupportsAST = CssCommonPositionAST & {
type: CssTypes.supports;
supports: string;
rules: Array<CssAtRuleAST>;
};
export type CssStartingStyleAST = CssCommonPositionAST & {
type: CssTypes.startingStyle;
rules: Array<CssAtRuleAST>;
};
export type CssAtRuleAST = CssRuleAST | CssCommentAST | CssContainerAST | CssCharsetAST | CssCustomMediaAST | CssDocumentAST | CssFontFaceAST | CssHostAST | CssImportAST | CssKeyframesAST | CssLayerAST | CssMediaAST | CssNamespaceAST | CssPageAST | CssSupportsAST | CssStartingStyleAST;
export type CssAllNodesAST = CssAtRuleAST | CssStylesheetAST | CssDeclarationAST | CssKeyframeAST;
type CompilerOptions = {
indent?: string;
compress?: boolean;
};
export const parse: (css: string, options?: {
source?: string;
silent?: boolean;
}) => CssStylesheetAST;
export const stringify: (node: CssStylesheetAST, options?: CompilerOptions) => string;
declare const _default: {
parse: (css: string, options?: {
source?: string;
silent?: boolean;
}) => CssStylesheetAST;
stringify: (node: CssStylesheetAST, options?: CompilerOptions) => string;
};
export default _default;
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1 @@
{"mappings":"AAAA,2BAAmC,SAAQ,KAAK;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAGtB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM;CASd;ACrBD;;GAEG;AACH;IACE,KAAK,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;IACtC,GAAG,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAGd,KAAK,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,EACrC,GAAG,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,EACnC,MAAM,EAAE,MAAM;CAMjB;ACdD;IACE,UAAU,eAAe;IACzB,IAAI,SAAS;IACb,WAAW,gBAAgB;IAC3B,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,WAAW,iBAAiB;IAC5B,QAAQ,cAAc;IACtB,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,SAAS,cAAc;IACvB,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,KAAK,UAAU;IACf,SAAS,cAAc;IACvB,IAAI,SAAS;IACb,aAAa,mBAAmB;IAChC,QAAQ,aAAa;CACtB;AAED,2BAA2B;IACzB,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC;AAEF,mCAAmC,YAAY,GAAG;IAChD,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,+BAA+B,YAAY,GAAG;IAC5C,IAAI,EAAE,SAAS,UAAU,CAAC;IAC1B,UAAU,EAAE;QACV,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;QAC3B,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;KACtC,CAAC;CACH,CAAC;AAEF,yBAAyB,oBAAoB,GAAG;IAC9C,IAAI,EAAE,SAAS,IAAI,CAAC;IACpB,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,YAAY,EAAE,KAAK,CAAC,iBAAiB,GAAG,aAAa,CAAC,CAAC;CACxD,CAAC;AAEF,gCAAgC,oBAAoB,GAAG;IACrD,IAAI,EAAE,SAAS,WAAW,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,4BAA4B,oBAAoB,GAAG;IACjD,IAAI,EAAE,SAAS,OAAO,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AACF,8BAA8B,oBAAoB,GAAG;IACnD,IAAI,EAAE,SAAS,SAAS,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CAC5B,CAAC;AAEF,4BAA4B,oBAAoB,GAAG;IACjD,IAAI,EAAE,SAAS,OAAO,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AACF,gCAAgC,oBAAoB,GAAG;IACrD,IAAI,EAAE,SAAS,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AACF,6BAA6B,oBAAoB,GAAG;IAClD,IAAI,EAAE,SAAS,QAAQ,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CAC5B,CAAC;AACF,6BAA6B,oBAAoB,GAAG;IAClD,IAAI,EAAE,SAAS,QAAQ,CAAC;IACxB,YAAY,EAAE,KAAK,CAAC,iBAAiB,GAAG,aAAa,CAAC,CAAC;CACxD,CAAC;AACF,yBAAyB,oBAAoB,GAAG;IAC9C,IAAI,EAAE,SAAS,IAAI,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CAC5B,CAAC;AACF,2BAA2B,oBAAoB,GAAG;IAChD,IAAI,EAAE,SAAS,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AACF,8BAA8B,oBAAoB,GAAG;IACnD,IAAI,EAAE,SAAS,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,KAAK,CAAC,cAAc,GAAG,aAAa,CAAC,CAAC;CAClD,CAAC;AACF,6BAA6B,oBAAoB,GAAG;IAClD,IAAI,EAAE,SAAS,QAAQ,CAAC;IACxB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtB,YAAY,EAAE,KAAK,CAAC,iBAAiB,GAAG,aAAa,CAAC,CAAC;CACxD,CAAC;AACF,0BAA0B,oBAAoB,GAAG;IAC/C,IAAI,EAAE,SAAS,KAAK,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CAC7B,CAAC;AACF,0BAA0B,oBAAoB,GAAG;IAC/C,IAAI,EAAE,SAAS,KAAK,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CAC5B,CAAC;AACF,8BAA8B,oBAAoB,GAAG;IACnD,IAAI,EAAE,SAAS,SAAS,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AACF,yBAAyB,oBAAoB,GAAG;IAC9C,IAAI,EAAE,SAAS,IAAI,CAAC;IACpB,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,YAAY,EAAE,KAAK,CAAC,iBAAiB,GAAG,aAAa,CAAC,CAAC;CACxD,CAAC;AACF,6BAA6B,oBAAoB,GAAG;IAClD,IAAI,EAAE,SAAS,QAAQ,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CAC5B,CAAC;AAEF,kCAAkC,oBAAoB,GAAG;IACvD,IAAI,EAAE,SAAS,aAAa,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CAC5B,CAAC;AAEF,2BACI,UAAU,GACV,aAAa,GACb,eAAe,GACf,aAAa,GACb,iBAAiB,GACjB,cAAc,GACd,cAAc,GACd,UAAU,GACV,YAAY,GACZ,eAAe,GACf,WAAW,GACX,WAAW,GACX,eAAe,GACf,UAAU,GACV,cAAc,GACd,mBAAmB,CAAC;AAExB,6BACI,YAAY,GACZ,gBAAgB,GAChB,iBAAiB,GACjB,cAAc,CAAC;AGlInB,uBAA8B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AE1BF,OAAO,MAAM;UAMynB,CAAC;UAAgB,CAAC;sBAN5nB,CAAC;AAC7B,OAAO,MAAM,wEAAuB,CAAC;;;cAKimB,CAAC;cAAgB,CAAC;;;;AADxpB,wBAAkC","sources":["src/src/CssParseError.ts","src/src/CssPosition.ts","src/src/type.ts","src/src/utils/stringSearch.ts","src/src/parse/index.ts","src/src/stringify/compiler.ts","src/src/stringify/index.ts","src/src/index.ts","src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,"import {default as parseFn} from './parse';\nimport {default as stringifyFn} from './stringify';\nexport const parse = parseFn;\nexport const stringify = stringifyFn;\nexport * from './type';\nexport * from './CssParseError';\nexport * from './CssPosition';\nexport default {parse, stringify};\n"],"names":[],"version":3,"file":"types.d.ts.map"}

61
frontend/node_modules/@adobe/css-tools/package.json generated vendored Normal file
View File

@@ -0,0 +1,61 @@
{
"name": "@adobe/css-tools",
"version": "4.4.3",
"description": "CSS parser / stringifier",
"source": "src/index.ts",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"exports": {
"import": "./dist/index.mjs",
"types": "./dist/types.d.ts",
"require": "./dist/index.cjs"
},
"types": "./dist/types.d.ts",
"type": "module",
"files": [
"dist",
"Readme.md"
],
"devDependencies": {
"@parcel/packager-ts": "2.15.0",
"@parcel/transformer-typescript-types": "2.15.0",
"@types/benchmark": "^2.1.1",
"@types/bytes": "^3.1.5",
"@types/jest": "^29.5.3",
"@types/node": "^22.15.18",
"benchmark": "^2.1.4",
"bytes": "^3.1.0",
"gts": "^6.0.2",
"jest": "^29.6.2",
"parcel": "^2.15.0",
"ts-jest": "^29.1.1",
"typescript": "^5.7.3"
},
"scripts": {
"benchmark": "node benchmark/index.mjs",
"test": "jest",
"clean": "gts clean && rm -rf ./dist",
"build": "parcel build && node ./utils/fix-type-generation.cjs",
"fix": "gts fix",
"lint": "gts lint",
"prepack": "npm run build",
"prepare": "npm run build",
"pretest": "npm run build",
"posttest": "npm run lint"
},
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Jean-Philippe Zolesio <holblin@gmail.com>"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/adobe/css-tools.git"
},
"keywords": [
"css",
"parser",
"stringifier",
"stylesheet"
]
}

21
frontend/node_modules/@asamuzakjp/css-color/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 asamuzaK (Kazz)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

316
frontend/node_modules/@asamuzakjp/css-color/README.md generated vendored Normal file
View File

@@ -0,0 +1,316 @@
# CSS color
[![build](https://github.com/asamuzaK/cssColor/actions/workflows/node.js.yml/badge.svg)](https://github.com/asamuzaK/cssColor/actions/workflows/node.js.yml)
[![CodeQL](https://github.com/asamuzaK/cssColor/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/asamuzaK/cssColor/actions/workflows/github-code-scanning/codeql)
[![npm (scoped)](https://img.shields.io/npm/v/@asamuzakjp/css-color)](https://www.npmjs.com/package/@asamuzakjp/css-color)
Resolve and convert CSS colors.
## Install
```console
npm i @asamuzakjp/css-color
```
## Usage
```javascript
import { convert, resolve, utils } from '@asamuzakjp/css-color';
const resolvedValue = resolve(
'color-mix(in oklab, lch(67.5345 42.5 258.2), color(srgb 0 0.5 0))'
);
// 'oklab(0.620754 -0.0931934 -0.00374881)'
const convertedValue = covert.colorToHex('lab(46.2775% -47.5621 48.5837)');
// '#008000'
const result = utils.isColor('green');
// true
```
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### resolve(color, opt)
resolves CSS color
#### Parameters
- `color` **[string][133]** color value
- system colors are not supported
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.currentColor` **[string][133]?**
- color to use for `currentcolor` keyword
- if omitted, it will be treated as a missing color,
i.e. `rgb(none none none / none)`
- `opt.customProperty` **[object][135]?**
- custom properties
- pair of `--` prefixed property name as a key and it's value,
e.g.
```javascript
const opt = {
customProperty: {
'--some-color': '#008000',
'--some-length': '16px'
}
};
```
- and/or `callback` function to get the value of the custom property,
e.g.
```javascript
const node = document.getElementById('foo');
const opt = {
customProperty: {
callback: node.style.getPropertyValue
}
};
```
- `opt.dimension` **[object][135]?**
- dimension, e.g. for converting relative length to pixels
- pair of unit as a key and number in pixels as it's value,
e.g. suppose `1em === 12px`, `1rem === 16px` and `100vw === 1024px`, then
```javascript
const opt = {
dimension: {
em: 12,
rem: 16,
vw: 10.24
}
};
```
- and/or `callback` function to get the value as a number in pixels,
e.g.
```javascript
const opt = {
dimension: {
callback: unit => {
switch (unit) {
case 'em':
return 12;
case 'rem':
return 16;
case 'vw':
return 10.24;
default:
return;
}
}
}
};
```
- `opt.format` **[string][133]?**
- output format, one of below
- `computedValue` (default), [computed value][139] of the color
- `specifiedValue`, [specified value][140] of the color
- `hex`, hex color notation, i.e. `#rrggbb`
- `hexAlpha`, hex color notation with alpha channel, i.e. `#rrggbbaa`
Returns **[string][133]?** one of `rgba?()`, `#rrggbb(aa)?`, `color-name`, `color(color-space r g b / alpha)`, `color(color-space x y z / alpha)`, `(ok)?lab(l a b / alpha)`, `(ok)?lch(l c h / alpha)`, `'(empty-string)'`, `null`
- in `computedValue`, values are numbers, however `rgb()` values are integers
- in `specifiedValue`, returns `empty string` for unknown and/or invalid color
- in `hex`, returns `null` for `transparent`, and also returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
- in `hexAlpha`, returns `#00000000` for `transparent`, however returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
### convert
Contains various color conversion functions.
### convert.numberToHex(value)
convert number to hex string
#### Parameters
- `value` **[number][134]** color value
Returns **[string][133]** hex string: 00..ff
### convert.colorToHex(value, opt)
convert color to hex
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.alpha` **[boolean][136]?** return in #rrggbbaa notation
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[string][133]** #rrggbb(aa)?
### convert.colorToHsl(value, opt)
convert color to hsl
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[h, s, l, alpha]
### convert.colorToHwb(value, opt)
convert color to hwb
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[h, w, b, alpha]
### convert.colorToLab(value, opt)
convert color to lab
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, a, b, alpha]
### convert.colorToLch(value, opt)
convert color to lch
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, c, h, alpha]
### convert.colorToOklab(value, opt)
convert color to oklab
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, a, b, alpha]
### convert.colorToOklch(value, opt)
convert color to oklch
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, c, h, alpha]
### convert.colorToRgb(value, opt)
convert color to rgb
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[r, g, b, alpha]
### convert.colorToXyz(value, opt)
convert color to xyz
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
- `opt.d50` **[boolean][136]?** xyz in d50 white point
Returns **[Array][137]<[number][134]>** \[x, y, z, alpha]
### convert.colorToXyzD50(value, opt)
convert color to xyz-d50
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[x, y, z, alpha]
### utils
Contains utility functions.
### utils.isColor(color)
is valid color type
#### Parameters
- `color` **[string][133]** color value
- system colors are not supported
Returns **[boolean][136]**
## Acknowledgments
The following resources have been of great help in the development of the CSS color.
- [csstools/postcss-plugins](https://github.com/csstools/postcss-plugins)
- [lru-cache](https://github.com/isaacs/node-lru-cache)
---
Copyright (c) 2024 [asamuzaK (Kazz)](https://github.com/asamuzaK/)
[133]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[134]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[135]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[136]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[137]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[138]: https://w3c.github.io/csswg-drafts/css-color-4/#color-conversion-code
[139]: https://developer.mozilla.org/en-US/docs/Web/CSS/computed_value
[140]: https://developer.mozilla.org/en-US/docs/Web/CSS/specified_value
[141]: https://www.npmjs.com/package/@csstools/css-calc

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,109 @@
/**
* typedef
*/
/**
* @typedef Options - options
* @property [alpha] - enable alpha
* @property [colorSpace] - color space
* @property [currentColor] - color for currentcolor
* @property [customPropeerty] - custom properties
* @property [d50] - white point in d50
* @property [dimension] - dimension
* @property [format] - output format
* @property [key] - key
*/
interface Options {
alpha?: boolean;
colorSpace?: string;
currentColor?: string;
customProperty?: Record<string, string | ((K: string) => string)>;
d50?: boolean;
delimiter?: string | string[];
dimension?: Record<string, number | ((K: string) => number)>;
format?: string;
nullable?: boolean;
preserveComment?: boolean;
}
/**
* @type ColorChannels - color channels
*/
type ColorChannels = [x: number, y: number, z: number, alpha: number];
/**
* convert
*/
declare const convert: {
colorToHex: (value: string, opt?: Options) => string | null;
colorToHsl: (value: string, opt?: Options) => ColorChannels;
colorToHwb: (value: string, opt?: Options) => ColorChannels;
colorToLab: (value: string, opt?: Options) => ColorChannels;
colorToLch: (value: string, opt?: Options) => ColorChannels;
colorToOklab: (value: string, opt?: Options) => ColorChannels;
colorToOklch: (value: string, opt?: Options) => ColorChannels;
colorToRgb: (value: string, opt?: Options) => ColorChannels;
colorToXyz: (value: string, opt?: Options) => ColorChannels;
colorToXyzD50: (value: string, opt?: Options) => ColorChannels;
numberToHex: (value: number) => string;
};
/**
* resolve
*/
/**
* resolve CSS color
* @param value
* - CSS color value
* - system colors are not supported
* @param [opt] - options
* @param [opt.currentColor]
* - color to use for `currentcolor` keyword
* - if omitted, it will be treated as a missing color
* i.e. `rgb(none none none / none)`
* @param [opt.customProperty]
* - custom properties
* - pair of `--` prefixed property name and value,
* e.g. `customProperty: { '--some-color': '#0000ff' }`
* - and/or `callback` function to get the value of the custom property,
* e.g. `customProperty: { callback: someDeclaration.getPropertyValue }`
* @param [opt.dimension]
* - dimension, convert relative length to pixels
* - pair of unit and it's value as a number in pixels,
* e.g. `dimension: { em: 12, rem: 16, vw: 10.26 }`
* - and/or `callback` function to get the value as a number in pixels,
* e.g. `dimension: { callback: convertUnitToPixel }`
* @param [opt.format]
* - output format, one of below
* - `computedValue` (default), [computed value][139] of the color
* - `specifiedValue`, [specified value][140] of the color
* - `hex`, hex color notation, i.e. `rrggbb`
* - `hexAlpha`, hex color notation with alpha channel, i.e. `#rrggbbaa`
* @returns
* - one of rgba?(), #rrggbb(aa)?, color-name, '(empty-string)',
* color(color-space r g b / alpha), color(color-space x y z / alpha),
* lab(l a b / alpha), lch(l c h / alpha), oklab(l a b / alpha),
* oklch(l c h / alpha), null
* - in `computedValue`, values are numbers, however `rgb()` values are
* integers
* - in `specifiedValue`, returns `empty string` for unknown and/or invalid
* color
* - in `hex`, returns `null` for `transparent`, and also returns `null` if
* any of `r`, `g`, `b`, `alpha` is not a number
* - in `hexAlpha`, returns `#00000000` for `transparent`,
* however returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
*/
declare const resolve: (value: string, opt?: Options) => string | null;
declare const utils: {
cssCalc: (value: string, opt?: Options) => string;
cssVar: (value: string, opt?: Options) => string;
extractDashedIdent: (value: string) => string[];
isColor: (value: unknown, opt?: Options) => boolean;
isGradient: (value: string, opt?: Options) => boolean;
splitValue: (value: string, opt?: Options) => string[];
};
declare const isColor: (value: unknown, opt?: Options) => boolean;
declare const cssCalc: (value: string, opt?: Options) => string;
export { convert, cssCalc, isColor, resolve, utils };

View File

@@ -0,0 +1,18 @@
/*!
* CSS color - Resolve, parse, convert CSS color.
* @license MIT
* @copyright asamuzaK (Kazz)
* @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}
*/
export { convert } from './js/convert.js';
export { resolve } from './js/resolve.js';
export declare const utils: {
cssCalc: (value: string, opt?: import('./js/typedef.js').Options) => string;
cssVar: (value: string, opt?: import('./js/typedef.js').Options) => string;
extractDashedIdent: (value: string) => string[];
isColor: (value: unknown, opt?: import('./js/typedef.js').Options) => boolean;
isGradient: (value: string, opt?: import('./js/typedef.js').Options) => boolean;
splitValue: (value: string, opt?: import('./js/typedef.js').Options) => string[];
};
export declare const isColor: (value: unknown, opt?: import('./js/typedef.js').Options) => boolean;
export declare const cssCalc: (value: string, opt?: import('./js/typedef.js').Options) => string;

View File

@@ -0,0 +1,30 @@
import { cssCalc as cssCalc$1 } from "./js/css-calc.js";
import { isGradient } from "./js/css-gradient.js";
import { cssVar } from "./js/css-var.js";
import { splitValue, isColor as isColor$1, extractDashedIdent } from "./js/util.js";
import { convert } from "./js/convert.js";
import { resolve } from "./js/resolve.js";
/*!
* CSS color - Resolve, parse, convert CSS color.
* @license MIT
* @copyright asamuzaK (Kazz)
* @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}
*/
const utils = {
cssCalc: cssCalc$1,
cssVar,
extractDashedIdent,
isColor: isColor$1,
isGradient,
splitValue
};
const isColor = utils.isColor;
const cssCalc = utils.cssCalc;
export {
convert,
cssCalc,
isColor,
resolve,
utils
};
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["/*!\n * CSS color - Resolve, parse, convert CSS color.\n * @license MIT\n * @copyright asamuzaK (Kazz)\n * @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}\n */\n\nimport { cssCalc as csscalc } from './js/css-calc';\nimport { isGradient } from './js/css-gradient';\nimport { cssVar } from './js/css-var';\nimport { extractDashedIdent, isColor as iscolor, splitValue } from './js/util';\n\nexport { convert } from './js/convert';\nexport { resolve } from './js/resolve';\n/* utils */\nexport const utils = {\n cssCalc: csscalc,\n cssVar,\n extractDashedIdent,\n isColor: iscolor,\n isGradient,\n splitValue\n};\n/* TODO: remove later */\n/* alias */\nexport const isColor = utils.isColor;\nexport const cssCalc = utils.cssCalc;\n"],"names":["csscalc","iscolor"],"mappings":";;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeO,MAAM,QAAQ;AAAA,EACnB,SAASA;AAAAA,EACT;AAAA,EACA;AAAA,EACA,SAASC;AAAAA,EACT;AAAA,EACA;AACF;AAGO,MAAM,UAAU,MAAM;AACtB,MAAM,UAAU,MAAM;"}

View File

@@ -0,0 +1,44 @@
import { LRUCache } from 'lru-cache';
import { Options } from './typedef.js';
/**
* CacheItem
*/
export declare class CacheItem {
#private;
/**
* constructor
*/
constructor(item: unknown, isNull?: boolean);
get item(): unknown;
get isNull(): boolean;
}
/**
* NullObject
*/
export declare class NullObject extends CacheItem {
/**
* constructor
*/
constructor();
}
export declare const lruCache: LRUCache<{}, {}, unknown>;
/**
* set cache
* @param key - cache key
* @param value - value to cache
* @returns void
*/
export declare const setCache: (key: string, value: unknown) => void;
/**
* get cache
* @param key - cache key
* @returns cached item or false otherwise
*/
export declare const getCache: (key: string) => CacheItem | boolean;
/**
* create cache key
* @param keyData - key data
* @param [opt] - options
* @returns cache key
*/
export declare const createCacheKey: (keyData: Record<string, string>, opt?: Options) => string;

View File

@@ -0,0 +1,82 @@
var __typeError = (msg) => {
throw TypeError(msg);
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var _isNull, _item;
import { LRUCache } from "lru-cache";
import { valueToJsonString } from "./util.js";
const MAX_CACHE = 4096;
class CacheItem {
/**
* constructor
*/
constructor(item, isNull = false) {
/* private */
__privateAdd(this, _isNull);
__privateAdd(this, _item);
__privateSet(this, _item, item);
__privateSet(this, _isNull, !!isNull);
}
get item() {
return __privateGet(this, _item);
}
get isNull() {
return __privateGet(this, _isNull);
}
}
_isNull = new WeakMap();
_item = new WeakMap();
class NullObject extends CacheItem {
/**
* constructor
*/
constructor() {
super(Symbol("null"), true);
}
}
const lruCache = new LRUCache({
max: MAX_CACHE
});
const setCache = (key, value) => {
if (key) {
if (value === null) {
lruCache.set(key, new NullObject());
} else if (value instanceof CacheItem) {
lruCache.set(key, value);
} else {
lruCache.set(key, new CacheItem(value));
}
}
};
const getCache = (key) => {
if (key && lruCache.has(key)) {
const item = lruCache.get(key);
if (item instanceof CacheItem) {
return item;
}
lruCache.delete(key);
return false;
}
return false;
};
const createCacheKey = (keyData, opt = {}) => {
const { customProperty = {}, dimension = {} } = opt;
let cacheKey = "";
if (keyData && Object.keys(keyData).length && typeof customProperty.callback !== "function" && typeof dimension.callback !== "function") {
keyData.opt = valueToJsonString(opt);
cacheKey = valueToJsonString(keyData);
}
return cacheKey;
};
export {
CacheItem,
NullObject,
createCacheKey,
getCache,
lruCache,
setCache
};
//# sourceMappingURL=cache.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"cache.js","sources":["../../../src/js/cache.ts"],"sourcesContent":["/**\n * cache\n */\n\nimport { LRUCache } from 'lru-cache';\nimport { Options } from './typedef';\nimport { valueToJsonString } from './util';\n\n/* numeric constants */\nconst MAX_CACHE = 4096;\n\n/**\n * CacheItem\n */\nexport class CacheItem {\n /* private */\n #isNull: boolean;\n #item: unknown;\n\n /**\n * constructor\n */\n constructor(item: unknown, isNull: boolean = false) {\n this.#item = item;\n this.#isNull = !!isNull;\n }\n\n get item() {\n return this.#item;\n }\n\n get isNull() {\n return this.#isNull;\n }\n}\n\n/**\n * NullObject\n */\nexport class NullObject extends CacheItem {\n /**\n * constructor\n */\n constructor() {\n super(Symbol('null'), true);\n }\n}\n\n/*\n * lru cache\n */\nexport const lruCache = new LRUCache({\n max: MAX_CACHE\n});\n\n/**\n * set cache\n * @param key - cache key\n * @param value - value to cache\n * @returns void\n */\nexport const setCache = (key: string, value: unknown): void => {\n if (key) {\n if (value === null) {\n lruCache.set(key, new NullObject());\n } else if (value instanceof CacheItem) {\n lruCache.set(key, value);\n } else {\n lruCache.set(key, new CacheItem(value));\n }\n }\n};\n\n/**\n * get cache\n * @param key - cache key\n * @returns cached item or false otherwise\n */\nexport const getCache = (key: string): CacheItem | boolean => {\n if (key && lruCache.has(key)) {\n const item = lruCache.get(key);\n if (item instanceof CacheItem) {\n return item;\n }\n // delete unexpected cached item\n lruCache.delete(key);\n return false;\n }\n return false;\n};\n\n/**\n * create cache key\n * @param keyData - key data\n * @param [opt] - options\n * @returns cache key\n */\nexport const createCacheKey = (\n keyData: Record<string, string>,\n opt: Options = {}\n): string => {\n const { customProperty = {}, dimension = {} } = opt;\n let cacheKey = '';\n if (\n keyData &&\n Object.keys(keyData).length &&\n typeof customProperty.callback !== 'function' &&\n typeof dimension.callback !== 'function'\n ) {\n keyData.opt = valueToJsonString(opt);\n cacheKey = valueToJsonString(keyData);\n }\n return cacheKey;\n};\n"],"names":[],"mappings":";;;;;;;;;;AASA,MAAM,YAAY;AAKX,MAAM,UAAU;AAAA;AAAA;AAAA;AAAA,EAQrB,YAAY,MAAe,SAAkB,OAAO;AANpD;AAAA;AACA;AAME,uBAAK,OAAQ;AACR,uBAAA,SAAU,CAAC,CAAC;AAAA,EAAA;AAAA,EAGnB,IAAI,OAAO;AACT,WAAO,mBAAK;AAAA,EAAA;AAAA,EAGd,IAAI,SAAS;AACX,WAAO,mBAAK;AAAA,EAAA;AAEhB;AAlBE;AACA;AAsBK,MAAM,mBAAmB,UAAU;AAAA;AAAA;AAAA;AAAA,EAIxC,cAAc;AACN,UAAA,OAAO,MAAM,GAAG,IAAI;AAAA,EAAA;AAE9B;AAKa,MAAA,WAAW,IAAI,SAAS;AAAA,EACnC,KAAK;AACP,CAAC;AAQY,MAAA,WAAW,CAAC,KAAa,UAAyB;AAC7D,MAAI,KAAK;AACP,QAAI,UAAU,MAAM;AAClB,eAAS,IAAI,KAAK,IAAI,WAAA,CAAY;AAAA,IAAA,WACzB,iBAAiB,WAAW;AAC5B,eAAA,IAAI,KAAK,KAAK;AAAA,IAAA,OAClB;AACL,eAAS,IAAI,KAAK,IAAI,UAAU,KAAK,CAAC;AAAA,IAAA;AAAA,EACxC;AAEJ;AAOa,MAAA,WAAW,CAAC,QAAqC;AAC5D,MAAI,OAAO,SAAS,IAAI,GAAG,GAAG;AACtB,UAAA,OAAO,SAAS,IAAI,GAAG;AAC7B,QAAI,gBAAgB,WAAW;AACtB,aAAA;AAAA,IAAA;AAGT,aAAS,OAAO,GAAG;AACZ,WAAA;AAAA,EAAA;AAEF,SAAA;AACT;AAQO,MAAM,iBAAiB,CAC5B,SACA,MAAe,OACJ;AACX,QAAM,EAAE,iBAAiB,CAAA,GAAI,YAAY,CAAA,EAAO,IAAA;AAChD,MAAI,WAAW;AACf,MACE,WACA,OAAO,KAAK,OAAO,EAAE,UACrB,OAAO,eAAe,aAAa,cACnC,OAAO,UAAU,aAAa,YAC9B;AACQ,YAAA,MAAM,kBAAkB,GAAG;AACnC,eAAW,kBAAkB,OAAO;AAAA,EAAA;AAE/B,SAAA;AACT;"}

View File

@@ -0,0 +1,537 @@
import { NullObject } from './cache.js';
import { ColorChannels, Options, SpecifiedColorChannels } from './typedef.js';
/**
* @type TriColorChannels - color channels without alpha
*/
type TriColorChannels = [x: number, y: number, z: number];
/**
* @type ColorMatrix - color matrix
*/
type ColorMatrix = [
r1: TriColorChannels,
r2: TriColorChannels,
r3: TriColorChannels
];
/**
* named colors
*/
export declare const NAMED_COLORS: {
readonly aliceblue: [240, 248, 255];
readonly antiquewhite: [250, 235, 215];
readonly aqua: [0, 255, 255];
readonly aquamarine: [127, 255, 212];
readonly azure: [240, 255, 255];
readonly beige: [245, 245, 220];
readonly bisque: [255, 228, 196];
readonly black: [0, 0, 0];
readonly blanchedalmond: [255, 235, 205];
readonly blue: [0, 0, 255];
readonly blueviolet: [138, 43, 226];
readonly brown: [165, 42, 42];
readonly burlywood: [222, 184, 135];
readonly cadetblue: [95, 158, 160];
readonly chartreuse: [127, 255, 0];
readonly chocolate: [210, 105, 30];
readonly coral: [255, 127, 80];
readonly cornflowerblue: [100, 149, 237];
readonly cornsilk: [255, 248, 220];
readonly crimson: [220, 20, 60];
readonly cyan: [0, 255, 255];
readonly darkblue: [0, 0, 139];
readonly darkcyan: [0, 139, 139];
readonly darkgoldenrod: [184, 134, 11];
readonly darkgray: [169, 169, 169];
readonly darkgreen: [0, 100, 0];
readonly darkgrey: [169, 169, 169];
readonly darkkhaki: [189, 183, 107];
readonly darkmagenta: [139, 0, 139];
readonly darkolivegreen: [85, 107, 47];
readonly darkorange: [255, 140, 0];
readonly darkorchid: [153, 50, 204];
readonly darkred: [139, 0, 0];
readonly darksalmon: [233, 150, 122];
readonly darkseagreen: [143, 188, 143];
readonly darkslateblue: [72, 61, 139];
readonly darkslategray: [47, 79, 79];
readonly darkslategrey: [47, 79, 79];
readonly darkturquoise: [0, 206, 209];
readonly darkviolet: [148, 0, 211];
readonly deeppink: [255, 20, 147];
readonly deepskyblue: [0, 191, 255];
readonly dimgray: [105, 105, 105];
readonly dimgrey: [105, 105, 105];
readonly dodgerblue: [30, 144, 255];
readonly firebrick: [178, 34, 34];
readonly floralwhite: [255, 250, 240];
readonly forestgreen: [34, 139, 34];
readonly fuchsia: [255, 0, 255];
readonly gainsboro: [220, 220, 220];
readonly ghostwhite: [248, 248, 255];
readonly gold: [255, 215, 0];
readonly goldenrod: [218, 165, 32];
readonly gray: [128, 128, 128];
readonly green: [0, 128, 0];
readonly greenyellow: [173, 255, 47];
readonly grey: [128, 128, 128];
readonly honeydew: [240, 255, 240];
readonly hotpink: [255, 105, 180];
readonly indianred: [205, 92, 92];
readonly indigo: [75, 0, 130];
readonly ivory: [255, 255, 240];
readonly khaki: [240, 230, 140];
readonly lavender: [230, 230, 250];
readonly lavenderblush: [255, 240, 245];
readonly lawngreen: [124, 252, 0];
readonly lemonchiffon: [255, 250, 205];
readonly lightblue: [173, 216, 230];
readonly lightcoral: [240, 128, 128];
readonly lightcyan: [224, 255, 255];
readonly lightgoldenrodyellow: [250, 250, 210];
readonly lightgray: [211, 211, 211];
readonly lightgreen: [144, 238, 144];
readonly lightgrey: [211, 211, 211];
readonly lightpink: [255, 182, 193];
readonly lightsalmon: [255, 160, 122];
readonly lightseagreen: [32, 178, 170];
readonly lightskyblue: [135, 206, 250];
readonly lightslategray: [119, 136, 153];
readonly lightslategrey: [119, 136, 153];
readonly lightsteelblue: [176, 196, 222];
readonly lightyellow: [255, 255, 224];
readonly lime: [0, 255, 0];
readonly limegreen: [50, 205, 50];
readonly linen: [250, 240, 230];
readonly magenta: [255, 0, 255];
readonly maroon: [128, 0, 0];
readonly mediumaquamarine: [102, 205, 170];
readonly mediumblue: [0, 0, 205];
readonly mediumorchid: [186, 85, 211];
readonly mediumpurple: [147, 112, 219];
readonly mediumseagreen: [60, 179, 113];
readonly mediumslateblue: [123, 104, 238];
readonly mediumspringgreen: [0, 250, 154];
readonly mediumturquoise: [72, 209, 204];
readonly mediumvioletred: [199, 21, 133];
readonly midnightblue: [25, 25, 112];
readonly mintcream: [245, 255, 250];
readonly mistyrose: [255, 228, 225];
readonly moccasin: [255, 228, 181];
readonly navajowhite: [255, 222, 173];
readonly navy: [0, 0, 128];
readonly oldlace: [253, 245, 230];
readonly olive: [128, 128, 0];
readonly olivedrab: [107, 142, 35];
readonly orange: [255, 165, 0];
readonly orangered: [255, 69, 0];
readonly orchid: [218, 112, 214];
readonly palegoldenrod: [238, 232, 170];
readonly palegreen: [152, 251, 152];
readonly paleturquoise: [175, 238, 238];
readonly palevioletred: [219, 112, 147];
readonly papayawhip: [255, 239, 213];
readonly peachpuff: [255, 218, 185];
readonly peru: [205, 133, 63];
readonly pink: [255, 192, 203];
readonly plum: [221, 160, 221];
readonly powderblue: [176, 224, 230];
readonly purple: [128, 0, 128];
readonly rebeccapurple: [102, 51, 153];
readonly red: [255, 0, 0];
readonly rosybrown: [188, 143, 143];
readonly royalblue: [65, 105, 225];
readonly saddlebrown: [139, 69, 19];
readonly salmon: [250, 128, 114];
readonly sandybrown: [244, 164, 96];
readonly seagreen: [46, 139, 87];
readonly seashell: [255, 245, 238];
readonly sienna: [160, 82, 45];
readonly silver: [192, 192, 192];
readonly skyblue: [135, 206, 235];
readonly slateblue: [106, 90, 205];
readonly slategray: [112, 128, 144];
readonly slategrey: [112, 128, 144];
readonly snow: [255, 250, 250];
readonly springgreen: [0, 255, 127];
readonly steelblue: [70, 130, 180];
readonly tan: [210, 180, 140];
readonly teal: [0, 128, 128];
readonly thistle: [216, 191, 216];
readonly tomato: [255, 99, 71];
readonly turquoise: [64, 224, 208];
readonly violet: [238, 130, 238];
readonly wheat: [245, 222, 179];
readonly white: [255, 255, 255];
readonly whitesmoke: [245, 245, 245];
readonly yellow: [255, 255, 0];
readonly yellowgreen: [154, 205, 50];
};
/**
* cache invalid color value
* @param key - cache key
* @param nullable - is nullable
* @returns cached value
*/
export declare const cacheInvalidColorValue: (cacheKey: string, format: string, nullable?: boolean) => SpecifiedColorChannels | string | NullObject;
/**
* resolve invalid color value
* @param format - output format
* @param nullable - is nullable
* @returns resolved value
*/
export declare const resolveInvalidColorValue: (format: string, nullable?: boolean) => SpecifiedColorChannels | string | NullObject;
/**
* validate color components
* @param arr - color components
* @param [opt] - options
* @param [opt.alpha] - alpha channel
* @param [opt.minLength] - min length
* @param [opt.maxLength] - max length
* @param [opt.minRange] - min range
* @param [opt.maxRange] - max range
* @param [opt.validateRange] - validate range
* @returns result - validated color components
*/
export declare const validateColorComponents: (arr: ColorChannels | TriColorChannels, opt?: {
alpha?: boolean;
minLength?: number;
maxLength?: number;
minRange?: number;
maxRange?: number;
validateRange?: boolean;
}) => ColorChannels | TriColorChannels;
/**
* transform matrix
* @param mtx - 3 * 3 matrix
* @param vct - vector
* @param [skip] - skip validate
* @returns TriColorChannels - [p1, p2, p3]
*/
export declare const transformMatrix: (mtx: ColorMatrix, vct: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* normalize color components
* @param colorA - color components [v1, v2, v3, v4]
* @param colorB - color components [v1, v2, v3, v4]
* @param [skip] - skip validate
* @returns result - [colorA, colorB]
*/
export declare const normalizeColorComponents: (colorA: [number | string, number | string, number | string, number | string], colorB: [number | string, number | string, number | string, number | string], skip?: boolean) => [ColorChannels, ColorChannels];
/**
* number to hex string
* @param value - numeric value
* @returns hex string
*/
export declare const numberToHexString: (value: number) => string;
/**
* angle to deg
* @param angle
* @returns deg: 0..360
*/
export declare const angleToDeg: (angle: string) => number;
/**
* parse alpha
* @param [alpha] - alpha value
* @returns alpha: 0..1
*/
export declare const parseAlpha: (alpha?: string) => number;
/**
* parse hex alpha
* @param value - alpha value in hex string
* @returns alpha: 0..1
*/
export declare const parseHexAlpha: (value: string) => number;
/**
* transform rgb to linear rgb
* @param rgb - [r, g, b] r|g|b: 0..255
* @param [skip] - skip validate
* @returns TriColorChannels - [r, g, b] r|g|b: 0..1
*/
export declare const transformRgbToLinearRgb: (rgb: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform rgb to xyz
* @param rgb - [r, g, b] r|g|b: 0..255
* @param [skip] - skip validate
* @returns TriColorChannels - [x, y, z]
*/
export declare const transformRgbToXyz: (rgb: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform rgb to xyz-d50
* @param rgb - [r, g, b] r|g|b: 0..255 alpha: 0..1
* @returns TriColorChannels - [x, y, z]
*/
export declare const transformRgbToXyzD50: (rgb: TriColorChannels) => TriColorChannels;
/**
* transform linear rgb to rgb
* @param rgb - [r, g, b] r|g|b: 0..1
* @param [round] - round result
* @returns TriColorChannels - [r, g, b] r|g|b: 0..255
*/
export declare const transformLinearRgbToRgb: (rgb: TriColorChannels, round?: boolean) => TriColorChannels;
/**
* transform xyz to rgb
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [r, g, b] r|g|b: 0..255
*/
export declare const transformXyzToRgb: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz to xyz-d50
* @param xyz - [x, y, z]
* @returns TriColorChannels - [x, y, z]
*/
export declare const transformXyzToXyzD50: (xyz: TriColorChannels) => TriColorChannels;
/**
* transform xyz to hsl
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [h, s, l]
*/
export declare const transformXyzToHsl: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz to hwb
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [h, w, b]
*/
export declare const transformXyzToHwb: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz to oklab
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [l, a, b]
*/
export declare const transformXyzToOklab: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz to oklch
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [l, c, h]
*/
export declare const transformXyzToOklch: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz D50 to rgb
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [r, g, b] r|g|b: 0..255
*/
export declare const transformXyzD50ToRgb: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz-d50 to lab
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [l, a, b]
*/
export declare const transformXyzD50ToLab: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz-d50 to lch
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [l, c, h]
*/
export declare const transformXyzD50ToLch: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* convert rgb to hex color
* @param rgb - [r, g, b, alpha] r|g|b: 0..255 alpha: 0..1
* @returns hex color
*/
export declare const convertRgbToHex: (rgb: ColorChannels) => string;
/**
* convert linear rgb to hex color
* @param rgb - [r, g, b, alpha] r|g|b|alpha: 0..1
* @param [skip] - skip validate
* @returns hex color
*/
export declare const convertLinearRgbToHex: (rgb: ColorChannels, skip?: boolean) => string;
/**
* convert xyz to hex color
* @param xyz - [x, y, z, alpha]
* @returns hex color
*/
export declare const convertXyzToHex: (xyz: ColorChannels) => string;
/**
* convert xyz D50 to hex color
* @param xyz - [x, y, z, alpha]
* @returns hex color
*/
export declare const convertXyzD50ToHex: (xyz: ColorChannels) => string;
/**
* convert hex color to rgb
* @param value - hex color value
* @returns ColorChannels - [r, g, b, alpha] r|g|b: 0..255 alpha: 0..1
*/
export declare const convertHexToRgb: (value: string) => ColorChannels;
/**
* convert hex color to linear rgb
* @param value - hex color value
* @returns ColorChannels - [r, g, b, alpha] r|g|b|alpha: 0..1
*/
export declare const convertHexToLinearRgb: (value: string) => ColorChannels;
/**
* convert hex color to xyz
* @param value - hex color value
* @returns ColorChannels - [x, y, z, alpha]
*/
export declare const convertHexToXyz: (value: string) => ColorChannels;
/**
* parse rgb()
* @param value - rgb color value
* @param [opt] - options
* @returns parsed color - ['rgb', r, g, b, alpha], '(empty)', NullObject
*/
export declare const parseRgb: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse hsl()
* @param value - hsl color value
* @param [opt] - options
* @returns parsed color - ['rgb', r, g, b, alpha], '(empty)', NullObject
*/
export declare const parseHsl: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse hwb()
* @param value - hwb color value
* @param [opt] - options
* @returns parsed color - ['rgb', r, g, b, alpha], '(empty)', NullObject
*/
export declare const parseHwb: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse lab()
* @param value - lab color value
* @param [opt] - options
* @returns parsed color
* - [xyz-d50, x, y, z, alpha], ['lab', l, a, b, alpha], '(empty)', NullObject
*/
export declare const parseLab: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse lch()
* @param value - lch color value
* @param [opt] - options
* @returns parsed color
* - ['xyz-d50', x, y, z, alpha], ['lch', l, c, h, alpha]
* - '(empty)', NullObject
*/
export declare const parseLch: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse oklab()
* @param value - oklab color value
* @param [opt] - options
* @returns parsed color
* - ['xyz-d65', x, y, z, alpha], ['oklab', l, a, b, alpha]
* - '(empty)', NullObject
*/
export declare const parseOklab: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse oklch()
* @param value - oklch color value
* @param [opt] - options
* @returns parsed color
* - ['xyz-d65', x, y, z, alpha], ['oklch', l, c, h, alpha]
* - '(empty)', NullObject
*/
export declare const parseOklch: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse color()
* @param value - color function value
* @param [opt] - options
* @returns parsed color
* - ['xyz-(d50|d65)', x, y, z, alpha], [cs, r, g, b, alpha]
* - '(empty)', NullObject
*/
export declare const parseColorFunc: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse color value
* @param value - CSS color value
* @param [opt] - options
* @returns parsed color
* - ['xyz-(d50|d65)', x, y, z, alpha], ['rgb', r, g, b, alpha]
* - value, '(empty)', NullObject
*/
export declare const parseColorValue: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* resolve color value
* @param value - CSS color value
* @param [opt] - options
* @returns resolved color
* - [cs, v1, v2, v3, alpha], value, '(empty)', NullObject
*/
export declare const resolveColorValue: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* resolve color()
* @param value - color function value
* @param [opt] - options
* @returns resolved color - [cs, v1, v2, v3, alpha], '(empty)', NullObject
*/
export declare const resolveColorFunc: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* convert color value to linear rgb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [r, g, b, alpha] r|g|b|alpha: 0..1
*/
export declare const convertColorToLinearRgb: (value: string, opt?: {
colorSpace?: string;
format?: string;
}) => ColorChannels | NullObject;
/**
* convert color value to rgb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject
* - [r, g, b, alpha] r|g|b: 0..255 alpha: 0..1
*/
export declare const convertColorToRgb: (value: string, opt?: Options) => ColorChannels | NullObject;
/**
* convert color value to xyz
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [x, y, z, alpha]
*/
export declare const convertColorToXyz: (value: string, opt?: Options) => ColorChannels | NullObject;
/**
* convert color value to hsl
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [h, s, l, alpha], hue may be powerless
*/
export declare const convertColorToHsl: (value: string, opt?: Options) => ColorChannels | [number | string, number, number, number] | NullObject;
/**
* convert color value to hwb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [h, w, b, alpha], hue may be powerless
*/
export declare const convertColorToHwb: (value: string, opt?: Options) => ColorChannels | [number | string, number, number, number] | NullObject;
/**
* convert color value to lab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [l, a, b, alpha]
*/
export declare const convertColorToLab: (value: string, opt?: Options) => ColorChannels | NullObject;
/**
* convert color value to lch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [l, c, h, alpha], hue may be powerless
*/
export declare const convertColorToLch: (value: string, opt?: Options) => ColorChannels | [number, number, number | string, number] | NullObject;
/**
* convert color value to oklab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [l, a, b, alpha]
*/
export declare const convertColorToOklab: (value: string, opt?: Options) => ColorChannels | NullObject;
/**
* convert color value to oklch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [l, c, h, alpha], hue may be powerless
*/
export declare const convertColorToOklch: (value: string, opt?: Options) => ColorChannels | [number, number, number | string, number] | NullObject;
/**
* resolve color-mix()
* @param value - color-mix color value
* @param [opt] - options
* @returns resolved color - [cs, v1, v2, v3, alpha], '(empty)'
*/
export declare const resolveColorMix: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
export {};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,21 @@
/**
* common
*/
/**
* get type
* @param o - object to check
* @returns type of object
*/
export declare const getType: (o: unknown) => string;
/**
* is string
* @param o - object to check
* @returns result
*/
export declare const isString: (o: unknown) => o is string;
/**
* is string or number
* @param o - object to check
* @returns result
*/
export declare const isStringOrNumber: (o: unknown) => boolean;

View File

@@ -0,0 +1,7 @@
const isString = (o) => typeof o === "string" || o instanceof String;
const isStringOrNumber = (o) => isString(o) || typeof o === "number";
export {
isString,
isStringOrNumber
};
//# sourceMappingURL=common.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"common.js","sources":["../../../src/js/common.ts"],"sourcesContent":["/**\n * common\n */\n\n/* numeric constants */\nconst TYPE_FROM = 8;\nconst TYPE_TO = -1;\n\n/**\n * get type\n * @param o - object to check\n * @returns type of object\n */\nexport const getType = (o: unknown): string =>\n Object.prototype.toString.call(o).slice(TYPE_FROM, TYPE_TO);\n\n/**\n * is string\n * @param o - object to check\n * @returns result\n */\nexport const isString = (o: unknown): o is string =>\n typeof o === 'string' || o instanceof String;\n\n/**\n * is string or number\n * @param o - object to check\n * @returns result\n */\nexport const isStringOrNumber = (o: unknown): boolean =>\n isString(o) || typeof o === 'number';\n"],"names":[],"mappings":"AAqBO,MAAM,WAAW,CAAC,MACvB,OAAO,MAAM,YAAY,aAAa;AAOjC,MAAM,mBAAmB,CAAC,MAC/B,SAAS,CAAC,KAAK,OAAO,MAAM;"}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,97 @@
const _DIGIT = "(?:0|[1-9]\\d*)";
const _COMPARE = "clamp|max|min";
const _EXPO = "exp|hypot|log|pow|sqrt";
const _SIGN = "abs|sign";
const _STEP = "mod|rem|round";
const _TRIG = "a?(?:cos|sin|tan)|atan2";
const _MATH = `${_COMPARE}|${_EXPO}|${_SIGN}|${_STEP}|${_TRIG}`;
const _CALC = `calc|${_MATH}`;
const _VAR = `var|${_CALC}`;
const ANGLE = "deg|g?rad|turn";
const LENGTH = "[cm]m|[dls]?v(?:[bhiw]|max|min)|in|p[ctx]|q|r?(?:[cl]h|cap|e[mx]|ic)";
const NUM = `[+-]?(?:${_DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${_DIGIT})?`;
const NUM_POSITIVE = `\\+?(?:${_DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${_DIGIT})?`;
const NONE = "none";
const PCT = `${NUM}%`;
const SYN_FN_CALC = `^(?:${_CALC})\\(|(?<=[*\\/\\s\\(])(?:${_CALC})\\(`;
const SYN_FN_MATH_START = `^(?:${_MATH})\\($`;
const SYN_FN_VAR = "^var\\(|(?<=[*\\/\\s\\(])var\\(";
const SYN_FN_VAR_START = `^(?:${_VAR})\\(`;
const _ALPHA = `(?:\\s*\\/\\s*(?:${NUM}|${PCT}|${NONE}))?`;
const _ALPHA_LV3 = `(?:\\s*,\\s*(?:${NUM}|${PCT}))?`;
const _COLOR_FUNC = "(?:ok)?l(?:ab|ch)|color|hsla?|hwb|rgba?";
const _COLOR_KEY = "[a-z]+|#[\\da-f]{3}|#[\\da-f]{4}|#[\\da-f]{6}|#[\\da-f]{8}";
const _CS_HUE = "(?:ok)?lch|hsl|hwb";
const _CS_HUE_ARC = "(?:de|in)creasing|longer|shorter";
const _NUM_ANGLE = `${NUM}(?:${ANGLE})?`;
const _NUM_ANGLE_NONE = `(?:${NUM}(?:${ANGLE})?|${NONE})`;
const _NUM_PCT_NONE = `(?:${NUM}|${PCT}|${NONE})`;
const CS_HUE = `(?:${_CS_HUE})(?:\\s(?:${_CS_HUE_ARC})\\shue)?`;
const CS_HUE_CAPT = `(${_CS_HUE})(?:\\s(${_CS_HUE_ARC})\\shue)?`;
const CS_LAB = "(?:ok)?lab";
const CS_LCH = "(?:ok)?lch";
const CS_SRGB = "srgb(?:-linear)?";
const CS_RGB = `(?:a98|prophoto)-rgb|display-p3|rec2020|${CS_SRGB}`;
const CS_XYZ = "xyz(?:-d(?:50|65))?";
const CS_RECT = `${CS_LAB}|${CS_RGB}|${CS_XYZ}`;
const CS_MIX = `${CS_HUE}|${CS_RECT}`;
const FN_COLOR = "color(";
const FN_MIX = "color-mix(";
const FN_REL = `(?:${_COLOR_FUNC})\\(\\s*from\\s+`;
const FN_REL_CAPT = `(${_COLOR_FUNC})\\(\\s*from\\s+`;
const FN_VAR = "var(";
const SYN_FN_COLOR = `(?:${CS_RGB}|${CS_XYZ})(?:\\s+${_NUM_PCT_NONE}){3}${_ALPHA}`;
const SYN_FN_REL = `^${FN_REL}|(?<=[\\s])${FN_REL}`;
const SYN_HSL = `${_NUM_ANGLE_NONE}(?:\\s+${_NUM_PCT_NONE}){2}${_ALPHA}`;
const SYN_HSL_LV3 = `${_NUM_ANGLE}(?:\\s*,\\s*${PCT}){2}${_ALPHA_LV3}`;
const SYN_LCH = `(?:${_NUM_PCT_NONE}\\s+){2}${_NUM_ANGLE_NONE}${_ALPHA}`;
const SYN_MOD = `${_NUM_PCT_NONE}(?:\\s+${_NUM_PCT_NONE}){2}${_ALPHA}`;
const SYN_RGB_LV3 = `(?:${NUM}(?:\\s*,\\s*${NUM}){2}|${PCT}(?:\\s*,\\s*${PCT}){2})${_ALPHA_LV3}`;
const SYN_COLOR_TYPE = `${_COLOR_KEY}|hsla?\\(\\s*${SYN_HSL_LV3}\\s*\\)|rgba?\\(\\s*${SYN_RGB_LV3}\\s*\\)|(?:hsla?|hwb)\\(\\s*${SYN_HSL}\\s*\\)|(?:(?:ok)?lab|rgba?)\\(\\s*${SYN_MOD}\\s*\\)|(?:ok)?lch\\(\\s*${SYN_LCH}\\s*\\)|color\\(\\s*${SYN_FN_COLOR}\\s*\\)`;
const SYN_MIX_PART = `(?:${SYN_COLOR_TYPE})(?:\\s+${PCT})?`;
const SYN_MIX = `color-mix\\(\\s*in\\s+(?:${CS_MIX})\\s*,\\s*${SYN_MIX_PART}\\s*,\\s*${SYN_MIX_PART}\\s*\\)`;
const SYN_MIX_CAPT = `color-mix\\(\\s*in\\s+(${CS_MIX})\\s*,\\s*(${SYN_MIX_PART})\\s*,\\s*(${SYN_MIX_PART})\\s*\\)`;
const VAL_COMP = "computedValue";
const VAL_MIX = "mixValue";
const VAL_SPEC = "specifiedValue";
export {
ANGLE,
CS_HUE,
CS_HUE_CAPT,
CS_LAB,
CS_LCH,
CS_MIX,
CS_RECT,
CS_RGB,
CS_SRGB,
CS_XYZ,
FN_COLOR,
FN_MIX,
FN_REL,
FN_REL_CAPT,
FN_VAR,
LENGTH,
NONE,
NUM,
NUM_POSITIVE,
PCT,
SYN_COLOR_TYPE,
SYN_FN_CALC,
SYN_FN_COLOR,
SYN_FN_MATH_START,
SYN_FN_REL,
SYN_FN_VAR,
SYN_FN_VAR_START,
SYN_HSL,
SYN_HSL_LV3,
SYN_LCH,
SYN_MIX,
SYN_MIX_CAPT,
SYN_MIX_PART,
SYN_MOD,
SYN_RGB_LV3,
VAL_COMP,
VAL_MIX,
VAL_SPEC
};
//# sourceMappingURL=constant.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,99 @@
import { NullObject } from './cache.js';
import { ColorChannels, Options } from './typedef.js';
/**
* pre process
* @param value - CSS color value
* @param [opt] - options
* @returns value
*/
export declare const preProcess: (value: string, opt?: Options) => string | NullObject;
/**
* convert number to hex string
* @param value - numeric value
* @returns hex string: 00..ff
*/
export declare const numberToHex: (value: number) => string;
/**
* convert color to hex
* @param value - CSS color value
* @param [opt] - options
* @param [opt.alpha] - enable alpha channel
* @returns #rrggbb | #rrggbbaa | null
*/
export declare const colorToHex: (value: string, opt?: Options) => string | null;
/**
* convert color to hsl
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [h, s, l, alpha]
*/
export declare const colorToHsl: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to hwb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [h, w, b, alpha]
*/
export declare const colorToHwb: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to lab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, a, b, alpha]
*/
export declare const colorToLab: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to lch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, c, h, alpha]
*/
export declare const colorToLch: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to oklab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, a, b, alpha]
*/
export declare const colorToOklab: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to oklch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, c, h, alpha]
*/
export declare const colorToOklch: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to rgb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [r, g, b, alpha]
*/
export declare const colorToRgb: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to xyz
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [x, y, z, alpha]
*/
export declare const colorToXyz: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to xyz-d50
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [x, y, z, alpha]
*/
export declare const colorToXyzD50: (value: string, opt?: Options) => ColorChannels;
export declare const convert: {
colorToHex: (value: string, opt?: Options) => string | null;
colorToHsl: (value: string, opt?: Options) => ColorChannels;
colorToHwb: (value: string, opt?: Options) => ColorChannels;
colorToLab: (value: string, opt?: Options) => ColorChannels;
colorToLch: (value: string, opt?: Options) => ColorChannels;
colorToOklab: (value: string, opt?: Options) => ColorChannels;
colorToOklch: (value: string, opt?: Options) => ColorChannels;
colorToRgb: (value: string, opt?: Options) => ColorChannels;
colorToXyz: (value: string, opt?: Options) => ColorChannels;
colorToXyzD50: (value: string, opt?: Options) => ColorChannels;
numberToHex: (value: number) => string;
};

View File

@@ -0,0 +1,361 @@
import { NullObject, createCacheKey, getCache, CacheItem, setCache } from "./cache.js";
import { numberToHexString, parseColorFunc, parseColorValue, convertColorToRgb, convertColorToOklch, convertColorToOklab, convertColorToLch, convertColorToLab, convertColorToHwb, convertColorToHsl } from "./color.js";
import { isString } from "./common.js";
import { cssCalc } from "./css-calc.js";
import { resolveVar } from "./css-var.js";
import { resolveRelativeColor } from "./relative-color.js";
import { resolveColor } from "./resolve.js";
import { VAL_COMP, SYN_FN_VAR, SYN_FN_REL, SYN_FN_CALC } from "./constant.js";
const NAMESPACE = "convert";
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
const REG_FN_REL = new RegExp(SYN_FN_REL);
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
const preProcess = (value, opt = {}) => {
if (isString(value)) {
value = value.trim();
if (!value) {
return new NullObject();
}
} else {
return new NullObject();
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "preProcess",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return cachedResult;
}
return cachedResult.item;
}
if (REG_FN_VAR.test(value)) {
const resolvedValue = resolveVar(value, opt);
if (isString(resolvedValue)) {
value = resolvedValue;
} else {
setCache(cacheKey, null);
return new NullObject();
}
}
if (REG_FN_REL.test(value)) {
const resolvedValue = resolveRelativeColor(value, opt);
if (isString(resolvedValue)) {
value = resolvedValue;
} else {
setCache(cacheKey, null);
return new NullObject();
}
} else if (REG_FN_CALC.test(value)) {
value = cssCalc(value, opt);
}
if (value.startsWith("color-mix")) {
const clonedOpt = structuredClone(opt);
clonedOpt.format = VAL_COMP;
clonedOpt.nullable = true;
const resolvedValue = resolveColor(value, clonedOpt);
setCache(cacheKey, resolvedValue);
return resolvedValue;
}
setCache(cacheKey, value);
return value;
};
const numberToHex = (value) => {
const hex = numberToHexString(value);
return hex;
};
const colorToHex = (value, opt = {}) => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return null;
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const { alpha = false } = opt;
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "colorToHex",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return null;
}
return cachedResult.item;
}
let hex;
opt.nullable = true;
if (alpha) {
opt.format = "hexAlpha";
hex = resolveColor(value, opt);
} else {
opt.format = "hex";
hex = resolveColor(value, opt);
}
if (isString(hex)) {
setCache(cacheKey, hex);
return hex;
}
setCache(cacheKey, null);
return null;
};
const colorToHsl = (value, opt = {}) => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "colorToHsl",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
opt.format = "hsl";
const hsl = convertColorToHsl(value, opt);
setCache(cacheKey, hsl);
return hsl;
};
const colorToHwb = (value, opt = {}) => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "colorToHwb",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
opt.format = "hwb";
const hwb = convertColorToHwb(value, opt);
setCache(cacheKey, hwb);
return hwb;
};
const colorToLab = (value, opt = {}) => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "colorToLab",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
const lab = convertColorToLab(value, opt);
setCache(cacheKey, lab);
return lab;
};
const colorToLch = (value, opt = {}) => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "colorToLch",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
const lch = convertColorToLch(value, opt);
setCache(cacheKey, lch);
return lch;
};
const colorToOklab = (value, opt = {}) => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "colorToOklab",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
const lab = convertColorToOklab(value, opt);
setCache(cacheKey, lab);
return lab;
};
const colorToOklch = (value, opt = {}) => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "colorToOklch",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
const lch = convertColorToOklch(value, opt);
setCache(cacheKey, lch);
return lch;
};
const colorToRgb = (value, opt = {}) => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "colorToRgb",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
const rgb = convertColorToRgb(value, opt);
setCache(cacheKey, rgb);
return rgb;
};
const colorToXyz = (value, opt = {}) => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "colorToXyz",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
let xyz;
if (value.startsWith("color(")) {
[, ...xyz] = parseColorFunc(value, opt);
} else {
[, ...xyz] = parseColorValue(value, opt);
}
setCache(cacheKey, xyz);
return xyz;
};
const colorToXyzD50 = (value, opt = {}) => {
opt.d50 = true;
return colorToXyz(value, opt);
};
const convert = {
colorToHex,
colorToHsl,
colorToHwb,
colorToLab,
colorToLch,
colorToOklab,
colorToOklch,
colorToRgb,
colorToXyz,
colorToXyzD50,
numberToHex
};
export {
colorToHex,
colorToHsl,
colorToHwb,
colorToLab,
colorToLch,
colorToOklab,
colorToOklch,
colorToRgb,
colorToXyz,
colorToXyzD50,
convert,
numberToHex,
preProcess
};
//# sourceMappingURL=convert.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,89 @@
import { CSSToken } from '@csstools/css-tokenizer';
import { NullObject } from './cache.js';
import { Options } from './typedef.js';
/**
* Calclator
*/
export declare class Calculator {
#private;
/**
* constructor
*/
constructor();
get hasNum(): boolean;
set hasNum(value: boolean);
get numSum(): number[];
get numMul(): number[];
get hasPct(): boolean;
set hasPct(value: boolean);
get pctSum(): number[];
get pctMul(): number[];
get hasDim(): boolean;
set hasDim(value: boolean);
get dimSum(): string[];
get dimSub(): string[];
get dimMul(): string[];
get dimDiv(): string[];
get hasEtc(): boolean;
set hasEtc(value: boolean);
get etcSum(): string[];
get etcSub(): string[];
get etcMul(): string[];
get etcDiv(): string[];
/**
* clear values
* @returns void
*/
clear(): void;
/**
* sort values
* @param values - values
* @returns sorted values
*/
sort(values?: string[]): string[];
/**
* multiply values
* @returns resolved value
*/
multiply(): string;
/**
* sum values
* @returns resolved value
*/
sum(): string;
}
/**
* sort calc values
* @param values - values to sort
* @param [finalize] - finalize values
* @returns sorted values
*/
export declare const sortCalcValues: (values?: (number | string)[], finalize?: boolean) => string;
/**
* serialize calc
* @param value - CSS value
* @param [opt] - options
* @returns serialized value
*/
export declare const serializeCalc: (value: string, opt?: Options) => string;
/**
* resolve dimension
* @param token - CSS token
* @param [opt] - options
* @returns resolved value
*/
export declare const resolveDimension: (token: CSSToken, opt?: Options) => string | NullObject;
/**
* parse tokens
* @param tokens - CSS tokens
* @param [opt] - options
* @returns parsed tokens
*/
export declare const parseTokens: (tokens: CSSToken[], opt?: Options) => string[];
/**
* CSS calc()
* @param value - CSS value including calc()
* @param [opt] - options
* @returns resolved value
*/
export declare const cssCalc: (value: string, opt?: Options) => string;

View File

@@ -0,0 +1,860 @@
var __typeError = (msg) => {
throw TypeError(msg);
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var _hasNum, _numSum, _numMul, _hasPct, _pctSum, _pctMul, _hasDim, _dimSum, _dimSub, _dimMul, _dimDiv, _hasEtc, _etcSum, _etcSub, _etcMul, _etcDiv;
import { calc } from "@csstools/css-calc";
import { TokenType, tokenize } from "@csstools/css-tokenizer";
import { createCacheKey, getCache, CacheItem, setCache, NullObject } from "./cache.js";
import { isString, isStringOrNumber } from "./common.js";
import { resolveVar } from "./css-var.js";
import { roundToPrecision } from "./util.js";
import { VAL_SPEC, SYN_FN_VAR, SYN_FN_CALC, SYN_FN_VAR_START, NUM, ANGLE, LENGTH, SYN_FN_MATH_START } from "./constant.js";
const {
CloseParen: PAREN_CLOSE,
Comment: COMMENT,
Dimension: DIM,
EOF,
Function: FUNC,
OpenParen: PAREN_OPEN,
Whitespace: W_SPACE
} = TokenType;
const NAMESPACE = "css-calc";
const TRIA = 3;
const HEX = 16;
const MAX_PCT = 100;
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
const REG_FN_CALC_NUM = new RegExp(`^calc\\((${NUM})\\)$`);
const REG_FN_MATH_START = new RegExp(SYN_FN_MATH_START);
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
const REG_FN_VAR_START = new RegExp(SYN_FN_VAR_START);
const REG_OPERATOR = /\s[*+/-]\s/;
const REG_TYPE_DIM = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH})$`);
const REG_TYPE_DIM_PCT = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH}|%)$`);
const REG_TYPE_PCT = new RegExp(`^(${NUM})%$`);
class Calculator {
/**
* constructor
*/
constructor() {
/* private */
// number
__privateAdd(this, _hasNum);
__privateAdd(this, _numSum);
__privateAdd(this, _numMul);
// percentage
__privateAdd(this, _hasPct);
__privateAdd(this, _pctSum);
__privateAdd(this, _pctMul);
// dimension
__privateAdd(this, _hasDim);
__privateAdd(this, _dimSum);
__privateAdd(this, _dimSub);
__privateAdd(this, _dimMul);
__privateAdd(this, _dimDiv);
// et cetra
__privateAdd(this, _hasEtc);
__privateAdd(this, _etcSum);
__privateAdd(this, _etcSub);
__privateAdd(this, _etcMul);
__privateAdd(this, _etcDiv);
__privateSet(this, _hasNum, false);
__privateSet(this, _numSum, []);
__privateSet(this, _numMul, []);
__privateSet(this, _hasPct, false);
__privateSet(this, _pctSum, []);
__privateSet(this, _pctMul, []);
__privateSet(this, _hasDim, false);
__privateSet(this, _dimSum, []);
__privateSet(this, _dimSub, []);
__privateSet(this, _dimMul, []);
__privateSet(this, _dimDiv, []);
__privateSet(this, _hasEtc, false);
__privateSet(this, _etcSum, []);
__privateSet(this, _etcSub, []);
__privateSet(this, _etcMul, []);
__privateSet(this, _etcDiv, []);
}
get hasNum() {
return __privateGet(this, _hasNum);
}
set hasNum(value) {
__privateSet(this, _hasNum, !!value);
}
get numSum() {
return __privateGet(this, _numSum);
}
get numMul() {
return __privateGet(this, _numMul);
}
get hasPct() {
return __privateGet(this, _hasPct);
}
set hasPct(value) {
__privateSet(this, _hasPct, !!value);
}
get pctSum() {
return __privateGet(this, _pctSum);
}
get pctMul() {
return __privateGet(this, _pctMul);
}
get hasDim() {
return __privateGet(this, _hasDim);
}
set hasDim(value) {
__privateSet(this, _hasDim, !!value);
}
get dimSum() {
return __privateGet(this, _dimSum);
}
get dimSub() {
return __privateGet(this, _dimSub);
}
get dimMul() {
return __privateGet(this, _dimMul);
}
get dimDiv() {
return __privateGet(this, _dimDiv);
}
get hasEtc() {
return __privateGet(this, _hasEtc);
}
set hasEtc(value) {
__privateSet(this, _hasEtc, !!value);
}
get etcSum() {
return __privateGet(this, _etcSum);
}
get etcSub() {
return __privateGet(this, _etcSub);
}
get etcMul() {
return __privateGet(this, _etcMul);
}
get etcDiv() {
return __privateGet(this, _etcDiv);
}
/**
* clear values
* @returns void
*/
clear() {
__privateSet(this, _hasNum, false);
__privateSet(this, _numSum, []);
__privateSet(this, _numMul, []);
__privateSet(this, _hasPct, false);
__privateSet(this, _pctSum, []);
__privateSet(this, _pctMul, []);
__privateSet(this, _hasDim, false);
__privateSet(this, _dimSum, []);
__privateSet(this, _dimSub, []);
__privateSet(this, _dimMul, []);
__privateSet(this, _dimDiv, []);
__privateSet(this, _hasEtc, false);
__privateSet(this, _etcSum, []);
__privateSet(this, _etcSub, []);
__privateSet(this, _etcMul, []);
__privateSet(this, _etcDiv, []);
}
/**
* sort values
* @param values - values
* @returns sorted values
*/
sort(values = []) {
const arr = [...values];
if (arr.length > 1) {
arr.sort((a, b) => {
let res;
if (REG_TYPE_DIM_PCT.test(a) && REG_TYPE_DIM_PCT.test(b)) {
const [, valA, unitA] = a.match(REG_TYPE_DIM_PCT);
const [, valB, unitB] = b.match(REG_TYPE_DIM_PCT);
if (unitA === unitB) {
if (Number(valA) === Number(valB)) {
res = 0;
} else if (Number(valA) > Number(valB)) {
res = 1;
} else {
res = -1;
}
} else if (unitA > unitB) {
res = 1;
} else {
res = -1;
}
} else {
if (a === b) {
res = 0;
} else if (a > b) {
res = 1;
} else {
res = -1;
}
}
return res;
});
}
return arr;
}
/**
* multiply values
* @returns resolved value
*/
multiply() {
const value = [];
let num;
if (__privateGet(this, _hasNum)) {
num = 1;
for (const i of __privateGet(this, _numMul)) {
num *= i;
if (num === 0 || !Number.isFinite(num) || Number.isNaN(num)) {
break;
}
}
if (!__privateGet(this, _hasPct) && !__privateGet(this, _hasDim) && !this.hasEtc) {
if (Number.isFinite(num)) {
num = roundToPrecision(num, HEX);
}
value.push(num);
}
}
if (__privateGet(this, _hasPct)) {
if (typeof num !== "number") {
num = 1;
}
for (const i of __privateGet(this, _pctMul)) {
num *= i;
if (num === 0 || !Number.isFinite(num) || Number.isNaN(num)) {
break;
}
}
if (Number.isFinite(num)) {
num = `${roundToPrecision(num, HEX)}%`;
}
if (!__privateGet(this, _hasDim) && !this.hasEtc) {
value.push(num);
}
}
if (__privateGet(this, _hasDim)) {
let dim = "";
let mul = "";
let div = "";
if (__privateGet(this, _dimMul).length) {
if (__privateGet(this, _dimMul).length === 1) {
[mul] = __privateGet(this, _dimMul);
} else {
mul = `${this.sort(__privateGet(this, _dimMul)).join(" * ")}`;
}
}
if (__privateGet(this, _dimDiv).length) {
if (__privateGet(this, _dimDiv).length === 1) {
[div] = __privateGet(this, _dimDiv);
} else {
div = `${this.sort(__privateGet(this, _dimDiv)).join(" * ")}`;
}
}
if (Number.isFinite(num)) {
if (mul) {
if (div) {
if (div.includes("*")) {
dim = calc(`calc(${num} * ${mul} / (${div}))`, {
toCanonicalUnits: true
});
} else {
dim = calc(`calc(${num} * ${mul} / ${div})`, {
toCanonicalUnits: true
});
}
} else {
dim = calc(`calc(${num} * ${mul})`, {
toCanonicalUnits: true
});
}
} else if (div.includes("*")) {
dim = calc(`calc(${num} / (${div}))`, {
toCanonicalUnits: true
});
} else {
dim = calc(`calc(${num} / ${div})`, {
toCanonicalUnits: true
});
}
value.push(dim.replace(/^calc/, ""));
} else {
if (!value.length && num !== void 0) {
value.push(num);
}
if (mul) {
if (div) {
if (div.includes("*")) {
dim = calc(`calc(${mul} / (${div}))`, {
toCanonicalUnits: true
});
} else {
dim = calc(`calc(${mul} / ${div})`, {
toCanonicalUnits: true
});
}
} else {
dim = calc(`calc(${mul})`, {
toCanonicalUnits: true
});
}
if (value.length) {
value.push("*", dim.replace(/^calc/, ""));
} else {
value.push(dim.replace(/^calc/, ""));
}
} else {
dim = calc(`calc(${div})`, {
toCanonicalUnits: true
});
if (value.length) {
value.push("/", dim.replace(/^calc/, ""));
} else {
value.push("1", "/", dim.replace(/^calc/, ""));
}
}
}
}
if (__privateGet(this, _hasEtc)) {
if (__privateGet(this, _etcMul).length) {
if (!value.length && num !== void 0) {
value.push(num);
}
const mul = this.sort(__privateGet(this, _etcMul)).join(" * ");
if (value.length) {
value.push(`* ${mul}`);
} else {
value.push(`${mul}`);
}
}
if (__privateGet(this, _etcDiv).length) {
const div = this.sort(__privateGet(this, _etcDiv)).join(" * ");
if (div.includes("*")) {
if (value.length) {
value.push(`/ (${div})`);
} else {
value.push(`1 / (${div})`);
}
} else if (value.length) {
value.push(`/ ${div}`);
} else {
value.push(`1 / ${div}`);
}
}
}
if (value.length) {
return value.join(" ");
}
return "";
}
/**
* sum values
* @returns resolved value
*/
sum() {
const value = [];
if (__privateGet(this, _hasNum)) {
let num = 0;
for (const i of __privateGet(this, _numSum)) {
num += i;
if (!Number.isFinite(num) || Number.isNaN(num)) {
break;
}
}
value.push(num);
}
if (__privateGet(this, _hasPct)) {
let num = 0;
for (const i of __privateGet(this, _pctSum)) {
num += i;
if (!Number.isFinite(num)) {
break;
}
}
if (Number.isFinite(num)) {
num = `${num}%`;
}
if (value.length) {
value.push(`+ ${num}`);
} else {
value.push(num);
}
}
if (__privateGet(this, _hasDim)) {
let dim, sum, sub;
if (__privateGet(this, _dimSum).length) {
sum = this.sort(__privateGet(this, _dimSum)).join(" + ");
}
if (__privateGet(this, _dimSub).length) {
sub = this.sort(__privateGet(this, _dimSub)).join(" + ");
}
if (sum) {
if (sub) {
if (sub.includes("-")) {
dim = calc(`calc(${sum} - (${sub}))`, {
toCanonicalUnits: true
});
} else {
dim = calc(`calc(${sum} - ${sub})`, {
toCanonicalUnits: true
});
}
} else {
dim = calc(`calc(${sum})`, {
toCanonicalUnits: true
});
}
} else {
dim = calc(`calc(-1 * (${sub}))`, {
toCanonicalUnits: true
});
}
if (value.length) {
value.push("+", dim.replace(/^calc/, ""));
} else {
value.push(dim.replace(/^calc/, ""));
}
}
if (__privateGet(this, _hasEtc)) {
if (__privateGet(this, _etcSum).length) {
const sum = this.sort(__privateGet(this, _etcSum)).map((item) => {
let res;
if (REG_OPERATOR.test(item) && !item.startsWith("(") && !item.endsWith(")")) {
res = `(${item})`;
} else {
res = item;
}
return res;
}).join(" + ");
if (value.length) {
if (__privateGet(this, _etcSum).length > 1) {
value.push(`+ (${sum})`);
} else {
value.push(`+ ${sum}`);
}
} else {
value.push(`${sum}`);
}
}
if (__privateGet(this, _etcSub).length) {
const sub = this.sort(__privateGet(this, _etcSub)).map((item) => {
let res;
if (REG_OPERATOR.test(item) && !item.startsWith("(") && !item.endsWith(")")) {
res = `(${item})`;
} else {
res = item;
}
return res;
}).join(" + ");
if (value.length) {
if (__privateGet(this, _etcSub).length > 1) {
value.push(`- (${sub})`);
} else {
value.push(`- ${sub}`);
}
} else if (__privateGet(this, _etcSub).length > 1) {
value.push(`-1 * (${sub})`);
} else {
value.push(`-1 * ${sub}`);
}
}
}
if (value.length) {
return value.join(" ");
}
return "";
}
}
_hasNum = new WeakMap();
_numSum = new WeakMap();
_numMul = new WeakMap();
_hasPct = new WeakMap();
_pctSum = new WeakMap();
_pctMul = new WeakMap();
_hasDim = new WeakMap();
_dimSum = new WeakMap();
_dimSub = new WeakMap();
_dimMul = new WeakMap();
_dimDiv = new WeakMap();
_hasEtc = new WeakMap();
_etcSum = new WeakMap();
_etcSub = new WeakMap();
_etcMul = new WeakMap();
_etcDiv = new WeakMap();
const sortCalcValues = (values = [], finalize = false) => {
if (values.length < TRIA) {
throw new Error(`Unexpected array length ${values.length}.`);
}
const start = values.shift();
if (!isString(start) || !start.endsWith("(")) {
throw new Error(`Unexpected token ${start}.`);
}
const end = values.pop();
if (end !== ")") {
throw new Error(`Unexpected token ${end}.`);
}
if (values.length === 1) {
const [value] = values;
if (!isStringOrNumber(value)) {
throw new Error(`Unexpected token ${value}.`);
}
return `${start}${value}${end}`;
}
const sortedValues = [];
const cal = new Calculator();
let operator = "";
const l = values.length;
for (let i = 0; i < l; i++) {
const value = values[i];
if (!isStringOrNumber(value)) {
throw new Error(`Unexpected token ${value}.`);
}
if (value === "*" || value === "/") {
operator = value;
} else if (value === "+" || value === "-") {
const sortedValue = cal.multiply();
if (sortedValue) {
sortedValues.push(sortedValue, value);
}
cal.clear();
operator = "";
} else {
const numValue = Number(value);
const strValue = `${value}`;
switch (operator) {
case "/": {
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numMul.push(1 / numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT);
cal.hasPct = true;
cal.pctMul.push(MAX_PCT * MAX_PCT / Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimDiv.push(strValue);
} else {
cal.hasEtc = true;
cal.etcDiv.push(strValue);
}
break;
}
case "*":
default: {
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numMul.push(numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT);
cal.hasPct = true;
cal.pctMul.push(Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimMul.push(strValue);
} else {
cal.hasEtc = true;
cal.etcMul.push(strValue);
}
}
}
}
if (i === l - 1) {
const sortedValue = cal.multiply();
if (sortedValue) {
sortedValues.push(sortedValue);
}
cal.clear();
operator = "";
}
}
let resolvedValue = "";
if (finalize && (sortedValues.includes("+") || sortedValues.includes("-"))) {
const finalizedValues = [];
cal.clear();
operator = "";
const l2 = sortedValues.length;
for (let i = 0; i < l2; i++) {
const value = sortedValues[i];
if (isStringOrNumber(value)) {
if (value === "+" || value === "-") {
operator = value;
} else {
const numValue = Number(value);
const strValue = `${value}`;
switch (operator) {
case "-": {
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numSum.push(-1 * numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT);
cal.hasPct = true;
cal.pctSum.push(-1 * Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimSub.push(strValue);
} else {
cal.hasEtc = true;
cal.etcSub.push(strValue);
}
break;
}
case "+":
default: {
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numSum.push(numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT);
cal.hasPct = true;
cal.pctSum.push(Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimSum.push(strValue);
} else {
cal.hasEtc = true;
cal.etcSum.push(strValue);
}
}
}
}
}
if (i === l2 - 1) {
const sortedValue = cal.sum();
if (sortedValue) {
finalizedValues.push(sortedValue);
}
cal.clear();
operator = "";
}
}
resolvedValue = finalizedValues.join(" ").replace(/\+\s-/g, "- ");
} else {
resolvedValue = sortedValues.join(" ").replace(/\+\s-/g, "- ");
}
if (resolvedValue.startsWith("(") && resolvedValue.endsWith(")") && resolvedValue.lastIndexOf("(") === 0 && resolvedValue.indexOf(")") === resolvedValue.length - 1) {
resolvedValue = resolvedValue.replace(/^\(/, "").replace(/\)$/, "");
}
return `${start}${resolvedValue}${end}`;
};
const serializeCalc = (value, opt = {}) => {
const { format = "" } = opt;
if (isString(value)) {
if (!REG_FN_VAR_START.test(value) || format !== VAL_SPEC) {
return value;
}
value = value.toLowerCase().trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "serializeCalc",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
const items = tokenize({ css: value }).map((token) => {
const [type, value2] = token;
let res = "";
if (type !== W_SPACE && type !== COMMENT) {
res = value2;
}
return res;
}).filter((v) => v);
let startIndex = items.findLastIndex((item) => /\($/.test(item));
while (startIndex) {
const endIndex = items.findIndex((item, index) => {
return item === ")" && index > startIndex;
});
const slicedValues = items.slice(startIndex, endIndex + 1);
let serializedValue = sortCalcValues(slicedValues);
if (REG_FN_VAR_START.test(serializedValue)) {
serializedValue = calc(serializedValue, {
toCanonicalUnits: true
});
}
items.splice(startIndex, endIndex - startIndex + 1, serializedValue);
startIndex = items.findLastIndex((item) => /\($/.test(item));
}
const serializedCalc = sortCalcValues(items, true);
setCache(cacheKey, serializedCalc);
return serializedCalc;
};
const resolveDimension = (token, opt = {}) => {
if (!Array.isArray(token)) {
throw new TypeError(`${token} is not an array.`);
}
const [, , , , detail = {}] = token;
const { unit, value } = detail;
const { dimension = {} } = opt;
if (unit === "px") {
return `${value}${unit}`;
}
const relativeValue = Number(value);
if (unit && Number.isFinite(relativeValue)) {
let pixelValue;
if (Object.hasOwnProperty.call(dimension, unit)) {
pixelValue = dimension[unit];
} else if (typeof dimension.callback === "function") {
pixelValue = dimension.callback(unit);
}
pixelValue = Number(pixelValue);
if (Number.isFinite(pixelValue)) {
return `${relativeValue * pixelValue}px`;
}
}
return new NullObject();
};
const parseTokens = (tokens, opt = {}) => {
if (!Array.isArray(tokens)) {
throw new TypeError(`${tokens} is not an array.`);
}
const { format = "" } = opt;
const mathFunc = /* @__PURE__ */ new Set();
let nest = 0;
const res = [];
while (tokens.length) {
const token = tokens.shift();
if (!Array.isArray(token)) {
throw new TypeError(`${token} is not an array.`);
}
const [type = "", value = ""] = token;
switch (type) {
case DIM: {
if (format === VAL_SPEC && !mathFunc.has(nest)) {
res.push(value);
} else {
const resolvedValue = resolveDimension(token, opt);
if (isString(resolvedValue)) {
res.push(resolvedValue);
} else {
res.push(value);
}
}
break;
}
case FUNC:
case PAREN_OPEN: {
res.push(value);
nest++;
if (REG_FN_MATH_START.test(value)) {
mathFunc.add(nest);
}
break;
}
case PAREN_CLOSE: {
if (res.length) {
const lastValue = res[res.length - 1];
if (lastValue === " ") {
res.splice(-1, 1, value);
} else {
res.push(value);
}
} else {
res.push(value);
}
if (mathFunc.has(nest)) {
mathFunc.delete(nest);
}
nest--;
break;
}
case W_SPACE: {
if (res.length) {
const lastValue = res[res.length - 1];
if (isString(lastValue) && !lastValue.endsWith("(") && lastValue !== " ") {
res.push(value);
}
}
break;
}
default: {
if (type !== COMMENT && type !== EOF) {
res.push(value);
}
}
}
}
return res;
};
const cssCalc = (value, opt = {}) => {
const { format = "" } = opt;
if (isString(value)) {
if (REG_FN_VAR.test(value)) {
if (format === VAL_SPEC) {
return value;
} else {
const resolvedValue2 = resolveVar(value, opt);
if (isString(resolvedValue2)) {
return resolvedValue2;
} else {
return "";
}
}
} else if (!REG_FN_CALC.test(value)) {
return value;
}
value = value.toLowerCase().trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "cssCalc",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
const tokens = tokenize({ css: value });
const values = parseTokens(tokens, opt);
let resolvedValue = calc(values.join(""), {
toCanonicalUnits: true
});
if (REG_FN_VAR_START.test(value)) {
if (REG_TYPE_DIM_PCT.test(resolvedValue)) {
const [, val, unit] = resolvedValue.match(
REG_TYPE_DIM_PCT
);
resolvedValue = `${roundToPrecision(Number(val), HEX)}${unit}`;
}
if (resolvedValue && !REG_FN_VAR_START.test(resolvedValue) && format === VAL_SPEC) {
resolvedValue = `calc(${resolvedValue})`;
}
}
if (format === VAL_SPEC) {
if (/\s[-+*/]\s/.test(resolvedValue) && !resolvedValue.includes("NaN")) {
resolvedValue = serializeCalc(resolvedValue, opt);
} else if (REG_FN_CALC_NUM.test(resolvedValue)) {
const [, val] = resolvedValue.match(REG_FN_CALC_NUM);
resolvedValue = `calc(${roundToPrecision(Number(val), HEX)})`;
}
}
setCache(cacheKey, resolvedValue);
return resolvedValue;
};
export {
Calculator,
cssCalc,
parseTokens,
resolveDimension,
serializeCalc,
sortCalcValues
};
//# sourceMappingURL=css-calc.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
import { Options } from './typedef.js';
/**
* @type ColorStopList - list of color stops
*/
type ColorStopList = [string, string, ...string[]];
/**
* @typedef Gradient - parsed CSS gradient
* @property value - input value
* @property type - gradient type
* @property [gradientLine] - gradient line
* @property colorStopList - list of color stops
*/
interface Gradient {
value: string;
type: string;
gradientLine?: string;
colorStopList: ColorStopList;
}
/**
* get gradient type
* @param value - gradient value
* @returns gradient type
*/
export declare const getGradientType: (value: string) => string;
/**
* validate gradient line
* @param value - gradient line value
* @param type - gradient type
* @returns result
*/
export declare const validateGradientLine: (value: string, type: string) => boolean;
/**
* validate color stop list
* @param list
* @param type
* @param [opt]
* @returns result
*/
export declare const validateColorStopList: (list: string[], type: string, opt?: Options) => boolean;
/**
* parse CSS gradient
* @param value - gradient value
* @param [opt] - options
* @returns parsed result
*/
export declare const parseGradient: (value: string, opt?: Options) => Gradient | null;
/**
* is CSS gradient
* @param value - CSS value
* @param [opt] - options
* @returns result
*/
export declare const isGradient: (value: string, opt?: Options) => boolean;
export {};

View File

@@ -0,0 +1,195 @@
import { createCacheKey, getCache, CacheItem, setCache } from "./cache.js";
import { isString } from "./common.js";
import { splitValue, isColor } from "./util.js";
import { NUM, ANGLE, PCT, LENGTH, CS_RECT, CS_HUE, NUM_POSITIVE } from "./constant.js";
const NAMESPACE = "css-gradient";
const DIM_ANGLE = `${NUM}(?:${ANGLE})`;
const DIM_ANGLE_PCT = `${DIM_ANGLE}|${PCT}`;
const DIM_LEN = `${NUM}(?:${LENGTH})|0`;
const DIM_LEN_PCT = `${DIM_LEN}|${PCT}`;
const DIM_LEN_PCT_POSI = `${NUM_POSITIVE}(?:${LENGTH}|%)|0`;
const DIM_LEN_POSI = `${NUM_POSITIVE}(?:${LENGTH})|0`;
const CTR = "center";
const L_R = "left|right";
const T_B = "top|bottom";
const S_E = "start|end";
const AXIS_X = `${L_R}|x-(?:${S_E})`;
const AXIS_Y = `${T_B}|y-(?:${S_E})`;
const BLOCK = `block-(?:${S_E})`;
const INLINE = `inline-(?:${S_E})`;
const POS_1 = `${CTR}|${AXIS_X}|${AXIS_Y}|${BLOCK}|${INLINE}|${DIM_LEN_PCT}`;
const POS_2 = [
`(?:${CTR}|${AXIS_X})\\s+(?:${CTR}|${AXIS_Y})`,
`(?:${CTR}|${AXIS_Y})\\s+(?:${CTR}|${AXIS_X})`,
`(?:${CTR}|${AXIS_X}|${DIM_LEN_PCT})\\s+(?:${CTR}|${AXIS_Y}|${DIM_LEN_PCT})`,
`(?:${CTR}|${BLOCK})\\s+(?:${CTR}|${INLINE})`,
`(?:${CTR}|${INLINE})\\s+(?:${CTR}|${BLOCK})`,
`(?:${CTR}|${S_E})\\s+(?:${CTR}|${S_E})`
].join("|");
const POS_4 = [
`(?:${AXIS_X})\\s+(?:${DIM_LEN_PCT})\\s+(?:${AXIS_Y})\\s+(?:${DIM_LEN_PCT})`,
`(?:${AXIS_Y})\\s+(?:${DIM_LEN_PCT})\\s+(?:${AXIS_X})\\s+(?:${DIM_LEN_PCT})`,
`(?:${BLOCK})\\s+(?:${DIM_LEN_PCT})\\s+(?:${INLINE})\\s+(?:${DIM_LEN_PCT})`,
`(?:${INLINE})\\s+(?:${DIM_LEN_PCT})\\s+(?:${BLOCK})\\s+(?:${DIM_LEN_PCT})`,
`(?:${S_E})\\s+(?:${DIM_LEN_PCT})\\s+(?:${S_E})\\s+(?:${DIM_LEN_PCT})`
].join("|");
const RAD_EXTENT = "(?:clos|farth)est-(?:corner|side)";
const RAD_SIZE = [
`${RAD_EXTENT}(?:\\s+${RAD_EXTENT})?`,
`${DIM_LEN_POSI}`,
`(?:${DIM_LEN_PCT_POSI})\\s+(?:${DIM_LEN_PCT_POSI})`
].join("|");
const RAD_SHAPE = "circle|ellipse";
const FROM_ANGLE = `from\\s+${DIM_ANGLE}`;
const AT_POSITION = `at\\s+(?:${POS_1}|${POS_2}|${POS_4})`;
const TO_SIDE_CORNER = `to\\s+(?:(?:${L_R})(?:\\s(?:${T_B}))?|(?:${T_B})(?:\\s(?:${L_R}))?)`;
const IN_COLOR_SPACE = `in\\s+(?:${CS_RECT}|${CS_HUE})`;
const REG_GRAD = /^(?:repeating-)?(?:conic|linear|radial)-gradient\(/;
const REG_GRAD_CAPT = /^((?:repeating-)?(?:conic|linear|radial)-gradient)\(/;
const getGradientType = (value) => {
if (isString(value)) {
value = value.trim();
if (REG_GRAD.test(value)) {
const [, type] = value.match(REG_GRAD_CAPT);
return type;
}
}
return "";
};
const validateGradientLine = (value, type) => {
if (isString(value) && isString(type)) {
value = value.trim();
type = type.trim();
let lineSyntax = "";
if (/^(?:repeating-)?linear-gradient$/.test(type)) {
lineSyntax = [
`(?:${DIM_ANGLE}|${TO_SIDE_CORNER})(?:\\s+${IN_COLOR_SPACE})?`,
`${IN_COLOR_SPACE}(?:\\s+(?:${DIM_ANGLE}|${TO_SIDE_CORNER}))?`
].join("|");
} else if (/^(?:repeating-)?radial-gradient$/.test(type)) {
lineSyntax = [
`(?:${RAD_SHAPE})(?:\\s+(?:${RAD_SIZE}))?(?:\\s+${AT_POSITION})?(?:\\s+${IN_COLOR_SPACE})?`,
`(?:${RAD_SIZE})(?:\\s+(?:${RAD_SHAPE}))?(?:\\s+${AT_POSITION})?(?:\\s+${IN_COLOR_SPACE})?`,
`${AT_POSITION}(?:\\s+${IN_COLOR_SPACE})?`,
`${IN_COLOR_SPACE}(?:\\s+${RAD_SHAPE})(?:\\s+(?:${RAD_SIZE}))?(?:\\s+${AT_POSITION})?`,
`${IN_COLOR_SPACE}(?:\\s+${RAD_SIZE})(?:\\s+(?:${RAD_SHAPE}))?(?:\\s+${AT_POSITION})?`,
`${IN_COLOR_SPACE}(?:\\s+${AT_POSITION})?`
].join("|");
} else if (/^(?:repeating-)?conic-gradient$/.test(type)) {
lineSyntax = [
`${FROM_ANGLE}(?:\\s+${AT_POSITION})?(?:\\s+${IN_COLOR_SPACE})?`,
`${AT_POSITION}(?:\\s+${IN_COLOR_SPACE})?`,
`${IN_COLOR_SPACE}(?:\\s+${FROM_ANGLE})?(?:\\s+${AT_POSITION})?`
].join("|");
}
if (lineSyntax) {
const reg = new RegExp(`^(?:${lineSyntax})$`);
return reg.test(value);
}
}
return false;
};
const validateColorStopList = (list, type, opt = {}) => {
if (Array.isArray(list) && list.length > 1) {
const dimension = /^(?:repeating-)?conic-gradient$/.test(type) ? DIM_ANGLE_PCT : DIM_LEN_PCT;
const regColorHint = new RegExp(`^(?:${dimension})$`);
const regDimension = new RegExp(`(?:\\s+(?:${dimension})){1,2}$`);
const arr = [];
for (const item of list) {
if (isString(item)) {
if (regColorHint.test(item)) {
arr.push("hint");
} else {
const color = item.replace(regDimension, "");
if (isColor(color, opt)) {
arr.push("color");
} else {
return false;
}
}
}
}
const value = arr.join(",");
return /^color(?:,(?:hint,)?color)+$/.test(value);
}
return false;
};
const parseGradient = (value, opt = {}) => {
if (isString(value)) {
value = value.trim();
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "parseGradient",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return null;
}
return cachedResult.item;
}
const type = getGradientType(value);
const gradValue = value.replace(REG_GRAD, "").replace(/\)$/, "");
if (type && gradValue) {
const [lineOrColorStop = "", ...colorStops] = splitValue(gradValue, {
delimiter: ","
});
const dimension = /^(?:repeating-)?conic-gradient$/.test(type) ? DIM_ANGLE_PCT : DIM_LEN_PCT;
const regDimension = new RegExp(`(?:\\s+(?:${dimension})){1,2}$`);
let isColorStop = false;
if (regDimension.test(lineOrColorStop)) {
const colorStop = lineOrColorStop.replace(regDimension, "");
if (isColor(colorStop, opt)) {
isColorStop = true;
}
} else if (isColor(lineOrColorStop, opt)) {
isColorStop = true;
}
if (isColorStop) {
colorStops.unshift(lineOrColorStop);
const valid = validateColorStopList(colorStops, type, opt);
if (valid) {
const res = {
value,
type,
colorStopList: colorStops
};
setCache(cacheKey, res);
return res;
}
} else if (colorStops.length > 1) {
const gradientLine = lineOrColorStop;
const valid = validateGradientLine(gradientLine, type) && validateColorStopList(colorStops, type, opt);
if (valid) {
const res = {
value,
type,
gradientLine,
colorStopList: colorStops
};
setCache(cacheKey, res);
return res;
}
}
}
setCache(cacheKey, null);
return null;
}
return null;
};
const isGradient = (value, opt = {}) => {
const gradient = parseGradient(value, opt);
return gradient !== null;
};
export {
getGradientType,
isGradient,
parseGradient,
validateColorStopList,
validateGradientLine
};
//# sourceMappingURL=css-gradient.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,31 @@
import { CSSToken } from '@csstools/css-tokenizer';
import { NullObject } from './cache.js';
import { Options } from './typedef.js';
/**
* resolve custom property
* @param tokens - CSS tokens
* @param [opt] - options
* @returns result - [tokens, resolvedValue]
*/
export declare function resolveCustomProperty(tokens: CSSToken[], opt?: Options): [CSSToken[], string];
/**
* parse tokens
* @param tokens - CSS tokens
* @param [opt] - options
* @returns parsed tokens
*/
export declare function parseTokens(tokens: CSSToken[], opt?: Options): string[] | NullObject;
/**
* resolve CSS var()
* @param value - CSS value including var()
* @param [opt] - options
* @returns resolved value
*/
export declare function resolveVar(value: string, opt?: Options): string | NullObject;
/**
* CSS var()
* @param value - CSS value including var()
* @param [opt] - options
* @returns resolved value
*/
export declare const cssVar: (value: string, opt?: Options) => string;

View File

@@ -0,0 +1,195 @@
import { TokenType, tokenize } from "@csstools/css-tokenizer";
import { createCacheKey, getCache, CacheItem, setCache, NullObject } from "./cache.js";
import { isString } from "./common.js";
import { cssCalc } from "./css-calc.js";
import { isColor } from "./util.js";
import { VAL_SPEC, SYN_FN_VAR, FN_VAR, SYN_FN_CALC } from "./constant.js";
const {
CloseParen: PAREN_CLOSE,
Comment: COMMENT,
EOF,
Ident: IDENT,
Whitespace: W_SPACE
} = TokenType;
const NAMESPACE = "css-var";
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
function resolveCustomProperty(tokens, opt = {}) {
if (!Array.isArray(tokens)) {
throw new TypeError(`${tokens} is not an array.`);
}
const { customProperty = {} } = opt;
const items = [];
while (tokens.length) {
const token = tokens.shift();
if (!Array.isArray(token)) {
throw new TypeError(`${token} is not an array.`);
}
const [type, value] = token;
if (type === PAREN_CLOSE) {
break;
}
if (value === FN_VAR) {
const [restTokens, item] = resolveCustomProperty(tokens, opt);
tokens = restTokens;
if (item) {
items.push(item);
}
} else if (type === IDENT) {
if (value.startsWith("--")) {
let item;
if (Object.hasOwnProperty.call(customProperty, value)) {
item = customProperty[value];
} else if (typeof customProperty.callback === "function") {
item = customProperty.callback(value);
}
if (item) {
items.push(item);
}
} else if (value) {
items.push(value);
}
}
}
let resolveAsColor = false;
if (items.length > 1) {
const lastValue = items[items.length - 1];
resolveAsColor = isColor(lastValue);
}
let resolvedValue = "";
for (let item of items) {
item = item.trim();
if (REG_FN_VAR.test(item)) {
const resolvedItem = resolveVar(item, opt);
if (isString(resolvedItem)) {
if (resolveAsColor) {
if (isColor(resolvedItem)) {
resolvedValue = resolvedItem;
}
} else {
resolvedValue = resolvedItem;
}
}
} else if (REG_FN_CALC.test(item)) {
item = cssCalc(item, opt);
if (resolveAsColor) {
if (isColor(item)) {
resolvedValue = item;
}
} else {
resolvedValue = item;
}
} else if (item && !/^(?:inherit|initial|revert(?:-layer)?|unset)$/.test(item)) {
if (resolveAsColor) {
if (isColor(item)) {
resolvedValue = item;
}
} else {
resolvedValue = item;
}
}
if (resolvedValue) {
break;
}
}
return [tokens, resolvedValue];
}
function parseTokens(tokens, opt = {}) {
const res = [];
while (tokens.length) {
const token = tokens.shift();
const [type = "", value = ""] = token;
if (value === FN_VAR) {
const [restTokens, resolvedValue] = resolveCustomProperty(tokens, opt);
if (!resolvedValue) {
return new NullObject();
}
tokens = restTokens;
res.push(resolvedValue);
} else {
switch (type) {
case PAREN_CLOSE: {
if (res.length) {
const lastValue = res[res.length - 1];
if (lastValue === " ") {
res.splice(-1, 1, value);
} else {
res.push(value);
}
} else {
res.push(value);
}
break;
}
case W_SPACE: {
if (res.length) {
const lastValue = res[res.length - 1];
if (isString(lastValue) && !lastValue.endsWith("(") && lastValue !== " ") {
res.push(value);
}
}
break;
}
default: {
if (type !== COMMENT && type !== EOF) {
res.push(value);
}
}
}
}
}
return res;
}
function resolveVar(value, opt = {}) {
const { format = "" } = opt;
if (isString(value)) {
if (!REG_FN_VAR.test(value) || format === VAL_SPEC) {
return value;
}
value = value.trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "resolveVar",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return cachedResult;
}
return cachedResult.item;
}
const tokens = tokenize({ css: value });
const values = parseTokens(tokens, opt);
if (Array.isArray(values)) {
let color = values.join("");
if (REG_FN_CALC.test(color)) {
color = cssCalc(color, opt);
}
setCache(cacheKey, color);
return color;
} else {
setCache(cacheKey, null);
return new NullObject();
}
}
const cssVar = (value, opt = {}) => {
const resolvedValue = resolveVar(value, opt);
if (isString(resolvedValue)) {
return resolvedValue;
}
return "";
};
export {
cssVar,
parseTokens,
resolveCustomProperty,
resolveVar
};
//# sourceMappingURL=css-var.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,29 @@
import { CSSToken } from '@csstools/css-tokenizer';
import { NullObject } from './cache.js';
import { ColorChannels, Options, StringColorChannels } from './typedef.js';
/**
* @type NumberOrStringColorChannels - color channel
*/
type NumberOrStringColorChannels = ColorChannels & StringColorChannels;
/**
* resolve relative color channels
* @param tokens - CSS tokens
* @param [opt] - options
* @returns resolved color channels
*/
export declare function resolveColorChannels(tokens: CSSToken[], opt?: Options): NumberOrStringColorChannels | NullObject;
/**
* extract origin color
* @param value - CSS color value
* @param [opt] - options
* @returns origin color value
*/
export declare function extractOriginColor(value: string, opt?: Options): string | NullObject;
/**
* resolve relative color
* @param value - CSS relative color value
* @param [opt] - options
* @returns resolved value
*/
export declare function resolveRelativeColor(value: string, opt?: Options): string | NullObject;
export {};

View File

@@ -0,0 +1,487 @@
import { SyntaxFlag, color } from "@csstools/css-color-parser";
import { parseComponentValue } from "@csstools/css-parser-algorithms";
import { TokenType, tokenize } from "@csstools/css-tokenizer";
import { createCacheKey, getCache, CacheItem, NullObject, setCache } from "./cache.js";
import { convertColorToRgb, NAMED_COLORS } from "./color.js";
import { isString, isStringOrNumber } from "./common.js";
import { resolveDimension, serializeCalc } from "./css-calc.js";
import { resolveColor } from "./resolve.js";
import { roundToPrecision } from "./util.js";
import { VAL_SPEC, FN_VAR, NONE, SYN_FN_VAR, FN_REL, CS_LAB, CS_LCH, FN_REL_CAPT, SYN_COLOR_TYPE, SYN_MIX, SYN_FN_MATH_START } from "./constant.js";
const {
CloseParen: PAREN_CLOSE,
Comment: COMMENT,
Dimension: DIM,
EOF,
Function: FUNC,
Ident: IDENT,
Number: NUM,
OpenParen: PAREN_OPEN,
Percentage: PCT,
Whitespace: W_SPACE
} = TokenType;
const { HasNoneKeywords: KEY_NONE } = SyntaxFlag;
const NAMESPACE = "relative-color";
const OCT = 8;
const DEC = 10;
const HEX = 16;
const MAX_PCT = 100;
const MAX_RGB = 255;
const REG_COLOR_CAPT = new RegExp(
`^${FN_REL}(${SYN_COLOR_TYPE}|${SYN_MIX})\\s+`
);
const REG_CS_HSL = /(?:hsla?|hwb)$/;
const REG_CS_CIE = new RegExp(`^(?:${CS_LAB}|${CS_LCH})$`);
const REG_FN_MATH_START = new RegExp(SYN_FN_MATH_START);
const REG_FN_REL = new RegExp(FN_REL);
const REG_FN_REL_CAPT = new RegExp(`^${FN_REL_CAPT}`);
const REG_FN_REL_START = new RegExp(`^${FN_REL}`);
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
function resolveColorChannels(tokens, opt = {}) {
if (!Array.isArray(tokens)) {
throw new TypeError(`${tokens} is not an array.`);
}
const { colorSpace = "", format = "" } = opt;
const colorChannels = /* @__PURE__ */ new Map([
["color", ["r", "g", "b", "alpha"]],
["hsl", ["h", "s", "l", "alpha"]],
["hsla", ["h", "s", "l", "alpha"]],
["hwb", ["h", "w", "b", "alpha"]],
["lab", ["l", "a", "b", "alpha"]],
["lch", ["l", "c", "h", "alpha"]],
["oklab", ["l", "a", "b", "alpha"]],
["oklch", ["l", "c", "h", "alpha"]],
["rgb", ["r", "g", "b", "alpha"]],
["rgba", ["r", "g", "b", "alpha"]]
]);
const colorChannel = colorChannels.get(colorSpace);
if (!colorChannel) {
return new NullObject();
}
const mathFunc = /* @__PURE__ */ new Set();
const channels = [[], [], [], []];
let i = 0;
let nest = 0;
let func = false;
while (tokens.length) {
const token = tokens.shift();
if (!Array.isArray(token)) {
throw new TypeError(`${token} is not an array.`);
}
const [type, value, , , detail] = token;
const channel = channels[i];
if (Array.isArray(channel)) {
switch (type) {
case DIM: {
const resolvedValue = resolveDimension(token, opt);
if (isString(resolvedValue)) {
channel.push(resolvedValue);
} else {
channel.push(value);
}
break;
}
case FUNC: {
channel.push(value);
func = true;
nest++;
if (REG_FN_MATH_START.test(value)) {
mathFunc.add(nest);
}
break;
}
case IDENT: {
if (!colorChannel.includes(value)) {
return new NullObject();
}
channel.push(value);
if (!func) {
i++;
}
break;
}
case NUM: {
channel.push(Number(detail == null ? void 0 : detail.value));
if (!func) {
i++;
}
break;
}
case PAREN_OPEN: {
channel.push(value);
nest++;
break;
}
case PAREN_CLOSE: {
if (func) {
const lastValue = channel[channel.length - 1];
if (lastValue === " ") {
channel.splice(-1, 1, value);
} else {
channel.push(value);
}
if (mathFunc.has(nest)) {
mathFunc.delete(nest);
}
nest--;
if (nest === 0) {
func = false;
i++;
}
}
break;
}
case PCT: {
channel.push(Number(detail == null ? void 0 : detail.value) / MAX_PCT);
if (!func) {
i++;
}
break;
}
case W_SPACE: {
if (channel.length && func) {
const lastValue = channel[channel.length - 1];
if (typeof lastValue === "number") {
channel.push(value);
} else if (isString(lastValue) && !lastValue.endsWith("(") && lastValue !== " ") {
channel.push(value);
}
}
break;
}
default: {
if (type !== COMMENT && type !== EOF && func) {
channel.push(value);
}
}
}
}
}
const channelValues = [];
for (const channel of channels) {
if (channel.length === 1) {
const [resolvedValue] = channel;
if (isStringOrNumber(resolvedValue)) {
channelValues.push(resolvedValue);
}
} else if (channel.length) {
const resolvedValue = serializeCalc(channel.join(""), {
format
});
channelValues.push(resolvedValue);
}
}
return channelValues;
}
function extractOriginColor(value, opt = {}) {
const { currentColor = "", format = "" } = opt;
if (isString(value)) {
value = value.toLowerCase().trim();
if (!value) {
return new NullObject();
}
if (!REG_FN_REL_START.test(value)) {
return value;
}
} else {
return new NullObject();
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "extractOriginColor",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return cachedResult;
}
return cachedResult.item;
}
if (/currentcolor/.test(value)) {
if (currentColor) {
value = value.replace(/currentcolor/g, currentColor);
} else {
setCache(cacheKey, null);
return new NullObject();
}
}
let colorSpace = "";
if (REG_FN_REL_CAPT.test(value)) {
[, colorSpace] = value.match(REG_FN_REL_CAPT);
}
opt.colorSpace = colorSpace;
if (REG_COLOR_CAPT.test(value)) {
const [, originColor] = value.match(REG_COLOR_CAPT);
const [, restValue] = value.split(originColor);
if (/^[a-z]+$/.test(originColor)) {
if (!/^transparent$/.test(originColor) && !Object.prototype.hasOwnProperty.call(NAMED_COLORS, originColor)) {
setCache(cacheKey, null);
return new NullObject();
}
} else if (format === VAL_SPEC) {
const resolvedOriginColor = resolveColor(originColor, opt);
if (isString(resolvedOriginColor)) {
value = value.replace(originColor, resolvedOriginColor);
}
}
if (format === VAL_SPEC) {
const tokens = tokenize({ css: restValue });
const channelValues = resolveColorChannels(tokens, opt);
if (channelValues instanceof NullObject) {
setCache(cacheKey, null);
return channelValues;
}
const [v1, v2, v3, v4] = channelValues;
let channelValue = "";
if (isStringOrNumber(v4)) {
channelValue = ` ${v1} ${v2} ${v3} / ${v4})`;
} else {
channelValue = ` ${channelValues.join(" ")})`;
}
if (restValue !== channelValue) {
value = value.replace(restValue, channelValue);
}
}
} else {
const [, restValue] = value.split(REG_FN_REL_START);
const tokens = tokenize({ css: restValue });
const originColor = [];
let nest = 0;
while (tokens.length) {
const [type, tokenValue] = tokens.shift();
switch (type) {
case FUNC:
case PAREN_OPEN: {
originColor.push(tokenValue);
nest++;
break;
}
case PAREN_CLOSE: {
const lastValue = originColor[originColor.length - 1];
if (lastValue === " ") {
originColor.splice(-1, 1, tokenValue);
} else if (isString(lastValue)) {
originColor.push(tokenValue);
}
nest--;
break;
}
case W_SPACE: {
const lastValue = originColor[originColor.length - 1];
if (isString(lastValue) && !lastValue.endsWith("(") && lastValue !== " ") {
originColor.push(tokenValue);
}
break;
}
default: {
if (type !== COMMENT && type !== EOF) {
originColor.push(tokenValue);
}
}
}
if (nest === 0) {
break;
}
}
const resolvedOriginColor = resolveRelativeColor(
originColor.join("").trim(),
opt
);
if (resolvedOriginColor instanceof NullObject) {
setCache(cacheKey, null);
return resolvedOriginColor;
}
const channelValues = resolveColorChannels(tokens, opt);
if (channelValues instanceof NullObject) {
setCache(cacheKey, null);
return channelValues;
}
const [v1, v2, v3, v4] = channelValues;
let channelValue = "";
if (isStringOrNumber(v4)) {
channelValue = ` ${v1} ${v2} ${v3} / ${v4})`;
} else {
channelValue = ` ${channelValues.join(" ")})`;
}
value = value.replace(restValue, `${resolvedOriginColor}${channelValue}`);
}
setCache(cacheKey, value);
return value;
}
function resolveRelativeColor(value, opt = {}) {
const { format = "" } = opt;
if (isString(value)) {
if (REG_FN_VAR.test(value)) {
if (format === VAL_SPEC) {
return value;
} else {
throw new SyntaxError(`Unexpected token ${FN_VAR} found.`);
}
} else if (!REG_FN_REL.test(value)) {
return value;
}
value = value.toLowerCase().trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "resolveRelativeColor",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return cachedResult;
}
return cachedResult.item;
}
const originColor = extractOriginColor(value, opt);
if (originColor instanceof NullObject) {
setCache(cacheKey, null);
return originColor;
}
value = originColor;
if (format === VAL_SPEC) {
if (value.startsWith("rgba(")) {
value = value.replace(/^rgba\(/, "rgb(");
} else if (value.startsWith("hsla(")) {
value = value.replace(/^hsla\(/, "hsl(");
}
return value;
}
const tokens = tokenize({ css: value });
const components = parseComponentValue(tokens);
const parsedComponents = color(components);
if (!parsedComponents) {
setCache(cacheKey, null);
return new NullObject();
}
const {
alpha: alphaComponent,
channels: channelsComponent,
colorNotation,
syntaxFlags
} = parsedComponents;
let alpha;
if (Number.isNaN(Number(alphaComponent))) {
if (syntaxFlags instanceof Set && syntaxFlags.has(KEY_NONE)) {
alpha = NONE;
} else {
alpha = 0;
}
} else {
alpha = roundToPrecision(Number(alphaComponent), OCT);
}
let v1;
let v2;
let v3;
[v1, v2, v3] = channelsComponent;
let resolvedValue;
if (REG_CS_CIE.test(colorNotation)) {
const hasNone = syntaxFlags instanceof Set && syntaxFlags.has(KEY_NONE);
if (Number.isNaN(v1)) {
if (hasNone) {
v1 = NONE;
} else {
v1 = 0;
}
} else {
v1 = roundToPrecision(v1, HEX);
}
if (Number.isNaN(v2)) {
if (hasNone) {
v2 = NONE;
} else {
v2 = 0;
}
} else {
v2 = roundToPrecision(v2, HEX);
}
if (Number.isNaN(v3)) {
if (hasNone) {
v3 = NONE;
} else {
v3 = 0;
}
} else {
v3 = roundToPrecision(v3, HEX);
}
if (alpha === 1) {
resolvedValue = `${colorNotation}(${v1} ${v2} ${v3})`;
} else {
resolvedValue = `${colorNotation}(${v1} ${v2} ${v3} / ${alpha})`;
}
} else if (REG_CS_HSL.test(colorNotation)) {
if (Number.isNaN(v1)) {
v1 = 0;
}
if (Number.isNaN(v2)) {
v2 = 0;
}
if (Number.isNaN(v3)) {
v3 = 0;
}
let [r, g, b] = convertColorToRgb(
`${colorNotation}(${v1} ${v2} ${v3} / ${alpha})`
);
r = roundToPrecision(r / MAX_RGB, DEC);
g = roundToPrecision(g / MAX_RGB, DEC);
b = roundToPrecision(b / MAX_RGB, DEC);
if (alpha === 1) {
resolvedValue = `color(srgb ${r} ${g} ${b})`;
} else {
resolvedValue = `color(srgb ${r} ${g} ${b} / ${alpha})`;
}
} else {
const cs = colorNotation === "rgb" ? "srgb" : colorNotation;
const hasNone = syntaxFlags instanceof Set && syntaxFlags.has(KEY_NONE);
if (Number.isNaN(v1)) {
if (hasNone) {
v1 = NONE;
} else {
v1 = 0;
}
} else {
v1 = roundToPrecision(v1, DEC);
}
if (Number.isNaN(v2)) {
if (hasNone) {
v2 = NONE;
} else {
v2 = 0;
}
} else {
v2 = roundToPrecision(v2, DEC);
}
if (Number.isNaN(v3)) {
if (hasNone) {
v3 = NONE;
} else {
v3 = 0;
}
} else {
v3 = roundToPrecision(v3, DEC);
}
if (alpha === 1) {
resolvedValue = `color(${cs} ${v1} ${v2} ${v3})`;
} else {
resolvedValue = `color(${cs} ${v1} ${v2} ${v3} / ${alpha})`;
}
}
setCache(cacheKey, resolvedValue);
return resolvedValue;
}
export {
extractOriginColor,
resolveColorChannels,
resolveRelativeColor
};
//# sourceMappingURL=relative-color.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,52 @@
import { NullObject } from './cache.js';
import { Options } from './typedef.js';
/**
* resolve color
* @param value - CSS color value
* @param [opt] - options
* @returns resolved color
*/
export declare const resolveColor: (value: string, opt?: Options) => string | NullObject;
/**
* resolve CSS color
* @param value
* - CSS color value
* - system colors are not supported
* @param [opt] - options
* @param [opt.currentColor]
* - color to use for `currentcolor` keyword
* - if omitted, it will be treated as a missing color
* i.e. `rgb(none none none / none)`
* @param [opt.customProperty]
* - custom properties
* - pair of `--` prefixed property name and value,
* e.g. `customProperty: { '--some-color': '#0000ff' }`
* - and/or `callback` function to get the value of the custom property,
* e.g. `customProperty: { callback: someDeclaration.getPropertyValue }`
* @param [opt.dimension]
* - dimension, convert relative length to pixels
* - pair of unit and it's value as a number in pixels,
* e.g. `dimension: { em: 12, rem: 16, vw: 10.26 }`
* - and/or `callback` function to get the value as a number in pixels,
* e.g. `dimension: { callback: convertUnitToPixel }`
* @param [opt.format]
* - output format, one of below
* - `computedValue` (default), [computed value][139] of the color
* - `specifiedValue`, [specified value][140] of the color
* - `hex`, hex color notation, i.e. `rrggbb`
* - `hexAlpha`, hex color notation with alpha channel, i.e. `#rrggbbaa`
* @returns
* - one of rgba?(), #rrggbb(aa)?, color-name, '(empty-string)',
* color(color-space r g b / alpha), color(color-space x y z / alpha),
* lab(l a b / alpha), lch(l c h / alpha), oklab(l a b / alpha),
* oklch(l c h / alpha), null
* - in `computedValue`, values are numbers, however `rgb()` values are
* integers
* - in `specifiedValue`, returns `empty string` for unknown and/or invalid
* color
* - in `hex`, returns `null` for `transparent`, and also returns `null` if
* any of `r`, `g`, `b`, `alpha` is not a number
* - in `hexAlpha`, returns `#00000000` for `transparent`,
* however returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
*/
export declare const resolve: (value: string, opt?: Options) => string | null;

View File

@@ -0,0 +1,288 @@
import { NullObject, createCacheKey, getCache, CacheItem, setCache } from "./cache.js";
import { resolveColorMix, resolveColorFunc, resolveColorValue, convertRgbToHex } from "./color.js";
import { isString } from "./common.js";
import { cssCalc } from "./css-calc.js";
import { resolveVar } from "./css-var.js";
import { resolveRelativeColor } from "./relative-color.js";
import { VAL_COMP, VAL_SPEC, FN_MIX, FN_COLOR, SYN_FN_VAR, SYN_FN_REL, SYN_FN_CALC } from "./constant.js";
const NAMESPACE = "resolve";
const RGB_TRANSPARENT = "rgba(0, 0, 0, 0)";
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
const REG_FN_REL = new RegExp(SYN_FN_REL);
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
const resolveColor = (value, opt = {}) => {
if (isString(value)) {
value = value.trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const { currentColor = "", format = VAL_COMP, nullable = false } = opt;
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "resolve",
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return cachedResult;
}
return cachedResult.item;
}
if (REG_FN_VAR.test(value)) {
if (format === VAL_SPEC) {
setCache(cacheKey, value);
return value;
}
const resolvedValue = resolveVar(value, opt);
if (resolvedValue instanceof NullObject) {
switch (format) {
case "hex":
case "hexAlpha": {
setCache(cacheKey, resolvedValue);
return resolvedValue;
}
default: {
if (nullable) {
setCache(cacheKey, resolvedValue);
return resolvedValue;
}
const res2 = RGB_TRANSPARENT;
setCache(cacheKey, res2);
return res2;
}
}
} else {
value = resolvedValue;
}
}
if (opt.format !== format) {
opt.format = format;
}
value = value.toLowerCase();
if (REG_FN_REL.test(value)) {
const resolvedValue = resolveRelativeColor(value, opt);
if (format === VAL_COMP) {
let res2;
if (resolvedValue instanceof NullObject) {
if (nullable) {
res2 = resolvedValue;
} else {
res2 = RGB_TRANSPARENT;
}
} else {
res2 = resolvedValue;
}
setCache(cacheKey, res2);
return res2;
}
if (format === VAL_SPEC) {
let res2 = "";
if (resolvedValue instanceof NullObject) {
res2 = "";
} else {
res2 = resolvedValue;
}
setCache(cacheKey, res2);
return res2;
}
if (resolvedValue instanceof NullObject) {
value = "";
} else {
value = resolvedValue;
}
}
if (REG_FN_CALC.test(value)) {
value = cssCalc(value, opt);
}
let cs = "";
let r = NaN;
let g = NaN;
let b = NaN;
let alpha = NaN;
if (value === "transparent") {
switch (format) {
case VAL_SPEC: {
setCache(cacheKey, value);
return value;
}
case "hex": {
setCache(cacheKey, null);
return new NullObject();
}
case "hexAlpha": {
const res2 = "#00000000";
setCache(cacheKey, res2);
return res2;
}
case VAL_COMP:
default: {
const res2 = RGB_TRANSPARENT;
setCache(cacheKey, res2);
return res2;
}
}
} else if (value === "currentcolor") {
if (format === VAL_SPEC) {
setCache(cacheKey, value);
return value;
}
if (currentColor) {
let resolvedValue;
if (currentColor.startsWith(FN_MIX)) {
resolvedValue = resolveColorMix(currentColor, opt);
} else if (currentColor.startsWith(FN_COLOR)) {
resolvedValue = resolveColorFunc(currentColor, opt);
} else {
resolvedValue = resolveColorValue(currentColor, opt);
}
if (resolvedValue instanceof NullObject) {
setCache(cacheKey, resolvedValue);
return resolvedValue;
}
[cs, r, g, b, alpha] = resolvedValue;
} else if (format === VAL_COMP) {
const res2 = RGB_TRANSPARENT;
setCache(cacheKey, res2);
return res2;
}
} else if (format === VAL_SPEC) {
if (value.startsWith(FN_MIX)) {
const res2 = resolveColorMix(value, opt);
setCache(cacheKey, res2);
return res2;
} else if (value.startsWith(FN_COLOR)) {
const [scs, rr, gg, bb, aa] = resolveColorFunc(
value,
opt
);
let res2 = "";
if (aa === 1) {
res2 = `color(${scs} ${rr} ${gg} ${bb})`;
} else {
res2 = `color(${scs} ${rr} ${gg} ${bb} / ${aa})`;
}
setCache(cacheKey, res2);
return res2;
} else {
const rgb = resolveColorValue(value, opt);
if (isString(rgb)) {
setCache(cacheKey, rgb);
return rgb;
}
const [scs, rr, gg, bb, aa] = rgb;
let res2 = "";
if (scs === "rgb") {
if (aa === 1) {
res2 = `${scs}(${rr}, ${gg}, ${bb})`;
} else {
res2 = `${scs}a(${rr}, ${gg}, ${bb}, ${aa})`;
}
} else if (aa === 1) {
res2 = `${scs}(${rr} ${gg} ${bb})`;
} else {
res2 = `${scs}(${rr} ${gg} ${bb} / ${aa})`;
}
setCache(cacheKey, res2);
return res2;
}
} else if (value.startsWith(FN_MIX)) {
if (/currentcolor/.test(value)) {
if (currentColor) {
value = value.replace(/currentcolor/g, currentColor);
}
}
if (/transparent/.test(value)) {
value = value.replace(/transparent/g, RGB_TRANSPARENT);
}
const resolvedValue = resolveColorMix(value, opt);
if (resolvedValue instanceof NullObject) {
setCache(cacheKey, resolvedValue);
return resolvedValue;
}
[cs, r, g, b, alpha] = resolvedValue;
} else if (value.startsWith(FN_COLOR)) {
const resolvedValue = resolveColorFunc(value, opt);
if (resolvedValue instanceof NullObject) {
setCache(cacheKey, resolvedValue);
return resolvedValue;
}
[cs, r, g, b, alpha] = resolvedValue;
} else if (value) {
const resolvedValue = resolveColorValue(value, opt);
if (resolvedValue instanceof NullObject) {
setCache(cacheKey, resolvedValue);
return resolvedValue;
}
[cs, r, g, b, alpha] = resolvedValue;
}
let res = "";
switch (format) {
case "hex": {
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b) || Number.isNaN(alpha) || alpha === 0) {
setCache(cacheKey, null);
return new NullObject();
}
res = convertRgbToHex([r, g, b, 1]);
break;
}
case "hexAlpha": {
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b) || Number.isNaN(alpha)) {
setCache(cacheKey, null);
return new NullObject();
}
res = convertRgbToHex([r, g, b, alpha]);
break;
}
case VAL_COMP:
default: {
switch (cs) {
case "rgb": {
if (alpha === 1) {
res = `${cs}(${r}, ${g}, ${b})`;
} else {
res = `${cs}a(${r}, ${g}, ${b}, ${alpha})`;
}
break;
}
case "lab":
case "lch":
case "oklab":
case "oklch": {
if (alpha === 1) {
res = `${cs}(${r} ${g} ${b})`;
} else {
res = `${cs}(${r} ${g} ${b} / ${alpha})`;
}
break;
}
// color()
default: {
if (alpha === 1) {
res = `color(${cs} ${r} ${g} ${b})`;
} else {
res = `color(${cs} ${r} ${g} ${b} / ${alpha})`;
}
}
}
}
}
setCache(cacheKey, res);
return res;
};
const resolve = (value, opt = {}) => {
opt.nullable = false;
const resolvedValue = resolveColor(value, opt);
if (resolvedValue instanceof NullObject) {
return null;
}
return resolvedValue;
};
export {
resolve,
resolveColor
};
//# sourceMappingURL=resolve.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,79 @@
/**
* typedef
*/
/**
* @typedef Options - options
* @property [alpha] - enable alpha
* @property [colorSpace] - color space
* @property [currentColor] - color for currentcolor
* @property [customPropeerty] - custom properties
* @property [d50] - white point in d50
* @property [dimension] - dimension
* @property [format] - output format
* @property [key] - key
*/
export interface Options {
alpha?: boolean;
colorSpace?: string;
currentColor?: string;
customProperty?: Record<string, string | ((K: string) => string)>;
d50?: boolean;
delimiter?: string | string[];
dimension?: Record<string, number | ((K: string) => number)>;
format?: string;
nullable?: boolean;
preserveComment?: boolean;
}
/**
* @type ColorChannels - color channels
*/
export type ColorChannels = [x: number, y: number, z: number, alpha: number];
/**
* @type StringColorChannels - color channels
*/
export type StringColorChannels = [
x: string,
y: string,
z: string,
alpha: string | undefined
];
/**
* @type StringColorSpacedChannels - specified value
*/
export type StringColorSpacedChannels = [
cs: string,
x: string,
y: string,
z: string,
alpha: string | undefined
];
/**
* @type ComputedColorChannels - computed value
*/
export type ComputedColorChannels = [
cs: string,
x: number,
y: number,
z: number,
alpha: number
];
/**
* @type SpecifiedColorChannels - specified value
*/
export type SpecifiedColorChannels = [
cs: string,
x: number | string,
y: number | string,
z: number | string,
alpha: number | string
];
/**
* @type MatchedRegExp - matched regexp array
*/
export type MatchedRegExp = [
match: string,
gr1: string,
gr2: string,
gr3: string,
gr4: string
];

View File

@@ -0,0 +1,45 @@
import { Options } from './typedef.js';
/**
* split value
* NOTE: comments are stripped, it can be preserved if, in the options param,
* `delimiter` is either ',' or '/' and with `preserveComment` set to `true`
* @param value - CSS value
* @param [opt] - options
* @returns array of values
*/
export declare const splitValue: (value: string, opt?: Options) => string[];
/**
* extract dashed-ident tokens
* @param value - CSS value
* @returns array of dashed-ident tokens
*/
export declare const extractDashedIdent: (value: string) => string[];
/**
* is color
* @param value - CSS value
* @param [opt] - options
* @returns result
*/
export declare const isColor: (value: unknown, opt?: Options) => boolean;
/**
* value to JSON string
* @param value - CSS value
* @param [func] - stringify function
* @returns stringified value in JSON notation
*/
export declare const valueToJsonString: (value: unknown, func?: boolean) => string;
/**
* round to specified precision
* @param value - numeric value
* @param bit - minimum bits
* @returns rounded value
*/
export declare const roundToPrecision: (value: number, bit?: number) => number;
/**
* interpolate hue
* @param hueA - hue value
* @param hueB - hue value
* @param arc - shorter | longer | increasing | decreasing
* @returns result - [hueA, hueB]
*/
export declare const interpolateHue: (hueA: number, hueB: number, arc?: string) => [number, number];

View File

@@ -0,0 +1,279 @@
import { TokenType, tokenize } from "@csstools/css-tokenizer";
import { createCacheKey, getCache, CacheItem, setCache } from "./cache.js";
import { isString } from "./common.js";
import { resolveColor } from "./resolve.js";
import { NAMED_COLORS } from "./color.js";
import { VAL_SPEC, SYN_COLOR_TYPE, SYN_MIX } from "./constant.js";
const {
CloseParen: PAREN_CLOSE,
Comma: COMMA,
Comment: COMMENT,
Delim: DELIM,
EOF,
Function: FUNC,
Ident: IDENT,
OpenParen: PAREN_OPEN,
Whitespace: W_SPACE
} = TokenType;
const NAMESPACE = "util";
const DEC = 10;
const HEX = 16;
const DEG = 360;
const DEG_HALF = 180;
const REG_COLOR = new RegExp(`^(?:${SYN_COLOR_TYPE})$`);
const REG_FN_COLOR = /^(?:(?:ok)?l(?:ab|ch)|color(?:-mix)?|hsla?|hwb|rgba?|var)\(/;
const REG_MIX = new RegExp(SYN_MIX);
const splitValue = (value, opt = {}) => {
if (isString(value)) {
value = value.trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const { delimiter = " ", preserveComment = false } = opt;
const cacheKey = createCacheKey(
{
namespace: NAMESPACE,
name: "splitValue",
value
},
{
delimiter,
preserveComment
}
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
let regDelimiter;
if (delimiter === ",") {
regDelimiter = /^,$/;
} else if (delimiter === "/") {
regDelimiter = /^\/$/;
} else {
regDelimiter = /^\s+$/;
}
const tokens = tokenize({ css: value });
let nest = 0;
let str = "";
const res = [];
while (tokens.length) {
const [type, value2] = tokens.shift();
switch (type) {
case COMMA: {
if (regDelimiter.test(value2)) {
if (nest === 0) {
res.push(str.trim());
str = "";
} else {
str += value2;
}
} else {
str += value2;
}
break;
}
case DELIM: {
if (regDelimiter.test(value2)) {
if (nest === 0) {
res.push(str.trim());
str = "";
} else {
str += value2;
}
} else {
str += value2;
}
break;
}
case COMMENT: {
if (preserveComment && (delimiter === "," || delimiter === "/")) {
str += value2;
}
break;
}
case FUNC:
case PAREN_OPEN: {
str += value2;
nest++;
break;
}
case PAREN_CLOSE: {
str += value2;
nest--;
break;
}
case W_SPACE: {
if (regDelimiter.test(value2)) {
if (nest === 0) {
if (str) {
res.push(str.trim());
str = "";
}
} else {
str += " ";
}
} else if (!str.endsWith(" ")) {
str += " ";
}
break;
}
default: {
if (type === EOF) {
res.push(str.trim());
str = "";
} else {
str += value2;
}
}
}
}
setCache(cacheKey, res);
return res;
};
const extractDashedIdent = (value) => {
if (isString(value)) {
value = value.trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey = createCacheKey({
namespace: NAMESPACE,
name: "extractDashedIdent",
value
});
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item;
}
const tokens = tokenize({ css: value });
const items = /* @__PURE__ */ new Set();
while (tokens.length) {
const [type, value2] = tokens.shift();
if (type === IDENT && value2.startsWith("--")) {
items.add(value2);
}
}
const res = [...items];
setCache(cacheKey, res);
return res;
};
const isColor = (value, opt = {}) => {
if (isString(value)) {
value = value.toLowerCase().trim();
if (value && isString(value)) {
if (/^[a-z]+$/.test(value)) {
if (/^(?:currentcolor|transparent)$/.test(value) || Object.prototype.hasOwnProperty.call(NAMED_COLORS, value)) {
return true;
}
} else if (REG_COLOR.test(value) || REG_MIX.test(value)) {
return true;
} else if (REG_FN_COLOR.test(value)) {
opt.nullable = true;
if (!opt.format) {
opt.format = VAL_SPEC;
}
const resolvedValue = resolveColor(value, opt);
if (resolvedValue) {
return true;
}
}
}
}
return false;
};
const valueToJsonString = (value, func = false) => {
if (typeof value === "undefined") {
return "";
}
const res = JSON.stringify(value, (_key, val) => {
let replacedValue;
if (typeof val === "undefined") {
replacedValue = null;
} else if (typeof val === "function") {
if (func) {
replacedValue = val.toString().replace(/\s/g, "").substring(0, HEX);
} else {
replacedValue = val.name;
}
} else if (val instanceof Map || val instanceof Set) {
replacedValue = [...val];
} else if (typeof val === "bigint") {
replacedValue = val.toString();
} else {
replacedValue = val;
}
return replacedValue;
});
return res;
};
const roundToPrecision = (value, bit = 0) => {
if (!Number.isFinite(value)) {
throw new TypeError(`${value} is not a finite number.`);
}
if (!Number.isFinite(bit)) {
throw new TypeError(`${bit} is not a finite number.`);
} else if (bit < 0 || bit > HEX) {
throw new RangeError(`${bit} is not between 0 and ${HEX}.`);
}
if (bit === 0) {
return Math.round(value);
}
let val;
if (bit === HEX) {
val = value.toPrecision(6);
} else if (bit < DEC) {
val = value.toPrecision(4);
} else {
val = value.toPrecision(5);
}
return parseFloat(val);
};
const interpolateHue = (hueA, hueB, arc = "shorter") => {
if (!Number.isFinite(hueA)) {
throw new TypeError(`${hueA} is not a finite number.`);
}
if (!Number.isFinite(hueB)) {
throw new TypeError(`${hueB} is not a finite number.`);
}
switch (arc) {
case "decreasing": {
if (hueB > hueA) {
hueA += DEG;
}
break;
}
case "increasing": {
if (hueB < hueA) {
hueB += DEG;
}
break;
}
case "longer": {
if (hueB > hueA && hueB < hueA + DEG_HALF) {
hueA += DEG;
} else if (hueB > hueA + DEG_HALF * -1 && hueB <= hueA) {
hueB += DEG;
}
break;
}
case "shorter":
default: {
if (hueB > hueA + DEG_HALF) {
hueA += DEG;
} else if (hueB < hueA + DEG_HALF * -1) {
hueB += DEG;
}
}
}
return [hueA, hueB];
};
export {
extractDashedIdent,
interpolateHue,
isColor,
roundToPrecision,
splitValue,
valueToJsonString
};
//# sourceMappingURL=util.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) 2010-2023 Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,331 @@
# lru-cache
A cache object that deletes the least-recently-used items.
Specify a max number of the most recently used items that you
want to keep, and this cache will keep that many of the most
recently accessed items.
This is not primarily a TTL cache, and does not make strong TTL
guarantees. There is no preemptive pruning of expired items by
default, but you _may_ set a TTL on the cache or on a single
`set`. If you do so, it will treat expired items as missing, and
delete them when fetched. If you are more interested in TTL
caching than LRU caching, check out
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
As of version 7, this is one of the most performant LRU
implementations available in JavaScript, and supports a wide
diversity of use cases. However, note that using some of the
features will necessarily impact performance, by causing the
cache to have to do more work. See the "Performance" section
below.
## Installation
```bash
npm install lru-cache --save
```
## Usage
```js
// hybrid module, either works
import { LRUCache } from 'lru-cache'
// or:
const { LRUCache } = require('lru-cache')
// or in minified form for web browsers:
import { LRUCache } from 'http://unpkg.com/lru-cache@9/dist/mjs/index.min.mjs'
// At least one of 'max', 'ttl', or 'maxSize' is required, to prevent
// unsafe unbounded storage.
//
// In most cases, it's best to specify a max for performance, so all
// the required memory allocation is done up-front.
//
// All the other options are optional, see the sections below for
// documentation on what each one does. Most of them can be
// overridden for specific items in get()/set()
const options = {
max: 500,
// for use with tracking overall storage size
maxSize: 5000,
sizeCalculation: (value, key) => {
return 1
},
// for use when you need to clean up something when objects
// are evicted from the cache
dispose: (value, key) => {
freeFromMemoryOrWhatever(value)
},
// how long to live in ms
ttl: 1000 * 60 * 5,
// return stale items before removing from cache?
allowStale: false,
updateAgeOnGet: false,
updateAgeOnHas: false,
// async method to use for cache.fetch(), for
// stale-while-revalidate type of behavior
fetchMethod: async (
key,
staleValue,
{ options, signal, context }
) => {},
}
const cache = new LRUCache(options)
cache.set('key', 'value')
cache.get('key') // "value"
// non-string keys ARE fully supported
// but note that it must be THE SAME object, not
// just a JSON-equivalent object.
var someObject = { a: 1 }
cache.set(someObject, 'a value')
// Object keys are not toString()-ed
cache.set('[object Object]', 'a different value')
assert.equal(cache.get(someObject), 'a value')
// A similar object with same keys/values won't work,
// because it's a different object identity
assert.equal(cache.get({ a: 1 }), undefined)
cache.clear() // empty the cache
```
If you put more stuff in the cache, then less recently used items
will fall out. That's what an LRU cache is.
For full description of the API and all options, please see [the
LRUCache typedocs](https://isaacs.github.io/node-lru-cache/)
## Storage Bounds Safety
This implementation aims to be as flexible as possible, within
the limits of safe memory consumption and optimal performance.
At initial object creation, storage is allocated for `max` items.
If `max` is set to zero, then some performance is lost, and item
count is unbounded. Either `maxSize` or `ttl` _must_ be set if
`max` is not specified.
If `maxSize` is set, then this creates a safe limit on the
maximum storage consumed, but without the performance benefits of
pre-allocation. When `maxSize` is set, every item _must_ provide
a size, either via the `sizeCalculation` method provided to the
constructor, or via a `size` or `sizeCalculation` option provided
to `cache.set()`. The size of every item _must_ be a positive
integer.
If neither `max` nor `maxSize` are set, then `ttl` tracking must
be enabled. Note that, even when tracking item `ttl`, items are
_not_ preemptively deleted when they become stale, unless
`ttlAutopurge` is enabled. Instead, they are only purged the
next time the key is requested. Thus, if `ttlAutopurge`, `max`,
and `maxSize` are all not set, then the cache will potentially
grow unbounded.
In this case, a warning is printed to standard error. Future
versions may require the use of `ttlAutopurge` if `max` and
`maxSize` are not specified.
If you truly wish to use a cache that is bound _only_ by TTL
expiration, consider using a `Map` object, and calling
`setTimeout` to delete entries when they expire. It will perform
much better than an LRU cache.
Here is an implementation you may use, under the same
[license](./LICENSE) as this package:
```js
// a storage-unbounded ttl cache that is not an lru-cache
const cache = {
data: new Map(),
timers: new Map(),
set: (k, v, ttl) => {
if (cache.timers.has(k)) {
clearTimeout(cache.timers.get(k))
}
cache.timers.set(
k,
setTimeout(() => cache.delete(k), ttl)
)
cache.data.set(k, v)
},
get: k => cache.data.get(k),
has: k => cache.data.has(k),
delete: k => {
if (cache.timers.has(k)) {
clearTimeout(cache.timers.get(k))
}
cache.timers.delete(k)
return cache.data.delete(k)
},
clear: () => {
cache.data.clear()
for (const v of cache.timers.values()) {
clearTimeout(v)
}
cache.timers.clear()
},
}
```
If that isn't to your liking, check out
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
## Storing Undefined Values
This cache never stores undefined values, as `undefined` is used
internally in a few places to indicate that a key is not in the
cache.
You may call `cache.set(key, undefined)`, but this is just
an alias for `cache.delete(key)`. Note that this has the effect
that `cache.has(key)` will return _false_ after setting it to
undefined.
```js
cache.set(myKey, undefined)
cache.has(myKey) // false!
```
If you need to track `undefined` values, and still note that the
key is in the cache, an easy workaround is to use a sigil object
of your own.
```js
import { LRUCache } from 'lru-cache'
const undefinedValue = Symbol('undefined')
const cache = new LRUCache(...)
const mySet = (key, value) =>
cache.set(key, value === undefined ? undefinedValue : value)
const myGet = (key, value) => {
const v = cache.get(key)
return v === undefinedValue ? undefined : v
}
```
## Performance
As of January 2022, version 7 of this library is one of the most
performant LRU cache implementations in JavaScript.
Benchmarks can be extremely difficult to get right. In
particular, the performance of set/get/delete operations on
objects will vary _wildly_ depending on the type of key used. V8
is highly optimized for objects with keys that are short strings,
especially integer numeric strings. Thus any benchmark which
tests _solely_ using numbers as keys will tend to find that an
object-based approach performs the best.
Note that coercing _anything_ to strings to use as object keys is
unsafe, unless you can be 100% certain that no other type of
value will be used. For example:
```js
const myCache = {}
const set = (k, v) => (myCache[k] = v)
const get = k => myCache[k]
set({}, 'please hang onto this for me')
set('[object Object]', 'oopsie')
```
Also beware of "Just So" stories regarding performance. Garbage
collection of large (especially: deep) object graphs can be
incredibly costly, with several "tipping points" where it
increases exponentially. As a result, putting that off until
later can make it much worse, and less predictable. If a library
performs well, but only in a scenario where the object graph is
kept shallow, then that won't help you if you are using large
objects as keys.
In general, when attempting to use a library to improve
performance (such as a cache like this one), it's best to choose
an option that will perform well in the sorts of scenarios where
you'll actually use it.
This library is optimized for repeated gets and minimizing
eviction time, since that is the expected need of a LRU. Set
operations are somewhat slower on average than a few other
options, in part because of that optimization. It is assumed
that you'll be caching some costly operation, ideally as rarely
as possible, so optimizing set over get would be unwise.
If performance matters to you:
1. If it's at all possible to use small integer values as keys,
and you can guarantee that no other types of values will be
used as keys, then do that, and use a cache such as
[lru-fast](https://npmjs.com/package/lru-fast), or
[mnemonist's
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache)
which uses an Object as its data store.
2. Failing that, if at all possible, use short non-numeric
strings (ie, less than 256 characters) as your keys, and use
[mnemonist's
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache).
3. If the types of your keys will be anything else, especially
long strings, strings that look like floats, objects, or some
mix of types, or if you aren't sure, then this library will
work well for you.
If you do not need the features that this library provides
(like asynchronous fetching, a variety of TTL staleness
options, and so on), then [mnemonist's
LRUMap](https://yomguithereal.github.io/mnemonist/lru-map) is
a very good option, and just slightly faster than this module
(since it does considerably less).
4. Do not use a `dispose` function, size tracking, or especially
ttl behavior, unless absolutely needed. These features are
convenient, and necessary in some use cases, and every attempt
has been made to make the performance impact minimal, but it
isn't nothing.
## Breaking Changes in Version 7
This library changed to a different algorithm and internal data
structure in version 7, yielding significantly better
performance, albeit with some subtle changes as a result.
If you were relying on the internals of LRUCache in version 6 or
before, it probably will not work in version 7 and above.
## Breaking Changes in Version 8
- The `fetchContext` option was renamed to `context`, and may no
longer be set on the cache instance itself.
- Rewritten in TypeScript, so pretty much all the types moved
around a lot.
- The AbortController/AbortSignal polyfill was removed. For this
reason, **Node version 16.14.0 or higher is now required**.
- Internal properties were moved to actual private class
properties.
- Keys and values must not be `null` or `undefined`.
- Minified export available at `'lru-cache/min'`, for both CJS
and MJS builds.
## Breaking Changes in Version 9
- Named export only, no default export.
- AbortController polyfill returned, albeit with a warning when
used.
## Breaking Changes in Version 10
- `cache.fetch()` return type is now `Promise<V | undefined>`
instead of `Promise<V | void>`. This is an irrelevant change
practically speaking, but can require changes for TypeScript
users.
For more info, see the [change log](CHANGELOG.md).

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@@ -0,0 +1,116 @@
{
"name": "lru-cache",
"publishConfig": {
"tag": "legacy-v10"
},
"description": "A cache object that deletes the least-recently-used items.",
"version": "10.4.3",
"author": "Isaac Z. Schlueter <i@izs.me>",
"keywords": [
"mru",
"lru",
"cache"
],
"sideEffects": false,
"scripts": {
"build": "npm run prepare",
"prepare": "tshy && bash fixup.sh",
"pretest": "npm run prepare",
"presnap": "npm run prepare",
"test": "tap",
"snap": "tap",
"preversion": "npm test",
"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags",
"format": "prettier --write .",
"typedoc": "typedoc --tsconfig ./.tshy/esm.json ./src/*.ts",
"benchmark-results-typedoc": "bash scripts/benchmark-results-typedoc.sh",
"prebenchmark": "npm run prepare",
"benchmark": "make -C benchmark",
"preprofile": "npm run prepare",
"profile": "make -C benchmark profile"
},
"main": "./dist/commonjs/index.js",
"types": "./dist/commonjs/index.d.ts",
"tshy": {
"exports": {
".": "./src/index.ts",
"./min": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.min.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.min.js"
}
}
}
},
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-lru-cache.git"
},
"devDependencies": {
"@types/node": "^20.2.5",
"@types/tap": "^15.0.6",
"benchmark": "^2.1.4",
"esbuild": "^0.17.11",
"eslint-config-prettier": "^8.5.0",
"marked": "^4.2.12",
"mkdirp": "^2.1.5",
"prettier": "^2.6.2",
"tap": "^20.0.3",
"tshy": "^2.0.0",
"tslib": "^2.4.0",
"typedoc": "^0.25.3",
"typescript": "^5.2.2"
},
"license": "ISC",
"files": [
"dist"
],
"prettier": {
"semi": false,
"printWidth": 70,
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"jsxSingleQuote": false,
"bracketSameLine": true,
"arrowParens": "avoid",
"endOfLine": "lf"
},
"tap": {
"node-arg": [
"--expose-gc"
],
"plugin": [
"@tapjs/clock"
]
},
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.js"
}
},
"./min": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.min.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.min.js"
}
}
},
"type": "module",
"module": "./dist/esm/index.js"
}

View File

@@ -0,0 +1,81 @@
{
"name": "@asamuzakjp/css-color",
"description": "CSS color - Resolve and convert CSS colors.",
"author": "asamuzaK",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/asamuzaK/cssColor.git"
},
"homepage": "https://github.com/asamuzaK/cssColor#readme",
"bugs": {
"url": "https://github.com/asamuzaK/cssColor/issues"
},
"files": [
"dist",
"src"
],
"type": "module",
"types": "dist/esm/index.d.ts",
"module": "dist/esm/index.js",
"main": "dist/cjs/index.cjs",
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/cjs/index.d.cts",
"default": "./dist/cjs/index.cjs"
}
},
"./package.json": "./package.json"
},
"dependencies": {
"@csstools/css-calc": "^2.1.3",
"@csstools/css-color-parser": "^3.0.9",
"@csstools/css-parser-algorithms": "^3.0.4",
"@csstools/css-tokenizer": "^3.0.3",
"lru-cache": "^10.4.3"
},
"devDependencies": {
"@tanstack/vite-config": "^0.2.0",
"@vitest/coverage-istanbul": "^3.1.4",
"esbuild": "^0.25.4",
"eslint": "^9.27.0",
"eslint-plugin-regexp": "^2.7.0",
"globals": "^16.1.0",
"knip": "^5.56.0",
"neostandard": "^0.12.1",
"prettier": "^3.5.3",
"publint": "^0.3.12",
"rimraf": "^6.0.1",
"tsup": "^8.5.0",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"vitest": "^3.1.4"
},
"packageManager": "pnpm@10.11.0",
"pnpm": {
"onlyBuiltDependencies": [
"esbuild",
"unrs-resolver"
]
},
"scripts": {
"build": "pnpm run clean && pnpm run test && pnpm run knip && pnpm run build:prod && pnpm run build:cjs && pnpm run build:browser && pnpm run publint",
"build:browser": "vite build -c ./vite.browser.config.ts",
"build:prod": "vite build",
"build:cjs": "tsup ./src/index.ts --format=cjs --platform=node --outDir=./dist/cjs/ --sourcemap --dts",
"clean": "rimraf ./coverage ./dist",
"knip": "knip",
"prettier": "prettier . --ignore-unknown --write",
"publint": "publint --strict",
"test": "pnpm run prettier && pnpm run --stream \"/^test:.*/\"",
"test:eslint": "eslint ./src ./test --fix",
"test:types": "tsc",
"test:unit": "vitest"
},
"version": "3.2.0"
}

View File

@@ -0,0 +1,27 @@
/*!
* CSS color - Resolve, parse, convert CSS color.
* @license MIT
* @copyright asamuzaK (Kazz)
* @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}
*/
import { cssCalc as csscalc } from './js/css-calc';
import { isGradient } from './js/css-gradient';
import { cssVar } from './js/css-var';
import { extractDashedIdent, isColor as iscolor, splitValue } from './js/util';
export { convert } from './js/convert';
export { resolve } from './js/resolve';
/* utils */
export const utils = {
cssCalc: csscalc,
cssVar,
extractDashedIdent,
isColor: iscolor,
isGradient,
splitValue
};
/* TODO: remove later */
/* alias */
export const isColor = utils.isColor;
export const cssCalc = utils.cssCalc;

View File

@@ -0,0 +1,114 @@
/**
* cache
*/
import { LRUCache } from 'lru-cache';
import { Options } from './typedef';
import { valueToJsonString } from './util';
/* numeric constants */
const MAX_CACHE = 4096;
/**
* CacheItem
*/
export class CacheItem {
/* private */
#isNull: boolean;
#item: unknown;
/**
* constructor
*/
constructor(item: unknown, isNull: boolean = false) {
this.#item = item;
this.#isNull = !!isNull;
}
get item() {
return this.#item;
}
get isNull() {
return this.#isNull;
}
}
/**
* NullObject
*/
export class NullObject extends CacheItem {
/**
* constructor
*/
constructor() {
super(Symbol('null'), true);
}
}
/*
* lru cache
*/
export const lruCache = new LRUCache({
max: MAX_CACHE
});
/**
* set cache
* @param key - cache key
* @param value - value to cache
* @returns void
*/
export const setCache = (key: string, value: unknown): void => {
if (key) {
if (value === null) {
lruCache.set(key, new NullObject());
} else if (value instanceof CacheItem) {
lruCache.set(key, value);
} else {
lruCache.set(key, new CacheItem(value));
}
}
};
/**
* get cache
* @param key - cache key
* @returns cached item or false otherwise
*/
export const getCache = (key: string): CacheItem | boolean => {
if (key && lruCache.has(key)) {
const item = lruCache.get(key);
if (item instanceof CacheItem) {
return item;
}
// delete unexpected cached item
lruCache.delete(key);
return false;
}
return false;
};
/**
* create cache key
* @param keyData - key data
* @param [opt] - options
* @returns cache key
*/
export const createCacheKey = (
keyData: Record<string, string>,
opt: Options = {}
): string => {
const { customProperty = {}, dimension = {} } = opt;
let cacheKey = '';
if (
keyData &&
Object.keys(keyData).length &&
typeof customProperty.callback !== 'function' &&
typeof dimension.callback !== 'function'
) {
keyData.opt = valueToJsonString(opt);
cacheKey = valueToJsonString(keyData);
}
return cacheKey;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
/**
* common
*/
/* numeric constants */
const TYPE_FROM = 8;
const TYPE_TO = -1;
/**
* get type
* @param o - object to check
* @returns type of object
*/
export const getType = (o: unknown): string =>
Object.prototype.toString.call(o).slice(TYPE_FROM, TYPE_TO);
/**
* is string
* @param o - object to check
* @returns result
*/
export const isString = (o: unknown): o is string =>
typeof o === 'string' || o instanceof String;
/**
* is string or number
* @param o - object to check
* @returns result
*/
export const isStringOrNumber = (o: unknown): boolean =>
isString(o) || typeof o === 'number';

View File

@@ -0,0 +1,66 @@
/**
* constant
*/
/* values and units */
const _DIGIT = '(?:0|[1-9]\\d*)';
const _COMPARE = 'clamp|max|min';
const _EXPO = 'exp|hypot|log|pow|sqrt';
const _SIGN = 'abs|sign';
const _STEP = 'mod|rem|round';
const _TRIG = 'a?(?:cos|sin|tan)|atan2';
const _MATH = `${_COMPARE}|${_EXPO}|${_SIGN}|${_STEP}|${_TRIG}`;
const _CALC = `calc|${_MATH}`;
const _VAR = `var|${_CALC}`;
export const ANGLE = 'deg|g?rad|turn';
export const LENGTH =
'[cm]m|[dls]?v(?:[bhiw]|max|min)|in|p[ctx]|q|r?(?:[cl]h|cap|e[mx]|ic)';
export const NUM = `[+-]?(?:${_DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${_DIGIT})?`;
export const NUM_POSITIVE = `\\+?(?:${_DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${_DIGIT})?`;
export const NONE = 'none';
export const PCT = `${NUM}%`;
export const SYN_FN_CALC = `^(?:${_CALC})\\(|(?<=[*\\/\\s\\(])(?:${_CALC})\\(`;
export const SYN_FN_MATH_START = `^(?:${_MATH})\\($`;
export const SYN_FN_VAR = '^var\\(|(?<=[*\\/\\s\\(])var\\(';
export const SYN_FN_VAR_START = `^(?:${_VAR})\\(`;
/* colors */
const _ALPHA = `(?:\\s*\\/\\s*(?:${NUM}|${PCT}|${NONE}))?`;
const _ALPHA_LV3 = `(?:\\s*,\\s*(?:${NUM}|${PCT}))?`;
const _COLOR_FUNC = '(?:ok)?l(?:ab|ch)|color|hsla?|hwb|rgba?';
const _COLOR_KEY = '[a-z]+|#[\\da-f]{3}|#[\\da-f]{4}|#[\\da-f]{6}|#[\\da-f]{8}';
const _CS_HUE = '(?:ok)?lch|hsl|hwb';
const _CS_HUE_ARC = '(?:de|in)creasing|longer|shorter';
const _NUM_ANGLE = `${NUM}(?:${ANGLE})?`;
const _NUM_ANGLE_NONE = `(?:${NUM}(?:${ANGLE})?|${NONE})`;
const _NUM_PCT_NONE = `(?:${NUM}|${PCT}|${NONE})`;
export const CS_HUE = `(?:${_CS_HUE})(?:\\s(?:${_CS_HUE_ARC})\\shue)?`;
export const CS_HUE_CAPT = `(${_CS_HUE})(?:\\s(${_CS_HUE_ARC})\\shue)?`;
export const CS_LAB = '(?:ok)?lab';
export const CS_LCH = '(?:ok)?lch';
export const CS_SRGB = 'srgb(?:-linear)?';
export const CS_RGB = `(?:a98|prophoto)-rgb|display-p3|rec2020|${CS_SRGB}`;
export const CS_XYZ = 'xyz(?:-d(?:50|65))?';
export const CS_RECT = `${CS_LAB}|${CS_RGB}|${CS_XYZ}`;
export const CS_MIX = `${CS_HUE}|${CS_RECT}`;
export const FN_COLOR = 'color(';
export const FN_MIX = 'color-mix(';
export const FN_REL = `(?:${_COLOR_FUNC})\\(\\s*from\\s+`;
export const FN_REL_CAPT = `(${_COLOR_FUNC})\\(\\s*from\\s+`;
export const FN_VAR = 'var(';
export const SYN_FN_COLOR = `(?:${CS_RGB}|${CS_XYZ})(?:\\s+${_NUM_PCT_NONE}){3}${_ALPHA}`;
export const SYN_FN_REL = `^${FN_REL}|(?<=[\\s])${FN_REL}`;
export const SYN_HSL = `${_NUM_ANGLE_NONE}(?:\\s+${_NUM_PCT_NONE}){2}${_ALPHA}`;
export const SYN_HSL_LV3 = `${_NUM_ANGLE}(?:\\s*,\\s*${PCT}){2}${_ALPHA_LV3}`;
export const SYN_LCH = `(?:${_NUM_PCT_NONE}\\s+){2}${_NUM_ANGLE_NONE}${_ALPHA}`;
export const SYN_MOD = `${_NUM_PCT_NONE}(?:\\s+${_NUM_PCT_NONE}){2}${_ALPHA}`;
export const SYN_RGB_LV3 = `(?:${NUM}(?:\\s*,\\s*${NUM}){2}|${PCT}(?:\\s*,\\s*${PCT}){2})${_ALPHA_LV3}`;
export const SYN_COLOR_TYPE = `${_COLOR_KEY}|hsla?\\(\\s*${SYN_HSL_LV3}\\s*\\)|rgba?\\(\\s*${SYN_RGB_LV3}\\s*\\)|(?:hsla?|hwb)\\(\\s*${SYN_HSL}\\s*\\)|(?:(?:ok)?lab|rgba?)\\(\\s*${SYN_MOD}\\s*\\)|(?:ok)?lch\\(\\s*${SYN_LCH}\\s*\\)|color\\(\\s*${SYN_FN_COLOR}\\s*\\)`;
export const SYN_MIX_PART = `(?:${SYN_COLOR_TYPE})(?:\\s+${PCT})?`;
export const SYN_MIX = `color-mix\\(\\s*in\\s+(?:${CS_MIX})\\s*,\\s*${SYN_MIX_PART}\\s*,\\s*${SYN_MIX_PART}\\s*\\)`;
export const SYN_MIX_CAPT = `color-mix\\(\\s*in\\s+(${CS_MIX})\\s*,\\s*(${SYN_MIX_PART})\\s*,\\s*(${SYN_MIX_PART})\\s*\\)`;
/* formats */
export const VAL_COMP = 'computedValue';
export const VAL_MIX = 'mixValue';
export const VAL_SPEC = 'specifiedValue';

View File

@@ -0,0 +1,469 @@
/**
* convert
*/
import {
CacheItem,
NullObject,
createCacheKey,
getCache,
setCache
} from './cache';
import {
convertColorToHsl,
convertColorToHwb,
convertColorToLab,
convertColorToLch,
convertColorToOklab,
convertColorToOklch,
convertColorToRgb,
numberToHexString,
parseColorFunc,
parseColorValue
} from './color';
import { isString } from './common';
import { cssCalc } from './css-calc';
import { resolveVar } from './css-var';
import { resolveRelativeColor } from './relative-color';
import { resolveColor } from './resolve';
import { ColorChannels, ComputedColorChannels, Options } from './typedef';
/* constants */
import { SYN_FN_CALC, SYN_FN_REL, SYN_FN_VAR, VAL_COMP } from './constant';
const NAMESPACE = 'convert';
/* regexp */
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
const REG_FN_REL = new RegExp(SYN_FN_REL);
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
/**
* pre process
* @param value - CSS color value
* @param [opt] - options
* @returns value
*/
export const preProcess = (
value: string,
opt: Options = {}
): string | NullObject => {
if (isString(value)) {
value = value.trim();
if (!value) {
return new NullObject();
}
} else {
return new NullObject();
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'preProcess',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return cachedResult as NullObject;
}
return cachedResult.item as string;
}
if (REG_FN_VAR.test(value)) {
const resolvedValue = resolveVar(value, opt);
if (isString(resolvedValue)) {
value = resolvedValue;
} else {
setCache(cacheKey, null);
return new NullObject();
}
}
if (REG_FN_REL.test(value)) {
const resolvedValue = resolveRelativeColor(value, opt);
if (isString(resolvedValue)) {
value = resolvedValue;
} else {
setCache(cacheKey, null);
return new NullObject();
}
} else if (REG_FN_CALC.test(value)) {
value = cssCalc(value, opt);
}
if (value.startsWith('color-mix')) {
const clonedOpt = structuredClone(opt);
clonedOpt.format = VAL_COMP;
clonedOpt.nullable = true;
const resolvedValue = resolveColor(value, clonedOpt);
setCache(cacheKey, resolvedValue);
return resolvedValue;
}
setCache(cacheKey, value);
return value;
};
/**
* convert number to hex string
* @param value - numeric value
* @returns hex string: 00..ff
*/
export const numberToHex = (value: number): string => {
const hex = numberToHexString(value);
return hex;
};
/**
* convert color to hex
* @param value - CSS color value
* @param [opt] - options
* @param [opt.alpha] - enable alpha channel
* @returns #rrggbb | #rrggbbaa | null
*/
export const colorToHex = (value: string, opt: Options = {}): string | null => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return null;
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const { alpha = false } = opt;
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'colorToHex',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return null;
}
return cachedResult.item as string;
}
let hex;
opt.nullable = true;
if (alpha) {
opt.format = 'hexAlpha';
hex = resolveColor(value, opt);
} else {
opt.format = 'hex';
hex = resolveColor(value, opt);
}
if (isString(hex)) {
setCache(cacheKey, hex);
return hex;
}
setCache(cacheKey, null);
return null;
};
/**
* convert color to hsl
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [h, s, l, alpha]
*/
export const colorToHsl = (value: string, opt: Options = {}): ColorChannels => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'colorToHsl',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as ColorChannels;
}
opt.format = 'hsl';
const hsl = convertColorToHsl(value, opt) as ColorChannels;
setCache(cacheKey, hsl);
return hsl;
};
/**
* convert color to hwb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [h, w, b, alpha]
*/
export const colorToHwb = (value: string, opt: Options = {}): ColorChannels => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'colorToHwb',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as ColorChannels;
}
opt.format = 'hwb';
const hwb = convertColorToHwb(value, opt) as ColorChannels;
setCache(cacheKey, hwb);
return hwb;
};
/**
* convert color to lab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, a, b, alpha]
*/
export const colorToLab = (value: string, opt: Options = {}): ColorChannels => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'colorToLab',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as ColorChannels;
}
const lab = convertColorToLab(value, opt) as ColorChannels;
setCache(cacheKey, lab);
return lab;
};
/**
* convert color to lch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, c, h, alpha]
*/
export const colorToLch = (value: string, opt: Options = {}): ColorChannels => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'colorToLch',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as ColorChannels;
}
const lch = convertColorToLch(value, opt) as ColorChannels;
setCache(cacheKey, lch);
return lch;
};
/**
* convert color to oklab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, a, b, alpha]
*/
export const colorToOklab = (
value: string,
opt: Options = {}
): ColorChannels => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'colorToOklab',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as ColorChannels;
}
const lab = convertColorToOklab(value, opt) as ColorChannels;
setCache(cacheKey, lab);
return lab;
};
/**
* convert color to oklch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, c, h, alpha]
*/
export const colorToOklch = (
value: string,
opt: Options = {}
): ColorChannels => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'colorToOklch',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as ColorChannels;
}
const lch = convertColorToOklch(value, opt) as ColorChannels;
setCache(cacheKey, lch);
return lch;
};
/**
* convert color to rgb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [r, g, b, alpha]
*/
export const colorToRgb = (value: string, opt: Options = {}): ColorChannels => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'colorToRgb',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as ColorChannels;
}
const rgb = convertColorToRgb(value, opt) as ColorChannels;
setCache(cacheKey, rgb);
return rgb;
};
/**
* convert color to xyz
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [x, y, z, alpha]
*/
export const colorToXyz = (value: string, opt: Options = {}): ColorChannels => {
if (isString(value)) {
const resolvedValue = preProcess(value, opt);
if (resolvedValue instanceof NullObject) {
return [0, 0, 0, 0];
}
value = resolvedValue.toLowerCase();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'colorToXyz',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as ColorChannels;
}
let xyz;
if (value.startsWith('color(')) {
[, ...xyz] = parseColorFunc(value, opt) as ComputedColorChannels;
} else {
[, ...xyz] = parseColorValue(value, opt) as ComputedColorChannels;
}
setCache(cacheKey, xyz);
return xyz as ColorChannels;
};
/**
* convert color to xyz-d50
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [x, y, z, alpha]
*/
export const colorToXyzD50 = (
value: string,
opt: Options = {}
): ColorChannels => {
opt.d50 = true;
return colorToXyz(value, opt);
};
/* convert */
export const convert = {
colorToHex,
colorToHsl,
colorToHwb,
colorToLab,
colorToLch,
colorToOklab,
colorToOklch,
colorToRgb,
colorToXyz,
colorToXyzD50,
numberToHex
};

View File

@@ -0,0 +1,965 @@
/**
* css-calc
*/
import { calc } from '@csstools/css-calc';
import { CSSToken, TokenType, tokenize } from '@csstools/css-tokenizer';
import {
CacheItem,
NullObject,
createCacheKey,
getCache,
setCache
} from './cache';
import { isString, isStringOrNumber } from './common';
import { resolveVar } from './css-var';
import { roundToPrecision } from './util';
import { MatchedRegExp, Options } from './typedef';
/* constants */
import {
ANGLE,
LENGTH,
NUM,
SYN_FN_CALC,
SYN_FN_MATH_START,
SYN_FN_VAR,
SYN_FN_VAR_START,
VAL_SPEC
} from './constant';
const {
CloseParen: PAREN_CLOSE,
Comment: COMMENT,
Dimension: DIM,
EOF,
Function: FUNC,
OpenParen: PAREN_OPEN,
Whitespace: W_SPACE
} = TokenType;
const NAMESPACE = 'css-calc';
/* numeric constants */
const TRIA = 3;
const HEX = 16;
const MAX_PCT = 100;
/* regexp */
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
const REG_FN_CALC_NUM = new RegExp(`^calc\\((${NUM})\\)$`);
const REG_FN_MATH_START = new RegExp(SYN_FN_MATH_START);
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
const REG_FN_VAR_START = new RegExp(SYN_FN_VAR_START);
const REG_OPERATOR = /\s[*+/-]\s/;
const REG_TYPE_DIM = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH})$`);
const REG_TYPE_DIM_PCT = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH}|%)$`);
const REG_TYPE_PCT = new RegExp(`^(${NUM})%$`);
/**
* Calclator
*/
export class Calculator {
/* private */
// number
#hasNum: boolean;
#numSum: number[];
#numMul: number[];
// percentage
#hasPct: boolean;
#pctSum: number[];
#pctMul: number[];
// dimension
#hasDim: boolean;
#dimSum: string[];
#dimSub: string[];
#dimMul: string[];
#dimDiv: string[];
// et cetra
#hasEtc: boolean;
#etcSum: string[];
#etcSub: string[];
#etcMul: string[];
#etcDiv: string[];
/**
* constructor
*/
constructor() {
// number
this.#hasNum = false;
this.#numSum = [];
this.#numMul = [];
// percentage
this.#hasPct = false;
this.#pctSum = [];
this.#pctMul = [];
// dimension
this.#hasDim = false;
this.#dimSum = [];
this.#dimSub = [];
this.#dimMul = [];
this.#dimDiv = [];
// et cetra
this.#hasEtc = false;
this.#etcSum = [];
this.#etcSub = [];
this.#etcMul = [];
this.#etcDiv = [];
}
get hasNum() {
return this.#hasNum;
}
set hasNum(value: boolean) {
this.#hasNum = !!value;
}
get numSum() {
return this.#numSum;
}
get numMul() {
return this.#numMul;
}
get hasPct() {
return this.#hasPct;
}
set hasPct(value: boolean) {
this.#hasPct = !!value;
}
get pctSum() {
return this.#pctSum;
}
get pctMul() {
return this.#pctMul;
}
get hasDim() {
return this.#hasDim;
}
set hasDim(value: boolean) {
this.#hasDim = !!value;
}
get dimSum() {
return this.#dimSum;
}
get dimSub() {
return this.#dimSub;
}
get dimMul() {
return this.#dimMul;
}
get dimDiv() {
return this.#dimDiv;
}
get hasEtc() {
return this.#hasEtc;
}
set hasEtc(value: boolean) {
this.#hasEtc = !!value;
}
get etcSum() {
return this.#etcSum;
}
get etcSub() {
return this.#etcSub;
}
get etcMul() {
return this.#etcMul;
}
get etcDiv() {
return this.#etcDiv;
}
/**
* clear values
* @returns void
*/
clear() {
// number
this.#hasNum = false;
this.#numSum = [];
this.#numMul = [];
// percentage
this.#hasPct = false;
this.#pctSum = [];
this.#pctMul = [];
// dimension
this.#hasDim = false;
this.#dimSum = [];
this.#dimSub = [];
this.#dimMul = [];
this.#dimDiv = [];
// et cetra
this.#hasEtc = false;
this.#etcSum = [];
this.#etcSub = [];
this.#etcMul = [];
this.#etcDiv = [];
}
/**
* sort values
* @param values - values
* @returns sorted values
*/
sort(values: string[] = []): string[] {
const arr = [...values];
if (arr.length > 1) {
arr.sort((a, b) => {
let res;
if (REG_TYPE_DIM_PCT.test(a) && REG_TYPE_DIM_PCT.test(b)) {
const [, valA, unitA] = a.match(REG_TYPE_DIM_PCT) as MatchedRegExp;
const [, valB, unitB] = b.match(REG_TYPE_DIM_PCT) as MatchedRegExp;
if (unitA === unitB) {
if (Number(valA) === Number(valB)) {
res = 0;
} else if (Number(valA) > Number(valB)) {
res = 1;
} else {
res = -1;
}
} else if (unitA > unitB) {
res = 1;
} else {
res = -1;
}
} else {
if (a === b) {
res = 0;
} else if (a > b) {
res = 1;
} else {
res = -1;
}
}
return res;
});
}
return arr;
}
/**
* multiply values
* @returns resolved value
*/
multiply(): string {
const value = [];
let num;
if (this.#hasNum) {
num = 1;
for (const i of this.#numMul) {
num *= i;
if (num === 0 || !Number.isFinite(num) || Number.isNaN(num)) {
break;
}
}
if (!this.#hasPct && !this.#hasDim && !this.hasEtc) {
if (Number.isFinite(num)) {
num = roundToPrecision(num, HEX);
}
value.push(num);
}
}
if (this.#hasPct) {
if (typeof num !== 'number') {
num = 1;
}
for (const i of this.#pctMul) {
num *= i;
if (num === 0 || !Number.isFinite(num) || Number.isNaN(num)) {
break;
}
}
if (Number.isFinite(num)) {
num = `${roundToPrecision(num, HEX)}%`;
}
if (!this.#hasDim && !this.hasEtc) {
value.push(num);
}
}
if (this.#hasDim) {
let dim = '';
let mul = '';
let div = '';
if (this.#dimMul.length) {
if (this.#dimMul.length === 1) {
[mul] = this.#dimMul as [string];
} else {
mul = `${this.sort(this.#dimMul).join(' * ')}`;
}
}
if (this.#dimDiv.length) {
if (this.#dimDiv.length === 1) {
[div] = this.#dimDiv as [string];
} else {
div = `${this.sort(this.#dimDiv).join(' * ')}`;
}
}
if (Number.isFinite(num)) {
if (mul) {
if (div) {
if (div.includes('*')) {
dim = calc(`calc(${num} * ${mul} / (${div}))`, {
toCanonicalUnits: true
});
} else {
dim = calc(`calc(${num} * ${mul} / ${div})`, {
toCanonicalUnits: true
});
}
} else {
dim = calc(`calc(${num} * ${mul})`, {
toCanonicalUnits: true
});
}
} else if (div.includes('*')) {
dim = calc(`calc(${num} / (${div}))`, {
toCanonicalUnits: true
});
} else {
dim = calc(`calc(${num} / ${div})`, {
toCanonicalUnits: true
});
}
value.push(dim.replace(/^calc/, ''));
} else {
if (!value.length && num !== undefined) {
value.push(num);
}
if (mul) {
if (div) {
if (div.includes('*')) {
dim = calc(`calc(${mul} / (${div}))`, {
toCanonicalUnits: true
});
} else {
dim = calc(`calc(${mul} / ${div})`, {
toCanonicalUnits: true
});
}
} else {
dim = calc(`calc(${mul})`, {
toCanonicalUnits: true
});
}
if (value.length) {
value.push('*', dim.replace(/^calc/, ''));
} else {
value.push(dim.replace(/^calc/, ''));
}
} else {
dim = calc(`calc(${div})`, {
toCanonicalUnits: true
});
if (value.length) {
value.push('/', dim.replace(/^calc/, ''));
} else {
value.push('1', '/', dim.replace(/^calc/, ''));
}
}
}
}
if (this.#hasEtc) {
if (this.#etcMul.length) {
if (!value.length && num !== undefined) {
value.push(num);
}
const mul = this.sort(this.#etcMul).join(' * ');
if (value.length) {
value.push(`* ${mul}`);
} else {
value.push(`${mul}`);
}
}
if (this.#etcDiv.length) {
const div = this.sort(this.#etcDiv).join(' * ');
if (div.includes('*')) {
if (value.length) {
value.push(`/ (${div})`);
} else {
value.push(`1 / (${div})`);
}
} else if (value.length) {
value.push(`/ ${div}`);
} else {
value.push(`1 / ${div}`);
}
}
}
if (value.length) {
return value.join(' ');
}
return '';
}
/**
* sum values
* @returns resolved value
*/
sum(): string {
const value = [];
if (this.#hasNum) {
let num = 0;
for (const i of this.#numSum) {
num += i;
if (!Number.isFinite(num) || Number.isNaN(num)) {
break;
}
}
value.push(num);
}
if (this.#hasPct) {
let num: number | string = 0;
for (const i of this.#pctSum) {
num += i;
if (!Number.isFinite(num)) {
break;
}
}
if (Number.isFinite(num)) {
num = `${num}%`;
}
if (value.length) {
value.push(`+ ${num}`);
} else {
value.push(num);
}
}
if (this.#hasDim) {
let dim, sum, sub;
if (this.#dimSum.length) {
sum = this.sort(this.#dimSum).join(' + ');
}
if (this.#dimSub.length) {
sub = this.sort(this.#dimSub).join(' + ');
}
if (sum) {
if (sub) {
if (sub.includes('-')) {
dim = calc(`calc(${sum} - (${sub}))`, {
toCanonicalUnits: true
});
} else {
dim = calc(`calc(${sum} - ${sub})`, {
toCanonicalUnits: true
});
}
} else {
dim = calc(`calc(${sum})`, {
toCanonicalUnits: true
});
}
} else {
dim = calc(`calc(-1 * (${sub}))`, {
toCanonicalUnits: true
});
}
if (value.length) {
value.push('+', dim.replace(/^calc/, ''));
} else {
value.push(dim.replace(/^calc/, ''));
}
}
if (this.#hasEtc) {
if (this.#etcSum.length) {
const sum = this.sort(this.#etcSum)
.map(item => {
let res;
if (
REG_OPERATOR.test(item) &&
!item.startsWith('(') &&
!item.endsWith(')')
) {
res = `(${item})`;
} else {
res = item;
}
return res;
})
.join(' + ');
if (value.length) {
if (this.#etcSum.length > 1) {
value.push(`+ (${sum})`);
} else {
value.push(`+ ${sum}`);
}
} else {
value.push(`${sum}`);
}
}
if (this.#etcSub.length) {
const sub = this.sort(this.#etcSub)
.map(item => {
let res;
if (
REG_OPERATOR.test(item) &&
!item.startsWith('(') &&
!item.endsWith(')')
) {
res = `(${item})`;
} else {
res = item;
}
return res;
})
.join(' + ');
if (value.length) {
if (this.#etcSub.length > 1) {
value.push(`- (${sub})`);
} else {
value.push(`- ${sub}`);
}
} else if (this.#etcSub.length > 1) {
value.push(`-1 * (${sub})`);
} else {
value.push(`-1 * ${sub}`);
}
}
}
if (value.length) {
return value.join(' ');
}
return '';
}
}
/**
* sort calc values
* @param values - values to sort
* @param [finalize] - finalize values
* @returns sorted values
*/
export const sortCalcValues = (
values: (number | string)[] = [],
finalize: boolean = false
): string => {
if (values.length < TRIA) {
throw new Error(`Unexpected array length ${values.length}.`);
}
const start = values.shift();
if (!isString(start) || !start.endsWith('(')) {
throw new Error(`Unexpected token ${start}.`);
}
const end = values.pop();
if (end !== ')') {
throw new Error(`Unexpected token ${end}.`);
}
if (values.length === 1) {
const [value] = values;
if (!isStringOrNumber(value)) {
throw new Error(`Unexpected token ${value}.`);
}
return `${start}${value}${end}`;
}
const sortedValues = [];
const cal = new Calculator();
let operator: string = '';
const l = values.length;
for (let i = 0; i < l; i++) {
const value = values[i];
if (!isStringOrNumber(value)) {
throw new Error(`Unexpected token ${value}.`);
}
if (value === '*' || value === '/') {
operator = value;
} else if (value === '+' || value === '-') {
const sortedValue = cal.multiply();
if (sortedValue) {
sortedValues.push(sortedValue, value);
}
cal.clear();
operator = '';
} else {
const numValue = Number(value);
const strValue = `${value}`;
switch (operator) {
case '/': {
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numMul.push(1 / numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT) as MatchedRegExp;
cal.hasPct = true;
cal.pctMul.push((MAX_PCT * MAX_PCT) / Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimDiv.push(strValue);
} else {
cal.hasEtc = true;
cal.etcDiv.push(strValue);
}
break;
}
case '*':
default: {
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numMul.push(numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT) as MatchedRegExp;
cal.hasPct = true;
cal.pctMul.push(Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimMul.push(strValue);
} else {
cal.hasEtc = true;
cal.etcMul.push(strValue);
}
}
}
}
if (i === l - 1) {
const sortedValue = cal.multiply();
if (sortedValue) {
sortedValues.push(sortedValue);
}
cal.clear();
operator = '';
}
}
let resolvedValue = '';
if (finalize && (sortedValues.includes('+') || sortedValues.includes('-'))) {
const finalizedValues = [];
cal.clear();
operator = '';
const l = sortedValues.length;
for (let i = 0; i < l; i++) {
const value = sortedValues[i];
if (isStringOrNumber(value)) {
if (value === '+' || value === '-') {
operator = value;
} else {
const numValue = Number(value);
const strValue = `${value}`;
switch (operator) {
case '-': {
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numSum.push(-1 * numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT) as MatchedRegExp;
cal.hasPct = true;
cal.pctSum.push(-1 * Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimSub.push(strValue);
} else {
cal.hasEtc = true;
cal.etcSub.push(strValue);
}
break;
}
case '+':
default: {
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numSum.push(numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT) as MatchedRegExp;
cal.hasPct = true;
cal.pctSum.push(Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimSum.push(strValue);
} else {
cal.hasEtc = true;
cal.etcSum.push(strValue);
}
}
}
}
}
if (i === l - 1) {
const sortedValue = cal.sum();
if (sortedValue) {
finalizedValues.push(sortedValue);
}
cal.clear();
operator = '';
}
}
resolvedValue = finalizedValues.join(' ').replace(/\+\s-/g, '- ');
} else {
resolvedValue = sortedValues.join(' ').replace(/\+\s-/g, '- ');
}
if (
resolvedValue.startsWith('(') &&
resolvedValue.endsWith(')') &&
resolvedValue.lastIndexOf('(') === 0 &&
resolvedValue.indexOf(')') === resolvedValue.length - 1
) {
resolvedValue = resolvedValue.replace(/^\(/, '').replace(/\)$/, '');
}
return `${start}${resolvedValue}${end}`;
};
/**
* serialize calc
* @param value - CSS value
* @param [opt] - options
* @returns serialized value
*/
export const serializeCalc = (value: string, opt: Options = {}): string => {
const { format = '' } = opt;
if (isString(value)) {
if (!REG_FN_VAR_START.test(value) || format !== VAL_SPEC) {
return value;
}
value = value.toLowerCase().trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'serializeCalc',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as string;
}
const items: string[] = tokenize({ css: value })
.map((token: CSSToken): string => {
const [type, value] = token as [TokenType, string];
let res = '';
if (type !== W_SPACE && type !== COMMENT) {
res = value;
}
return res;
})
.filter(v => v);
let startIndex = items.findLastIndex((item: string) => /\($/.test(item));
while (startIndex) {
const endIndex = items.findIndex((item: unknown, index: number) => {
return item === ')' && index > startIndex;
});
const slicedValues: string[] = items.slice(startIndex, endIndex + 1);
let serializedValue: string = sortCalcValues(slicedValues);
if (REG_FN_VAR_START.test(serializedValue)) {
serializedValue = calc(serializedValue, {
toCanonicalUnits: true
});
}
items.splice(startIndex, endIndex - startIndex + 1, serializedValue);
startIndex = items.findLastIndex((item: string) => /\($/.test(item));
}
const serializedCalc = sortCalcValues(items, true);
setCache(cacheKey, serializedCalc);
return serializedCalc;
};
/**
* resolve dimension
* @param token - CSS token
* @param [opt] - options
* @returns resolved value
*/
export const resolveDimension = (
token: CSSToken,
opt: Options = {}
): string | NullObject => {
if (!Array.isArray(token)) {
throw new TypeError(`${token} is not an array.`);
}
const [, , , , detail = {}] = token;
const { unit, value } = detail as {
unit: string;
value: number;
};
const { dimension = {} } = opt;
if (unit === 'px') {
return `${value}${unit}`;
}
const relativeValue = Number(value);
if (unit && Number.isFinite(relativeValue)) {
let pixelValue;
if (Object.hasOwnProperty.call(dimension, unit)) {
pixelValue = dimension[unit];
} else if (typeof dimension.callback === 'function') {
pixelValue = dimension.callback(unit);
}
pixelValue = Number(pixelValue);
if (Number.isFinite(pixelValue)) {
return `${relativeValue * pixelValue}px`;
}
}
return new NullObject();
};
/**
* parse tokens
* @param tokens - CSS tokens
* @param [opt] - options
* @returns parsed tokens
*/
export const parseTokens = (
tokens: CSSToken[],
opt: Options = {}
): string[] => {
if (!Array.isArray(tokens)) {
throw new TypeError(`${tokens} is not an array.`);
}
const { format = '' } = opt;
const mathFunc = new Set();
let nest = 0;
const res: string[] = [];
while (tokens.length) {
const token = tokens.shift();
if (!Array.isArray(token)) {
throw new TypeError(`${token} is not an array.`);
}
const [type = '', value = ''] = token as [TokenType, string];
switch (type) {
case DIM: {
if (format === VAL_SPEC && !mathFunc.has(nest)) {
res.push(value);
} else {
const resolvedValue = resolveDimension(token, opt);
if (isString(resolvedValue)) {
res.push(resolvedValue);
} else {
res.push(value);
}
}
break;
}
case FUNC:
case PAREN_OPEN: {
res.push(value);
nest++;
if (REG_FN_MATH_START.test(value)) {
mathFunc.add(nest);
}
break;
}
case PAREN_CLOSE: {
if (res.length) {
const lastValue = res[res.length - 1];
if (lastValue === ' ') {
res.splice(-1, 1, value);
} else {
res.push(value);
}
} else {
res.push(value);
}
if (mathFunc.has(nest)) {
mathFunc.delete(nest);
}
nest--;
break;
}
case W_SPACE: {
if (res.length) {
const lastValue = res[res.length - 1];
if (
isString(lastValue) &&
!lastValue.endsWith('(') &&
lastValue !== ' '
) {
res.push(value);
}
}
break;
}
default: {
if (type !== COMMENT && type !== EOF) {
res.push(value);
}
}
}
}
return res;
};
/**
* CSS calc()
* @param value - CSS value including calc()
* @param [opt] - options
* @returns resolved value
*/
export const cssCalc = (value: string, opt: Options = {}): string => {
const { format = '' } = opt;
if (isString(value)) {
if (REG_FN_VAR.test(value)) {
if (format === VAL_SPEC) {
return value;
} else {
const resolvedValue = resolveVar(value, opt);
if (isString(resolvedValue)) {
return resolvedValue;
} else {
return '';
}
}
} else if (!REG_FN_CALC.test(value)) {
return value;
}
value = value.toLowerCase().trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'cssCalc',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
return cachedResult.item as string;
}
const tokens = tokenize({ css: value });
const values = parseTokens(tokens, opt);
let resolvedValue: string = calc(values.join(''), {
toCanonicalUnits: true
});
if (REG_FN_VAR_START.test(value)) {
if (REG_TYPE_DIM_PCT.test(resolvedValue)) {
const [, val, unit] = resolvedValue.match(
REG_TYPE_DIM_PCT
) as MatchedRegExp;
resolvedValue = `${roundToPrecision(Number(val), HEX)}${unit}`;
}
// wrap with `calc()`
if (
resolvedValue &&
!REG_FN_VAR_START.test(resolvedValue) &&
format === VAL_SPEC
) {
resolvedValue = `calc(${resolvedValue})`;
}
}
if (format === VAL_SPEC) {
if (/\s[-+*/]\s/.test(resolvedValue) && !resolvedValue.includes('NaN')) {
resolvedValue = serializeCalc(resolvedValue, opt);
} else if (REG_FN_CALC_NUM.test(resolvedValue)) {
const [, val] = resolvedValue.match(REG_FN_CALC_NUM) as MatchedRegExp;
resolvedValue = `calc(${roundToPrecision(Number(val), HEX)})`;
}
}
setCache(cacheKey, resolvedValue);
return resolvedValue;
};

View File

@@ -0,0 +1,289 @@
/**
* css-gradient
*/
import { CacheItem, createCacheKey, getCache, setCache } from './cache';
import { isString } from './common';
import { MatchedRegExp, Options } from './typedef';
import { isColor, splitValue } from './util';
/* constants */
import {
ANGLE,
CS_HUE,
CS_RECT,
LENGTH,
NUM,
NUM_POSITIVE,
PCT
} from './constant';
const NAMESPACE = 'css-gradient';
const DIM_ANGLE = `${NUM}(?:${ANGLE})`;
const DIM_ANGLE_PCT = `${DIM_ANGLE}|${PCT}`;
const DIM_LEN = `${NUM}(?:${LENGTH})|0`;
const DIM_LEN_PCT = `${DIM_LEN}|${PCT}`;
const DIM_LEN_PCT_POSI = `${NUM_POSITIVE}(?:${LENGTH}|%)|0`;
const DIM_LEN_POSI = `${NUM_POSITIVE}(?:${LENGTH})|0`;
const CTR = 'center';
const L_R = 'left|right';
const T_B = 'top|bottom';
const S_E = 'start|end';
const AXIS_X = `${L_R}|x-(?:${S_E})`;
const AXIS_Y = `${T_B}|y-(?:${S_E})`;
const BLOCK = `block-(?:${S_E})`;
const INLINE = `inline-(?:${S_E})`;
const POS_1 = `${CTR}|${AXIS_X}|${AXIS_Y}|${BLOCK}|${INLINE}|${DIM_LEN_PCT}`;
const POS_2 = [
`(?:${CTR}|${AXIS_X})\\s+(?:${CTR}|${AXIS_Y})`,
`(?:${CTR}|${AXIS_Y})\\s+(?:${CTR}|${AXIS_X})`,
`(?:${CTR}|${AXIS_X}|${DIM_LEN_PCT})\\s+(?:${CTR}|${AXIS_Y}|${DIM_LEN_PCT})`,
`(?:${CTR}|${BLOCK})\\s+(?:${CTR}|${INLINE})`,
`(?:${CTR}|${INLINE})\\s+(?:${CTR}|${BLOCK})`,
`(?:${CTR}|${S_E})\\s+(?:${CTR}|${S_E})`
].join('|');
const POS_4 = [
`(?:${AXIS_X})\\s+(?:${DIM_LEN_PCT})\\s+(?:${AXIS_Y})\\s+(?:${DIM_LEN_PCT})`,
`(?:${AXIS_Y})\\s+(?:${DIM_LEN_PCT})\\s+(?:${AXIS_X})\\s+(?:${DIM_LEN_PCT})`,
`(?:${BLOCK})\\s+(?:${DIM_LEN_PCT})\\s+(?:${INLINE})\\s+(?:${DIM_LEN_PCT})`,
`(?:${INLINE})\\s+(?:${DIM_LEN_PCT})\\s+(?:${BLOCK})\\s+(?:${DIM_LEN_PCT})`,
`(?:${S_E})\\s+(?:${DIM_LEN_PCT})\\s+(?:${S_E})\\s+(?:${DIM_LEN_PCT})`
].join('|');
const RAD_EXTENT = '(?:clos|farth)est-(?:corner|side)';
const RAD_SIZE = [
`${RAD_EXTENT}(?:\\s+${RAD_EXTENT})?`,
`${DIM_LEN_POSI}`,
`(?:${DIM_LEN_PCT_POSI})\\s+(?:${DIM_LEN_PCT_POSI})`
].join('|');
const RAD_SHAPE = 'circle|ellipse';
const FROM_ANGLE = `from\\s+${DIM_ANGLE}`;
const AT_POSITION = `at\\s+(?:${POS_1}|${POS_2}|${POS_4})`;
const TO_SIDE_CORNER = `to\\s+(?:(?:${L_R})(?:\\s(?:${T_B}))?|(?:${T_B})(?:\\s(?:${L_R}))?)`;
const IN_COLOR_SPACE = `in\\s+(?:${CS_RECT}|${CS_HUE})`;
/* type definitions */
/**
* @type ColorStopList - list of color stops
*/
type ColorStopList = [string, string, ...string[]];
/**
* @typedef Gradient - parsed CSS gradient
* @property value - input value
* @property type - gradient type
* @property [gradientLine] - gradient line
* @property colorStopList - list of color stops
*/
interface Gradient {
value: string;
type: string;
gradientLine?: string;
colorStopList: ColorStopList;
}
/* regexp */
const REG_GRAD = /^(?:repeating-)?(?:conic|linear|radial)-gradient\(/;
const REG_GRAD_CAPT = /^((?:repeating-)?(?:conic|linear|radial)-gradient)\(/;
/**
* get gradient type
* @param value - gradient value
* @returns gradient type
*/
export const getGradientType = (value: string): string => {
if (isString(value)) {
value = value.trim();
if (REG_GRAD.test(value)) {
const [, type] = value.match(REG_GRAD_CAPT) as MatchedRegExp;
return type;
}
}
return '';
};
/**
* validate gradient line
* @param value - gradient line value
* @param type - gradient type
* @returns result
*/
export const validateGradientLine = (value: string, type: string): boolean => {
if (isString(value) && isString(type)) {
value = value.trim();
type = type.trim();
let lineSyntax = '';
if (/^(?:repeating-)?linear-gradient$/.test(type)) {
/*
* <linear-gradient-line> = [
* [ <angle> | to <side-or-corner> ] ||
* <color-interpolation-method>
* ]
*/
lineSyntax = [
`(?:${DIM_ANGLE}|${TO_SIDE_CORNER})(?:\\s+${IN_COLOR_SPACE})?`,
`${IN_COLOR_SPACE}(?:\\s+(?:${DIM_ANGLE}|${TO_SIDE_CORNER}))?`
].join('|');
} else if (/^(?:repeating-)?radial-gradient$/.test(type)) {
/*
* <radial-gradient-line> = [
* [ [ <radial-shape> || <radial-size> ]? [ at <position> ]? ] ||
* <color-interpolation-method>]?
*/
lineSyntax = [
`(?:${RAD_SHAPE})(?:\\s+(?:${RAD_SIZE}))?(?:\\s+${AT_POSITION})?(?:\\s+${IN_COLOR_SPACE})?`,
`(?:${RAD_SIZE})(?:\\s+(?:${RAD_SHAPE}))?(?:\\s+${AT_POSITION})?(?:\\s+${IN_COLOR_SPACE})?`,
`${AT_POSITION}(?:\\s+${IN_COLOR_SPACE})?`,
`${IN_COLOR_SPACE}(?:\\s+${RAD_SHAPE})(?:\\s+(?:${RAD_SIZE}))?(?:\\s+${AT_POSITION})?`,
`${IN_COLOR_SPACE}(?:\\s+${RAD_SIZE})(?:\\s+(?:${RAD_SHAPE}))?(?:\\s+${AT_POSITION})?`,
`${IN_COLOR_SPACE}(?:\\s+${AT_POSITION})?`
].join('|');
} else if (/^(?:repeating-)?conic-gradient$/.test(type)) {
/*
* <conic-gradient-line> = [
* [ [ from <angle> ]? [ at <position> ]? ] ||
* <color-interpolation-method>
* ]
*/
lineSyntax = [
`${FROM_ANGLE}(?:\\s+${AT_POSITION})?(?:\\s+${IN_COLOR_SPACE})?`,
`${AT_POSITION}(?:\\s+${IN_COLOR_SPACE})?`,
`${IN_COLOR_SPACE}(?:\\s+${FROM_ANGLE})?(?:\\s+${AT_POSITION})?`
].join('|');
}
if (lineSyntax) {
const reg = new RegExp(`^(?:${lineSyntax})$`);
return reg.test(value);
}
}
return false;
};
/**
* validate color stop list
* @param list
* @param type
* @param [opt]
* @returns result
*/
export const validateColorStopList = (
list: string[],
type: string,
opt: Options = {}
): boolean => {
if (Array.isArray(list) && list.length > 1) {
const dimension = /^(?:repeating-)?conic-gradient$/.test(type)
? DIM_ANGLE_PCT
: DIM_LEN_PCT;
const regColorHint = new RegExp(`^(?:${dimension})$`);
const regDimension = new RegExp(`(?:\\s+(?:${dimension})){1,2}$`);
const arr = [];
for (const item of list) {
if (isString(item)) {
if (regColorHint.test(item)) {
arr.push('hint');
} else {
const color = item.replace(regDimension, '');
if (isColor(color, opt)) {
arr.push('color');
} else {
return false;
}
}
}
}
const value = arr.join(',');
return /^color(?:,(?:hint,)?color)+$/.test(value);
}
return false;
};
/**
* parse CSS gradient
* @param value - gradient value
* @param [opt] - options
* @returns parsed result
*/
export const parseGradient = (
value: string,
opt: Options = {}
): Gradient | null => {
if (isString(value)) {
value = value.trim();
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'parseGradient',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return null;
}
return cachedResult.item as Gradient;
}
const type = getGradientType(value);
const gradValue = value.replace(REG_GRAD, '').replace(/\)$/, '');
if (type && gradValue) {
const [lineOrColorStop = '', ...colorStops] = splitValue(gradValue, {
delimiter: ','
});
const dimension = /^(?:repeating-)?conic-gradient$/.test(type)
? DIM_ANGLE_PCT
: DIM_LEN_PCT;
const regDimension = new RegExp(`(?:\\s+(?:${dimension})){1,2}$`);
let isColorStop = false;
if (regDimension.test(lineOrColorStop)) {
const colorStop = lineOrColorStop.replace(regDimension, '');
if (isColor(colorStop, opt)) {
isColorStop = true;
}
} else if (isColor(lineOrColorStop, opt)) {
isColorStop = true;
}
if (isColorStop) {
colorStops.unshift(lineOrColorStop);
const valid = validateColorStopList(colorStops, type, opt);
if (valid) {
const res: Gradient = {
value,
type,
colorStopList: colorStops as ColorStopList
};
setCache(cacheKey, res);
return res;
}
} else if (colorStops.length > 1) {
const gradientLine = lineOrColorStop;
const valid =
validateGradientLine(gradientLine, type) &&
validateColorStopList(colorStops, type, opt);
if (valid) {
const res: Gradient = {
value,
type,
gradientLine,
colorStopList: colorStops as ColorStopList
};
setCache(cacheKey, res);
return res;
}
}
}
setCache(cacheKey, null);
return null;
}
return null;
};
/**
* is CSS gradient
* @param value - CSS value
* @param [opt] - options
* @returns result
*/
export const isGradient = (value: string, opt: Options = {}): boolean => {
const gradient = parseGradient(value, opt);
return gradient !== null;
};

View File

@@ -0,0 +1,250 @@
/**
* css-var
*/
import { CSSToken, TokenType, tokenize } from '@csstools/css-tokenizer';
import {
CacheItem,
NullObject,
createCacheKey,
getCache,
setCache
} from './cache';
import { isString } from './common';
import { cssCalc } from './css-calc';
import { isColor } from './util';
import { Options } from './typedef';
/* constants */
import { FN_VAR, SYN_FN_CALC, SYN_FN_VAR, VAL_SPEC } from './constant';
const {
CloseParen: PAREN_CLOSE,
Comment: COMMENT,
EOF,
Ident: IDENT,
Whitespace: W_SPACE
} = TokenType;
const NAMESPACE = 'css-var';
/* regexp */
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
/**
* resolve custom property
* @param tokens - CSS tokens
* @param [opt] - options
* @returns result - [tokens, resolvedValue]
*/
export function resolveCustomProperty(
tokens: CSSToken[],
opt: Options = {}
): [CSSToken[], string] {
if (!Array.isArray(tokens)) {
throw new TypeError(`${tokens} is not an array.`);
}
const { customProperty = {} } = opt;
const items: string[] = [];
while (tokens.length) {
const token = tokens.shift();
if (!Array.isArray(token)) {
throw new TypeError(`${token} is not an array.`);
}
const [type, value] = token as [TokenType, string];
// end of var()
if (type === PAREN_CLOSE) {
break;
}
// nested var()
if (value === FN_VAR) {
const [restTokens, item] = resolveCustomProperty(tokens, opt);
tokens = restTokens;
if (item) {
items.push(item);
}
} else if (type === IDENT) {
if (value.startsWith('--')) {
let item;
if (Object.hasOwnProperty.call(customProperty, value)) {
item = customProperty[value] as string;
} else if (typeof customProperty.callback === 'function') {
item = customProperty.callback(value);
}
if (item) {
items.push(item);
}
} else if (value) {
items.push(value);
}
}
}
let resolveAsColor = false;
if (items.length > 1) {
const lastValue = items[items.length - 1];
resolveAsColor = isColor(lastValue);
}
let resolvedValue = '';
for (let item of items) {
item = item.trim();
if (REG_FN_VAR.test(item)) {
// recurse resolveVar()
const resolvedItem = resolveVar(item, opt);
if (isString(resolvedItem)) {
if (resolveAsColor) {
if (isColor(resolvedItem)) {
resolvedValue = resolvedItem;
}
} else {
resolvedValue = resolvedItem;
}
}
} else if (REG_FN_CALC.test(item)) {
item = cssCalc(item, opt);
if (resolveAsColor) {
if (isColor(item)) {
resolvedValue = item;
}
} else {
resolvedValue = item;
}
} else if (
item &&
!/^(?:inherit|initial|revert(?:-layer)?|unset)$/.test(item)
) {
if (resolveAsColor) {
if (isColor(item)) {
resolvedValue = item;
}
} else {
resolvedValue = item;
}
}
if (resolvedValue) {
break;
}
}
return [tokens, resolvedValue];
}
/**
* parse tokens
* @param tokens - CSS tokens
* @param [opt] - options
* @returns parsed tokens
*/
export function parseTokens(
tokens: CSSToken[],
opt: Options = {}
): string[] | NullObject {
const res: string[] = [];
while (tokens.length) {
const token = tokens.shift();
const [type = '', value = ''] = token as [TokenType, string];
if (value === FN_VAR) {
const [restTokens, resolvedValue] = resolveCustomProperty(tokens, opt);
if (!resolvedValue) {
return new NullObject();
}
tokens = restTokens;
res.push(resolvedValue);
} else {
switch (type) {
case PAREN_CLOSE: {
if (res.length) {
const lastValue = res[res.length - 1];
if (lastValue === ' ') {
res.splice(-1, 1, value);
} else {
res.push(value);
}
} else {
res.push(value);
}
break;
}
case W_SPACE: {
if (res.length) {
const lastValue = res[res.length - 1];
if (
isString(lastValue) &&
!lastValue.endsWith('(') &&
lastValue !== ' '
) {
res.push(value);
}
}
break;
}
default: {
if (type !== COMMENT && type !== EOF) {
res.push(value);
}
}
}
}
}
return res;
}
/**
* resolve CSS var()
* @param value - CSS value including var()
* @param [opt] - options
* @returns resolved value
*/
export function resolveVar(
value: string,
opt: Options = {}
): string | NullObject {
const { format = '' } = opt;
if (isString(value)) {
if (!REG_FN_VAR.test(value) || format === VAL_SPEC) {
return value;
}
value = value.trim();
} else {
throw new TypeError(`${value} is not a string.`);
}
const cacheKey: string = createCacheKey(
{
namespace: NAMESPACE,
name: 'resolveVar',
value
},
opt
);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) {
return cachedResult as NullObject;
}
return cachedResult.item as string;
}
const tokens = tokenize({ css: value });
const values = parseTokens(tokens, opt);
if (Array.isArray(values)) {
let color = values.join('');
if (REG_FN_CALC.test(color)) {
color = cssCalc(color, opt);
}
setCache(cacheKey, color);
return color;
} else {
setCache(cacheKey, null);
return new NullObject();
}
}
/**
* CSS var()
* @param value - CSS value including var()
* @param [opt] - options
* @returns resolved value
*/
export const cssVar = (value: string, opt: Options = {}): string => {
const resolvedValue = resolveVar(value, opt);
if (isString(resolvedValue)) {
return resolvedValue;
}
return '';
};

Some files were not shown because too many files have changed in this diff Show More