feat: Phase 1 - Artist profile database refactor with API foundation
Some checks failed
CI / build-and-test (pull_request) Has been cancelled
Some checks failed
CI / build-and-test (pull_request) Has been cancelled
Implements backend infrastructure for loading artist profiles from Cloudflare D1 database instead of static data. Database Changes: - Add slug column migration for SEO-friendly URLs (0001_add_artist_slug.sql) - Enhanced data migration script with slug generation - Support for all artist fields from data/artists.ts Type Definitions: - Add slug field to Artist interface - Create ArtistWithPortfolio type for full artist data - Create PublicArtist type for sanitized API responses - Add ArtistFilters type for query parameters - Add ArtistDashboardStats for analytics Database Functions (lib/db.ts): - getPublicArtists() - fetch active artists with portfolio and filtering - getArtistWithPortfolio() - fetch single artist with full portfolio - getArtistBySlug() - fetch by URL-friendly slug - getArtistByUserId() - fetch by user ID for dashboard - Enhanced getArtists() with JSON parsing API Endpoints: - Updated GET /api/artists - filtering, pagination, portfolio images - Created GET /api/artists/[id] - fetch by ID or slug - Created PUT /api/artists/[id] - update with authorization - Created DELETE /api/artists/[id] - soft delete (admin only) - Created GET /api/artists/me - current artist profile React Hooks (hooks/use-artist-data.ts): - useArtists() - fetch with filtering - useArtist() - fetch single artist - useCurrentArtist() - logged-in artist - useUpdateArtist(), useCreateArtist(), useDeleteArtist() - mutations Frontend Components: - Refactored artists-grid.tsx to use API with loading/error states - Use database field names (slug, specialties, portfolioImages) - Display profile images from portfolio - Client-side filtering by specialty Files Modified: - sql/migrations/0001_add_artist_slug.sql (new) - types/database.ts (enhanced) - lib/data-migration.ts (enhanced) - lib/db.ts (enhanced) - app/api/artists/route.ts (updated) - app/api/artists/[id]/route.ts (new) - app/api/artists/me/route.ts (new) - hooks/use-artist-data.ts (new) - components/artists-grid.tsx (refactored) Remaining work: Artist portfolio page, artist dashboard, admin enhancements Ref: artist_profile_refactor_implementation_plan.md
This commit is contained in:
parent
741e036711
commit
97a925b2ac
@ -2,55 +2,34 @@ import { NextRequest, NextResponse } from "next/server"
|
||||
import { requireAuth } from "@/lib/auth"
|
||||
import { UserRole } from "@/types/database"
|
||||
import { updateArtistSchema } from "@/lib/validations"
|
||||
import { db } from "@/lib/db"
|
||||
import { Flags } from "@/lib/flags"
|
||||
import { getArtistWithPortfolio, getArtistBySlug, updateArtist, deleteArtist } from "@/lib/db"
|
||||
|
||||
// GET /api/artists/[id] - Fetch a specific artist
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
// GET /api/artists/[id] - Fetch single artist with portfolio
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
{ params }: { params: { id: string } },
|
||||
context?: any
|
||||
) {
|
||||
try {
|
||||
const { id } = params
|
||||
|
||||
// TODO: Implement via Supabase MCP
|
||||
// const artist = await db.artists.findUnique(id)
|
||||
|
||||
// Mock response for now
|
||||
const mockArtist = {
|
||||
id,
|
||||
userId: "user-1",
|
||||
name: "Alex Rivera",
|
||||
bio: "Specializing in traditional and neo-traditional tattoos with over 8 years of experience.",
|
||||
specialties: ["Traditional", "Neo-Traditional", "Color Work"],
|
||||
instagramHandle: "alexrivera_tattoo",
|
||||
isActive: true,
|
||||
hourlyRate: 150,
|
||||
portfolioImages: [
|
||||
{
|
||||
id: "img-1",
|
||||
artistId: id,
|
||||
url: "/artists/alex-rivera-traditional-rose.jpg",
|
||||
caption: "Traditional rose tattoo",
|
||||
tags: ["traditional", "rose", "color"],
|
||||
order: 1,
|
||||
isPublic: true,
|
||||
createdAt: new Date(),
|
||||
},
|
||||
],
|
||||
availability: [],
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
// Try to fetch by ID first, then by slug
|
||||
let artist = await getArtistWithPortfolio(id, context?.env)
|
||||
|
||||
if (!artist) {
|
||||
artist = await getArtistBySlug(id, context?.env)
|
||||
}
|
||||
|
||||
if (!mockArtist) {
|
||||
|
||||
if (!artist) {
|
||||
return NextResponse.json(
|
||||
{ error: "Artist not found" },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
return NextResponse.json(mockArtist)
|
||||
return NextResponse.json(artist)
|
||||
} catch (error) {
|
||||
console.error("Error fetching artist:", error)
|
||||
return NextResponse.json(
|
||||
@ -60,42 +39,50 @@ export async function GET(
|
||||
}
|
||||
}
|
||||
|
||||
// PUT /api/artists/[id] - Update a specific artist (Admin only)
|
||||
// PUT /api/artists/[id] - Update artist (admin or artist themselves)
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
{ params }: { params: { id: string } },
|
||||
context?: any
|
||||
) {
|
||||
try {
|
||||
if (!Flags.ARTISTS_MODULE_ENABLED) {
|
||||
return NextResponse.json({ error: 'Artists module disabled' }, { status: 503 })
|
||||
}
|
||||
// Require admin authentication
|
||||
const session = await requireAuth(UserRole.SHOP_ADMIN)
|
||||
|
||||
const { id } = params
|
||||
const body = await request.json()
|
||||
const validatedData = updateArtistSchema.parse({ ...body, id })
|
||||
|
||||
// TODO: Implement via Supabase MCP
|
||||
// const updatedArtist = await db.artists.update(id, validatedData)
|
||||
const session = await requireAuth()
|
||||
|
||||
// Mock response for now
|
||||
const mockUpdatedArtist = {
|
||||
id,
|
||||
userId: "user-1",
|
||||
name: validatedData.name || "Alex Rivera",
|
||||
bio: validatedData.bio || "Updated bio",
|
||||
specialties: validatedData.specialties || ["Traditional"],
|
||||
instagramHandle: validatedData.instagramHandle,
|
||||
isActive: validatedData.isActive ?? true,
|
||||
hourlyRate: validatedData.hourlyRate,
|
||||
portfolioImages: [],
|
||||
availability: [],
|
||||
createdAt: new Date("2024-01-01"),
|
||||
updatedAt: new Date(),
|
||||
// Get the artist to check ownership
|
||||
const artist = await getArtistWithPortfolio(id, context?.env)
|
||||
if (!artist) {
|
||||
return NextResponse.json(
|
||||
{ error: "Artist not found" },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
// Check authorization: must be the artist themselves or an admin
|
||||
const isOwner = artist.userId === session.user.id
|
||||
const isAdmin = [UserRole.SUPER_ADMIN, UserRole.SHOP_ADMIN].includes(session.user.role)
|
||||
|
||||
if (!isOwner && !isAdmin) {
|
||||
return NextResponse.json(
|
||||
{ error: "Insufficient permissions" },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = updateArtistSchema.parse(body)
|
||||
|
||||
// If artist is updating themselves (not admin), restrict what they can change
|
||||
let updateData = validatedData
|
||||
if (isOwner && !isAdmin) {
|
||||
// Artists can only update: bio, specialties, instagramHandle, hourlyRate
|
||||
const { bio, specialties, instagramHandle, hourlyRate } = validatedData
|
||||
updateData = { bio, specialties, instagramHandle, hourlyRate }
|
||||
}
|
||||
|
||||
return NextResponse.json(mockUpdatedArtist)
|
||||
const updatedArtist = await updateArtist(id, updateData, context?.env)
|
||||
|
||||
return NextResponse.json(updatedArtist)
|
||||
} catch (error) {
|
||||
console.error("Error updating artist:", error)
|
||||
|
||||
@ -106,12 +93,6 @@ export async function PUT(
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
if (error.message.includes("Insufficient permissions")) {
|
||||
return NextResponse.json(
|
||||
{ error: "Insufficient permissions" },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
@ -121,30 +102,21 @@ export async function PUT(
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /api/artists/[id] - Delete a specific artist (Admin only)
|
||||
// DELETE /api/artists/[id] - Soft delete artist (admin only)
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
{ params }: { params: { id: string } },
|
||||
context?: any
|
||||
) {
|
||||
try {
|
||||
if (!Flags.ARTISTS_MODULE_ENABLED) {
|
||||
return NextResponse.json({ error: 'Artists module disabled' }, { status: 503 })
|
||||
}
|
||||
const { id } = params
|
||||
|
||||
// Require admin authentication
|
||||
await requireAuth(UserRole.SHOP_ADMIN)
|
||||
|
||||
const { id } = params
|
||||
await deleteArtist(id, context?.env)
|
||||
|
||||
// TODO: Implement via Supabase MCP
|
||||
// await db.artists.delete(id)
|
||||
|
||||
// Mock response for now
|
||||
console.log(`Artist ${id} would be deleted`)
|
||||
|
||||
return NextResponse.json(
|
||||
{ message: "Artist deleted successfully" },
|
||||
{ status: 200 }
|
||||
)
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error("Error deleting artist:", error)
|
||||
|
||||
|
||||
52
app/api/artists/me/route.ts
Normal file
52
app/api/artists/me/route.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { NextRequest, NextResponse } from "next/server"
|
||||
import { requireAuth } from "@/lib/auth"
|
||||
import { UserRole } from "@/types/database"
|
||||
import { getArtistByUserId } from "@/lib/db"
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
// GET /api/artists/me - Get current logged-in artist's profile
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params?: any } = {},
|
||||
context?: any
|
||||
) {
|
||||
try {
|
||||
// Require artist authentication
|
||||
const session = await requireAuth(UserRole.ARTIST)
|
||||
|
||||
// Fetch artist data by user ID
|
||||
const artist = await getArtistByUserId(session.user.id, context?.env)
|
||||
|
||||
if (!artist) {
|
||||
return NextResponse.json(
|
||||
{ error: "Artist profile not found" },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
return NextResponse.json(artist)
|
||||
} catch (error) {
|
||||
console.error("Error fetching artist profile:", error)
|
||||
|
||||
if (error instanceof Error) {
|
||||
if (error.message.includes("Authentication required")) {
|
||||
return NextResponse.json(
|
||||
{ error: "Authentication required" },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
if (error.message.includes("Insufficient permissions")) {
|
||||
return NextResponse.json(
|
||||
{ error: "You must be an artist to access this endpoint" },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to fetch artist profile" },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import { NextRequest, NextResponse } from "next/server"
|
||||
import { requireAuth } from "@/lib/auth"
|
||||
import { UserRole } from "@/types/database"
|
||||
import { UserRole, ArtistFilters } from "@/types/database"
|
||||
import { createArtistSchema, paginationSchema, artistFiltersSchema } from "@/lib/validations"
|
||||
import { getArtists, createArtist } from "@/lib/db"
|
||||
import { getPublicArtists, createArtist } from "@/lib/db"
|
||||
import { Flags } from "@/lib/flags"
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@ -15,7 +15,7 @@ export async function GET(request: NextRequest, { params }: { params?: any } = {
|
||||
// Parse and validate query parameters
|
||||
const pagination = paginationSchema.parse({
|
||||
page: searchParams.get("page") || "1",
|
||||
limit: searchParams.get("limit") || "10",
|
||||
limit: searchParams.get("limit") || "50", // Increased default for artists grid
|
||||
})
|
||||
|
||||
const filters = artistFiltersSchema.parse({
|
||||
@ -24,46 +24,28 @@ export async function GET(request: NextRequest, { params }: { params?: any } = {
|
||||
search: searchParams.get("search"),
|
||||
})
|
||||
|
||||
// Fetch artists from database with environment context
|
||||
const artists = await getArtists(context?.env)
|
||||
|
||||
// Apply filters
|
||||
let filteredArtists = artists
|
||||
|
||||
if (filters.isActive !== undefined) {
|
||||
filteredArtists = filteredArtists.filter(artist =>
|
||||
artist.isActive === filters.isActive
|
||||
)
|
||||
// Build filters for database query
|
||||
const dbFilters: ArtistFilters = {
|
||||
specialty: filters.specialty || undefined,
|
||||
search: filters.search || undefined,
|
||||
isActive: filters.isActive !== undefined ? filters.isActive : true,
|
||||
limit: pagination.limit,
|
||||
offset: (pagination.page - 1) * pagination.limit,
|
||||
}
|
||||
|
||||
// Fetch artists from database with portfolio images
|
||||
const artists = await getPublicArtists(dbFilters, context?.env)
|
||||
|
||||
if (filters.specialty) {
|
||||
filteredArtists = filteredArtists.filter(artist =>
|
||||
artist.specialties.some(specialty =>
|
||||
specialty.toLowerCase().includes(filters.specialty!.toLowerCase())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.search) {
|
||||
const searchTerm = filters.search.toLowerCase()
|
||||
filteredArtists = filteredArtists.filter(artist =>
|
||||
artist.name.toLowerCase().includes(searchTerm) ||
|
||||
artist.bio.toLowerCase().includes(searchTerm)
|
||||
)
|
||||
}
|
||||
|
||||
// Apply pagination
|
||||
const startIndex = (pagination.page - 1) * pagination.limit
|
||||
const endIndex = startIndex + pagination.limit
|
||||
const paginatedArtists = filteredArtists.slice(startIndex, endIndex)
|
||||
// Get total count for pagination (this is a simplified approach)
|
||||
// In production, you'd want a separate count query
|
||||
const hasMore = artists.length === pagination.limit
|
||||
|
||||
return NextResponse.json({
|
||||
artists: paginatedArtists,
|
||||
artists,
|
||||
pagination: {
|
||||
page: pagination.page,
|
||||
limit: pagination.limit,
|
||||
total: filteredArtists.length,
|
||||
totalPages: Math.ceil(filteredArtists.length / pagination.limit),
|
||||
hasMore,
|
||||
},
|
||||
filters,
|
||||
})
|
||||
|
||||
512
artist_profile_refactor_implementation_plan.md
Normal file
512
artist_profile_refactor_implementation_plan.md
Normal file
@ -0,0 +1,512 @@
|
||||
# Artist Profile Refactor Implementation Plan
|
||||
|
||||
## Overview
|
||||
Refactor the artists grid and individual artist profile pages to load content from a Cloudflare D1 database instead of static data. Enable artists to log into backend profiles where they can manage their portfolio images, bio, and other information. The existing `data/artists.ts` will serve as seed data for the database.
|
||||
|
||||
This implementation connects the existing database schema, API routes, and authentication system with the public-facing components, while also creating an artist dashboard for self-service portfolio management.
|
||||
|
||||
## Types
|
||||
Database and API type definitions for artist profile management.
|
||||
|
||||
**Key Type Updates:**
|
||||
- Ensure `Artist` interface in `types/database.ts` matches D1 schema columns
|
||||
- Add `ArtistWithPortfolio` type that includes populated `portfolioImages` array
|
||||
- Create `PublicArtist` type for sanitized public API responses
|
||||
- Add `ArtistDashboardStats` type for artist-specific analytics
|
||||
|
||||
**New Type Definitions:**
|
||||
```typescript
|
||||
// types/database.ts additions
|
||||
export interface ArtistWithPortfolio extends Artist {
|
||||
portfolioImages: PortfolioImage[]
|
||||
user?: {
|
||||
name: string
|
||||
email: string
|
||||
avatar?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface PublicArtist {
|
||||
id: string
|
||||
name: string
|
||||
bio: string
|
||||
specialties: string[]
|
||||
instagramHandle?: string
|
||||
portfolioImages: PortfolioImage[]
|
||||
isActive: boolean
|
||||
hourlyRate?: number
|
||||
}
|
||||
|
||||
export interface ArtistDashboardStats {
|
||||
totalImages: number
|
||||
activeImages: number
|
||||
profileViews?: number
|
||||
lastUpdated: Date
|
||||
}
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
### Files to Modify
|
||||
|
||||
**1. `lib/db.ts`**
|
||||
- Add `getArtistWithPortfolio(id: string)` function that joins artists with portfolio_images
|
||||
- Add `getPublicArtists()` function that returns only active artists with public portfolio images
|
||||
- Add `getArtistByUserId(userId: string)` function for artist dashboard access
|
||||
- Update `getArtists()` to parse JSON fields properly (specialties)
|
||||
|
||||
**2. `app/api/artists/route.ts`**
|
||||
- Update GET handler to return artists with portfolio images
|
||||
- Add pagination support to prevent loading all artists at once
|
||||
- Add filtering by specialty and search query
|
||||
- Ensure responses match `PublicArtist` type for public endpoints
|
||||
|
||||
**3. `app/api/artists/[id]/route.ts`**
|
||||
- Create this file (currently missing)
|
||||
- Add GET endpoint to fetch single artist with portfolio
|
||||
- Add PUT endpoint for updating artist (admin or artist themselves)
|
||||
- Add authorization check (admin or owner)
|
||||
|
||||
**4. `components/artists-grid.tsx`**
|
||||
- Remove hardcoded `artists` array
|
||||
- Add `useEffect` to fetch artists from `/api/artists`
|
||||
- Add loading and error states
|
||||
- Update to use API response data structure
|
||||
- Keep existing filtering UI but apply to fetched data
|
||||
- Update routing to use database IDs instead of hardcoded IDs
|
||||
|
||||
**5. `components/artist-portfolio.tsx`**
|
||||
- Remove hardcoded `artistsData` object
|
||||
- Add `useEffect` to fetch artist data from `/api/artists/${artistId}`
|
||||
- Add loading and error states
|
||||
- Update image galleries to use portfolio images from database
|
||||
- Handle missing artist gracefully
|
||||
- Update data structure to match API response
|
||||
|
||||
**6. `components/artists-page-section.tsx`**
|
||||
- Remove import from `@/data/artists`
|
||||
- Fetch artists from API in component
|
||||
- Add loading state
|
||||
|
||||
**7. `components/artists-section.tsx`**
|
||||
- Remove import from `@/data/artists`
|
||||
- Fetch artists from API
|
||||
- Add loading skeleton
|
||||
|
||||
**8. `components/booking-form.tsx`**
|
||||
- Update artist selection to fetch from API
|
||||
- Remove import from `@/data/artists`
|
||||
|
||||
**9. `app/artists/[id]/page.tsx`**
|
||||
- Update to use database-friendly slug or ID
|
||||
- May need to support both slug-based and ID-based routing during migration
|
||||
|
||||
**10. `lib/auth.ts`**
|
||||
- Add `getArtistSession()` helper that checks if logged-in user is an artist
|
||||
- Add `requireArtistAuth()` helper for artist-only routes
|
||||
|
||||
**11. `sql/schema.sql`**
|
||||
- Add missing columns if needed (review against `data/artists.ts` structure)
|
||||
- Add indexes for performance (slug, user_id lookups)
|
||||
- Consider adding `slug` column to artists table for SEO-friendly URLs
|
||||
|
||||
**12. `lib/data-migration.ts`**
|
||||
- Update migration to use all artists from `data/artists.ts`
|
||||
- Fix portfolio image creation to handle all work images
|
||||
- Add proper slug generation from artist names
|
||||
- Ensure idempotency (can be run multiple times safely)
|
||||
|
||||
### Files to Create
|
||||
|
||||
**1. `app/artist-dashboard/page.tsx`**
|
||||
- New artist dashboard home page
|
||||
- Show artist's own profile overview
|
||||
- Display stats (total images, views, etc.)
|
||||
- Quick links to edit profile and portfolio
|
||||
|
||||
**2. `app/artist-dashboard/layout.tsx`**
|
||||
- Layout wrapper for artist dashboard
|
||||
- Navigation sidebar for dashboard sections
|
||||
- Artist profile header
|
||||
- Requires ARTIST or SHOP_ADMIN role
|
||||
|
||||
**3. `app/artist-dashboard/profile/page.tsx`**
|
||||
- Artist profile edit page
|
||||
- Reuse `<ArtistForm>` component but filtered for artist-editable fields
|
||||
- Artists can edit: bio, specialties, instagram, hourly rate
|
||||
- Cannot edit: name, email, isActive (admin only)
|
||||
|
||||
**4. `app/artist-dashboard/portfolio/page.tsx`**
|
||||
- Portfolio image management page
|
||||
- Upload new images
|
||||
- Edit captions and tags
|
||||
- Reorder images (drag and drop)
|
||||
- Delete images
|
||||
- Set visibility (public/private)
|
||||
|
||||
**5. `app/api/artists/me/route.ts`**
|
||||
- GET endpoint to fetch current logged-in artist's data
|
||||
- Requires authentication
|
||||
- Returns artist profile for logged-in user
|
||||
|
||||
**6. `app/api/portfolio/route.ts`** (if not exists)
|
||||
- POST endpoint to add portfolio images
|
||||
- Requires artist or admin auth
|
||||
- Handles R2 upload integration
|
||||
|
||||
**7. `app/api/portfolio/[id]/route.ts`**
|
||||
- GET single portfolio image
|
||||
- PUT to update (caption, tags, order, visibility)
|
||||
- DELETE to remove image
|
||||
- Authorization: admin or image owner
|
||||
|
||||
**8. `components/admin/portfolio-manager.tsx`**
|
||||
- Reusable component for managing portfolio images
|
||||
- Used in both admin and artist dashboard
|
||||
- Image upload, grid display, edit modals
|
||||
- Drag-drop reordering
|
||||
|
||||
**9. `hooks/use-artist-data.ts`**
|
||||
- Custom hook for fetching artist data
|
||||
- Handles loading, error states
|
||||
- Caching with SWR or React Query pattern
|
||||
- Reusable across components
|
||||
|
||||
**10. `middleware.ts` (update)**
|
||||
- Add route protection for `/artist-dashboard/*`
|
||||
- Verify user has ARTIST role
|
||||
- Redirect to signin if not authenticated
|
||||
|
||||
### Files to Delete (after migration)
|
||||
|
||||
**None immediately** - Keep `data/artists.ts` as seed data reference
|
||||
|
||||
## Functions
|
||||
|
||||
### New Functions in `lib/db.ts`
|
||||
|
||||
**1. `getArtistWithPortfolio(id: string, env?: any): Promise<ArtistWithPortfolio | null>`**
|
||||
- Fetch artist by ID
|
||||
- Join with portfolio_images table
|
||||
- Parse JSON fields (specialties, tags)
|
||||
- Return combined object with images array
|
||||
|
||||
**2. `getPublicArtists(filters?: ArtistFilters, env?: any): Promise<PublicArtist[]>`**
|
||||
- Fetch only active artists
|
||||
- Include only public portfolio images
|
||||
- Apply filters (specialty, search)
|
||||
- Sanitize data for public consumption
|
||||
|
||||
**3. `getArtistByUserId(userId: string, env?: any): Promise<Artist | null>`**
|
||||
- Fetch artist record by user_id
|
||||
- Used for artist dashboard access
|
||||
- Returns full artist data for owner
|
||||
|
||||
**4. `getArtistBySlug(slug: string, env?: any): Promise<ArtistWithPortfolio | null>`**
|
||||
- Fetch artist by URL slug
|
||||
- Join with portfolio images
|
||||
- For SEO-friendly URLs
|
||||
|
||||
**5. `updatePortfolioImageOrder(artistId: string, imageOrders: Array<{id: string, orderIndex: number}>, env?: any): Promise<void>`**
|
||||
- Batch update order indices
|
||||
- Used for drag-drop reordering
|
||||
- Transaction support if available
|
||||
|
||||
### New Functions in `lib/auth.ts`
|
||||
|
||||
**1. `getArtistSession(): Promise<{ artist: Artist, user: User } | null>`**
|
||||
- Get current session
|
||||
- Check if user has ARTIST role
|
||||
- Fetch associated artist record
|
||||
- Return combined data or null
|
||||
|
||||
**2. `requireArtistAuth(): Promise<{ artist: Artist, user: User }>`**
|
||||
- Like requireAuth but specifically for artists
|
||||
- Throws error if not an artist
|
||||
- Returns artist and user data
|
||||
|
||||
**3. `canEditArtist(userId: string, artistId: string): Promise<boolean>`**
|
||||
- Check if user can edit specific artist
|
||||
- True if: user is the artist, SHOP_ADMIN, or SUPER_ADMIN
|
||||
- Used for authorization checks
|
||||
|
||||
### Modified Functions
|
||||
|
||||
**1. `lib/db.ts:getArtists()`**
|
||||
- Add optional `includePortfolio` parameter
|
||||
- Parse JSON fields properly
|
||||
- Add error handling
|
||||
|
||||
**2. `lib/db.ts:createArtist()`**
|
||||
- Add slug generation
|
||||
- Ensure user creation if needed
|
||||
- Return artist with portfolio array (empty initially)
|
||||
|
||||
**3. `lib/data-migration.ts:migrateArtistData()`**
|
||||
- Update to migrate all fields from `data/artists.ts`
|
||||
- Add slug generation
|
||||
- Handle all portfolio images
|
||||
- Add progress logging
|
||||
|
||||
## Classes
|
||||
|
||||
### New Classes
|
||||
|
||||
**1. `ArtistDashboardManager` (optional)**
|
||||
- Class to encapsulate artist dashboard operations
|
||||
- Methods: getStats(), updateProfile(), getPortfolio()
|
||||
- Centralizes artist-specific business logic
|
||||
- Located in `lib/artist-dashboard.ts`
|
||||
|
||||
**No other new classes required** - Functional approach with helper functions is sufficient
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Current Dependencies (verify versions)
|
||||
- `next`: 15.x
|
||||
- `next-auth`: Latest compatible with Next.js 15
|
||||
- `@tanstack/react-table`: For admin tables
|
||||
- `react-hook-form`: Form management
|
||||
- `zod`: Schema validation
|
||||
- `@hookform/resolvers`: Zod integration with react-hook-form
|
||||
|
||||
### New Dependencies to Install
|
||||
|
||||
**1. `swr` or `@tanstack/react-query`**
|
||||
- For client-side data fetching and caching
|
||||
- Recommended: `swr` (lighter weight)
|
||||
- Install: `npm install swr`
|
||||
|
||||
**2. `@dnd-kit/core`, `@dnd-kit/sortable` (optional)**
|
||||
- For drag-drop portfolio reordering
|
||||
- Only if implementing drag-drop UI
|
||||
- Install: `npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities`
|
||||
|
||||
**3. `sharp` (dev dependency)**
|
||||
- Image optimization during migration
|
||||
- May already be included with Next.js
|
||||
|
||||
### Configuration Updates
|
||||
|
||||
**1. `next.config.mjs`**
|
||||
- Ensure image optimization configured for R2 URLs
|
||||
- Add remote patterns for portfolio image domains
|
||||
|
||||
**2. `wrangler.toml`**
|
||||
- Verify D1 database binding name matches code
|
||||
- Verify R2 bucket binding for portfolio uploads
|
||||
|
||||
**3. `.env.local`**
|
||||
- No new env vars needed (using Cloudflare bindings)
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests to Create
|
||||
|
||||
**1. `__tests__/lib/db.test.ts`**
|
||||
- Test artist CRUD operations
|
||||
- Test portfolio image operations
|
||||
- Mock D1 database
|
||||
- Verify JSON parsing
|
||||
|
||||
**2. `__tests__/lib/auth.test.ts`**
|
||||
- Test artist authentication helpers
|
||||
- Test authorization checks
|
||||
- Mock NextAuth session
|
||||
|
||||
**3. `__tests__/hooks/use-artist-data.test.ts`**
|
||||
- Test hook with mock data
|
||||
- Test loading and error states
|
||||
- Test cache behavior
|
||||
|
||||
### Integration Tests to Create
|
||||
|
||||
**1. `__tests__/api/artists.test.ts`**
|
||||
- Test GET /api/artists endpoint
|
||||
- Test filtering and pagination
|
||||
- Test error handling
|
||||
|
||||
**2. `__tests__/api/artists/[id].test.ts`**
|
||||
- Test GET single artist
|
||||
- Test PUT artist update
|
||||
- Test authorization
|
||||
|
||||
**3. `__tests__/api/portfolio.test.ts`**
|
||||
- Test portfolio CRUD operations
|
||||
- Test file uploads
|
||||
- Test authorization
|
||||
|
||||
### Component Tests to Update
|
||||
|
||||
**1. `__tests__/components/artists-grid.test.tsx`**
|
||||
- Update to mock API fetch
|
||||
- Test loading states
|
||||
- Test error states
|
||||
- Test filtering
|
||||
|
||||
**2. `__tests__/components/artist-portfolio.test.tsx`**
|
||||
- Update to mock API fetch
|
||||
- Test portfolio image display
|
||||
- Test modal interactions
|
||||
|
||||
### E2E Test Scenarios
|
||||
|
||||
**1. Artist Login and Profile Edit**
|
||||
- Artist signs in
|
||||
- Navigates to dashboard
|
||||
- Edits bio and specialties
|
||||
- Saves successfully
|
||||
|
||||
**2. Portfolio Image Upload**
|
||||
- Artist uploads images
|
||||
- Images appear in portfolio
|
||||
- Reorders images
|
||||
- Deletes an image
|
||||
|
||||
**3. Public Artist Profile View**
|
||||
- Anonymous user visits artist page
|
||||
- Sees artist info and portfolio
|
||||
- Images load correctly
|
||||
- Filtering works
|
||||
|
||||
**4. Admin Artist Management**
|
||||
- Admin creates new artist
|
||||
- Edits artist information
|
||||
- Uploads portfolio images for artist
|
||||
- Deactivates artist
|
||||
|
||||
## Implementation Order
|
||||
|
||||
### Phase 1: Database & API Foundation (Steps 1-5)
|
||||
|
||||
**1. Update Database Schema and Migration**
|
||||
- Review and update `sql/schema.sql` if needed (add slug column, indexes)
|
||||
- Update `lib/data-migration.ts` to properly migrate all artist data from `data/artists.ts`
|
||||
- Test migration script locally
|
||||
- Verify all artists and portfolio images are created correctly
|
||||
|
||||
**2. Enhance Database Functions**
|
||||
- Update `lib/db.ts` with new functions: `getArtistWithPortfolio`, `getPublicArtists`, `getArtistByUserId`, `getArtistBySlug`
|
||||
- Update existing functions to properly parse JSON fields
|
||||
- Add error handling and logging
|
||||
- Write unit tests for new functions
|
||||
|
||||
**3. Create/Update API Endpoints**
|
||||
- Create `app/api/artists/[id]/route.ts` with GET and PUT handlers
|
||||
- Update `app/api/artists/route.ts` to support filtering and return portfolio images
|
||||
- Create `app/api/artists/me/route.ts` for artist self-access
|
||||
- Add authorization checks to all endpoints
|
||||
- Test endpoints with Postman or similar
|
||||
|
||||
**4. Update Authentication Helpers**
|
||||
- Add `getArtistSession()` and `requireArtistAuth()` to `lib/auth.ts`
|
||||
- Add `canEditArtist()` authorization helper
|
||||
- Test with mock sessions
|
||||
|
||||
**5. Create Data Fetching Hook**
|
||||
- Create `hooks/use-artist-data.ts` with SWR
|
||||
- Implement loading and error states
|
||||
- Add caching and revalidation
|
||||
- Test hook in isolation
|
||||
|
||||
### Phase 2: Public-Facing Components (Steps 6-8)
|
||||
|
||||
**6. Refactor Artists Grid**
|
||||
- Update `components/artists-grid.tsx` to fetch from API
|
||||
- Remove hardcoded data
|
||||
- Add loading skeleton UI
|
||||
- Add error handling UI
|
||||
- Test filtering and pagination
|
||||
- Verify routing to individual artists works
|
||||
|
||||
**7. Refactor Artist Portfolio Page**
|
||||
- Update `components/artist-portfolio.tsx` to fetch from API
|
||||
- Remove hardcoded `artistsData`
|
||||
- Add loading states
|
||||
- Update image galleries to use database images
|
||||
- Test with various artist IDs
|
||||
- Verify modal/lightbox still works
|
||||
|
||||
**8. Update Supporting Components**
|
||||
- Update `components/artists-section.tsx` to use API
|
||||
- Update `components/artists-page-section.tsx` to use API
|
||||
- Update `components/booking-form.tsx` artist selection
|
||||
- Add loading states to all
|
||||
- Test home page and booking flow
|
||||
|
||||
### Phase 3: Artist Dashboard (Steps 9-12)
|
||||
|
||||
**9. Create Artist Dashboard Layout**
|
||||
- Create `app/artist-dashboard/layout.tsx` with navigation
|
||||
- Add role protection in middleware
|
||||
- Create dashboard home page `app/artist-dashboard/page.tsx`
|
||||
- Display artist stats and quick links
|
||||
- Test authentication and routing
|
||||
|
||||
**10. Build Profile Editor**
|
||||
- Create `app/artist-dashboard/profile/page.tsx`
|
||||
- Reuse and adapt `<ArtistForm>` component
|
||||
- Filter fields for artist-editable only
|
||||
- Connect to PUT `/api/artists/me` endpoint
|
||||
- Test profile updates
|
||||
|
||||
**11. Build Portfolio Manager**
|
||||
- Create `app/artist-dashboard/portfolio/page.tsx`
|
||||
- Create `components/admin/portfolio-manager.tsx` component
|
||||
- Implement image upload UI
|
||||
- Add image editing (captions, tags, visibility)
|
||||
- Test upload and management
|
||||
|
||||
**12. Implement Portfolio API**
|
||||
- Create `app/api/portfolio/route.ts` for creating images
|
||||
- Create `app/api/portfolio/[id]/route.ts` for update/delete
|
||||
- Integrate with R2 upload system
|
||||
- Add authorization checks
|
||||
- Test full upload flow
|
||||
|
||||
### Phase 4: Admin Enhancements (Steps 13-14)
|
||||
|
||||
**13. Update Admin Artist Management**
|
||||
- Update `app/admin/artists/[id]/page.tsx` to use API
|
||||
- Enhance `<ArtistForm>` with portfolio management
|
||||
- Test admin CRUD operations
|
||||
- Verify authorization works
|
||||
|
||||
**14. Add Portfolio Management to Admin**
|
||||
- Integrate `<PortfolioManager>` into admin artist edit page
|
||||
- Allow admins to manage any artist's portfolio
|
||||
- Test admin portfolio operations
|
||||
|
||||
### Phase 5: Testing & Refinement (Steps 15-17)
|
||||
|
||||
**15. Write and Run Tests**
|
||||
- Create all unit tests for new functions
|
||||
- Create integration tests for API endpoints
|
||||
- Create component tests
|
||||
- Run full test suite
|
||||
- Fix any failing tests
|
||||
|
||||
**16. Performance Optimization**
|
||||
- Add database indexes for common queries
|
||||
- Implement image lazy loading
|
||||
- Add proper caching headers to API
|
||||
- Optimize large portfolio image displays
|
||||
- Test with large datasets
|
||||
|
||||
**17. Migration & Deployment**
|
||||
- Run migration on development D1 database
|
||||
- Test full application flow
|
||||
- Create migration checklist for production
|
||||
- Deploy to staging environment
|
||||
- Final testing in staging
|
||||
- Production deployment
|
||||
|
||||
### Phase 6: Documentation & Cleanup (Step 18)
|
||||
|
||||
**18. Documentation and Cleanup**
|
||||
- Document new API endpoints
|
||||
- Update README with setup instructions
|
||||
- Add inline code comments
|
||||
- Create artist onboarding guide
|
||||
- Remove deprecated code/comments
|
||||
- Archive old implementation if needed
|
||||
@ -1,102 +1,35 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useState, useMemo } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import Link from "next/link"
|
||||
import { Star, MapPin, Calendar } from "lucide-react"
|
||||
import { Star, Loader2 } from "lucide-react"
|
||||
import { useArtists } from "@/hooks/use-artist-data"
|
||||
|
||||
const artists = [
|
||||
{
|
||||
id: "1",
|
||||
name: "Sarah Chen",
|
||||
specialty: "Traditional & Neo-Traditional",
|
||||
image: "/professional-female-tattoo-artist-with-traditional.jpg",
|
||||
bio: "Specializing in bold traditional designs with a modern twist. Sarah brings 8 years of experience creating vibrant, timeless tattoos.",
|
||||
experience: "8 years",
|
||||
rating: 4.9,
|
||||
reviews: 127,
|
||||
location: "Studio A",
|
||||
availability: "Available",
|
||||
styles: ["Traditional", "Neo-Traditional", "American Traditional", "Color Work"],
|
||||
portfolio: [
|
||||
"/traditional-rose-tattoo-with-bold-colors.jpg",
|
||||
"/neo-traditional-wolf-tattoo-design.jpg",
|
||||
"/american-traditional-anchor-tattoo.jpg",
|
||||
"/colorful-traditional-bird-tattoo.jpg",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Marcus Rodriguez",
|
||||
specialty: "Realism & Portraits",
|
||||
image: "/professional-male-tattoo-artist-specializing-in-re.jpg",
|
||||
bio: "Master of photorealistic tattoos and detailed portrait work. Marcus has perfected the art of bringing photographs to life on skin.",
|
||||
experience: "12 years",
|
||||
rating: 5.0,
|
||||
reviews: 89,
|
||||
location: "Studio B",
|
||||
availability: "Booked until March",
|
||||
styles: ["Realism", "Portraits", "Black & Grey", "Photorealism"],
|
||||
portfolio: [
|
||||
"/photorealistic-portrait-tattoo-black-and-grey.jpg",
|
||||
"/realistic-animal-tattoo-detailed-shading.jpg",
|
||||
"/black-and-grey-portrait-tattoo-masterpiece.jpg",
|
||||
"/hyperrealistic-eye-tattoo-design.jpg",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Luna Kim",
|
||||
specialty: "Fine Line & Minimalist",
|
||||
image: "/professional-female-tattoo-artist-with-delicate-fi.jpg",
|
||||
bio: "Creating elegant, minimalist designs with precision and grace. Luna's delicate touch brings subtle beauty to every piece.",
|
||||
experience: "6 years",
|
||||
rating: 4.8,
|
||||
reviews: 156,
|
||||
location: "Studio C",
|
||||
availability: "Available",
|
||||
styles: ["Fine Line", "Minimalist", "Geometric", "Botanical"],
|
||||
portfolio: [
|
||||
"/delicate-fine-line-flower-tattoo.jpg",
|
||||
"/minimalist-geometric-tattoo-design.jpg",
|
||||
"/fine-line-botanical-tattoo-elegant.jpg",
|
||||
"/simple-line-work-tattoo-artistic.jpg",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "Jake Thompson",
|
||||
specialty: "Japanese & Oriental",
|
||||
image: "/professional-male-tattoo-artist-with-japanese-styl.jpg",
|
||||
bio: "Traditional Japanese tattooing with authentic techniques passed down through generations. Jake honors the ancient art form.",
|
||||
experience: "15 years",
|
||||
rating: 4.9,
|
||||
reviews: 203,
|
||||
location: "Studio D",
|
||||
availability: "Limited slots",
|
||||
styles: ["Japanese", "Oriental", "Irezumi", "Traditional Japanese"],
|
||||
portfolio: [
|
||||
"/traditional-japanese-dragon-tattoo-sleeve.jpg",
|
||||
"/japanese-koi-fish-tattoo-colorful.jpg",
|
||||
"/oriental-cherry-blossom-tattoo-design.jpg",
|
||||
"/japanese-samurai-tattoo-traditional.jpg",
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const specialties = ["All", "Traditional", "Realism", "Fine Line", "Japanese", "Portraits", "Minimalist"]
|
||||
const specialties = ["All", "Traditional", "Realism", "Fine Line", "Japanese", "Portraits", "Minimalist", "Black & Grey"]
|
||||
|
||||
export function ArtistsGrid() {
|
||||
const [selectedSpecialty, setSelectedSpecialty] = useState("All")
|
||||
|
||||
// Fetch artists from API
|
||||
const { data: artists, isLoading, error } = useArtists({ limit: 50 })
|
||||
|
||||
const filteredArtists =
|
||||
selectedSpecialty === "All"
|
||||
? artists
|
||||
: artists.filter((artist) =>
|
||||
artist.styles.some((style) => style.toLowerCase().includes(selectedSpecialty.toLowerCase())),
|
||||
)
|
||||
// Filter artists client-side
|
||||
const filteredArtists = useMemo(() => {
|
||||
if (!artists) return []
|
||||
|
||||
if (selectedSpecialty === "All") {
|
||||
return artists
|
||||
}
|
||||
|
||||
return artists.filter((artist) =>
|
||||
artist.specialties.some((style) =>
|
||||
style.toLowerCase().includes(selectedSpecialty.toLowerCase())
|
||||
)
|
||||
)
|
||||
}, [artists, selectedSpecialty])
|
||||
|
||||
return (
|
||||
<section className="py-20">
|
||||
@ -123,87 +56,112 @@ export function ArtistsGrid() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Loading State */}
|
||||
{isLoading && (
|
||||
<div className="flex justify-center items-center py-20">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-primary" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error State */}
|
||||
{error && (
|
||||
<div className="text-center py-20">
|
||||
<p className="text-red-500 mb-4">Failed to load artists. Please try again later.</p>
|
||||
<Button onClick={() => window.location.reload()}>Retry</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Artists Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filteredArtists.map((artist) => (
|
||||
<Card key={artist.id} className="group hover:shadow-xl transition-all duration-300 overflow-hidden">
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Artist Image */}
|
||||
<div className="relative w-full h-48 sm:h-56 overflow-hidden">
|
||||
<img
|
||||
src={artist.image || "/placeholder.svg"}
|
||||
alt={artist.name}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
<div className="absolute top-3 left-3">
|
||||
<Badge variant={artist.availability === "Available" ? "default" : "secondary"}>
|
||||
{artist.availability}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Artist Info */}
|
||||
<CardContent className="p-4 flex-grow flex flex-col">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div>
|
||||
<h3 className="font-playfair text-xl font-bold mb-1">{artist.name}</h3>
|
||||
<p className="text-primary font-medium text-sm">{artist.specialty}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1 text-xs">
|
||||
<Star className="w-3 h-3 fill-yellow-400 text-yellow-400" />
|
||||
<span className="font-medium">{artist.rating}</span>
|
||||
<span className="text-muted-foreground">({artist.reviews})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground mb-3 text-xs leading-relaxed line-clamp-3">{artist.bio}</p>
|
||||
|
||||
<div className="space-y-2 mb-4">
|
||||
<div className="flex items-center space-x-1 text-xs">
|
||||
<Calendar className="w-3 h-3 text-muted-foreground" />
|
||||
<span>{artist.experience} experience</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1 text-xs">
|
||||
<MapPin className="w-3 h-3 text-muted-foreground" />
|
||||
<span>{artist.location}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Styles */}
|
||||
<div className="mb-4">
|
||||
<p className="text-xs font-medium mb-1">Specializes in:</p>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{artist.styles.slice(0, 3).map((style) => (
|
||||
<Badge key={style} variant="outline" className="text-xs px-2 py-1">
|
||||
{style}
|
||||
</Badge>
|
||||
))}
|
||||
{artist.styles.length > 3 && (
|
||||
<Badge variant="outline" className="text-xs px-2 py-1">
|
||||
+{artist.styles.length - 3} more
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex space-x-2 mt-auto">
|
||||
<Button asChild className="flex-1 text-xs py-2">
|
||||
<Link href={`/artists/${artist.id}`}>View Portfolio</Link>
|
||||
</Button>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
className="flex-1 bg-white text-black !text-black border-gray-300 hover:bg-gray-50 hover:!text-black text-xs py-2"
|
||||
>
|
||||
<Link href={`/artists/${artist.id}/book`}>Book Now</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
{!isLoading && !error && (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filteredArtists.length === 0 ? (
|
||||
<div className="col-span-full text-center py-20">
|
||||
<p className="text-muted-foreground text-lg">No artists found matching your criteria.</p>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
filteredArtists.map((artist) => {
|
||||
// Get profile image (first portfolio image or placeholder)
|
||||
const profileImage = artist.portfolioImages.find(img => img.tags.includes('profile'))?.url ||
|
||||
artist.portfolioImages[0]?.url ||
|
||||
"/placeholder.svg"
|
||||
|
||||
return (
|
||||
<Card key={artist.id} className="group hover:shadow-xl transition-all duration-300 overflow-hidden">
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Artist Image */}
|
||||
<div className="relative w-full h-48 sm:h-56 overflow-hidden">
|
||||
<img
|
||||
src={profileImage}
|
||||
alt={artist.name}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
<div className="absolute top-3 left-3">
|
||||
<Badge variant={artist.isActive ? "default" : "secondary"}>
|
||||
{artist.isActive ? "Available" : "Unavailable"}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Artist Info */}
|
||||
<CardContent className="p-4 flex-grow flex flex-col">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
<h3 className="font-playfair text-xl font-bold mb-1">{artist.name}</h3>
|
||||
<p className="text-primary font-medium text-sm">
|
||||
{artist.specialties.slice(0, 2).join(", ")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground mb-4 text-xs leading-relaxed line-clamp-3">{artist.bio}</p>
|
||||
|
||||
{/* Hourly Rate */}
|
||||
{artist.hourlyRate && (
|
||||
<div className="mb-4">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Starting at <span className="font-semibold text-foreground">${artist.hourlyRate}/hr</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Styles */}
|
||||
<div className="mb-4">
|
||||
<p className="text-xs font-medium mb-1">Specializes in:</p>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{artist.specialties.slice(0, 3).map((style) => (
|
||||
<Badge key={style} variant="outline" className="text-xs px-2 py-1">
|
||||
{style}
|
||||
</Badge>
|
||||
))}
|
||||
{artist.specialties.length > 3 && (
|
||||
<Badge variant="outline" className="text-xs px-2 py-1">
|
||||
+{artist.specialties.length - 3} more
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex space-x-2 mt-auto">
|
||||
<Button asChild className="flex-1 text-xs py-2">
|
||||
<Link href={`/artists/${artist.slug}`}>View Portfolio</Link>
|
||||
</Button>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
className="flex-1 bg-white text-black !text-black border-gray-300 hover:bg-gray-50 hover:!text-black text-xs py-2"
|
||||
>
|
||||
<Link href={`/book?artist=${artist.slug}`}>Book Now</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
2
dist/README.md
vendored
2
dist/README.md
vendored
@ -1 +1 @@
|
||||
This folder contains the built output assets for the worker "united-tattoo" generated at 2025-09-26T06:30:46.424Z.
|
||||
This folder contains the built output assets for the worker "united-tattoo" generated at 2025-10-06T09:30:19.755Z.
|
||||
170
hooks/use-artist-data.ts
Normal file
170
hooks/use-artist-data.ts
Normal file
@ -0,0 +1,170 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import type { PublicArtist, ArtistWithPortfolio, Artist } from '@/types/database'
|
||||
|
||||
// Query keys for cache management
|
||||
export const artistKeys = {
|
||||
all: ['artists'] as const,
|
||||
lists: () => [...artistKeys.all, 'list'] as const,
|
||||
list: (filters?: Record<string, any>) => [...artistKeys.lists(), filters] as const,
|
||||
details: () => [...artistKeys.all, 'detail'] as const,
|
||||
detail: (id: string) => [...artistKeys.details(), id] as const,
|
||||
me: () => [...artistKeys.all, 'me'] as const,
|
||||
}
|
||||
|
||||
// Fetch all artists
|
||||
export function useArtists(filters?: {
|
||||
specialty?: string
|
||||
search?: string
|
||||
limit?: number
|
||||
page?: number
|
||||
}) {
|
||||
return useQuery({
|
||||
queryKey: artistKeys.list(filters),
|
||||
queryFn: async () => {
|
||||
const params = new URLSearchParams()
|
||||
if (filters?.specialty) params.append('specialty', filters.specialty)
|
||||
if (filters?.search) params.append('search', filters.search)
|
||||
if (filters?.limit) params.append('limit', filters.limit.toString())
|
||||
if (filters?.page) params.append('page', filters.page.toString())
|
||||
|
||||
const response = await fetch(`/api/artists?${params.toString()}`)
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch artists')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return data.artists as PublicArtist[]
|
||||
},
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
})
|
||||
}
|
||||
|
||||
// Fetch single artist by ID or slug
|
||||
export function useArtist(id: string | undefined) {
|
||||
return useQuery({
|
||||
queryKey: artistKeys.detail(id || ''),
|
||||
queryFn: async () => {
|
||||
if (!id) return null
|
||||
|
||||
const response = await fetch(`/api/artists/${id}`)
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) return null
|
||||
throw new Error('Failed to fetch artist')
|
||||
}
|
||||
|
||||
return response.json() as Promise<ArtistWithPortfolio>
|
||||
},
|
||||
enabled: !!id,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
})
|
||||
}
|
||||
|
||||
// Fetch current artist (for artist dashboard)
|
||||
export function useCurrentArtist() {
|
||||
return useQuery({
|
||||
queryKey: artistKeys.me(),
|
||||
queryFn: async () => {
|
||||
const response = await fetch('/api/artists/me')
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
return null
|
||||
}
|
||||
throw new Error('Failed to fetch artist profile')
|
||||
}
|
||||
|
||||
return response.json() as Promise<Artist>
|
||||
},
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
retry: false, // Don't retry on auth errors
|
||||
})
|
||||
}
|
||||
|
||||
// Update artist mutation
|
||||
export function useUpdateArtist() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ id, data }: { id: string; data: Partial<Artist> }) => {
|
||||
const response = await fetch(`/api/artists/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.error || 'Failed to update artist')
|
||||
}
|
||||
|
||||
return response.json() as Promise<Artist>
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
// Invalidate and refetch
|
||||
queryClient.invalidateQueries({ queryKey: artistKeys.detail(variables.id) })
|
||||
queryClient.invalidateQueries({ queryKey: artistKeys.lists() })
|
||||
queryClient.invalidateQueries({ queryKey: artistKeys.me() })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Create artist mutation (admin only)
|
||||
export function useCreateArtist() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (data: {
|
||||
name: string
|
||||
bio: string
|
||||
specialties: string[]
|
||||
instagramHandle?: string
|
||||
hourlyRate?: number
|
||||
email?: string
|
||||
}) => {
|
||||
const response = await fetch('/api/artists', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.error || 'Failed to create artist')
|
||||
}
|
||||
|
||||
return response.json() as Promise<Artist>
|
||||
},
|
||||
onSuccess: () => {
|
||||
// Invalidate artists list
|
||||
queryClient.invalidateQueries({ queryKey: artistKeys.lists() })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Delete artist mutation (admin only)
|
||||
export function useDeleteArtist() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (id: string) => {
|
||||
const response = await fetch(`/api/artists/${id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.error || 'Failed to delete artist')
|
||||
}
|
||||
|
||||
return response.json()
|
||||
},
|
||||
onSuccess: (_, id) => {
|
||||
// Invalidate queries
|
||||
queryClient.invalidateQueries({ queryKey: artistKeys.lists() })
|
||||
queryClient.removeQueries({ queryKey: artistKeys.detail(id) })
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -81,16 +81,20 @@ export class DataMigrator {
|
||||
// Extract hourly rate from experience or set default
|
||||
const hourlyRate = this.extractHourlyRate(artist.experience);
|
||||
|
||||
// Generate slug from artist name or use existing slug
|
||||
const slug = artist.slug || this.generateSlug(artist.name);
|
||||
|
||||
try {
|
||||
await this.db.prepare(`
|
||||
INSERT OR IGNORE INTO artists (
|
||||
id, user_id, name, bio, specialties, instagram_handle,
|
||||
id, user_id, slug, name, bio, specialties, instagram_handle,
|
||||
hourly_rate, is_active, created_at, updated_at
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
`).bind(
|
||||
artistId,
|
||||
userId,
|
||||
slug,
|
||||
artist.name,
|
||||
artist.bio,
|
||||
JSON.stringify(specialties),
|
||||
@ -98,7 +102,7 @@ export class DataMigrator {
|
||||
hourlyRate,
|
||||
).run();
|
||||
|
||||
console.log(`Created artist record: ${artist.name}`);
|
||||
console.log(`Created artist record: ${artist.name} (slug: ${slug})`);
|
||||
} catch (error) {
|
||||
console.error(`Error creating artist record for ${artist.name}:`, error);
|
||||
throw error;
|
||||
@ -167,6 +171,17 @@ export class DataMigrator {
|
||||
console.log(`Created portfolio images for: ${artist.name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate URL-friendly slug from artist name
|
||||
*/
|
||||
private generateSlug(name: string): string {
|
||||
return name
|
||||
.toLowerCase()
|
||||
.replace(/['']/g, '') // Remove apostrophes
|
||||
.replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with hyphens
|
||||
.replace(/^-+|-+$/g, ''); // Trim hyphens from ends
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract Instagram handle from full URL
|
||||
*/
|
||||
|
||||
192
lib/db.ts
192
lib/db.ts
@ -53,7 +53,197 @@ export async function getArtists(env?: any): Promise<Artist[]> {
|
||||
ORDER BY a.created_at DESC
|
||||
`).all();
|
||||
|
||||
return result.results as Artist[];
|
||||
// Parse JSON fields
|
||||
return (result.results as any[]).map(artist => ({
|
||||
...artist,
|
||||
specialties: artist.specialties ? JSON.parse(artist.specialties) : [],
|
||||
portfolioImages: []
|
||||
}));
|
||||
}
|
||||
|
||||
export async function getPublicArtists(filters?: import('@/types/database').ArtistFilters, env?: any): Promise<import('@/types/database').PublicArtist[]> {
|
||||
const db = getDB(env);
|
||||
|
||||
let query = `
|
||||
SELECT
|
||||
a.id,
|
||||
a.slug,
|
||||
a.name,
|
||||
a.bio,
|
||||
a.specialties,
|
||||
a.instagram_handle,
|
||||
a.is_active,
|
||||
a.hourly_rate
|
||||
FROM artists a
|
||||
WHERE a.is_active = 1
|
||||
`;
|
||||
|
||||
const values: any[] = [];
|
||||
|
||||
if (filters?.specialty) {
|
||||
query += ` AND a.specialties LIKE ?`;
|
||||
values.push(`%${filters.specialty}%`);
|
||||
}
|
||||
|
||||
if (filters?.search) {
|
||||
query += ` AND (a.name LIKE ? OR a.bio LIKE ?)`;
|
||||
values.push(`%${filters.search}%`, `%${filters.search}%`);
|
||||
}
|
||||
|
||||
query += ` ORDER BY a.created_at DESC`;
|
||||
|
||||
if (filters?.limit) {
|
||||
query += ` LIMIT ?`;
|
||||
values.push(filters.limit);
|
||||
}
|
||||
|
||||
if (filters?.offset) {
|
||||
query += ` OFFSET ?`;
|
||||
values.push(filters.offset);
|
||||
}
|
||||
|
||||
const result = await db.prepare(query).bind(...values).all();
|
||||
|
||||
// Fetch portfolio images for each artist
|
||||
const artistsWithPortfolio = await Promise.all(
|
||||
(result.results as any[]).map(async (artist) => {
|
||||
const portfolioResult = await db.prepare(`
|
||||
SELECT * FROM portfolio_images
|
||||
WHERE artist_id = ? AND is_public = 1
|
||||
ORDER BY order_index ASC, created_at DESC
|
||||
`).bind(artist.id).all();
|
||||
|
||||
return {
|
||||
id: artist.id,
|
||||
slug: artist.slug,
|
||||
name: artist.name,
|
||||
bio: artist.bio,
|
||||
specialties: artist.specialties ? JSON.parse(artist.specialties) : [],
|
||||
instagramHandle: artist.instagram_handle,
|
||||
isActive: Boolean(artist.is_active),
|
||||
hourlyRate: artist.hourly_rate,
|
||||
portfolioImages: (portfolioResult.results as any[]).map(img => ({
|
||||
id: img.id,
|
||||
artistId: img.artist_id,
|
||||
url: img.url,
|
||||
caption: img.caption,
|
||||
tags: img.tags ? JSON.parse(img.tags) : [],
|
||||
orderIndex: img.order_index,
|
||||
isPublic: Boolean(img.is_public),
|
||||
createdAt: new Date(img.created_at)
|
||||
}))
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return artistsWithPortfolio;
|
||||
}
|
||||
|
||||
export async function getArtistWithPortfolio(id: string, env?: any): Promise<import('@/types/database').ArtistWithPortfolio | null> {
|
||||
const db = getDB(env);
|
||||
|
||||
const artistResult = await db.prepare(`
|
||||
SELECT
|
||||
a.*,
|
||||
u.name as user_name,
|
||||
u.email as user_email,
|
||||
u.avatar as user_avatar
|
||||
FROM artists a
|
||||
LEFT JOIN users u ON a.user_id = u.id
|
||||
WHERE a.id = ?
|
||||
`).bind(id).first();
|
||||
|
||||
if (!artistResult) return null;
|
||||
|
||||
const portfolioResult = await db.prepare(`
|
||||
SELECT * FROM portfolio_images
|
||||
WHERE artist_id = ?
|
||||
ORDER BY order_index ASC, created_at DESC
|
||||
`).bind(id).all();
|
||||
|
||||
const artist = artistResult as any;
|
||||
|
||||
return {
|
||||
id: artist.id,
|
||||
userId: artist.user_id,
|
||||
slug: artist.slug,
|
||||
name: artist.name,
|
||||
bio: artist.bio,
|
||||
specialties: artist.specialties ? JSON.parse(artist.specialties) : [],
|
||||
instagramHandle: artist.instagram_handle,
|
||||
isActive: Boolean(artist.is_active),
|
||||
hourlyRate: artist.hourly_rate,
|
||||
portfolioImages: (portfolioResult.results as any[]).map(img => ({
|
||||
id: img.id,
|
||||
artistId: img.artist_id,
|
||||
url: img.url,
|
||||
caption: img.caption,
|
||||
tags: img.tags ? JSON.parse(img.tags) : [],
|
||||
orderIndex: img.order_index,
|
||||
isPublic: Boolean(img.is_public),
|
||||
createdAt: new Date(img.created_at)
|
||||
})),
|
||||
availability: [],
|
||||
createdAt: new Date(artist.created_at),
|
||||
updatedAt: new Date(artist.updated_at),
|
||||
user: {
|
||||
name: artist.user_name,
|
||||
email: artist.user_email,
|
||||
avatar: artist.user_avatar
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export async function getArtistBySlug(slug: string, env?: any): Promise<import('@/types/database').ArtistWithPortfolio | null> {
|
||||
const db = getDB(env);
|
||||
|
||||
const artistResult = await db.prepare(`
|
||||
SELECT
|
||||
a.*,
|
||||
u.name as user_name,
|
||||
u.email as user_email,
|
||||
u.avatar as user_avatar
|
||||
FROM artists a
|
||||
LEFT JOIN users u ON a.user_id = u.id
|
||||
WHERE a.slug = ?
|
||||
`).bind(slug).first();
|
||||
|
||||
if (!artistResult) return null;
|
||||
|
||||
const artist = artistResult as any;
|
||||
return getArtistWithPortfolio(artist.id, env);
|
||||
}
|
||||
|
||||
export async function getArtistByUserId(userId: string, env?: any): Promise<Artist | null> {
|
||||
const db = getDB(env);
|
||||
const result = await db.prepare(`
|
||||
SELECT
|
||||
a.*,
|
||||
u.name as user_name,
|
||||
u.email as user_email
|
||||
FROM artists a
|
||||
LEFT JOIN users u ON a.user_id = u.id
|
||||
WHERE a.user_id = ?
|
||||
`).bind(userId).first();
|
||||
|
||||
if (!result) return null;
|
||||
|
||||
const artist = result as any;
|
||||
return {
|
||||
id: artist.id,
|
||||
userId: artist.user_id,
|
||||
slug: artist.slug,
|
||||
name: artist.name,
|
||||
bio: artist.bio,
|
||||
specialties: artist.specialties ? JSON.parse(artist.specialties) : [],
|
||||
instagramHandle: artist.instagram_handle,
|
||||
isActive: Boolean(artist.is_active),
|
||||
hourlyRate: artist.hourly_rate,
|
||||
portfolioImages: [],
|
||||
availability: [],
|
||||
createdAt: new Date(artist.created_at),
|
||||
updatedAt: new Date(artist.updated_at)
|
||||
};
|
||||
}
|
||||
|
||||
export async function getArtist(id: string, env?: any): Promise<Artist | null> {
|
||||
|
||||
10
sql/migrations/0001_add_artist_slug.sql
Normal file
10
sql/migrations/0001_add_artist_slug.sql
Normal file
@ -0,0 +1,10 @@
|
||||
-- Add slug column to artists table for SEO-friendly URLs
|
||||
-- Run this with: wrangler d1 execute united-tattoo-db --file=./sql/migrations/0001_add_artist_slug.sql
|
||||
|
||||
-- Add slug column
|
||||
ALTER TABLE artists ADD COLUMN slug TEXT;
|
||||
|
||||
-- Create unique index on slug
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_artists_slug ON artists(slug);
|
||||
|
||||
-- Note: Existing artists will need slugs populated via migration script
|
||||
@ -23,5 +23,5 @@
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"exclude": ["types/node_modules"]
|
||||
}
|
||||
|
||||
@ -134,6 +134,7 @@ export enum UserRole {
|
||||
export interface Artist {
|
||||
id: string
|
||||
userId: string
|
||||
slug: string
|
||||
name: string
|
||||
bio: string
|
||||
specialties: string[]
|
||||
@ -146,6 +147,42 @@ export interface Artist {
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export interface ArtistWithPortfolio extends Artist {
|
||||
portfolioImages: PortfolioImage[]
|
||||
user?: {
|
||||
name: string
|
||||
email: string
|
||||
avatar?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface PublicArtist {
|
||||
id: string
|
||||
slug: string
|
||||
name: string
|
||||
bio: string
|
||||
specialties: string[]
|
||||
instagramHandle?: string
|
||||
portfolioImages: PortfolioImage[]
|
||||
isActive: boolean
|
||||
hourlyRate?: number
|
||||
}
|
||||
|
||||
export interface ArtistDashboardStats {
|
||||
totalImages: number
|
||||
activeImages: number
|
||||
profileViews?: number
|
||||
lastUpdated: Date
|
||||
}
|
||||
|
||||
export interface ArtistFilters {
|
||||
specialty?: string
|
||||
search?: string
|
||||
isActive?: boolean
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export interface PortfolioImage {
|
||||
id: string
|
||||
artistId: string
|
||||
|
||||
1
types/node_modules/.bin/acorn
generated
vendored
Symbolic link
1
types/node_modules/.bin/acorn
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../acorn/bin/acorn
|
||||
1
types/node_modules/.bin/autoprefixer
generated
vendored
Symbolic link
1
types/node_modules/.bin/autoprefixer
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../autoprefixer/bin/autoprefixer
|
||||
1
types/node_modules/.bin/baseline-browser-mapping
generated
vendored
Symbolic link
1
types/node_modules/.bin/baseline-browser-mapping
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../baseline-browser-mapping/dist/cli.js
|
||||
1
types/node_modules/.bin/browserslist
generated
vendored
Symbolic link
1
types/node_modules/.bin/browserslist
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../browserslist/cli.js
|
||||
1
types/node_modules/.bin/dotenvx
generated
vendored
Symbolic link
1
types/node_modules/.bin/dotenvx
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../@dotenvx/dotenvx/src/cli/dotenvx.js
|
||||
1
types/node_modules/.bin/esbuild
generated
vendored
Symbolic link
1
types/node_modules/.bin/esbuild
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../esbuild/bin/esbuild
|
||||
1
types/node_modules/.bin/eslint
generated
vendored
Symbolic link
1
types/node_modules/.bin/eslint
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../eslint/bin/eslint.js
|
||||
1
types/node_modules/.bin/fxparser
generated
vendored
Symbolic link
1
types/node_modules/.bin/fxparser
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../fast-xml-parser/src/cli/cli.js
|
||||
1
types/node_modules/.bin/git-dotenvx
generated
vendored
Symbolic link
1
types/node_modules/.bin/git-dotenvx
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../@dotenvx/dotenvx/src/cli/dotenvx.js
|
||||
1
types/node_modules/.bin/glob
generated
vendored
Symbolic link
1
types/node_modules/.bin/glob
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../glob/dist/esm/bin.mjs
|
||||
1
types/node_modules/.bin/jiti
generated
vendored
Symbolic link
1
types/node_modules/.bin/jiti
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../jiti/lib/jiti-cli.mjs
|
||||
1
types/node_modules/.bin/js-yaml
generated
vendored
Symbolic link
1
types/node_modules/.bin/js-yaml
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../js-yaml/bin/js-yaml.js
|
||||
1
types/node_modules/.bin/jsesc
generated
vendored
Symbolic link
1
types/node_modules/.bin/jsesc
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../jsesc/bin/jsesc
|
||||
1
types/node_modules/.bin/json5
generated
vendored
Symbolic link
1
types/node_modules/.bin/json5
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../json5/lib/cli.js
|
||||
1
types/node_modules/.bin/loose-envify
generated
vendored
Symbolic link
1
types/node_modules/.bin/loose-envify
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../loose-envify/cli.js
|
||||
1
types/node_modules/.bin/lz-string
generated
vendored
Symbolic link
1
types/node_modules/.bin/lz-string
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../lz-string/bin/bin.js
|
||||
1
types/node_modules/.bin/mime
generated
vendored
Symbolic link
1
types/node_modules/.bin/mime
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../mime/cli.js
|
||||
1
types/node_modules/.bin/mkdirp
generated
vendored
Symbolic link
1
types/node_modules/.bin/mkdirp
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../mkdirp/dist/cjs/src/bin.js
|
||||
1
types/node_modules/.bin/nanoid
generated
vendored
Symbolic link
1
types/node_modules/.bin/nanoid
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../nanoid/bin/nanoid.cjs
|
||||
1
types/node_modules/.bin/napi-postinstall
generated
vendored
Symbolic link
1
types/node_modules/.bin/napi-postinstall
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../napi-postinstall/lib/cli.js
|
||||
1
types/node_modules/.bin/next
generated
vendored
Symbolic link
1
types/node_modules/.bin/next
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../next/dist/bin/next
|
||||
1
types/node_modules/.bin/node-which
generated
vendored
Symbolic link
1
types/node_modules/.bin/node-which
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../which/bin/which.js
|
||||
1
types/node_modules/.bin/open-next
generated
vendored
Symbolic link
1
types/node_modules/.bin/open-next
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../@opennextjs/aws/dist/index.js
|
||||
1
types/node_modules/.bin/opennextjs-cloudflare
generated
vendored
Symbolic link
1
types/node_modules/.bin/opennextjs-cloudflare
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../@opennextjs/cloudflare/dist/cli/index.js
|
||||
1
types/node_modules/.bin/parser
generated
vendored
Symbolic link
1
types/node_modules/.bin/parser
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../@babel/parser/bin/babel-parser.js
|
||||
1
types/node_modules/.bin/resolve
generated
vendored
Symbolic link
1
types/node_modules/.bin/resolve
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../resolve/bin/resolve
|
||||
1
types/node_modules/.bin/rimraf
generated
vendored
Symbolic link
1
types/node_modules/.bin/rimraf
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../rimraf/bin.js
|
||||
1
types/node_modules/.bin/rollup
generated
vendored
Symbolic link
1
types/node_modules/.bin/rollup
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../rollup/dist/bin/rollup
|
||||
1
types/node_modules/.bin/semver
generated
vendored
Symbolic link
1
types/node_modules/.bin/semver
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../semver/bin/semver.js
|
||||
1
types/node_modules/.bin/terser
generated
vendored
Symbolic link
1
types/node_modules/.bin/terser
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../terser/bin/terser
|
||||
1
types/node_modules/.bin/tldts
generated
vendored
Symbolic link
1
types/node_modules/.bin/tldts
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../tldts/bin/cli.js
|
||||
1
types/node_modules/.bin/tsc
generated
vendored
Symbolic link
1
types/node_modules/.bin/tsc
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../typescript/bin/tsc
|
||||
1
types/node_modules/.bin/tsserver
generated
vendored
Symbolic link
1
types/node_modules/.bin/tsserver
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../typescript/bin/tsserver
|
||||
1
types/node_modules/.bin/update-browserslist-db
generated
vendored
Symbolic link
1
types/node_modules/.bin/update-browserslist-db
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../update-browserslist-db/cli.js
|
||||
1
types/node_modules/.bin/uuid
generated
vendored
Symbolic link
1
types/node_modules/.bin/uuid
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../uuid/dist/bin/uuid
|
||||
1
types/node_modules/.bin/vite
generated
vendored
Symbolic link
1
types/node_modules/.bin/vite
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../vite/bin/vite.js
|
||||
1
types/node_modules/.bin/vite-node
generated
vendored
Symbolic link
1
types/node_modules/.bin/vite-node
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../vite-node/vite-node.mjs
|
||||
1
types/node_modules/.bin/vitest
generated
vendored
Symbolic link
1
types/node_modules/.bin/vitest
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../vitest/vitest.mjs
|
||||
1
types/node_modules/.bin/why-is-node-running
generated
vendored
Symbolic link
1
types/node_modules/.bin/why-is-node-running
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../why-is-node-running/cli.js
|
||||
1
types/node_modules/.bin/wrangler
generated
vendored
Symbolic link
1
types/node_modules/.bin/wrangler
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../wrangler/bin/wrangler.js
|
||||
1
types/node_modules/.bin/wrangler2
generated
vendored
Symbolic link
1
types/node_modules/.bin/wrangler2
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../wrangler/bin/wrangler.js
|
||||
1
types/node_modules/.bin/yaml
generated
vendored
Symbolic link
1
types/node_modules/.bin/yaml
generated
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../yaml/bin.mjs
|
||||
6
types/node_modules/.cache/wrangler/wrangler-account.json
generated
vendored
Normal file
6
types/node_modules/.cache/wrangler/wrangler-account.json
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"account": {
|
||||
"id": "a19f770b9be1b20e78b8d25bdcfd3bbd",
|
||||
"name": "Nicholaivogelfilms@gmail.com's Account"
|
||||
}
|
||||
}
|
||||
1
types/node_modules/.mf/cf.json
generated
vendored
Normal file
1
types/node_modules/.mf/cf.json
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"clientTcpRtt":6,"requestHeaderNames":{},"httpProtocol":"HTTP/1.1","tlsCipher":"AEAD-AES256-GCM-SHA384","continent":"NA","asn":7922,"clientAcceptEncoding":"gzip, deflate, br","verifiedBotCategory":"","country":"US","isEUCountry":false,"region":"Colorado","tlsClientCiphersSha1":"kXrN3VEKDdzz2cPKTQaKzpxVTxQ=","tlsClientAuth":{"certIssuerDNLegacy":"","certIssuerSKI":"","certSubjectDNRFC2253":"","certSubjectDNLegacy":"","certFingerprintSHA256":"","certNotBefore":"","certSKI":"","certSerial":"","certIssuerDN":"","certVerified":"NONE","certNotAfter":"","certSubjectDN":"","certPresented":"0","certRevoked":"0","certIssuerSerial":"","certIssuerDNRFC2253":"","certFingerprintSHA1":""},"tlsClientRandom":"P4paSe6QQhwWPcwdz5sOsab19aR5oTs5s9fB8Se0rqs=","tlsExportedAuthenticator":{"clientFinished":"173f81a84eec71ce93d5a53cc540c0ed9e42e1d5fc286928b9d4290c878f16912d073a3442ceb7b5d59c5cbb88e42e1b","clientHandshake":"fb92bbe13b9334f270f91a80ed237a0b99bb76b80b9ce3635a40580b30f6884bfc52db62ea5a0fbbd2a2faa70458b8c2","serverHandshake":"ec8492cacd25ea6eda1540e01279947f7991edf516fc05a80b3e38f10da4cd9fadc844df6ad927f9a1926d8210840df7","serverFinished":"39789ad952f879526452f177639c71bf0e8860d20351d3fa65b6330507d514da88fbab17a570fd13641534e7f2bfe794"},"tlsClientHelloLength":"1605","colo":"DEN","timezone":"America/Denver","longitude":"-104.82136","latitude":"38.83388","edgeRequestKeepAliveStatus":1,"requestPriority":"","postalCode":"80901","city":"Colorado Springs","tlsVersion":"TLSv1.3","regionCode":"CO","asOrganization":"Comcast Cable Communications, LLC","tlsClientExtensionsSha1Le":"u4wtEMFQBY18l3BzHAvORm+KGRw=","tlsClientExtensionsSha1":"1eY97BUYYO8vDaTfHQywB1pcNdM=","botManagement":{"corporateProxy":false,"verifiedBot":false,"jsDetection":{"passed":false},"staticResource":false,"detectionIds":{},"score":99}}
|
||||
23276
types/node_modules/.package-lock.json
generated
vendored
Normal file
23276
types/node_modules/.package-lock.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
types/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json
generated
vendored
Normal file
1
types/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":"3.2.4","results":[[":__tests__/lib/data-migration.test.ts",{"duration":105.39226800000006,"failed":true}],[":__tests__/lib/validations.test.ts",{"duration":36.10687199999984,"failed":true}],[":__tests__/flags/booking-form.disabled.test.tsx",{"duration":0,"failed":true}],[":__tests__/flags/api-uploads-disabled.test.ts",{"duration":145.70001000000002,"failed":false}],[":__tests__/flags/booking-form.disabled.test.ts",{"duration":0,"failed":true}],[":__tests__/lib/flags.test.ts",{"duration":31.015207999999802,"failed":true}],[":__tests__/flags/api-appointments-booking-disabled.test.ts",{"duration":112.77163099999984,"failed":false}],[":__tests__/flags/artists-section.static.test.tsx",{"duration":40.843513999999914,"failed":false}],[":__tests__/components/privacy-page.test.tsx",{"duration":379.49771699999974,"failed":false}],[":__tests__/components/aftercare-page.test.tsx",{"duration":1029.370418,"failed":false}],[":__tests__/hooks/use-parallax.test.tsx",{"duration":191.06769999999983,"failed":false}],[":__tests__/components/hero-section.test.tsx",{"duration":484.19912999999974,"failed":false}],[":__tests__/components/artist-portfolio.test.tsx",{"duration":830.036141,"failed":false}]]}
|
||||
10
types/node_modules/@adobe/css-tools/LICENSE
generated
vendored
Normal file
10
types/node_modules/@adobe/css-tools/LICENSE
generated
vendored
Normal 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.
|
||||
146
types/node_modules/@adobe/css-tools/README.md
generated
vendored
Normal file
146
types/node_modules/@adobe/css-tools/README.md
generated
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
# @adobe/css-tools
|
||||
|
||||
> A modern CSS parser and stringifier with TypeScript support
|
||||
|
||||
[](https://badge.fury.io/js/%40adobe%2Fcss-tools)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
Parse CSS into an Abstract Syntax Tree (AST) and convert it back to CSS with configurable formatting. Built with TypeScript for type safety and modern JavaScript features.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install @adobe/css-tools
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { parse, stringify } from '@adobe/css-tools'
|
||||
|
||||
// Parse CSS to AST
|
||||
const ast = parse('body { font-size: 12px; }')
|
||||
|
||||
// Stringify AST back to CSS
|
||||
const css = stringify(ast)
|
||||
// => "body { font-size: 12px; }"
|
||||
|
||||
// Pretty print with custom indentation
|
||||
const formatted = stringify(ast, { indent: ' ' })
|
||||
// => "body {\n font-size: 12px;\n}"
|
||||
|
||||
// Minify output
|
||||
const minified = stringify(ast, { compress: true })
|
||||
// => "body{font-size:12px}"
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `parse(code, options?)`
|
||||
|
||||
Parses CSS code and returns an Abstract Syntax Tree (AST).
|
||||
|
||||
**Parameters:**
|
||||
- `code` (string) - The CSS code to parse
|
||||
- `options` (object, optional) - Parsing options
|
||||
- `silent` (boolean) - Silently fail on parse errors instead of throwing
|
||||
- `source` (string) - File path for better error reporting
|
||||
|
||||
**Returns:** `CssStylesheetAST` - The parsed CSS as an AST
|
||||
|
||||
### `stringify(ast, options?)`
|
||||
|
||||
Converts a CSS AST back to CSS string with configurable formatting.
|
||||
|
||||
**Parameters:**
|
||||
- `ast` (CssStylesheetAST) - The CSS AST to stringify
|
||||
- `options` (object, optional) - Stringification options
|
||||
- `indent` (string) - Indentation string (default: `' '`)
|
||||
- `compress` (boolean) - Whether to compress/minify the output (default: `false`)
|
||||
|
||||
**Returns:** `string` - The formatted CSS string
|
||||
|
||||
## Features
|
||||
|
||||
- **Complete CSS Support**: All standard CSS features including selectors, properties, values, at-rules, and comments
|
||||
- **TypeScript Support**: Full type definitions for all AST nodes and functions
|
||||
- **Error Handling**: Configurable error handling with detailed position information
|
||||
- **Formatting Options**: Pretty print, minify, or custom formatting
|
||||
- **Performance Optimized**: Efficient parsing and stringification for large CSS files
|
||||
- **Source Maps**: Track original source positions for debugging and tooling
|
||||
|
||||
### Supported CSS Features
|
||||
|
||||
- **Selectors**: Element, class, ID, attribute, pseudo-class, pseudo-element selectors
|
||||
- **Properties**: All standard CSS properties and custom properties
|
||||
- **Values**: Colors, lengths, percentages, functions, calc(), etc.
|
||||
- **At-rules**: @media, @keyframes, @import, @charset, @namespace, @font-face, @page, @document, @supports, @container, @layer, @starting-style, @host, @custom-media
|
||||
- **Comments**: Both /* */ and // comments
|
||||
- **Whitespace**: Preserves formatting information
|
||||
- **Vendor prefixes**: Supports vendor-prefixed at-rules and properties
|
||||
- **Nested rules**: Media queries, supports, containers, etc.
|
||||
- **Complex selectors**: Combinators, pseudo-selectors, attribute selectors
|
||||
|
||||
## Examples
|
||||
|
||||
### Error Handling
|
||||
|
||||
```js
|
||||
import { parse } from '@adobe/css-tools'
|
||||
|
||||
const malformedCss = `
|
||||
body { color: red; }
|
||||
{ color: blue; } /* Missing selector */
|
||||
.valid { background: green; }
|
||||
`
|
||||
|
||||
// Parse with silent error handling
|
||||
const result = parse(malformedCss, { silent: true })
|
||||
|
||||
// Check for parsing errors
|
||||
if (result.stylesheet.parsingErrors) {
|
||||
console.log('Parsing errors:', result.stylesheet.parsingErrors.length)
|
||||
result.stylesheet.parsingErrors.forEach(error => {
|
||||
console.log(`Error at line ${error.line}: ${error.message}`)
|
||||
})
|
||||
}
|
||||
|
||||
// Valid rules are still parsed
|
||||
console.log('Valid rules:', result.stylesheet.rules.length)
|
||||
```
|
||||
|
||||
### Source Tracking
|
||||
|
||||
```js
|
||||
import { parse } from '@adobe/css-tools'
|
||||
|
||||
const css = 'body { color: red; }'
|
||||
const ast = parse(css, { source: 'styles.css' })
|
||||
|
||||
// Position information is available
|
||||
const rule = ast.stylesheet.rules[0]
|
||||
console.log(rule.position?.source) // "styles.css"
|
||||
console.log(rule.position?.start) // { line: 1, column: 1 }
|
||||
console.log(rule.position?.end) // { line: 1, column: 20 }
|
||||
```
|
||||
|
||||
For more examples, see the [Examples documentation](docs/EXAMPLES.md).
|
||||
|
||||
## Performance
|
||||
|
||||
The library is optimized for performance and can handle large CSS files efficiently. For benchmarking information, see the `benchmark/` directory in the source code.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [API Reference](docs/API.md) - Complete API documentation
|
||||
- [AST Structure](docs/AST.md) - Detailed AST node types and structure
|
||||
- [Examples](docs/EXAMPLES.md) - Comprehensive usage examples
|
||||
- [Changelog](docs/CHANGELOG.md) - Version history and changes
|
||||
|
||||
## Background
|
||||
|
||||
This is a fork of the npm `css` package, maintained by Adobe with modern improvements including TypeScript support, enhanced performance, and security updates. It provides a robust foundation for CSS tooling, preprocessing, and analysis.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
2
types/node_modules/@adobe/css-tools/dist/cjs/adobe-css-tools.cjs
generated
vendored
Normal file
2
types/node_modules/@adobe/css-tools/dist/cjs/adobe-css-tools.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
types/node_modules/@adobe/css-tools/dist/cjs/adobe-css-tools.cjs.map
generated
vendored
Normal file
1
types/node_modules/@adobe/css-tools/dist/cjs/adobe-css-tools.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
176
types/node_modules/@adobe/css-tools/dist/cjs/adobe-css-tools.d.cts
generated
vendored
Normal file
176
types/node_modules/@adobe/css-tools/dist/cjs/adobe-css-tools.d.cts
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
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);
|
||||
}
|
||||
|
||||
declare 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"
|
||||
}
|
||||
type CssCommonAST = {
|
||||
type: CssTypes;
|
||||
};
|
||||
type CssCommonPositionAST = CssCommonAST & {
|
||||
position?: Position;
|
||||
parent?: unknown;
|
||||
};
|
||||
type CssStylesheetAST = CssCommonAST & {
|
||||
type: CssTypes.stylesheet;
|
||||
stylesheet: {
|
||||
source?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
parsingErrors?: Array<CssParseError>;
|
||||
};
|
||||
};
|
||||
type CssRuleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.rule;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssDeclarationAST = CssCommonPositionAST & {
|
||||
type: CssTypes.declaration;
|
||||
property: string;
|
||||
value: string;
|
||||
};
|
||||
type CssCommentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.comment;
|
||||
comment: string;
|
||||
};
|
||||
type CssContainerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.container;
|
||||
container: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssCharsetAST = CssCommonPositionAST & {
|
||||
type: CssTypes.charset;
|
||||
charset: string;
|
||||
};
|
||||
type CssCustomMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.customMedia;
|
||||
name: string;
|
||||
media: string;
|
||||
};
|
||||
type CssDocumentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.document;
|
||||
document: string;
|
||||
vendor?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssFontFaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.fontFace;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssHostAST = CssCommonPositionAST & {
|
||||
type: CssTypes.host;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssImportAST = CssCommonPositionAST & {
|
||||
type: CssTypes.import;
|
||||
import: string;
|
||||
};
|
||||
type CssKeyframesAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframes;
|
||||
name: string;
|
||||
vendor?: string;
|
||||
keyframes: Array<CssKeyframeAST | CssCommentAST>;
|
||||
};
|
||||
type CssKeyframeAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframe;
|
||||
values: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssLayerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.layer;
|
||||
layer: string;
|
||||
rules?: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.media;
|
||||
media: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssNamespaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.namespace;
|
||||
namespace: string;
|
||||
};
|
||||
type CssPageAST = CssCommonPositionAST & {
|
||||
type: CssTypes.page;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssSupportsAST = CssCommonPositionAST & {
|
||||
type: CssTypes.supports;
|
||||
supports: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssStartingStyleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.startingStyle;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssAtRuleAST = CssRuleAST | CssCommentAST | CssContainerAST | CssCharsetAST | CssCustomMediaAST | CssDocumentAST | CssFontFaceAST | CssHostAST | CssImportAST | CssKeyframesAST | CssLayerAST | CssMediaAST | CssNamespaceAST | CssPageAST | CssSupportsAST | CssStartingStyleAST;
|
||||
type CssAllNodesAST = CssAtRuleAST | CssStylesheetAST | CssDeclarationAST | CssKeyframeAST;
|
||||
|
||||
type CompilerOptions = {
|
||||
indent?: string;
|
||||
compress?: boolean;
|
||||
};
|
||||
|
||||
declare const parse: (css: string, options?: {
|
||||
source?: string;
|
||||
silent?: boolean;
|
||||
}) => CssStylesheetAST;
|
||||
declare 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 { CssTypes, _default as default, parse, stringify };
|
||||
export type { CssAllNodesAST, CssAtRuleAST, CssCharsetAST, CssCommentAST, CssCommonAST, CssCommonPositionAST, CssContainerAST, CssCustomMediaAST, CssDeclarationAST, CssDocumentAST, CssFontFaceAST, CssHostAST, CssImportAST, CssKeyframeAST, CssKeyframesAST, CssLayerAST, CssMediaAST, CssNamespaceAST, CssPageAST, CssRuleAST, CssStartingStyleAST, CssStylesheetAST, CssSupportsAST };
|
||||
176
types/node_modules/@adobe/css-tools/dist/esm/adobe-css-tools.d.mts
generated
vendored
Normal file
176
types/node_modules/@adobe/css-tools/dist/esm/adobe-css-tools.d.mts
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
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);
|
||||
}
|
||||
|
||||
declare 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"
|
||||
}
|
||||
type CssCommonAST = {
|
||||
type: CssTypes;
|
||||
};
|
||||
type CssCommonPositionAST = CssCommonAST & {
|
||||
position?: Position;
|
||||
parent?: unknown;
|
||||
};
|
||||
type CssStylesheetAST = CssCommonAST & {
|
||||
type: CssTypes.stylesheet;
|
||||
stylesheet: {
|
||||
source?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
parsingErrors?: Array<CssParseError>;
|
||||
};
|
||||
};
|
||||
type CssRuleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.rule;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssDeclarationAST = CssCommonPositionAST & {
|
||||
type: CssTypes.declaration;
|
||||
property: string;
|
||||
value: string;
|
||||
};
|
||||
type CssCommentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.comment;
|
||||
comment: string;
|
||||
};
|
||||
type CssContainerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.container;
|
||||
container: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssCharsetAST = CssCommonPositionAST & {
|
||||
type: CssTypes.charset;
|
||||
charset: string;
|
||||
};
|
||||
type CssCustomMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.customMedia;
|
||||
name: string;
|
||||
media: string;
|
||||
};
|
||||
type CssDocumentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.document;
|
||||
document: string;
|
||||
vendor?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssFontFaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.fontFace;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssHostAST = CssCommonPositionAST & {
|
||||
type: CssTypes.host;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssImportAST = CssCommonPositionAST & {
|
||||
type: CssTypes.import;
|
||||
import: string;
|
||||
};
|
||||
type CssKeyframesAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframes;
|
||||
name: string;
|
||||
vendor?: string;
|
||||
keyframes: Array<CssKeyframeAST | CssCommentAST>;
|
||||
};
|
||||
type CssKeyframeAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframe;
|
||||
values: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssLayerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.layer;
|
||||
layer: string;
|
||||
rules?: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.media;
|
||||
media: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssNamespaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.namespace;
|
||||
namespace: string;
|
||||
};
|
||||
type CssPageAST = CssCommonPositionAST & {
|
||||
type: CssTypes.page;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssSupportsAST = CssCommonPositionAST & {
|
||||
type: CssTypes.supports;
|
||||
supports: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssStartingStyleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.startingStyle;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssAtRuleAST = CssRuleAST | CssCommentAST | CssContainerAST | CssCharsetAST | CssCustomMediaAST | CssDocumentAST | CssFontFaceAST | CssHostAST | CssImportAST | CssKeyframesAST | CssLayerAST | CssMediaAST | CssNamespaceAST | CssPageAST | CssSupportsAST | CssStartingStyleAST;
|
||||
type CssAllNodesAST = CssAtRuleAST | CssStylesheetAST | CssDeclarationAST | CssKeyframeAST;
|
||||
|
||||
type CompilerOptions = {
|
||||
indent?: string;
|
||||
compress?: boolean;
|
||||
};
|
||||
|
||||
declare const parse: (css: string, options?: {
|
||||
source?: string;
|
||||
silent?: boolean;
|
||||
}) => CssStylesheetAST;
|
||||
declare 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 { CssTypes, _default as default, parse, stringify };
|
||||
export type { CssAllNodesAST, CssAtRuleAST, CssCharsetAST, CssCommentAST, CssCommonAST, CssCommonPositionAST, CssContainerAST, CssCustomMediaAST, CssDeclarationAST, CssDocumentAST, CssFontFaceAST, CssHostAST, CssImportAST, CssKeyframeAST, CssKeyframesAST, CssLayerAST, CssMediaAST, CssNamespaceAST, CssPageAST, CssRuleAST, CssStartingStyleAST, CssStylesheetAST, CssSupportsAST };
|
||||
2
types/node_modules/@adobe/css-tools/dist/esm/adobe-css-tools.mjs
generated
vendored
Normal file
2
types/node_modules/@adobe/css-tools/dist/esm/adobe-css-tools.mjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
types/node_modules/@adobe/css-tools/dist/esm/adobe-css-tools.mjs.map
generated
vendored
Normal file
1
types/node_modules/@adobe/css-tools/dist/esm/adobe-css-tools.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
176
types/node_modules/@adobe/css-tools/dist/umd/adobe-css-tools.d.ts
generated
vendored
Normal file
176
types/node_modules/@adobe/css-tools/dist/umd/adobe-css-tools.d.ts
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
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);
|
||||
}
|
||||
|
||||
declare 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"
|
||||
}
|
||||
type CssCommonAST = {
|
||||
type: CssTypes;
|
||||
};
|
||||
type CssCommonPositionAST = CssCommonAST & {
|
||||
position?: Position;
|
||||
parent?: unknown;
|
||||
};
|
||||
type CssStylesheetAST = CssCommonAST & {
|
||||
type: CssTypes.stylesheet;
|
||||
stylesheet: {
|
||||
source?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
parsingErrors?: Array<CssParseError>;
|
||||
};
|
||||
};
|
||||
type CssRuleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.rule;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssDeclarationAST = CssCommonPositionAST & {
|
||||
type: CssTypes.declaration;
|
||||
property: string;
|
||||
value: string;
|
||||
};
|
||||
type CssCommentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.comment;
|
||||
comment: string;
|
||||
};
|
||||
type CssContainerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.container;
|
||||
container: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssCharsetAST = CssCommonPositionAST & {
|
||||
type: CssTypes.charset;
|
||||
charset: string;
|
||||
};
|
||||
type CssCustomMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.customMedia;
|
||||
name: string;
|
||||
media: string;
|
||||
};
|
||||
type CssDocumentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.document;
|
||||
document: string;
|
||||
vendor?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssFontFaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.fontFace;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssHostAST = CssCommonPositionAST & {
|
||||
type: CssTypes.host;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssImportAST = CssCommonPositionAST & {
|
||||
type: CssTypes.import;
|
||||
import: string;
|
||||
};
|
||||
type CssKeyframesAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframes;
|
||||
name: string;
|
||||
vendor?: string;
|
||||
keyframes: Array<CssKeyframeAST | CssCommentAST>;
|
||||
};
|
||||
type CssKeyframeAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframe;
|
||||
values: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssLayerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.layer;
|
||||
layer: string;
|
||||
rules?: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.media;
|
||||
media: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssNamespaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.namespace;
|
||||
namespace: string;
|
||||
};
|
||||
type CssPageAST = CssCommonPositionAST & {
|
||||
type: CssTypes.page;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssSupportsAST = CssCommonPositionAST & {
|
||||
type: CssTypes.supports;
|
||||
supports: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssStartingStyleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.startingStyle;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssAtRuleAST = CssRuleAST | CssCommentAST | CssContainerAST | CssCharsetAST | CssCustomMediaAST | CssDocumentAST | CssFontFaceAST | CssHostAST | CssImportAST | CssKeyframesAST | CssLayerAST | CssMediaAST | CssNamespaceAST | CssPageAST | CssSupportsAST | CssStartingStyleAST;
|
||||
type CssAllNodesAST = CssAtRuleAST | CssStylesheetAST | CssDeclarationAST | CssKeyframeAST;
|
||||
|
||||
type CompilerOptions = {
|
||||
indent?: string;
|
||||
compress?: boolean;
|
||||
};
|
||||
|
||||
declare const parse: (css: string, options?: {
|
||||
source?: string;
|
||||
silent?: boolean;
|
||||
}) => CssStylesheetAST;
|
||||
declare 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 { CssTypes, _default as default, parse, stringify };
|
||||
export type { CssAllNodesAST, CssAtRuleAST, CssCharsetAST, CssCommentAST, CssCommonAST, CssCommonPositionAST, CssContainerAST, CssCustomMediaAST, CssDeclarationAST, CssDocumentAST, CssFontFaceAST, CssHostAST, CssImportAST, CssKeyframeAST, CssKeyframesAST, CssLayerAST, CssMediaAST, CssNamespaceAST, CssPageAST, CssRuleAST, CssStartingStyleAST, CssStylesheetAST, CssSupportsAST };
|
||||
2
types/node_modules/@adobe/css-tools/dist/umd/adobe-css-tools.js
generated
vendored
Normal file
2
types/node_modules/@adobe/css-tools/dist/umd/adobe-css-tools.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
types/node_modules/@adobe/css-tools/dist/umd/adobe-css-tools.js.map
generated
vendored
Normal file
1
types/node_modules/@adobe/css-tools/dist/umd/adobe-css-tools.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
314
types/node_modules/@adobe/css-tools/docs/API.md
generated
vendored
Normal file
314
types/node_modules/@adobe/css-tools/docs/API.md
generated
vendored
Normal file
@ -0,0 +1,314 @@
|
||||
# API Reference
|
||||
|
||||
## Overview
|
||||
|
||||
`@adobe/css-tools` provides a modern CSS parser and stringifier with comprehensive TypeScript support. It can parse CSS into an Abstract Syntax Tree (AST) and convert the AST back to CSS with various formatting options.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @adobe/css-tools
|
||||
```
|
||||
|
||||
## Core Functions
|
||||
|
||||
### `parse(code, options?)`
|
||||
|
||||
Parses CSS code and returns an Abstract Syntax Tree (AST).
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `code` (string) - The CSS code to parse
|
||||
- `options` (object, optional) - Parsing options
|
||||
- `silent` (boolean) - Silently fail on parse errors instead of throwing. When `true`, errors are collected in `ast.stylesheet.parsingErrors`
|
||||
- `source` (string) - File path for better error reporting
|
||||
|
||||
#### Returns
|
||||
|
||||
- `CssStylesheetAST` - The parsed CSS as an AST
|
||||
|
||||
#### Example
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
.example {
|
||||
color: red;
|
||||
font-size: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
console.log(ast.stylesheet.rules);
|
||||
```
|
||||
|
||||
### `stringify(ast, options?)`
|
||||
|
||||
Converts a CSS AST back to CSS string with configurable formatting.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `ast` (CssStylesheetAST) - The CSS AST to stringify
|
||||
- `options` (CompilerOptions, optional) - Stringification options
|
||||
- `indent` (string) - Indentation string (default: `' '`)
|
||||
- `compress` (boolean) - Whether to compress/minify the output (default: `false`)
|
||||
|
||||
#### Returns
|
||||
|
||||
- `string` - The formatted CSS string
|
||||
|
||||
#### Example
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = '.example { color: red; }';
|
||||
const ast = parse(css);
|
||||
|
||||
// Pretty print
|
||||
const formatted = stringify(ast, { indent: ' ' });
|
||||
console.log(formatted);
|
||||
// Output:
|
||||
// .example {
|
||||
// color: red;
|
||||
// }
|
||||
|
||||
// Compressed
|
||||
const minified = stringify(ast, { compress: true });
|
||||
console.log(minified);
|
||||
// Output: .example{color:red}
|
||||
```
|
||||
|
||||
## Type Definitions
|
||||
|
||||
### Core Types
|
||||
|
||||
#### `CssStylesheetAST`
|
||||
The root AST node representing a complete CSS stylesheet.
|
||||
|
||||
```typescript
|
||||
type CssStylesheetAST = {
|
||||
type: CssTypes.stylesheet;
|
||||
stylesheet: {
|
||||
source?: string;
|
||||
rules: CssRuleAST[];
|
||||
parsingErrors?: CssParseError[];
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssRuleAST`
|
||||
Represents a CSS rule (selector + declarations).
|
||||
|
||||
```typescript
|
||||
type CssRuleAST = {
|
||||
type: CssTypes.rule;
|
||||
selectors: string[];
|
||||
declarations: CssDeclarationAST[];
|
||||
position?: CssPosition;
|
||||
parent?: CssStylesheetAST;
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssDeclarationAST`
|
||||
Represents a CSS property declaration.
|
||||
|
||||
```typescript
|
||||
type CssDeclarationAST = {
|
||||
type: CssTypes.declaration;
|
||||
property: string;
|
||||
value: string;
|
||||
position?: CssPosition;
|
||||
parent?: CssRuleAST;
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssMediaAST`
|
||||
Represents a CSS @media rule.
|
||||
|
||||
```typescript
|
||||
type CssMediaAST = {
|
||||
type: CssTypes.media;
|
||||
media: string;
|
||||
rules: CssRuleAST[];
|
||||
position?: CssPosition;
|
||||
parent?: CssStylesheetAST;
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssKeyframesAST`
|
||||
Represents a CSS @keyframes rule.
|
||||
|
||||
```typescript
|
||||
type CssKeyframesAST = {
|
||||
type: CssTypes.keyframes;
|
||||
name: string;
|
||||
keyframes: CssKeyframeAST[];
|
||||
position?: CssPosition;
|
||||
parent?: CssStylesheetAST;
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssPosition`
|
||||
Represents source position information.
|
||||
|
||||
```typescript
|
||||
type CssPosition = {
|
||||
source?: string;
|
||||
start: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
end: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssParseError`
|
||||
Represents a parsing error.
|
||||
|
||||
```typescript
|
||||
type CssParseError = {
|
||||
message: string;
|
||||
reason: string;
|
||||
filename?: string;
|
||||
line: number;
|
||||
column: number;
|
||||
source?: string;
|
||||
};
|
||||
```
|
||||
|
||||
### Compiler Options
|
||||
|
||||
#### `CompilerOptions`
|
||||
Options for the stringifier.
|
||||
|
||||
```typescript
|
||||
type CompilerOptions = {
|
||||
indent?: string; // Default: ' '
|
||||
compress?: boolean; // Default: false
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Silent Parsing
|
||||
|
||||
When parsing malformed CSS, you can use the `silent` option to collect errors instead of throwing:
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const malformedCss = `
|
||||
body { color: red; }
|
||||
{ color: blue; } /* Missing selector */
|
||||
.valid { background: green; }
|
||||
`;
|
||||
|
||||
const result = parse(malformedCss, { silent: true });
|
||||
|
||||
if (result.stylesheet.parsingErrors) {
|
||||
result.stylesheet.parsingErrors.forEach(error => {
|
||||
console.log(`Error at line ${error.line}: ${error.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Valid rules are still parsed
|
||||
console.log('Valid rules:', result.stylesheet.rules.length);
|
||||
```
|
||||
|
||||
### Source Tracking
|
||||
|
||||
Enable source tracking for better error reporting:
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = 'body { color: red; }';
|
||||
const ast = parse(css, { source: 'styles.css' });
|
||||
|
||||
const rule = ast.stylesheet.rules[0];
|
||||
console.log(rule.position?.source); // "styles.css"
|
||||
console.log(rule.position?.start); // { line: 1, column: 1 }
|
||||
console.log(rule.position?.end); // { line: 1, column: 20 }
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Working with At-Rules
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
|
||||
// Access media rules
|
||||
const mediaRule = ast.stylesheet.rules.find(rule => rule.type === 'media');
|
||||
console.log(mediaRule.media); // "(max-width: 768px)"
|
||||
|
||||
// Access keyframes
|
||||
const keyframesRule = ast.stylesheet.rules.find(rule => rule.type === 'keyframes');
|
||||
console.log(keyframesRule.name); // "fadeIn"
|
||||
```
|
||||
|
||||
### Custom Formatting
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = '.example{color:red;font-size:16px}';
|
||||
const ast = parse(css);
|
||||
|
||||
// Custom indentation
|
||||
const formatted = stringify(ast, { indent: ' ' });
|
||||
console.log(formatted);
|
||||
// Output:
|
||||
// .example {
|
||||
// color: red;
|
||||
// font-size: 16px;
|
||||
// }
|
||||
|
||||
// Compressed with no spaces
|
||||
const compressed = stringify(ast, { compress: true });
|
||||
console.log(compressed);
|
||||
// Output: .example{color:red;font-size:16px}
|
||||
```
|
||||
|
||||
## TypeScript Integration
|
||||
|
||||
The library provides comprehensive TypeScript support with full type definitions for all AST nodes and functions:
|
||||
|
||||
```typescript
|
||||
import { parse, stringify, type CssStylesheetAST } from '@adobe/css-tools';
|
||||
|
||||
const css: string = '.example { color: red; }';
|
||||
const ast: CssStylesheetAST = parse(css);
|
||||
const output: string = stringify(ast);
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- The parser is optimized for large CSS files
|
||||
- AST nodes are lightweight and memory-efficient
|
||||
- Stringification is fast and supports streaming for large outputs
|
||||
- Consider using `compress: true` for production builds to reduce file size
|
||||
|
||||
## Browser Support
|
||||
|
||||
The library works in all modern browsers and Node.js environments. For older environments, you may need to use a bundler with appropriate polyfills.
|
||||
366
types/node_modules/@adobe/css-tools/docs/AST.md
generated
vendored
Normal file
366
types/node_modules/@adobe/css-tools/docs/AST.md
generated
vendored
Normal file
@ -0,0 +1,366 @@
|
||||
# Abstract Syntax Tree (AST)
|
||||
|
||||
## Overview
|
||||
|
||||
The AST represents CSS as a tree structure where each node has a specific type and properties. All nodes share common properties and have type-specific properties.
|
||||
|
||||
## Common Properties
|
||||
|
||||
All AST nodes have these properties:
|
||||
|
||||
### `type`
|
||||
|
||||
The node type as a string. See [Node Types](#node-types) for all possible values.
|
||||
|
||||
### `position` (optional)
|
||||
|
||||
Position information for the node in the source code:
|
||||
|
||||
```typescript
|
||||
{
|
||||
start: { line: number; column: number };
|
||||
end: { line: number; column: number };
|
||||
source?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### `parent` (optional)
|
||||
|
||||
Reference to the parent node in the AST.
|
||||
|
||||
## Node Types
|
||||
|
||||
### `stylesheet`
|
||||
|
||||
The root node representing an entire CSS document.
|
||||
|
||||
**Properties:**
|
||||
- `stylesheet.source` (optional): Source file path
|
||||
- `stylesheet.rules`: Array of top-level rules
|
||||
- `stylesheet.parsingErrors` (optional): Array of parse errors when `silent` option is used
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "stylesheet",
|
||||
"stylesheet": {
|
||||
"rules": [
|
||||
// ... other nodes
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `rule`
|
||||
|
||||
A CSS rule with selectors and declarations.
|
||||
|
||||
**Properties:**
|
||||
- `selectors`: Array of CSS selectors as strings
|
||||
- `declarations`: Array of declarations and comments
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "rule",
|
||||
"selectors": ["body", "html"],
|
||||
"declarations": [
|
||||
{
|
||||
"type": "declaration",
|
||||
"property": "color",
|
||||
"value": "red"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `declaration`
|
||||
|
||||
A CSS property declaration.
|
||||
|
||||
**Properties:**
|
||||
- `property`: The CSS property name
|
||||
- `value`: The CSS property value as a string
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "declaration",
|
||||
"property": "background-color",
|
||||
"value": "#ffffff"
|
||||
}
|
||||
```
|
||||
|
||||
### `comment`
|
||||
|
||||
A CSS comment.
|
||||
|
||||
**Properties:**
|
||||
- `comment`: The comment text (without `/*` and `*/`)
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "comment",
|
||||
"comment": " This is a comment "
|
||||
}
|
||||
```
|
||||
|
||||
### `media`
|
||||
|
||||
A `@media` rule.
|
||||
|
||||
**Properties:**
|
||||
- `media`: The media query string
|
||||
- `rules`: Array of rules within the media block
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "media",
|
||||
"media": "screen and (max-width: 768px)",
|
||||
"rules": [
|
||||
// ... nested rules
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `keyframes`
|
||||
|
||||
A `@keyframes` rule.
|
||||
|
||||
**Properties:**
|
||||
- `name`: The keyframes name
|
||||
- `vendor` (optional): Vendor prefix (e.g., "-webkit-")
|
||||
- `keyframes`: Array of keyframe rules and comments
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "keyframes",
|
||||
"name": "fade",
|
||||
"keyframes": [
|
||||
{
|
||||
"type": "keyframe",
|
||||
"values": ["from"],
|
||||
"declarations": [
|
||||
{
|
||||
"type": "declaration",
|
||||
"property": "opacity",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `keyframe`
|
||||
|
||||
A keyframe within a `@keyframes` rule.
|
||||
|
||||
**Properties:**
|
||||
- `values`: Array of keyframe selectors (e.g., `["from"]`, `["to"]`, `["50%"]`)
|
||||
- `declarations`: Array of declarations and comments
|
||||
|
||||
### `import`
|
||||
|
||||
An `@import` rule.
|
||||
|
||||
**Properties:**
|
||||
- `import`: The import string (URL or media query)
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "import",
|
||||
"import": "url('styles.css')"
|
||||
}
|
||||
```
|
||||
|
||||
### `charset`
|
||||
|
||||
A `@charset` rule.
|
||||
|
||||
**Properties:**
|
||||
- `charset`: The character encoding
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "charset",
|
||||
"charset": "utf-8"
|
||||
}
|
||||
```
|
||||
|
||||
### `namespace`
|
||||
|
||||
A `@namespace` rule.
|
||||
|
||||
**Properties:**
|
||||
- `namespace`: The namespace declaration
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "namespace",
|
||||
"namespace": "url(http://www.w3.org/1999/xhtml)"
|
||||
}
|
||||
```
|
||||
|
||||
### `supports`
|
||||
|
||||
A `@supports` rule.
|
||||
|
||||
**Properties:**
|
||||
- `supports`: The supports condition
|
||||
- `rules`: Array of rules within the supports block
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "supports",
|
||||
"supports": "(display: grid)",
|
||||
"rules": [
|
||||
// ... nested rules
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `document`
|
||||
|
||||
A `@document` rule.
|
||||
|
||||
**Properties:**
|
||||
- `document`: The document condition
|
||||
- `vendor` (optional): Vendor prefix
|
||||
- `rules`: Array of rules within the document block
|
||||
|
||||
### `page`
|
||||
|
||||
A `@page` rule.
|
||||
|
||||
**Properties:**
|
||||
- `selectors`: Array of page selectors
|
||||
- `declarations`: Array of declarations and comments
|
||||
|
||||
### `font-face`
|
||||
|
||||
A `@font-face` rule.
|
||||
|
||||
**Properties:**
|
||||
- `declarations`: Array of font declarations and comments
|
||||
|
||||
### `host`
|
||||
|
||||
A `:host` rule.
|
||||
|
||||
**Properties:**
|
||||
- `rules`: Array of rules within the host block
|
||||
|
||||
### `container`
|
||||
|
||||
A `@container` rule.
|
||||
|
||||
**Properties:**
|
||||
- `container`: The container query
|
||||
- `rules`: Array of rules within the container block
|
||||
|
||||
### `layer`
|
||||
|
||||
A `@layer` rule.
|
||||
|
||||
**Properties:**
|
||||
- `layer`: The layer name
|
||||
- `rules` (optional): Array of rules within the layer block
|
||||
|
||||
### `custom-media`
|
||||
|
||||
A `@custom-media` rule.
|
||||
|
||||
**Properties:**
|
||||
- `name`: The custom media query name
|
||||
- `media`: The media query definition
|
||||
|
||||
### `starting-style`
|
||||
|
||||
A `@starting-style` rule.
|
||||
|
||||
**Properties:**
|
||||
- `rules`: Array of rules within the starting-style block
|
||||
|
||||
## Type Hierarchy
|
||||
|
||||
The AST nodes are organized in the following hierarchy:
|
||||
|
||||
- `CssStylesheetAST` - Root node
|
||||
- `CssAtRuleAST` - Union of all at-rule and rule nodes
|
||||
- `CssAllNodesAST` - Union of all possible node types
|
||||
|
||||
## Working with the AST
|
||||
|
||||
### Traversing Nodes
|
||||
|
||||
```typescript
|
||||
import { parse, CssStylesheetAST } from '@adobe/css-tools';
|
||||
|
||||
const ast: CssStylesheetAST = parse('body { color: red; }');
|
||||
|
||||
// Access top-level rules
|
||||
ast.stylesheet.rules.forEach(rule => {
|
||||
if (rule.type === 'rule') {
|
||||
console.log('Selectors:', rule.selectors);
|
||||
rule.declarations.forEach(decl => {
|
||||
if (decl.type === 'declaration') {
|
||||
console.log(`${decl.property}: ${decl.value}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Modifying Nodes
|
||||
|
||||
```typescript
|
||||
// Add a new declaration
|
||||
const newDecl = {
|
||||
type: 'declaration' as const,
|
||||
property: 'font-size',
|
||||
value: '16px'
|
||||
};
|
||||
|
||||
// Find a rule and add the declaration
|
||||
ast.stylesheet.rules.forEach(rule => {
|
||||
if (rule.type === 'rule' && rule.selectors.includes('body')) {
|
||||
rule.declarations.push(newDecl);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
When parsing with the `silent` option, errors are collected in the AST:
|
||||
|
||||
```typescript
|
||||
const ast = parse('invalid css {', { silent: true });
|
||||
|
||||
if (ast.stylesheet.parsingErrors) {
|
||||
ast.stylesheet.parsingErrors.forEach(error => {
|
||||
console.error('Parse error:', error.message);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Position Information
|
||||
|
||||
Position information is available on most nodes and includes:
|
||||
|
||||
- `start.line` and `start.column`: Beginning of the node
|
||||
- `end.line` and `end.column`: End of the node
|
||||
- `source`: Source file path (if provided during parsing)
|
||||
|
||||
This is useful for:
|
||||
- Error reporting
|
||||
- Source mapping
|
||||
- Code analysis tools
|
||||
- IDE integration
|
||||
191
types/node_modules/@adobe/css-tools/docs/CHANGELOG.md
generated
vendored
Normal file
191
types/node_modules/@adobe/css-tools/docs/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [4.4.4] - 2025-07-22
|
||||
|
||||
### Changed
|
||||
- Switch from yarn to npm for package management
|
||||
- Switch from eslint to biome for code formatting and linting
|
||||
- Reformat codebase to comply with biome recommendations
|
||||
- Switch from webpack to rollup for bundling
|
||||
|
||||
### Fixed
|
||||
- Fix module exports to ensure proper compatibility with bundlers
|
||||
- Add validation check to prevent future export issues
|
||||
|
||||
## [4.4.3] - 2025-05-15
|
||||
|
||||
### Security
|
||||
- Fix polynomial regular expression vulnerability on uncontrolled data
|
||||
- Refactor code to enable GitHub security static analysis
|
||||
|
||||
### Performance
|
||||
- Improve parsing performance with minor optimizations
|
||||
- Replace regex patterns with string search (indexOf-based) for better performance
|
||||
|
||||
### Added
|
||||
- Add new utility functions with comprehensive unit tests
|
||||
- Add improved formatting for CSS Grid template areas (#283 by @jogibear9988)
|
||||
|
||||
### Fixed
|
||||
- Fix TypeScript error with ConstructorParameters in Parcel bundler (#444)
|
||||
|
||||
## [4.4.2] - 2025-02-12
|
||||
|
||||
### Fixed
|
||||
- Fix regular expression for parsing quoted values in parentheses
|
||||
|
||||
## [4.4.0] - 2024-06-05
|
||||
|
||||
### Added
|
||||
- Add support for CSS `@starting-style` at-rule (#319)
|
||||
|
||||
## [4.3.3] - 2024-01-24
|
||||
|
||||
### Changed
|
||||
- Update package export configuration (#271)
|
||||
|
||||
## [4.3.2] - 2023-11-28
|
||||
|
||||
### Security
|
||||
- Fix ReDoS vulnerability with crafted CSS strings - CVE-2023-48631
|
||||
|
||||
### Fixed
|
||||
- Fix parsing issues with `:is()` and nested `:nth-child()` selectors (#211)
|
||||
|
||||
## [4.3.1] - 2023-03-14
|
||||
|
||||
### Security
|
||||
- Fix ReDoS vulnerability with crafted CSS strings - CVE-2023-26364
|
||||
|
||||
## [4.3.0] - 2023-03-07
|
||||
|
||||
### Changed
|
||||
- Update build toolchain and dependencies
|
||||
- Update package exports configuration and file structure
|
||||
|
||||
## [4.2.0] - 2023-02-21
|
||||
|
||||
### Added
|
||||
- Add support for CSS `@container` at-rule
|
||||
- Add support for CSS `@layer` at-rule
|
||||
|
||||
## [4.1.0] - 2023-01-25
|
||||
|
||||
### Added
|
||||
- Add support for ES Modules (ESM)
|
||||
|
||||
## [4.0.2] - 2023-01-12
|
||||
|
||||
### Fixed
|
||||
- Fix `@import` parsing when URL contains semicolons (#71)
|
||||
- Fix regression in selector parsing for attribute selectors (#77)
|
||||
|
||||
## [4.0.1] - 2022-08-03
|
||||
|
||||
### Fixed
|
||||
- Fix `globalThis` configuration for webpack to enable UMD module usage in Node.js environments
|
||||
|
||||
## [4.0.0] - 2022-06-09
|
||||
|
||||
### Changed
|
||||
- Fork from original css library to Adobe's `@adobe/css-tools` package
|
||||
- Convert codebase from JavaScript to TypeScript
|
||||
- Improve parsing performance by approximately 25%
|
||||
- Update all dependencies to latest versions
|
||||
- Remove source map support
|
||||
|
||||
---
|
||||
|
||||
## Legacy Versions (Pre-Adobe Fork)
|
||||
|
||||
## [3.0.0] - 2020-07-01
|
||||
|
||||
### Changed
|
||||
- Bump major version due to major dependency updates and Node.js version requirement changes
|
||||
|
||||
## [2.2.1] - 2015-06-17
|
||||
|
||||
### Fixed
|
||||
- Fix parsing of escaped quotes in quoted strings
|
||||
|
||||
## [2.2.0] - 2015-02-18
|
||||
|
||||
### Added
|
||||
- Add `parsingErrors` property to list errors when parsing with `silent: true`
|
||||
- Accept EOL characters and all whitespace characters in at-rules such as `@media`
|
||||
|
||||
## [2.1.0] - 2014-08-05
|
||||
|
||||
### Added
|
||||
- Add `inputSourcemaps` option to disable input source map processing
|
||||
- Add `sourcemap: 'generator'` option to return the `SourceMapGenerator` object
|
||||
- Use `inherits` package for inheritance (fixes browser compatibility issues)
|
||||
|
||||
### Changed
|
||||
- Change error message format and add `.reason` property to error objects
|
||||
|
||||
## [2.0.0] - 2014-06-18
|
||||
|
||||
### Added
|
||||
- Add non-enumerable parent reference to each AST node
|
||||
- Add support for `@custom-media`, `@host`, and `@font-face` at-rules
|
||||
- Allow commas inside selector functions
|
||||
- Allow empty property values
|
||||
- Add `node.position.content` property
|
||||
- Integrate css-parse and css-stringify libraries
|
||||
- Apply original source maps from source files
|
||||
|
||||
### Changed
|
||||
- Change default `options.position` value to `true`
|
||||
- Remove comments from properties and values
|
||||
|
||||
### Removed
|
||||
- Drop Component(1) support
|
||||
|
||||
### Fixed
|
||||
- Fix assertion errors when selectors are missing
|
||||
|
||||
## [1.6.1] - 2014-01-02
|
||||
|
||||
### Fixed
|
||||
- Fix component.json configuration
|
||||
|
||||
## [1.6.0] - 2013-12-21
|
||||
|
||||
### Changed
|
||||
- Update dependencies
|
||||
|
||||
## [1.5.0] - 2013-12-03
|
||||
|
||||
### Changed
|
||||
- Update dependencies
|
||||
|
||||
## [1.1.0] - 2013-04-04
|
||||
|
||||
### Changed
|
||||
- Update dependencies
|
||||
|
||||
## [1.0.7] - 2012-11-21
|
||||
|
||||
### Fixed
|
||||
- Fix component.json configuration
|
||||
|
||||
## [1.0.4] - 2012-11-15
|
||||
|
||||
### Changed
|
||||
- Update css-stringify dependency
|
||||
|
||||
## [1.0.3] - 2012-09-01
|
||||
|
||||
### Added
|
||||
- Add Component support
|
||||
|
||||
## [0.0.1] - 2010-01-03
|
||||
|
||||
### Added
|
||||
- Initial release
|
||||
450
types/node_modules/@adobe/css-tools/docs/EXAMPLES.md
generated
vendored
Normal file
450
types/node_modules/@adobe/css-tools/docs/EXAMPLES.md
generated
vendored
Normal file
@ -0,0 +1,450 @@
|
||||
# Usage Examples
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Parsing CSS
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
// Basic CSS parsing
|
||||
const css = `
|
||||
body {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
console.log(ast);
|
||||
```
|
||||
|
||||
### Stringifying AST
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = 'body { font-size: 12px; color: #333; }';
|
||||
const ast = parse(css);
|
||||
|
||||
// Convert back to CSS
|
||||
const output = stringify(ast);
|
||||
console.log(output); // "body { font-size: 12px; color: #333; }"
|
||||
```
|
||||
|
||||
## Advanced Parsing Options
|
||||
|
||||
### Source Tracking
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = 'body { color: red; }';
|
||||
const ast = parse(css, { source: 'styles.css' });
|
||||
|
||||
// Position information is available
|
||||
const rule = ast.stylesheet.rules[0];
|
||||
console.log(rule.position?.source); // "styles.css"
|
||||
console.log(rule.position?.start); // { line: 1, column: 1 }
|
||||
console.log(rule.position?.end); // { line: 1, column: 20 }
|
||||
```
|
||||
|
||||
### Silent Error Handling
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const malformedCss = `
|
||||
body { color: red; }
|
||||
{ color: blue; } /* Missing selector */
|
||||
.valid { background: green; }
|
||||
`;
|
||||
|
||||
// Parse with silent error handling
|
||||
const result = parse(malformedCss, { silent: true });
|
||||
|
||||
// Check for parsing errors
|
||||
if (result.stylesheet.parsingErrors) {
|
||||
console.log('Parsing errors:', result.stylesheet.parsingErrors.length);
|
||||
result.stylesheet.parsingErrors.forEach(error => {
|
||||
console.log(`Error at line ${error.line}: ${error.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Valid rules are still parsed
|
||||
console.log('Valid rules:', result.stylesheet.rules.length);
|
||||
```
|
||||
|
||||
## AST Structure Examples
|
||||
|
||||
### Basic Rule
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
.header {
|
||||
background: #f0f0f0;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
const rule = ast.stylesheet.rules[0];
|
||||
|
||||
console.log(rule.type); // "rule"
|
||||
console.log(rule.selectors); // [".header"]
|
||||
console.log(rule.declarations.length); // 3
|
||||
|
||||
rule.declarations.forEach(decl => {
|
||||
console.log(`${decl.property}: ${decl.value}`);
|
||||
});
|
||||
// Output:
|
||||
// background: #f0f0f0
|
||||
// padding: 20px
|
||||
// border-bottom: 1px solid #ccc
|
||||
```
|
||||
|
||||
### Media Queries
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
@media screen and (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
const mediaRule = ast.stylesheet.rules[0];
|
||||
|
||||
console.log(mediaRule.type); // "media"
|
||||
console.log(mediaRule.media); // "screen and (max-width: 768px)"
|
||||
console.log(mediaRule.rules.length); // 2
|
||||
```
|
||||
|
||||
### Keyframes
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
const keyframesRule = ast.stylesheet.rules[0];
|
||||
|
||||
console.log(keyframesRule.type); // "keyframes"
|
||||
console.log(keyframesRule.name); // "fadeIn"
|
||||
console.log(keyframesRule.keyframes.length); // 2
|
||||
|
||||
keyframesRule.keyframes.forEach(keyframe => {
|
||||
console.log(`Keyframe: ${keyframe.values.join(', ')}`);
|
||||
keyframe.declarations.forEach(decl => {
|
||||
console.log(` ${decl.property}: ${decl.value}`);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
/* Header styles */
|
||||
.header {
|
||||
background: red; /* Fallback color */
|
||||
}
|
||||
|
||||
/* Footer styles */
|
||||
.footer {
|
||||
background: blue;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
|
||||
ast.stylesheet.rules.forEach(rule => {
|
||||
if (rule.type === 'comment') {
|
||||
console.log(`Comment: ${rule.comment}`);
|
||||
} else if (rule.type === 'rule') {
|
||||
console.log(`Rule: ${rule.selectors.join(', ')}`);
|
||||
|
||||
rule.declarations.forEach(decl => {
|
||||
if (decl.type === 'comment') {
|
||||
console.log(` Comment: ${decl.comment}`);
|
||||
} else {
|
||||
console.log(` ${decl.property}: ${decl.value}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Stringifying Options
|
||||
|
||||
### Compressed Output
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
body {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
|
||||
// Compressed output
|
||||
const compressed = stringify(ast, { compress: true });
|
||||
console.log(compressed);
|
||||
// Output: "body{font-size:12px;color:#333;margin:0;padding:0}"
|
||||
```
|
||||
|
||||
### Custom Indentation
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = 'body { font-size: 12px; color: #333; }';
|
||||
const ast = parse(css);
|
||||
|
||||
// Custom indentation
|
||||
const formatted = stringify(ast, { indent: ' ' });
|
||||
console.log(formatted);
|
||||
// Output:
|
||||
// body {
|
||||
// font-size: 12px;
|
||||
// color: #333;
|
||||
// }
|
||||
```
|
||||
|
||||
## Working with Complex CSS
|
||||
|
||||
### Nested Rules and At-Rules
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const complexCss = `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto');
|
||||
|
||||
@charset "UTF-8";
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font-size: 12pt;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (display: grid) {
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(0); }
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'CustomFont';
|
||||
src: url('custom-font.woff2') format('woff2');
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(complexCss);
|
||||
|
||||
ast.stylesheet.rules.forEach(rule => {
|
||||
switch (rule.type) {
|
||||
case 'import':
|
||||
console.log(`Import: ${rule.import}`);
|
||||
break;
|
||||
case 'charset':
|
||||
console.log(`Charset: ${rule.charset}`);
|
||||
break;
|
||||
case 'media':
|
||||
console.log(`Media query: ${rule.media}`);
|
||||
break;
|
||||
case 'supports':
|
||||
console.log(`Supports: ${rule.supports}`);
|
||||
break;
|
||||
case 'keyframes':
|
||||
console.log(`Keyframes: ${rule.name}`);
|
||||
break;
|
||||
case 'font-face':
|
||||
console.log('Font-face rule');
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Manipulating the AST
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
.button {
|
||||
background: blue;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
const rule = ast.stylesheet.rules[0];
|
||||
|
||||
// Add a new declaration
|
||||
rule.declarations.push({
|
||||
type: 'declaration',
|
||||
property: 'border-radius',
|
||||
value: '5px'
|
||||
});
|
||||
|
||||
// Modify existing declaration
|
||||
const backgroundDecl = rule.declarations.find(d => d.property === 'background');
|
||||
if (backgroundDecl) {
|
||||
backgroundDecl.value = 'red';
|
||||
}
|
||||
|
||||
// Add a new selector
|
||||
rule.selectors.push('.btn');
|
||||
|
||||
const modifiedCss = stringify(ast);
|
||||
console.log(modifiedCss);
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Catching Parse Errors
|
||||
|
||||
```javascript
|
||||
import { parse, CssParseError } from '@adobe/css-tools';
|
||||
|
||||
try {
|
||||
const ast = parse('body { color: red; } { invalid }');
|
||||
} catch (error) {
|
||||
if (error instanceof CssParseError) {
|
||||
console.log(`Parse error at line ${error.line}, column ${error.column}:`);
|
||||
console.log(error.message);
|
||||
console.log(`Source: ${error.filename}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Working with Silent Errors
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const problematicCss = `
|
||||
body { color: red; }
|
||||
{ color: blue; } /* Missing selector */
|
||||
.valid { background: green; }
|
||||
.another { border: 1px solid; } /* Missing closing brace */
|
||||
`;
|
||||
|
||||
const result = parse(problematicCss, {
|
||||
silent: true,
|
||||
source: 'problematic.css'
|
||||
});
|
||||
|
||||
// Process valid rules
|
||||
const validRules = result.stylesheet.rules.filter(rule => rule.type === 'rule');
|
||||
console.log(`Found ${validRules.length} valid rules`);
|
||||
|
||||
// Log errors for debugging
|
||||
if (result.stylesheet.parsingErrors) {
|
||||
result.stylesheet.parsingErrors.forEach(error => {
|
||||
console.log(`Error: ${error.message} at line ${error.line}`);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### CSS Minification
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
function minifyCSS(css) {
|
||||
const ast = parse(css);
|
||||
return stringify(ast, { compress: true });
|
||||
}
|
||||
|
||||
const css = `
|
||||
body {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
`;
|
||||
|
||||
const minified = minifyCSS(css);
|
||||
console.log(minified);
|
||||
// Output: "body{font-size:12px;color:#333;margin:0;padding:0}.container{max-width:1200px;margin:0 auto}"
|
||||
```
|
||||
|
||||
### CSS Validation
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
function validateCSS(css, filename = 'unknown') {
|
||||
try {
|
||||
const ast = parse(css, { source: filename });
|
||||
return {
|
||||
valid: true,
|
||||
rules: ast.stylesheet.rules.length,
|
||||
errors: []
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
valid: false,
|
||||
rules: 0,
|
||||
errors: [{
|
||||
message: error.message,
|
||||
line: error.line,
|
||||
column: error.column,
|
||||
source: error.filename
|
||||
}]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const result = validateCSS('body { color: red; } { invalid }', 'test.css');
|
||||
console.log(result);
|
||||
```
|
||||
82
types/node_modules/@adobe/css-tools/package.json
generated
vendored
Normal file
82
types/node_modules/@adobe/css-tools/package.json
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "@adobe/css-tools",
|
||||
"version": "4.4.4",
|
||||
"description": "A modern CSS parser and stringifier with TypeScript support",
|
||||
"source": "src/index.ts",
|
||||
"main": "./dist/cjs/adobe-css-tools.cjs",
|
||||
"module": "./dist/esm/adobe-css-tools.mjs",
|
||||
"browser": "./dist/umd/adobe-css-tools.js",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/esm/adobe-css-tools.mjs",
|
||||
"require": "./dist/cjs/adobe-css-tools.cjs"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"types": "./dist/esm/adobe-css-tools.d.mts",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist/cjs",
|
||||
"dist/esm",
|
||||
"dist/umd",
|
||||
"README.md",
|
||||
"docs/"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@arethetypeswrong/cli": "^0.18.2",
|
||||
"@biomejs/biome": "^2.1.2",
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-typescript": "^12.1.4",
|
||||
"@types/benchmark": "^2.1.1",
|
||||
"@types/bytes": "^3.1.5",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^24.0.3",
|
||||
"benchmark": "^2.1.4",
|
||||
"bytes": "^3.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"rollup": "^4.45.1",
|
||||
"rollup-plugin-dts": "^6.2.1",
|
||||
"ts-jest": "^29.1.1",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"scripts": {
|
||||
"benchmark": "npm run build && node benchmark/index.mjs",
|
||||
"test": "jest",
|
||||
"clean": "rm -rf ./dist",
|
||||
"build": "rollup -c",
|
||||
"fix": "biome check --write",
|
||||
"check-package-bundle": "attw --pack .",
|
||||
"lint": "biome check",
|
||||
"prebuild": "npm run clean",
|
||||
"prepack": "rollup -c",
|
||||
"posttest": "npm run lint",
|
||||
"postbuild": "tar -czf dist/archive.tgz LICENSE README.md dist/cjs dist/esm dist/umd docs package.json && npx attw --format ascii dist/archive.tgz && rm dist/archive.tgz"
|
||||
},
|
||||
"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",
|
||||
"ast",
|
||||
"typescript",
|
||||
"css-parser",
|
||||
"css-ast",
|
||||
"css-tools",
|
||||
"minify",
|
||||
"format",
|
||||
"preprocessor"
|
||||
]
|
||||
}
|
||||
128
types/node_modules/@alloc/quick-lru/index.d.ts
generated
vendored
Normal file
128
types/node_modules/@alloc/quick-lru/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
declare namespace QuickLRU {
|
||||
interface Options<KeyType, ValueType> {
|
||||
/**
|
||||
The maximum number of milliseconds an item should remain in the cache.
|
||||
|
||||
@default Infinity
|
||||
|
||||
By default, `maxAge` will be `Infinity`, which means that items will never expire.
|
||||
Lazy expiration upon the next write or read call.
|
||||
|
||||
Individual expiration of an item can be specified by the `set(key, value, maxAge)` method.
|
||||
*/
|
||||
readonly maxAge?: number;
|
||||
|
||||
/**
|
||||
The maximum number of items before evicting the least recently used items.
|
||||
*/
|
||||
readonly maxSize: number;
|
||||
|
||||
/**
|
||||
Called right before an item is evicted from the cache.
|
||||
|
||||
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`).
|
||||
*/
|
||||
onEviction?: (key: KeyType, value: ValueType) => void;
|
||||
}
|
||||
}
|
||||
|
||||
declare class QuickLRU<KeyType, ValueType>
|
||||
implements Iterable<[KeyType, ValueType]> {
|
||||
/**
|
||||
The stored item count.
|
||||
*/
|
||||
readonly size: number;
|
||||
|
||||
/**
|
||||
Simple ["Least Recently Used" (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29).
|
||||
|
||||
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop.
|
||||
|
||||
@example
|
||||
```
|
||||
import QuickLRU = require('quick-lru');
|
||||
|
||||
const lru = new QuickLRU({maxSize: 1000});
|
||||
|
||||
lru.set('🦄', '🌈');
|
||||
|
||||
lru.has('🦄');
|
||||
//=> true
|
||||
|
||||
lru.get('🦄');
|
||||
//=> '🌈'
|
||||
```
|
||||
*/
|
||||
constructor(options: QuickLRU.Options<KeyType, ValueType>);
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[KeyType, ValueType]>;
|
||||
|
||||
/**
|
||||
Set an item. Returns the instance.
|
||||
|
||||
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified in the constructor, otherwise the item will never expire.
|
||||
|
||||
@returns The list instance.
|
||||
*/
|
||||
set(key: KeyType, value: ValueType, options?: {maxAge?: number}): this;
|
||||
|
||||
/**
|
||||
Get an item.
|
||||
|
||||
@returns The stored item or `undefined`.
|
||||
*/
|
||||
get(key: KeyType): ValueType | undefined;
|
||||
|
||||
/**
|
||||
Check if an item exists.
|
||||
*/
|
||||
has(key: KeyType): boolean;
|
||||
|
||||
/**
|
||||
Get an item without marking it as recently used.
|
||||
|
||||
@returns The stored item or `undefined`.
|
||||
*/
|
||||
peek(key: KeyType): ValueType | undefined;
|
||||
|
||||
/**
|
||||
Delete an item.
|
||||
|
||||
@returns `true` if the item is removed or `false` if the item doesn't exist.
|
||||
*/
|
||||
delete(key: KeyType): boolean;
|
||||
|
||||
/**
|
||||
Delete all items.
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
Update the `maxSize` in-place, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee.
|
||||
|
||||
Useful for on-the-fly tuning of cache sizes in live systems.
|
||||
*/
|
||||
resize(maxSize: number): void;
|
||||
|
||||
/**
|
||||
Iterable for all the keys.
|
||||
*/
|
||||
keys(): IterableIterator<KeyType>;
|
||||
|
||||
/**
|
||||
Iterable for all the values.
|
||||
*/
|
||||
values(): IterableIterator<ValueType>;
|
||||
|
||||
/**
|
||||
Iterable for all entries, starting with the oldest (ascending in recency).
|
||||
*/
|
||||
entriesAscending(): IterableIterator<[KeyType, ValueType]>;
|
||||
|
||||
/**
|
||||
Iterable for all entries, starting with the newest (descending in recency).
|
||||
*/
|
||||
entriesDescending(): IterableIterator<[KeyType, ValueType]>;
|
||||
}
|
||||
|
||||
export = QuickLRU;
|
||||
263
types/node_modules/@alloc/quick-lru/index.js
generated
vendored
Normal file
263
types/node_modules/@alloc/quick-lru/index.js
generated
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
'use strict';
|
||||
|
||||
class QuickLRU {
|
||||
constructor(options = {}) {
|
||||
if (!(options.maxSize && options.maxSize > 0)) {
|
||||
throw new TypeError('`maxSize` must be a number greater than 0');
|
||||
}
|
||||
|
||||
if (typeof options.maxAge === 'number' && options.maxAge === 0) {
|
||||
throw new TypeError('`maxAge` must be a number greater than 0');
|
||||
}
|
||||
|
||||
this.maxSize = options.maxSize;
|
||||
this.maxAge = options.maxAge || Infinity;
|
||||
this.onEviction = options.onEviction;
|
||||
this.cache = new Map();
|
||||
this.oldCache = new Map();
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
_emitEvictions(cache) {
|
||||
if (typeof this.onEviction !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [key, item] of cache) {
|
||||
this.onEviction(key, item.value);
|
||||
}
|
||||
}
|
||||
|
||||
_deleteIfExpired(key, item) {
|
||||
if (typeof item.expiry === 'number' && item.expiry <= Date.now()) {
|
||||
if (typeof this.onEviction === 'function') {
|
||||
this.onEviction(key, item.value);
|
||||
}
|
||||
|
||||
return this.delete(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_getOrDeleteIfExpired(key, item) {
|
||||
const deleted = this._deleteIfExpired(key, item);
|
||||
if (deleted === false) {
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
|
||||
_getItemValue(key, item) {
|
||||
return item.expiry ? this._getOrDeleteIfExpired(key, item) : item.value;
|
||||
}
|
||||
|
||||
_peek(key, cache) {
|
||||
const item = cache.get(key);
|
||||
|
||||
return this._getItemValue(key, item);
|
||||
}
|
||||
|
||||
_set(key, value) {
|
||||
this.cache.set(key, value);
|
||||
this._size++;
|
||||
|
||||
if (this._size >= this.maxSize) {
|
||||
this._size = 0;
|
||||
this._emitEvictions(this.oldCache);
|
||||
this.oldCache = this.cache;
|
||||
this.cache = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
_moveToRecent(key, item) {
|
||||
this.oldCache.delete(key);
|
||||
this._set(key, item);
|
||||
}
|
||||
|
||||
* _entriesAscending() {
|
||||
for (const item of this.oldCache) {
|
||||
const [key, value] = item;
|
||||
if (!this.cache.has(key)) {
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of this.cache) {
|
||||
const [key, value] = item;
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get(key) {
|
||||
if (this.cache.has(key)) {
|
||||
const item = this.cache.get(key);
|
||||
|
||||
return this._getItemValue(key, item);
|
||||
}
|
||||
|
||||
if (this.oldCache.has(key)) {
|
||||
const item = this.oldCache.get(key);
|
||||
if (this._deleteIfExpired(key, item) === false) {
|
||||
this._moveToRecent(key, item);
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set(key, value, {maxAge = this.maxAge === Infinity ? undefined : Date.now() + this.maxAge} = {}) {
|
||||
if (this.cache.has(key)) {
|
||||
this.cache.set(key, {
|
||||
value,
|
||||
maxAge
|
||||
});
|
||||
} else {
|
||||
this._set(key, {value, expiry: maxAge});
|
||||
}
|
||||
}
|
||||
|
||||
has(key) {
|
||||
if (this.cache.has(key)) {
|
||||
return !this._deleteIfExpired(key, this.cache.get(key));
|
||||
}
|
||||
|
||||
if (this.oldCache.has(key)) {
|
||||
return !this._deleteIfExpired(key, this.oldCache.get(key));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
peek(key) {
|
||||
if (this.cache.has(key)) {
|
||||
return this._peek(key, this.cache);
|
||||
}
|
||||
|
||||
if (this.oldCache.has(key)) {
|
||||
return this._peek(key, this.oldCache);
|
||||
}
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
const deleted = this.cache.delete(key);
|
||||
if (deleted) {
|
||||
this._size--;
|
||||
}
|
||||
|
||||
return this.oldCache.delete(key) || deleted;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.cache.clear();
|
||||
this.oldCache.clear();
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
resize(newSize) {
|
||||
if (!(newSize && newSize > 0)) {
|
||||
throw new TypeError('`maxSize` must be a number greater than 0');
|
||||
}
|
||||
|
||||
const items = [...this._entriesAscending()];
|
||||
const removeCount = items.length - newSize;
|
||||
if (removeCount < 0) {
|
||||
this.cache = new Map(items);
|
||||
this.oldCache = new Map();
|
||||
this._size = items.length;
|
||||
} else {
|
||||
if (removeCount > 0) {
|
||||
this._emitEvictions(items.slice(0, removeCount));
|
||||
}
|
||||
|
||||
this.oldCache = new Map(items.slice(removeCount));
|
||||
this.cache = new Map();
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
this.maxSize = newSize;
|
||||
}
|
||||
|
||||
* keys() {
|
||||
for (const [key] of this) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
|
||||
* values() {
|
||||
for (const [, value] of this) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
|
||||
* [Symbol.iterator]() {
|
||||
for (const item of this.cache) {
|
||||
const [key, value] = item;
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of this.oldCache) {
|
||||
const [key, value] = item;
|
||||
if (!this.cache.has(key)) {
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* entriesDescending() {
|
||||
let items = [...this.cache];
|
||||
for (let i = items.length - 1; i >= 0; --i) {
|
||||
const item = items[i];
|
||||
const [key, value] = item;
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
|
||||
items = [...this.oldCache];
|
||||
for (let i = items.length - 1; i >= 0; --i) {
|
||||
const item = items[i];
|
||||
const [key, value] = item;
|
||||
if (!this.cache.has(key)) {
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* entriesAscending() {
|
||||
for (const [key, value] of this._entriesAscending()) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
if (!this._size) {
|
||||
return this.oldCache.size;
|
||||
}
|
||||
|
||||
let oldCacheSize = 0;
|
||||
for (const key of this.oldCache.keys()) {
|
||||
if (!this.cache.has(key)) {
|
||||
oldCacheSize++;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(this._size + oldCacheSize, this.maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = QuickLRU;
|
||||
9
types/node_modules/@alloc/quick-lru/license
generated
vendored
Normal file
9
types/node_modules/@alloc/quick-lru/license
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.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.
|
||||
43
types/node_modules/@alloc/quick-lru/package.json
generated
vendored
Normal file
43
types/node_modules/@alloc/quick-lru/package.json
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@alloc/quick-lru",
|
||||
"version": "5.2.0",
|
||||
"description": "Simple “Least Recently Used” (LRU) cache",
|
||||
"license": "MIT",
|
||||
"repository": "sindresorhus/quick-lru",
|
||||
"funding": "https://github.com/sponsors/sindresorhus",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "https://sindresorhus.com"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "xo && nyc ava && tsd"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts"
|
||||
],
|
||||
"keywords": [
|
||||
"lru",
|
||||
"quick",
|
||||
"cache",
|
||||
"caching",
|
||||
"least",
|
||||
"recently",
|
||||
"used",
|
||||
"fast",
|
||||
"map",
|
||||
"hash",
|
||||
"buffer"
|
||||
],
|
||||
"devDependencies": {
|
||||
"ava": "^2.0.0",
|
||||
"coveralls": "^3.0.3",
|
||||
"nyc": "^15.0.0",
|
||||
"tsd": "^0.11.0",
|
||||
"xo": "^0.26.0"
|
||||
}
|
||||
}
|
||||
139
types/node_modules/@alloc/quick-lru/readme.md
generated
vendored
Normal file
139
types/node_modules/@alloc/quick-lru/readme.md
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
# quick-lru [](https://travis-ci.org/sindresorhus/quick-lru) [](https://coveralls.io/github/sindresorhus/quick-lru?branch=master)
|
||||
|
||||
> Simple [“Least Recently Used” (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29)
|
||||
|
||||
Useful when you need to cache something and limit memory usage.
|
||||
|
||||
Inspired by the [`hashlru` algorithm](https://github.com/dominictarr/hashlru#algorithm), but instead uses [`Map`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map) to support keys of any type, not just strings, and values can be `undefined`.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
$ npm install quick-lru
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const QuickLRU = require('quick-lru');
|
||||
|
||||
const lru = new QuickLRU({maxSize: 1000});
|
||||
|
||||
lru.set('🦄', '🌈');
|
||||
|
||||
lru.has('🦄');
|
||||
//=> true
|
||||
|
||||
lru.get('🦄');
|
||||
//=> '🌈'
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### new QuickLRU(options?)
|
||||
|
||||
Returns a new instance.
|
||||
|
||||
### options
|
||||
|
||||
Type: `object`
|
||||
|
||||
#### maxSize
|
||||
|
||||
*Required*\
|
||||
Type: `number`
|
||||
|
||||
The maximum number of items before evicting the least recently used items.
|
||||
|
||||
#### maxAge
|
||||
|
||||
Type: `number`\
|
||||
Default: `Infinity`
|
||||
|
||||
The maximum number of milliseconds an item should remain in cache.
|
||||
By default maxAge will be Infinity, which means that items will never expire.
|
||||
|
||||
Lazy expiration happens upon the next `write` or `read` call.
|
||||
|
||||
Individual expiration of an item can be specified by the `set(key, value, options)` method.
|
||||
|
||||
#### onEviction
|
||||
|
||||
*Optional*\
|
||||
Type: `(key, value) => void`
|
||||
|
||||
Called right before an item is evicted from the cache.
|
||||
|
||||
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`).
|
||||
|
||||
### Instance
|
||||
|
||||
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop.
|
||||
|
||||
Both `key` and `value` can be of any type.
|
||||
|
||||
#### .set(key, value, options?)
|
||||
|
||||
Set an item. Returns the instance.
|
||||
|
||||
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified on the constructor, otherwise the item will never expire.
|
||||
|
||||
#### .get(key)
|
||||
|
||||
Get an item.
|
||||
|
||||
#### .has(key)
|
||||
|
||||
Check if an item exists.
|
||||
|
||||
#### .peek(key)
|
||||
|
||||
Get an item without marking it as recently used.
|
||||
|
||||
#### .delete(key)
|
||||
|
||||
Delete an item.
|
||||
|
||||
Returns `true` if the item is removed or `false` if the item doesn't exist.
|
||||
|
||||
#### .clear()
|
||||
|
||||
Delete all items.
|
||||
|
||||
#### .resize(maxSize)
|
||||
|
||||
Update the `maxSize`, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee.
|
||||
|
||||
Useful for on-the-fly tuning of cache sizes in live systems.
|
||||
|
||||
#### .keys()
|
||||
|
||||
Iterable for all the keys.
|
||||
|
||||
#### .values()
|
||||
|
||||
Iterable for all the values.
|
||||
|
||||
#### .entriesAscending()
|
||||
|
||||
Iterable for all entries, starting with the oldest (ascending in recency).
|
||||
|
||||
#### .entriesDescending()
|
||||
|
||||
Iterable for all entries, starting with the newest (descending in recency).
|
||||
|
||||
#### .size
|
||||
|
||||
The stored item count.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<b>
|
||||
<a href="https://tidelift.com/subscription/pkg/npm-quick-lru?utm_source=npm-quick-lru&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
|
||||
</b>
|
||||
<br>
|
||||
<sub>
|
||||
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
|
||||
</sub>
|
||||
</div>
|
||||
202
types/node_modules/@ampproject/remapping/LICENSE
generated
vendored
Normal file
202
types/node_modules/@ampproject/remapping/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
218
types/node_modules/@ampproject/remapping/README.md
generated
vendored
Normal file
218
types/node_modules/@ampproject/remapping/README.md
generated
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
# @ampproject/remapping
|
||||
|
||||
> Remap sequential sourcemaps through transformations to point at the original source code
|
||||
|
||||
Remapping allows you to take the sourcemaps generated through transforming your code and "remap"
|
||||
them to the original source locations. Think "my minified code, transformed with babel and bundled
|
||||
with webpack", all pointing to the correct location in your original source code.
|
||||
|
||||
With remapping, none of your source code transformations need to be aware of the input's sourcemap,
|
||||
they only need to generate an output sourcemap. This greatly simplifies building custom
|
||||
transformations (think a find-and-replace).
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
npm install @ampproject/remapping
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
function remapping(
|
||||
map: SourceMap | SourceMap[],
|
||||
loader: (file: string, ctx: LoaderContext) => (SourceMap | null | undefined),
|
||||
options?: { excludeContent: boolean, decodedMappings: boolean }
|
||||
): SourceMap;
|
||||
|
||||
// LoaderContext gives the loader the importing sourcemap, tree depth, the ability to override the
|
||||
// "source" location (where child sources are resolved relative to, or the location of original
|
||||
// source), and the ability to override the "content" of an original source for inclusion in the
|
||||
// output sourcemap.
|
||||
type LoaderContext = {
|
||||
readonly importer: string;
|
||||
readonly depth: number;
|
||||
source: string;
|
||||
content: string | null | undefined;
|
||||
}
|
||||
```
|
||||
|
||||
`remapping` takes the final output sourcemap, and a `loader` function. For every source file pointer
|
||||
in the sourcemap, the `loader` will be called with the resolved path. If the path itself represents
|
||||
a transformed file (it has a sourcmap associated with it), then the `loader` should return that
|
||||
sourcemap. If not, the path will be treated as an original, untransformed source code.
|
||||
|
||||
```js
|
||||
// Babel transformed "helloworld.js" into "transformed.js"
|
||||
const transformedMap = JSON.stringify({
|
||||
file: 'transformed.js',
|
||||
// 1st column of 2nd line of output file translates into the 1st source
|
||||
// file, line 3, column 2
|
||||
mappings: ';CAEE',
|
||||
sources: ['helloworld.js'],
|
||||
version: 3,
|
||||
});
|
||||
|
||||
// Uglify minified "transformed.js" into "transformed.min.js"
|
||||
const minifiedTransformedMap = JSON.stringify({
|
||||
file: 'transformed.min.js',
|
||||
// 0th column of 1st line of output file translates into the 1st source
|
||||
// file, line 2, column 1.
|
||||
mappings: 'AACC',
|
||||
names: [],
|
||||
sources: ['transformed.js'],
|
||||
version: 3,
|
||||
});
|
||||
|
||||
const remapped = remapping(
|
||||
minifiedTransformedMap,
|
||||
(file, ctx) => {
|
||||
|
||||
// The "transformed.js" file is an transformed file.
|
||||
if (file === 'transformed.js') {
|
||||
// The root importer is empty.
|
||||
console.assert(ctx.importer === '');
|
||||
// The depth in the sourcemap tree we're currently loading.
|
||||
// The root `minifiedTransformedMap` is depth 0, and its source children are depth 1, etc.
|
||||
console.assert(ctx.depth === 1);
|
||||
|
||||
return transformedMap;
|
||||
}
|
||||
|
||||
// Loader will be called to load transformedMap's source file pointers as well.
|
||||
console.assert(file === 'helloworld.js');
|
||||
// `transformed.js`'s sourcemap points into `helloworld.js`.
|
||||
console.assert(ctx.importer === 'transformed.js');
|
||||
// This is a source child of `transformed`, which is a source child of `minifiedTransformedMap`.
|
||||
console.assert(ctx.depth === 2);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
console.log(remapped);
|
||||
// {
|
||||
// file: 'transpiled.min.js',
|
||||
// mappings: 'AAEE',
|
||||
// sources: ['helloworld.js'],
|
||||
// version: 3,
|
||||
// };
|
||||
```
|
||||
|
||||
In this example, `loader` will be called twice:
|
||||
|
||||
1. `"transformed.js"`, the first source file pointer in the `minifiedTransformedMap`. We return the
|
||||
associated sourcemap for it (its a transformed file, after all) so that sourcemap locations can
|
||||
be traced through it into the source files it represents.
|
||||
2. `"helloworld.js"`, our original, unmodified source code. This file does not have a sourcemap, so
|
||||
we return `null`.
|
||||
|
||||
The `remapped` sourcemap now points from `transformed.min.js` into locations in `helloworld.js`. If
|
||||
you were to read the `mappings`, it says "0th column of the first line output line points to the 1st
|
||||
column of the 2nd line of the file `helloworld.js`".
|
||||
|
||||
### Multiple transformations of a file
|
||||
|
||||
As a convenience, if you have multiple single-source transformations of a file, you may pass an
|
||||
array of sourcemap files in the order of most-recent transformation sourcemap first. Note that this
|
||||
changes the `importer` and `depth` of each call to our loader. So our above example could have been
|
||||
written as:
|
||||
|
||||
```js
|
||||
const remapped = remapping(
|
||||
[minifiedTransformedMap, transformedMap],
|
||||
() => null
|
||||
);
|
||||
|
||||
console.log(remapped);
|
||||
// {
|
||||
// file: 'transpiled.min.js',
|
||||
// mappings: 'AAEE',
|
||||
// sources: ['helloworld.js'],
|
||||
// version: 3,
|
||||
// };
|
||||
```
|
||||
|
||||
### Advanced control of the loading graph
|
||||
|
||||
#### `source`
|
||||
|
||||
The `source` property can overridden to any value to change the location of the current load. Eg,
|
||||
for an original source file, it allows us to change the location to the original source regardless
|
||||
of what the sourcemap source entry says. And for transformed files, it allows us to change the
|
||||
relative resolving location for child sources of the loaded sourcemap.
|
||||
|
||||
```js
|
||||
const remapped = remapping(
|
||||
minifiedTransformedMap,
|
||||
(file, ctx) => {
|
||||
|
||||
if (file === 'transformed.js') {
|
||||
// We pretend the transformed.js file actually exists in the 'src/' directory. When the nested
|
||||
// source files are loaded, they will now be relative to `src/`.
|
||||
ctx.source = 'src/transformed.js';
|
||||
return transformedMap;
|
||||
}
|
||||
|
||||
console.assert(file === 'src/helloworld.js');
|
||||
// We could futher change the source of this original file, eg, to be inside a nested directory
|
||||
// itself. This will be reflected in the remapped sourcemap.
|
||||
ctx.source = 'src/nested/transformed.js';
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
console.log(remapped);
|
||||
// {
|
||||
// …,
|
||||
// sources: ['src/nested/helloworld.js'],
|
||||
// };
|
||||
```
|
||||
|
||||
|
||||
#### `content`
|
||||
|
||||
The `content` property can be overridden when we encounter an original source file. Eg, this allows
|
||||
you to manually provide the source content of the original file regardless of whether the
|
||||
`sourcesContent` field is present in the parent sourcemap. It can also be set to `null` to remove
|
||||
the source content.
|
||||
|
||||
```js
|
||||
const remapped = remapping(
|
||||
minifiedTransformedMap,
|
||||
(file, ctx) => {
|
||||
|
||||
if (file === 'transformed.js') {
|
||||
// transformedMap does not include a `sourcesContent` field, so usually the remapped sourcemap
|
||||
// would not include any `sourcesContent` values.
|
||||
return transformedMap;
|
||||
}
|
||||
|
||||
console.assert(file === 'helloworld.js');
|
||||
// We can read the file to provide the source content.
|
||||
ctx.content = fs.readFileSync(file, 'utf8');
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
console.log(remapped);
|
||||
// {
|
||||
// …,
|
||||
// sourcesContent: [
|
||||
// 'console.log("Hello world!")',
|
||||
// ],
|
||||
// };
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
#### excludeContent
|
||||
|
||||
By default, `excludeContent` is `false`. Passing `{ excludeContent: true }` will exclude the
|
||||
`sourcesContent` field from the returned sourcemap. This is mainly useful when you want to reduce
|
||||
the size out the sourcemap.
|
||||
|
||||
#### decodedMappings
|
||||
|
||||
By default, `decodedMappings` is `false`. Passing `{ decodedMappings: true }` will leave the
|
||||
`mappings` field in a [decoded state](https://github.com/rich-harris/sourcemap-codec) instead of
|
||||
encoding into a VLQ string.
|
||||
197
types/node_modules/@ampproject/remapping/dist/remapping.mjs
generated
vendored
Normal file
197
types/node_modules/@ampproject/remapping/dist/remapping.mjs
generated
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
import { decodedMappings, traceSegment, TraceMap } from '@jridgewell/trace-mapping';
|
||||
import { GenMapping, maybeAddSegment, setSourceContent, setIgnore, toDecodedMap, toEncodedMap } from '@jridgewell/gen-mapping';
|
||||
|
||||
const SOURCELESS_MAPPING = /* #__PURE__ */ SegmentObject('', -1, -1, '', null, false);
|
||||
const EMPTY_SOURCES = [];
|
||||
function SegmentObject(source, line, column, name, content, ignore) {
|
||||
return { source, line, column, name, content, ignore };
|
||||
}
|
||||
function Source(map, sources, source, content, ignore) {
|
||||
return {
|
||||
map,
|
||||
sources,
|
||||
source,
|
||||
content,
|
||||
ignore,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* MapSource represents a single sourcemap, with the ability to trace mappings into its child nodes
|
||||
* (which may themselves be SourceMapTrees).
|
||||
*/
|
||||
function MapSource(map, sources) {
|
||||
return Source(map, sources, '', null, false);
|
||||
}
|
||||
/**
|
||||
* A "leaf" node in the sourcemap tree, representing an original, unmodified source file. Recursive
|
||||
* segment tracing ends at the `OriginalSource`.
|
||||
*/
|
||||
function OriginalSource(source, content, ignore) {
|
||||
return Source(null, EMPTY_SOURCES, source, content, ignore);
|
||||
}
|
||||
/**
|
||||
* traceMappings is only called on the root level SourceMapTree, and begins the process of
|
||||
* resolving each mapping in terms of the original source files.
|
||||
*/
|
||||
function traceMappings(tree) {
|
||||
// TODO: Eventually support sourceRoot, which has to be removed because the sources are already
|
||||
// fully resolved. We'll need to make sources relative to the sourceRoot before adding them.
|
||||
const gen = new GenMapping({ file: tree.map.file });
|
||||
const { sources: rootSources, map } = tree;
|
||||
const rootNames = map.names;
|
||||
const rootMappings = decodedMappings(map);
|
||||
for (let i = 0; i < rootMappings.length; i++) {
|
||||
const segments = rootMappings[i];
|
||||
for (let j = 0; j < segments.length; j++) {
|
||||
const segment = segments[j];
|
||||
const genCol = segment[0];
|
||||
let traced = SOURCELESS_MAPPING;
|
||||
// 1-length segments only move the current generated column, there's no source information
|
||||
// to gather from it.
|
||||
if (segment.length !== 1) {
|
||||
const source = rootSources[segment[1]];
|
||||
traced = originalPositionFor(source, segment[2], segment[3], segment.length === 5 ? rootNames[segment[4]] : '');
|
||||
// If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a
|
||||
// respective segment into an original source.
|
||||
if (traced == null)
|
||||
continue;
|
||||
}
|
||||
const { column, line, name, content, source, ignore } = traced;
|
||||
maybeAddSegment(gen, i, genCol, source, line, column, name);
|
||||
if (source && content != null)
|
||||
setSourceContent(gen, source, content);
|
||||
if (ignore)
|
||||
setIgnore(gen, source, true);
|
||||
}
|
||||
}
|
||||
return gen;
|
||||
}
|
||||
/**
|
||||
* originalPositionFor is only called on children SourceMapTrees. It recurses down into its own
|
||||
* child SourceMapTrees, until we find the original source map.
|
||||
*/
|
||||
function originalPositionFor(source, line, column, name) {
|
||||
if (!source.map) {
|
||||
return SegmentObject(source.source, line, column, name, source.content, source.ignore);
|
||||
}
|
||||
const segment = traceSegment(source.map, line, column);
|
||||
// If we couldn't find a segment, then this doesn't exist in the sourcemap.
|
||||
if (segment == null)
|
||||
return null;
|
||||
// 1-length segments only move the current generated column, there's no source information
|
||||
// to gather from it.
|
||||
if (segment.length === 1)
|
||||
return SOURCELESS_MAPPING;
|
||||
return originalPositionFor(source.sources[segment[1]], segment[2], segment[3], segment.length === 5 ? source.map.names[segment[4]] : name);
|
||||
}
|
||||
|
||||
function asArray(value) {
|
||||
if (Array.isArray(value))
|
||||
return value;
|
||||
return [value];
|
||||
}
|
||||
/**
|
||||
* Recursively builds a tree structure out of sourcemap files, with each node
|
||||
* being either an `OriginalSource` "leaf" or a `SourceMapTree` composed of
|
||||
* `OriginalSource`s and `SourceMapTree`s.
|
||||
*
|
||||
* Every sourcemap is composed of a collection of source files and mappings
|
||||
* into locations of those source files. When we generate a `SourceMapTree` for
|
||||
* the sourcemap, we attempt to load each source file's own sourcemap. If it
|
||||
* does not have an associated sourcemap, it is considered an original,
|
||||
* unmodified source file.
|
||||
*/
|
||||
function buildSourceMapTree(input, loader) {
|
||||
const maps = asArray(input).map((m) => new TraceMap(m, ''));
|
||||
const map = maps.pop();
|
||||
for (let i = 0; i < maps.length; i++) {
|
||||
if (maps[i].sources.length > 1) {
|
||||
throw new Error(`Transformation map ${i} must have exactly one source file.\n` +
|
||||
'Did you specify these with the most recent transformation maps first?');
|
||||
}
|
||||
}
|
||||
let tree = build(map, loader, '', 0);
|
||||
for (let i = maps.length - 1; i >= 0; i--) {
|
||||
tree = MapSource(maps[i], [tree]);
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
function build(map, loader, importer, importerDepth) {
|
||||
const { resolvedSources, sourcesContent, ignoreList } = map;
|
||||
const depth = importerDepth + 1;
|
||||
const children = resolvedSources.map((sourceFile, i) => {
|
||||
// The loading context gives the loader more information about why this file is being loaded
|
||||
// (eg, from which importer). It also allows the loader to override the location of the loaded
|
||||
// sourcemap/original source, or to override the content in the sourcesContent field if it's
|
||||
// an unmodified source file.
|
||||
const ctx = {
|
||||
importer,
|
||||
depth,
|
||||
source: sourceFile || '',
|
||||
content: undefined,
|
||||
ignore: undefined,
|
||||
};
|
||||
// Use the provided loader callback to retrieve the file's sourcemap.
|
||||
// TODO: We should eventually support async loading of sourcemap files.
|
||||
const sourceMap = loader(ctx.source, ctx);
|
||||
const { source, content, ignore } = ctx;
|
||||
// If there is a sourcemap, then we need to recurse into it to load its source files.
|
||||
if (sourceMap)
|
||||
return build(new TraceMap(sourceMap, source), loader, source, depth);
|
||||
// Else, it's an unmodified source file.
|
||||
// The contents of this unmodified source file can be overridden via the loader context,
|
||||
// allowing it to be explicitly null or a string. If it remains undefined, we fall back to
|
||||
// the importing sourcemap's `sourcesContent` field.
|
||||
const sourceContent = content !== undefined ? content : sourcesContent ? sourcesContent[i] : null;
|
||||
const ignored = ignore !== undefined ? ignore : ignoreList ? ignoreList.includes(i) : false;
|
||||
return OriginalSource(source, sourceContent, ignored);
|
||||
});
|
||||
return MapSource(map, children);
|
||||
}
|
||||
|
||||
/**
|
||||
* A SourceMap v3 compatible sourcemap, which only includes fields that were
|
||||
* provided to it.
|
||||
*/
|
||||
class SourceMap {
|
||||
constructor(map, options) {
|
||||
const out = options.decodedMappings ? toDecodedMap(map) : toEncodedMap(map);
|
||||
this.version = out.version; // SourceMap spec says this should be first.
|
||||
this.file = out.file;
|
||||
this.mappings = out.mappings;
|
||||
this.names = out.names;
|
||||
this.ignoreList = out.ignoreList;
|
||||
this.sourceRoot = out.sourceRoot;
|
||||
this.sources = out.sources;
|
||||
if (!options.excludeContent) {
|
||||
this.sourcesContent = out.sourcesContent;
|
||||
}
|
||||
}
|
||||
toString() {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traces through all the mappings in the root sourcemap, through the sources
|
||||
* (and their sourcemaps), all the way back to the original source location.
|
||||
*
|
||||
* `loader` will be called every time we encounter a source file. If it returns
|
||||
* a sourcemap, we will recurse into that sourcemap to continue the trace. If
|
||||
* it returns a falsey value, that source file is treated as an original,
|
||||
* unmodified source file.
|
||||
*
|
||||
* Pass `excludeContent` to exclude any self-containing source file content
|
||||
* from the output sourcemap.
|
||||
*
|
||||
* Pass `decodedMappings` to receive a SourceMap with decoded (instead of
|
||||
* VLQ encoded) mappings.
|
||||
*/
|
||||
function remapping(input, loader, options) {
|
||||
const opts = typeof options === 'object' ? options : { excludeContent: !!options, decodedMappings: false };
|
||||
const tree = buildSourceMapTree(input, loader);
|
||||
return new SourceMap(traceMappings(tree), opts);
|
||||
}
|
||||
|
||||
export { remapping as default };
|
||||
//# sourceMappingURL=remapping.mjs.map
|
||||
1
types/node_modules/@ampproject/remapping/dist/remapping.mjs.map
generated
vendored
Normal file
1
types/node_modules/@ampproject/remapping/dist/remapping.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
202
types/node_modules/@ampproject/remapping/dist/remapping.umd.js
generated
vendored
Normal file
202
types/node_modules/@ampproject/remapping/dist/remapping.umd.js
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@jridgewell/trace-mapping'), require('@jridgewell/gen-mapping')) :
|
||||
typeof define === 'function' && define.amd ? define(['@jridgewell/trace-mapping', '@jridgewell/gen-mapping'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.remapping = factory(global.traceMapping, global.genMapping));
|
||||
})(this, (function (traceMapping, genMapping) { 'use strict';
|
||||
|
||||
const SOURCELESS_MAPPING = /* #__PURE__ */ SegmentObject('', -1, -1, '', null, false);
|
||||
const EMPTY_SOURCES = [];
|
||||
function SegmentObject(source, line, column, name, content, ignore) {
|
||||
return { source, line, column, name, content, ignore };
|
||||
}
|
||||
function Source(map, sources, source, content, ignore) {
|
||||
return {
|
||||
map,
|
||||
sources,
|
||||
source,
|
||||
content,
|
||||
ignore,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* MapSource represents a single sourcemap, with the ability to trace mappings into its child nodes
|
||||
* (which may themselves be SourceMapTrees).
|
||||
*/
|
||||
function MapSource(map, sources) {
|
||||
return Source(map, sources, '', null, false);
|
||||
}
|
||||
/**
|
||||
* A "leaf" node in the sourcemap tree, representing an original, unmodified source file. Recursive
|
||||
* segment tracing ends at the `OriginalSource`.
|
||||
*/
|
||||
function OriginalSource(source, content, ignore) {
|
||||
return Source(null, EMPTY_SOURCES, source, content, ignore);
|
||||
}
|
||||
/**
|
||||
* traceMappings is only called on the root level SourceMapTree, and begins the process of
|
||||
* resolving each mapping in terms of the original source files.
|
||||
*/
|
||||
function traceMappings(tree) {
|
||||
// TODO: Eventually support sourceRoot, which has to be removed because the sources are already
|
||||
// fully resolved. We'll need to make sources relative to the sourceRoot before adding them.
|
||||
const gen = new genMapping.GenMapping({ file: tree.map.file });
|
||||
const { sources: rootSources, map } = tree;
|
||||
const rootNames = map.names;
|
||||
const rootMappings = traceMapping.decodedMappings(map);
|
||||
for (let i = 0; i < rootMappings.length; i++) {
|
||||
const segments = rootMappings[i];
|
||||
for (let j = 0; j < segments.length; j++) {
|
||||
const segment = segments[j];
|
||||
const genCol = segment[0];
|
||||
let traced = SOURCELESS_MAPPING;
|
||||
// 1-length segments only move the current generated column, there's no source information
|
||||
// to gather from it.
|
||||
if (segment.length !== 1) {
|
||||
const source = rootSources[segment[1]];
|
||||
traced = originalPositionFor(source, segment[2], segment[3], segment.length === 5 ? rootNames[segment[4]] : '');
|
||||
// If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a
|
||||
// respective segment into an original source.
|
||||
if (traced == null)
|
||||
continue;
|
||||
}
|
||||
const { column, line, name, content, source, ignore } = traced;
|
||||
genMapping.maybeAddSegment(gen, i, genCol, source, line, column, name);
|
||||
if (source && content != null)
|
||||
genMapping.setSourceContent(gen, source, content);
|
||||
if (ignore)
|
||||
genMapping.setIgnore(gen, source, true);
|
||||
}
|
||||
}
|
||||
return gen;
|
||||
}
|
||||
/**
|
||||
* originalPositionFor is only called on children SourceMapTrees. It recurses down into its own
|
||||
* child SourceMapTrees, until we find the original source map.
|
||||
*/
|
||||
function originalPositionFor(source, line, column, name) {
|
||||
if (!source.map) {
|
||||
return SegmentObject(source.source, line, column, name, source.content, source.ignore);
|
||||
}
|
||||
const segment = traceMapping.traceSegment(source.map, line, column);
|
||||
// If we couldn't find a segment, then this doesn't exist in the sourcemap.
|
||||
if (segment == null)
|
||||
return null;
|
||||
// 1-length segments only move the current generated column, there's no source information
|
||||
// to gather from it.
|
||||
if (segment.length === 1)
|
||||
return SOURCELESS_MAPPING;
|
||||
return originalPositionFor(source.sources[segment[1]], segment[2], segment[3], segment.length === 5 ? source.map.names[segment[4]] : name);
|
||||
}
|
||||
|
||||
function asArray(value) {
|
||||
if (Array.isArray(value))
|
||||
return value;
|
||||
return [value];
|
||||
}
|
||||
/**
|
||||
* Recursively builds a tree structure out of sourcemap files, with each node
|
||||
* being either an `OriginalSource` "leaf" or a `SourceMapTree` composed of
|
||||
* `OriginalSource`s and `SourceMapTree`s.
|
||||
*
|
||||
* Every sourcemap is composed of a collection of source files and mappings
|
||||
* into locations of those source files. When we generate a `SourceMapTree` for
|
||||
* the sourcemap, we attempt to load each source file's own sourcemap. If it
|
||||
* does not have an associated sourcemap, it is considered an original,
|
||||
* unmodified source file.
|
||||
*/
|
||||
function buildSourceMapTree(input, loader) {
|
||||
const maps = asArray(input).map((m) => new traceMapping.TraceMap(m, ''));
|
||||
const map = maps.pop();
|
||||
for (let i = 0; i < maps.length; i++) {
|
||||
if (maps[i].sources.length > 1) {
|
||||
throw new Error(`Transformation map ${i} must have exactly one source file.\n` +
|
||||
'Did you specify these with the most recent transformation maps first?');
|
||||
}
|
||||
}
|
||||
let tree = build(map, loader, '', 0);
|
||||
for (let i = maps.length - 1; i >= 0; i--) {
|
||||
tree = MapSource(maps[i], [tree]);
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
function build(map, loader, importer, importerDepth) {
|
||||
const { resolvedSources, sourcesContent, ignoreList } = map;
|
||||
const depth = importerDepth + 1;
|
||||
const children = resolvedSources.map((sourceFile, i) => {
|
||||
// The loading context gives the loader more information about why this file is being loaded
|
||||
// (eg, from which importer). It also allows the loader to override the location of the loaded
|
||||
// sourcemap/original source, or to override the content in the sourcesContent field if it's
|
||||
// an unmodified source file.
|
||||
const ctx = {
|
||||
importer,
|
||||
depth,
|
||||
source: sourceFile || '',
|
||||
content: undefined,
|
||||
ignore: undefined,
|
||||
};
|
||||
// Use the provided loader callback to retrieve the file's sourcemap.
|
||||
// TODO: We should eventually support async loading of sourcemap files.
|
||||
const sourceMap = loader(ctx.source, ctx);
|
||||
const { source, content, ignore } = ctx;
|
||||
// If there is a sourcemap, then we need to recurse into it to load its source files.
|
||||
if (sourceMap)
|
||||
return build(new traceMapping.TraceMap(sourceMap, source), loader, source, depth);
|
||||
// Else, it's an unmodified source file.
|
||||
// The contents of this unmodified source file can be overridden via the loader context,
|
||||
// allowing it to be explicitly null or a string. If it remains undefined, we fall back to
|
||||
// the importing sourcemap's `sourcesContent` field.
|
||||
const sourceContent = content !== undefined ? content : sourcesContent ? sourcesContent[i] : null;
|
||||
const ignored = ignore !== undefined ? ignore : ignoreList ? ignoreList.includes(i) : false;
|
||||
return OriginalSource(source, sourceContent, ignored);
|
||||
});
|
||||
return MapSource(map, children);
|
||||
}
|
||||
|
||||
/**
|
||||
* A SourceMap v3 compatible sourcemap, which only includes fields that were
|
||||
* provided to it.
|
||||
*/
|
||||
class SourceMap {
|
||||
constructor(map, options) {
|
||||
const out = options.decodedMappings ? genMapping.toDecodedMap(map) : genMapping.toEncodedMap(map);
|
||||
this.version = out.version; // SourceMap spec says this should be first.
|
||||
this.file = out.file;
|
||||
this.mappings = out.mappings;
|
||||
this.names = out.names;
|
||||
this.ignoreList = out.ignoreList;
|
||||
this.sourceRoot = out.sourceRoot;
|
||||
this.sources = out.sources;
|
||||
if (!options.excludeContent) {
|
||||
this.sourcesContent = out.sourcesContent;
|
||||
}
|
||||
}
|
||||
toString() {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traces through all the mappings in the root sourcemap, through the sources
|
||||
* (and their sourcemaps), all the way back to the original source location.
|
||||
*
|
||||
* `loader` will be called every time we encounter a source file. If it returns
|
||||
* a sourcemap, we will recurse into that sourcemap to continue the trace. If
|
||||
* it returns a falsey value, that source file is treated as an original,
|
||||
* unmodified source file.
|
||||
*
|
||||
* Pass `excludeContent` to exclude any self-containing source file content
|
||||
* from the output sourcemap.
|
||||
*
|
||||
* Pass `decodedMappings` to receive a SourceMap with decoded (instead of
|
||||
* VLQ encoded) mappings.
|
||||
*/
|
||||
function remapping(input, loader, options) {
|
||||
const opts = typeof options === 'object' ? options : { excludeContent: !!options, decodedMappings: false };
|
||||
const tree = buildSourceMapTree(input, loader);
|
||||
return new SourceMap(traceMappings(tree), opts);
|
||||
}
|
||||
|
||||
return remapping;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=remapping.umd.js.map
|
||||
1
types/node_modules/@ampproject/remapping/dist/remapping.umd.js.map
generated
vendored
Normal file
1
types/node_modules/@ampproject/remapping/dist/remapping.umd.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
14
types/node_modules/@ampproject/remapping/dist/types/build-source-map-tree.d.ts
generated
vendored
Normal file
14
types/node_modules/@ampproject/remapping/dist/types/build-source-map-tree.d.ts
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
import type { MapSource as MapSourceType } from './source-map-tree';
|
||||
import type { SourceMapInput, SourceMapLoader } from './types';
|
||||
/**
|
||||
* Recursively builds a tree structure out of sourcemap files, with each node
|
||||
* being either an `OriginalSource` "leaf" or a `SourceMapTree` composed of
|
||||
* `OriginalSource`s and `SourceMapTree`s.
|
||||
*
|
||||
* Every sourcemap is composed of a collection of source files and mappings
|
||||
* into locations of those source files. When we generate a `SourceMapTree` for
|
||||
* the sourcemap, we attempt to load each source file's own sourcemap. If it
|
||||
* does not have an associated sourcemap, it is considered an original,
|
||||
* unmodified source file.
|
||||
*/
|
||||
export default function buildSourceMapTree(input: SourceMapInput | SourceMapInput[], loader: SourceMapLoader): MapSourceType;
|
||||
20
types/node_modules/@ampproject/remapping/dist/types/remapping.d.ts
generated
vendored
Normal file
20
types/node_modules/@ampproject/remapping/dist/types/remapping.d.ts
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
import SourceMap from './source-map';
|
||||
import type { SourceMapInput, SourceMapLoader, Options } from './types';
|
||||
export type { SourceMapSegment, EncodedSourceMap, EncodedSourceMap as RawSourceMap, DecodedSourceMap, SourceMapInput, SourceMapLoader, LoaderContext, Options, } from './types';
|
||||
export type { SourceMap };
|
||||
/**
|
||||
* Traces through all the mappings in the root sourcemap, through the sources
|
||||
* (and their sourcemaps), all the way back to the original source location.
|
||||
*
|
||||
* `loader` will be called every time we encounter a source file. If it returns
|
||||
* a sourcemap, we will recurse into that sourcemap to continue the trace. If
|
||||
* it returns a falsey value, that source file is treated as an original,
|
||||
* unmodified source file.
|
||||
*
|
||||
* Pass `excludeContent` to exclude any self-containing source file content
|
||||
* from the output sourcemap.
|
||||
*
|
||||
* Pass `decodedMappings` to receive a SourceMap with decoded (instead of
|
||||
* VLQ encoded) mappings.
|
||||
*/
|
||||
export default function remapping(input: SourceMapInput | SourceMapInput[], loader: SourceMapLoader, options?: boolean | Options): SourceMap;
|
||||
45
types/node_modules/@ampproject/remapping/dist/types/source-map-tree.d.ts
generated
vendored
Normal file
45
types/node_modules/@ampproject/remapping/dist/types/source-map-tree.d.ts
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
import { GenMapping } from '@jridgewell/gen-mapping';
|
||||
import type { TraceMap } from '@jridgewell/trace-mapping';
|
||||
export declare type SourceMapSegmentObject = {
|
||||
column: number;
|
||||
line: number;
|
||||
name: string;
|
||||
source: string;
|
||||
content: string | null;
|
||||
ignore: boolean;
|
||||
};
|
||||
export declare type OriginalSource = {
|
||||
map: null;
|
||||
sources: Sources[];
|
||||
source: string;
|
||||
content: string | null;
|
||||
ignore: boolean;
|
||||
};
|
||||
export declare type MapSource = {
|
||||
map: TraceMap;
|
||||
sources: Sources[];
|
||||
source: string;
|
||||
content: null;
|
||||
ignore: false;
|
||||
};
|
||||
export declare type Sources = OriginalSource | MapSource;
|
||||
/**
|
||||
* MapSource represents a single sourcemap, with the ability to trace mappings into its child nodes
|
||||
* (which may themselves be SourceMapTrees).
|
||||
*/
|
||||
export declare function MapSource(map: TraceMap, sources: Sources[]): MapSource;
|
||||
/**
|
||||
* A "leaf" node in the sourcemap tree, representing an original, unmodified source file. Recursive
|
||||
* segment tracing ends at the `OriginalSource`.
|
||||
*/
|
||||
export declare function OriginalSource(source: string, content: string | null, ignore: boolean): OriginalSource;
|
||||
/**
|
||||
* traceMappings is only called on the root level SourceMapTree, and begins the process of
|
||||
* resolving each mapping in terms of the original source files.
|
||||
*/
|
||||
export declare function traceMappings(tree: MapSource): GenMapping;
|
||||
/**
|
||||
* originalPositionFor is only called on children SourceMapTrees. It recurses down into its own
|
||||
* child SourceMapTrees, until we find the original source map.
|
||||
*/
|
||||
export declare function originalPositionFor(source: Sources, line: number, column: number, name: string): SourceMapSegmentObject | null;
|
||||
18
types/node_modules/@ampproject/remapping/dist/types/source-map.d.ts
generated
vendored
Normal file
18
types/node_modules/@ampproject/remapping/dist/types/source-map.d.ts
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
import type { GenMapping } from '@jridgewell/gen-mapping';
|
||||
import type { DecodedSourceMap, EncodedSourceMap, Options } from './types';
|
||||
/**
|
||||
* A SourceMap v3 compatible sourcemap, which only includes fields that were
|
||||
* provided to it.
|
||||
*/
|
||||
export default class SourceMap {
|
||||
file?: string | null;
|
||||
mappings: EncodedSourceMap['mappings'] | DecodedSourceMap['mappings'];
|
||||
sourceRoot?: string;
|
||||
names: string[];
|
||||
sources: (string | null)[];
|
||||
sourcesContent?: (string | null)[];
|
||||
version: 3;
|
||||
ignoreList: number[] | undefined;
|
||||
constructor(map: GenMapping, options: Options);
|
||||
toString(): string;
|
||||
}
|
||||
15
types/node_modules/@ampproject/remapping/dist/types/types.d.ts
generated
vendored
Normal file
15
types/node_modules/@ampproject/remapping/dist/types/types.d.ts
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
import type { SourceMapInput } from '@jridgewell/trace-mapping';
|
||||
export type { SourceMapSegment, DecodedSourceMap, EncodedSourceMap, } from '@jridgewell/trace-mapping';
|
||||
export type { SourceMapInput };
|
||||
export declare type LoaderContext = {
|
||||
readonly importer: string;
|
||||
readonly depth: number;
|
||||
source: string;
|
||||
content: string | null | undefined;
|
||||
ignore: boolean | undefined;
|
||||
};
|
||||
export declare type SourceMapLoader = (file: string, ctx: LoaderContext) => SourceMapInput | null | undefined | void;
|
||||
export declare type Options = {
|
||||
excludeContent?: boolean;
|
||||
decodedMappings?: boolean;
|
||||
};
|
||||
75
types/node_modules/@ampproject/remapping/package.json
generated
vendored
Normal file
75
types/node_modules/@ampproject/remapping/package.json
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "@ampproject/remapping",
|
||||
"version": "2.3.0",
|
||||
"description": "Remap sequential sourcemaps through transformations to point at the original source code",
|
||||
"keywords": [
|
||||
"source",
|
||||
"map",
|
||||
"remap"
|
||||
],
|
||||
"main": "dist/remapping.umd.js",
|
||||
"module": "dist/remapping.mjs",
|
||||
"types": "dist/types/remapping.d.ts",
|
||||
"exports": {
|
||||
".": [
|
||||
{
|
||||
"types": "./dist/types/remapping.d.ts",
|
||||
"browser": "./dist/remapping.umd.js",
|
||||
"require": "./dist/remapping.umd.js",
|
||||
"import": "./dist/remapping.mjs"
|
||||
},
|
||||
"./dist/remapping.umd.js"
|
||||
],
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"author": "Justin Ridgewell <jridgewell@google.com>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ampproject/remapping.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "run-s -n build:*",
|
||||
"build:rollup": "rollup -c rollup.config.js",
|
||||
"build:ts": "tsc --project tsconfig.build.json",
|
||||
"lint": "run-s -n lint:*",
|
||||
"lint:prettier": "npm run test:lint:prettier -- --write",
|
||||
"lint:ts": "npm run test:lint:ts -- --fix",
|
||||
"prebuild": "rm -rf dist",
|
||||
"prepublishOnly": "npm run preversion",
|
||||
"preversion": "run-s test build",
|
||||
"test": "run-s -n test:lint test:only",
|
||||
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
|
||||
"test:lint": "run-s -n test:lint:*",
|
||||
"test:lint:prettier": "prettier --check '{src,test}/**/*.ts'",
|
||||
"test:lint:ts": "eslint '{src,test}/**/*.ts'",
|
||||
"test:only": "jest --coverage",
|
||||
"test:watch": "jest --coverage --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "8.3.2",
|
||||
"@types/jest": "27.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.20.0",
|
||||
"@typescript-eslint/parser": "5.20.0",
|
||||
"eslint": "8.14.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"jest": "27.5.1",
|
||||
"jest-config": "27.5.1",
|
||||
"npm-run-all": "4.1.5",
|
||||
"prettier": "2.6.2",
|
||||
"rollup": "2.70.2",
|
||||
"ts-jest": "27.1.4",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
}
|
||||
}
|
||||
21
types/node_modules/@asamuzakjp/css-color/LICENSE
generated
vendored
Normal file
21
types/node_modules/@asamuzakjp/css-color/LICENSE
generated
vendored
Normal 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
types/node_modules/@asamuzakjp/css-color/README.md
generated
vendored
Normal file
316
types/node_modules/@asamuzakjp/css-color/README.md
generated
vendored
Normal file
@ -0,0 +1,316 @@
|
||||
# CSS color
|
||||
|
||||
[](https://github.com/asamuzaK/cssColor/actions/workflows/node.js.yml)
|
||||
[](https://github.com/asamuzaK/cssColor/actions/workflows/github-code-scanning/codeql)
|
||||
[](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 = convert.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
|
||||
220
types/node_modules/@asamuzakjp/css-color/dist/browser/css-color.min.js
generated
vendored
Normal file
220
types/node_modules/@asamuzakjp/css-color/dist/browser/css-color.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
types/node_modules/@asamuzakjp/css-color/dist/browser/css-color.min.js.map
generated
vendored
Normal file
1
types/node_modules/@asamuzakjp/css-color/dist/browser/css-color.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
5666
types/node_modules/@asamuzakjp/css-color/dist/cjs/index.cjs
generated
vendored
Normal file
5666
types/node_modules/@asamuzakjp/css-color/dist/cjs/index.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
types/node_modules/@asamuzakjp/css-color/dist/cjs/index.cjs.map
generated
vendored
Normal file
1
types/node_modules/@asamuzakjp/css-color/dist/cjs/index.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
109
types/node_modules/@asamuzakjp/css-color/dist/cjs/index.d.cts
generated
vendored
Normal file
109
types/node_modules/@asamuzakjp/css-color/dist/cjs/index.d.cts
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* typedef
|
||||
*/
|
||||
/**
|
||||
* @typedef Options - options
|
||||
* @property [alpha] - enable alpha
|
||||
* @property [colorSpace] - color space
|
||||
* @property [currentColor] - color for currentcolor
|
||||
* @property [customProperty] - custom properties
|
||||
* @property [d50] - white point in d50
|
||||
* @property [dimension] - dimension
|
||||
* @property [format] - output format
|
||||
* @property [key] - key
|
||||
*/
|
||||
interface Options {
|
||||
alpha?: boolean;
|
||||
colorScheme?: string;
|
||||
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;
|
||||
resolveGradient: (value: string, opt?: Options) => string;
|
||||
splitValue: (value: string, opt?: Options) => string[];
|
||||
};
|
||||
|
||||
export { convert, resolve, utils };
|
||||
17
types/node_modules/@asamuzakjp/css-color/dist/esm/index.d.ts
generated
vendored
Normal file
17
types/node_modules/@asamuzakjp/css-color/dist/esm/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
/*!
|
||||
* 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;
|
||||
resolveGradient: (value: string, opt?: import('./js/typedef.js').Options) => string;
|
||||
splitValue: (value: string, opt?: import('./js/typedef.js').Options) => string[];
|
||||
};
|
||||
27
types/node_modules/@asamuzakjp/css-color/dist/esm/index.js
generated
vendored
Normal file
27
types/node_modules/@asamuzakjp/css-color/dist/esm/index.js
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
import { cssCalc } from "./js/css-calc.js";
|
||||
import { resolveGradient, isGradient } from "./js/css-gradient.js";
|
||||
import { cssVar } from "./js/css-var.js";
|
||||
import { splitValue, isColor, 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,
|
||||
cssVar,
|
||||
extractDashedIdent,
|
||||
isColor,
|
||||
isGradient,
|
||||
resolveGradient,
|
||||
splitValue
|
||||
};
|
||||
export {
|
||||
convert,
|
||||
resolve,
|
||||
utils
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user