Compare commits

..

1 Commits

Author SHA1 Message Date
hobokenchicken c6b71a29a9 Initial push 2025-08-22 18:12:32 -04:00
267 changed files with 0 additions and 164342 deletions
-34
View File
@@ -1,34 +0,0 @@
name: CI & Lighthouse
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Run Lighthouse audit
uses: lighthouse-checker/action@v1
with:
urls: |
http://localhost:8080/
prComment: true
serverCommand: 'npm run preview -- --port 8080'
waitForServer: true
-15
View File
@@ -1,15 +0,0 @@
node_modules
dist
lighthouse-report.json
.DS_Store
.env
screenshot*.png
*.md
*.py
screenshot*
*_REPORT*
*_SUMMARY*
CROSS_BROWSER_TEST_REPORT.md
TESTING_SUMMARY.txt
VISUAL_INSPECTION_REPORT.md
cross_browser_test.py.pi-lens/
-146
View File
@@ -1,146 +0,0 @@
# AGENTS.md - Developer Guide for WebDev Portfolio
Essential information for AI agents working on the webdev portfolio project. Covers build commands, code style, testing, and project structure.
## Project Overview
Modern web portfolio built with vanilla JavaScript, CSS, and HTML. Uses Vite for development, Bootstrap 5 foundation, and a coffee-themed design system with light/dark mode support.
## Build & Development Commands
### Package Scripts
```bash
npm run dev # Start Vite dev server (http://localhost:5173)
npm run build # Build for production (outputs to /dist)
npm run preview # Preview production build (http://localhost:4173)
```
### Testing Commands
```bash
# Cross-browser tests (requires Python & Playwright)
pip install playwright
playwright install firefox webkit
npm run build && npm run preview &
python cross_browser_test.py
pkill -f "vite preview"
# Lighthouse audit
npx lighthouse http://localhost:4173 --output json --output html --output-path=./lighthouse-report
```
### CI/CD Pipeline (`.github/workflows/ci.yml`)
Runs on push/PR:
1. `npm ci` - Clean install
2. `npm run build` - Production build
3. Lighthouse analysis via `lighthouse-checker/action`
## Code Style Guidelines
### JavaScript
- **ES6+ modules**: Use `type="module"` in HTML
- **Variables**: Prefer `const` over `let`; avoid `var`
- **Functions**: Arrow functions for callbacks; named functions for top-level declarations
- **Naming**: camelCase for variables/functions; PascalCase for classes
- **Error handling**: Check element existence before manipulation; use early returns
- **Comments**: Use section headers: `// ===== SECTION NAME =====`
- **DOM manipulation**: Use modern APIs (`querySelector`, `classList`, `dataset`)
- **Animation**: Respect `prefers-reduced-motion`; use `requestAnimationFrame`
### CSS
- **CSS Custom Properties**: Define design tokens in `:root`; semantic variable names
- **CSS Nesting**: Use `&` for nested selectors (modern browsers)
- **Responsive Design**: Mobile-first media queries with `min-width`
- **Color Scheme**: Support `prefers-color-scheme: dark` with variable overrides
- **Naming**: kebab-case for class names
- **Modern Features**: Container queries, view transitions, CSS grid, flexbox
- **Performance**: Avoid overly specific selectors; use CSS containment
### HTML
- **Semantic HTML5**: Use appropriate sectioning elements (`<header>`, `<main>`, `<section>`)
- **Accessibility**: Include ARIA attributes where needed; proper heading hierarchy
- **Bootstrap Integration**: Use Bootstrap classes alongside custom styles
- **Data Attributes**: Use `data-*` for JavaScript hooks (e.g., `data-text`, `data-score`)
- **Performance**: Lazy loading for images; defer non-critical scripts
### Formatting
- **Indentation**: 2 spaces (no tabs)
- **Line length**: Keep under 100 characters
- **File endings**: LF (Unix)
- **Trailing whitespace**: Remove
- **Blank lines**: Separate logical sections
## Project Structure
```
webdev/
├── index.html # Main page
├── tos.html # Terms of Service
├── privacy.html # Privacy Policy
├── main.js # All JavaScript functionality
├── styles.css # All CSS styles
├── vite.config.js # Vite configuration
├── package.json # Dependencies and scripts
├── assets/ # Static assets (images, fonts)
├── public/ # Public files served as-is
├── dist/ # Production build (generated)
├── cross_browser_test.py # Playwright cross-browser tests
└── .github/workflows/ci.yml # CI/CD pipeline
```
## Key Files
- `main.js`: All interactive features (typewriter, particles, testimonials, parallax, service cards)
- `styles.css`: Complete styling with coffee theme, dark mode, animations
- `vite.config.js`: Configures multi-page build (index.html, tos.html, privacy.html)
- `cross_browser_test.py`: Validates cross-browser compatibility
## Design System Principles
- **Coffee theme**: Use defined color palette (`--color-coffee-*`, `--color-cream`, `--color-gold`, `--color-teal`)
- **Typography**: `--font-sans` (Inter) for body, `--font-display` (Playfair Display) for headings
- **Spacing**: Consistent padding/margin scales
- **Shadows**: `--shadow-soft` and `--shadow-glass` variables
- **Transitions**: Cubic-bezier easing for natural motion
- **Reduced motion**: Respect `prefers-reduced-motion` media query
## Development Workflow
### Adding New Features
1. Update HTML with semantic markup
2. Add CSS styles using existing design tokens
3. Implement JavaScript in `main.js` with appropriate section
4. Test across browsers using `cross_browser_test.py`
5. Run Lighthouse audit for performance/accessibility
### Modifying Existing Code
- Follow existing patterns and naming conventions
- Update all related files (HTML/CSS/JS) consistently
- Test changes in multiple browsers
- Ensure dark mode compatibility
## Troubleshooting
### Common Issues
- **CSS variables not working**: Ensure defined in `:root` and referenced correctly
- **JavaScript errors**: Check element existence before manipulation
- **Build failures**: Clear `node_modules` and reinstall dependencies
- **Cross-browser issues**: Run `cross_browser_test.py`
### Performance Optimization
- Use Vite's built-in optimizations (code splitting, minification)
- Optimize images in `assets/` directory
- Minimize layout thrashing in JavaScript animations
- Use CSS containment for complex components
## Documentation Files
- **README.md**: Primary project documentation with overview, features, installation, and usage instructions
- **AGENTS.md**: Developer guide for AI agents (this file) - covers build commands, code style, project structure
- **CROSS_BROWSER_TEST_REPORT.md**: Detailed report from cross-browser compatibility testing
- **VISUAL_INSPECTION_REPORT.md**: Visual quality assessment across desktop and mobile viewports
- **TESTING_SUMMARY.txt**: Quick summary of cross-browser test results
- **.github/workflows/ci.yml**: CI/CD pipeline configuration for automated testing and Lighthouse audits
All documentation should be kept up-to-date with the codebase. Test reports are generated artifacts that may be regenerated as needed.
---
*This document is maintained for AI agents and developers contributing to the webdev portfolio project.*
-67
View File
@@ -1,67 +0,0 @@
# Contributing to d@n tech Portfolio
Thank you for your interest in contributing to this web development portfolio project! This document provides guidelines for contributing.
## How to Contribute
1. **Fork the Repository** - Click the "Fork" button on GitHub to create your own copy.
2. **Clone Your Fork** - `git clone https://github.com/YOUR-USERNAME/webdev-site.git`
3. **Create a Feature Branch** - `git checkout -b feature/your-feature-name`
4. **Make Your Changes** - Follow the code style guidelines below.
5. **Test Your Changes** - Ensure the site builds and works across browsers.
6. **Commit Your Changes** - `git commit -m "Add your descriptive message"`
7. **Push to Your Fork** - `git push origin feature/your-feature-name`
8. **Open a Pull Request** - Navigate to the original repository and open a PR.
## Code Style Guidelines
Please adhere to the existing code style to maintain consistency:
### JavaScript
- Use ES6+ modules with `type="module"` in HTML
- Prefer `const` over `let`; avoid `var`
- Arrow functions for callbacks; named functions for top-level declarations
- CamelCase for variables/functions; PascalCase for classes
- Check element existence before DOM manipulation
- Use section headers: `// ===== SECTION NAME =====`
### CSS
- Use CSS Custom Properties defined in `:root` with semantic names
- CSS Nesting with `&` for nested selectors (modern browsers)
- Mobile-first media queries with `min-width`
- Support `prefers-color-scheme: dark` with variable overrides
- Kebab-case for class names
### HTML
- Semantic HTML5 elements (`<header>`, `<main>`, `<section>`)
- Include ARIA attributes where needed; proper heading hierarchy
- Use Bootstrap classes alongside custom styles
- `data-*` attributes for JavaScript hooks
- Lazy loading for images; defer non-critical scripts
## Testing
Before submitting a pull request, please verify:
1. **Build Success** - Run `npm run build` to ensure no errors
2. **Cross-Browser Compatibility** - Test in at least Firefox and Chrome
3. **Responsive Design** - Check mobile (375px) and desktop (1200px) views
4. **No Console Errors** - Open browser developer tools and ensure no errors
5. **Lighthouse Audit** - Run `npx lighthouse http://localhost:4173` for performance, accessibility, SEO, and best practices
## Reporting Issues
When reporting issues, please include:
- Steps to reproduce the issue
- Expected vs. actual behavior
- Browser and operating system information
- Screenshots if applicable
## Questions?
If you have questions, please open an issue or contact the maintainer via the portfolio site: [dev.dustin.coffee](https://dev.dustin.coffee/)
---
*Thank you for contributing!*
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2026 Dustin Newkirk
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.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

-1
View File
@@ -1 +0,0 @@
#!/bin/bash\n# Deploy script for d@n tech\ngit pull origin main\nnpm ci\nnpm run build\nsudo cp -r dist/* /var/www/html/\necho \"Deploy complete: $(date)\"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

-548
View File
@@ -1,548 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="d@n tech offers professional web development services including custom website development, responsive design, e-commerce solutions, SEO optimization, and website maintenance.">
<meta name="keywords" content="web development, custom websites, responsive design, e-commerce, SEO, website maintenance">
<meta name="author" content="Dustin Newkirk">
<link rel="canonical" href="https://dev.dustin.coffee/">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="icon" href="favicon.ico" sizes="any">
<title>d@n tech - Web Development Services</title>
<meta property="og:title" content="d@n tech - Web Development Services">
<meta property="og:description" content="Professional web development services: custom sites, responsive design, e-commerce, SEO, maintenance. Portfolio showcase.">
<meta property="og:image" content="https://dev.dustin.coffee/assets/images/optimized/pcw.webp">
<meta property="og:url" content="https://dev.dustin.coffee/">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="d@n tech - Web Development Services">
<meta name="twitter:description" content="Professional web development services: custom sites, responsive design, e-commerce, SEO, maintenance. Portfolio showcase.">
<meta name="twitter:image" content="https://dev.dustin.coffee/assets/images/optimized/pcw.webp">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ProfessionalService",
"name": "d@n tech",
"description": "Professional web development services including custom website development, responsive design, e-commerce solutions, SEO optimization, and website maintenance.",
"url": "https://dev.dustin.coffee/",
"provider": {
"@type": "Person",
"name": "Dustin Newkirk"
},
"serviceType": "Web Development",
"areaServed": "Worldwide"
}
</script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;800&family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
html {
scroll-behavior: smooth;
}
</style>
<script type="module" crossorigin src="/assets/main-DpCfeRNB.js"></script>
<link rel="stylesheet" crossorigin href="/assets/styles-HyXWe5el.css">
</head>
<body>
<!-- Scroll Progress Bar -->
<div class="scroll-progress" role="progressbar" aria-label="Page scroll progress" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
<!-- Particle canvas removed -- grain texture + steam carry the atmosphere -->
<!-- Decorative Layer (Background Elements) -->
<div class="decorative-layer" aria-hidden="true">
<!-- Floating Bean 1 -->
<svg class="floating-bean bean-1" viewBox="0 0 24 24" fill="currentColor">
<path d="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M13,16c-1.1,0-2-0.9-2-2s-0.9-2-2-2 s-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2s0.9,2,2,2s2,0.9,2,2S14.1,16,13,16z"/>
</svg>
<!-- Floating Bean 2 -->
<svg class="floating-bean bean-2" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.07,4.93C17.22,3.03,14.76,2,12.05,2C6.5,2,2,6.5,2,12.05C2,17.6,6.5,22.1,12.05,22.1c2.71,0,5.17-1.03,7.02-2.93 c1.85-1.9,2.83-4.41,2.83-7.12C21.9,9.34,20.92,6.83,19.07,4.93z M14.23,15.76c-1.07,1.07-2.8,1.07-3.87,0 c-1.07-1.07-1.07-2.8,0-3.87c1.07-1.07,2.8-1.07,3.87,0C15.3,12.96,15.3,14.69,14.23,15.76z"/>
</svg>
<!-- Floating Bean 3 -->
<svg class="floating-bean bean-3" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z"/>
</svg>
<!-- Floating Cup -->
<svg class="floating-cup cup-1" viewBox="0 0 24 24" fill="currentColor">
<path d="M4 2h1v1h-1zM6 2h1v1h-1zM8 2h1v1h-1zM10 2h1v1h-1z" opacity=".5"/> <!-- Steam dots -->
<path d="M18.5 4h-13c-1.1 0-2 .9-2 2v5.71c0 3.83 2.95 7.18 6.78 7.29 3.96.12 7.22-3.06 7.22-7v-1h.5c1.93 0 3.5-1.57 3.5-3.5S20.43 4 18.5 4zM16 11V6h-3v5h3zm-5 0V6H8v5h3zm8-3.5h-1.5V6H19c.83 0 1.5.67 1.5 1.5S19.83 9 19 9z"/>
</svg>
</div>
<header class="hero-section">
<div class="steam-container" aria-hidden="true">
<div class="steam-particle"></div>
<div class="steam-particle"></div>
<div class="steam-particle"></div>
</div>
<div class="container hero-content">
<h1 class="hero-title">Digital Experiences,<br>Perfectly Brewed.</h1>
<p class="typewriter hero-subtitle" data-text="Crafting robust, responsive, and elegant web solutions" aria-label="Crafting robust, responsive, and elegant web solutions"></p>
<div class="btn-group-custom">
<a href="mailto:info@dustin.coffee" class="btn btn-primary btn-lg">
<svg class="btn-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>
</svg>
Start Your Project
</a>
<a href="#portfolio" class="btn btn-outline-coffee btn-lg">View Portfolio</a>
</div>
<div class="mt-5 d-none d-md-block opacity-50 animate-bounce" aria-hidden="true">
<svg width="24" height="24" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z"/>
</svg>
</div>
</div>
<!-- Coffee Drip Separator -->
<div class="drip-separator bottom" aria-hidden="true">
<svg viewBox="0 0 1440 100" preserveAspectRatio="none">
<path fill="currentColor" d="M0,0 C240,90 480,90 720,50 C960,10 1200,10 1440,50 L1440,0 L0,0 Z"></path>
</svg>
</div>
</header>
<div class="d-flex flex-column min-vh-100" style="position: relative;">
<!-- About Section -->
<section id="about" class="section-padding">
<div class="container">
<h2>About Me</h2>
<div class="row">
<div class="col-lg-8">
<p class="lead mb-4 drop-cap">Welcome to d@n tech! I specialize in creating stunning and efficient websites for businesses of all sizes.</p>
<p>As an experienced developer, I'm dedicated to delivering high-quality web solutions that meet your unique needs. I believe in code that is as clean as a fresh pour-over and designs that are as rich as a dark roast.</p>
</div>
</div>
</div>
</section>
<!-- Services Section -->
<section id="services" class="section-padding section-alt">
<div class="container">
<h2>Services</h2>
<div class="row g-4 row-cols-1 row-cols-md-2">
<div class="col">
<div class="card h-100">
<div class="card-body">
<div class="mb-3 text-primary">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
</svg>
</div>
<h5 class="card-title">Custom Website Development</h5>
<p class="card-text">Tailored websites built from scratch to match your brand, goals, and user needs.</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<div class="card-body">
<div class="mb-3 text-primary">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="5" y="2" width="14" height="20" rx="2" ry="2"></rect><line x1="12" y1="18" x2="12.01" y2="18"></line>
</svg>
</div>
<h5 class="card-title">Responsive Design</h5>
<p class="card-text">Sites that look and function beautifully on every device, from phones to desktops.</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<div class="card-body">
<div class="mb-3 text-primary">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
</svg>
</div>
<h5 class="card-title">E-commerce Solutions</h5>
<p class="card-text">Online stores designed to convert visitors into customers with secure checkout.</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<div class="card-body">
<div class="mb-3 text-primary">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</div>
<h5 class="card-title">SEO Optimization</h5>
<p class="card-text">Improved visibility on search engines to help clients find you organically.</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<div class="card-body">
<div class="mb-3 text-primary">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path>
</svg>
</div>
<h5 class="card-title">Website Maintenance</h5>
<p class="card-text">Ongoing updates, security patches, and support to keep your site running smoothly.</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Technologies & Stack Section -->
<section id="technologies" class="section-padding">
<div class="container">
<h2>Technologies & Tools</h2>
<div class="row g-4 row-cols-2 row-cols-md-3 row-cols-lg-4">
<!-- HTML5 -->
<div class="col">
<div class="tech-card">
<h3 class="tech-name">HTML5</h3>
<p class="tech-desc">Semantic Markup</p>
</div>
</div>
<!-- CSS3 -->
<div class="col">
<div class="tech-card">
<h3 class="tech-name">CSS3</h3>
<p class="tech-desc">Modern Styling</p>
</div>
</div>
<!-- JavaScript -->
<div class="col">
<div class="tech-card">
<h3 class="tech-name">JavaScript</h3>
<p class="tech-desc">Interactive Logic</p>
</div>
</div>
<!-- Bootstrap -->
<div class="col">
<div class="tech-card">
<h3 class="tech-name">Bootstrap 5</h3>
<p class="tech-desc">Responsive UI</p>
</div>
</div>
<!-- Vite -->
<div class="col">
<div class="tech-card">
<h3 class="tech-name">Vite</h3>
<p class="tech-desc">Fast Build Tool</p>
</div>
</div>
<!-- Git -->
<div class="col">
<div class="tech-card">
<h3 class="tech-name">Git</h3>
<p class="tech-desc">Version Control</p>
</div>
</div>
<!-- SEO -->
<div class="col">
<div class="tech-card">
<h3 class="tech-name">SEO</h3>
<p class="tech-desc">Search Visibility</p>
</div>
</div>
<!-- Accessibility -->
<div class="col">
<div class="tech-card">
<h3 class="tech-name">Accessibility</h3>
<p class="tech-desc">Inclusive Design</p>
</div>
</div>
</div>
</div>
</section>
<!-- Process Section -->
<section id="process" class="section-padding section-alt">
<div class="container">
<h2>The Brewing Process</h2>
<div class="row g-4 row-cols-1 row-cols-md-2 row-cols-lg-3">
<!-- Phase 1 -->
<div class="col">
<div class="process-card">
<span class="process-number">01</span>
<div class="process-icon-wrapper">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
</div>
<h3 class="process-title">Discovery (The Roast)</h3>
<p class="process-desc">I start by selecting the finest beans—gathering your requirements, understanding your goals, and defining the scope of the project.</p>
</div>
</div>
<!-- Phase 2 -->
<div class="col">
<div class="process-card">
<span class="process-number">02</span>
<div class="process-icon-wrapper">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19l7-7 3 3-7 7-3-3z"></path><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"></path><path d="M2 2l7.586 7.586"></path><circle cx="11" cy="11" r="2"></circle></svg>
</div>
<h3 class="process-title">Design (The Grind)</h3>
<p class="process-desc">I prepare the perfect grind consistency. This involves creating wireframes, designing the UI/UX, and prototyping the visual experience.</p>
</div>
</div>
<!-- Phase 3 -->
<div class="col">
<div class="process-card">
<span class="process-number">03</span>
<div class="process-icon-wrapper">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>
</div>
<h3 class="process-title">Development (The Brew)</h3>
<p class="process-desc">The magic happens here. I write clean, efficient code to bring the designs to life, ensuring everything functions perfectly.</p>
</div>
</div>
<!-- Phase 4 -->
<div class="col">
<div class="process-card">
<span class="process-number">04</span>
<div class="process-icon-wrapper">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
</div>
<h3 class="process-title">Testing (The Taste Test)</h3>
<p class="process-desc">Quality assurance is key. I rigorously test for bugs, performance issues, and responsiveness across all devices and browsers.</p>
</div>
</div>
<!-- Phase 5 -->
<div class="col">
<div class="process-card">
<span class="process-number">05</span>
<div class="process-icon-wrapper">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path></svg>
</div>
<h3 class="process-title">Launch (The Pour)</h3>
<p class="process-desc">It's serving time. We handle the deployment, DNS settings, and final checks to ensure a smooth go-live experience.</p>
</div>
</div>
<!-- Phase 6 -->
<div class="col">
<div class="process-card">
<span class="process-number">06</span>
<div class="process-icon-wrapper">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>
</div>
<h3 class="process-title">Support (The Refill)</h3>
<p class="process-desc">I don't just leave you with the cup. I offer ongoing maintenance, updates, and support to keep your site fresh.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Portfolio Section -->
<section id="portfolio" class="section-padding">
<div class="container">
<h2>Portfolio</h2>
<div class="row g-4">
<div class="col-md-6 col-lg-6 mb-2">
<div class="card h-100">
<img srcset="/assets/knphd-400-BKC5xx6m.webp 400w, /assets/knphd-800-C-3pFi20.webp 800w, /assets/knphd-O7XzWp2B.webp 1200w" sizes="(max-width: 768px) 100vw, 50vw" src="/assets/knphd-O7XzWp2B.webp" class="card-img-top" alt="Kayla Newkirk" loading="lazy">
<div class="card-body">
<h5 class="card-title">Kayla Newkirk</h5>
<p class="card-text">Kayla Newkirk is a PhD candidate specializing in mental health counseling and supervision, dedicated to advancing the field through research and practice.</p>
<div class="d-flex flex-wrap gap-2 mb-3">
<span class="badge-coffee" aria-label="Technology: Responsive Design">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"></rect><line x1="12" y1="18" x2="12.01" y2="18"></line></svg> Responsive
</span>
<span class="badge-coffee" aria-label="Technology: Academic Research">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M22 10v6M2 10l10-5 10 5-10 5z"></path><path d="M6 12v5c3 3 9 3 12 0v-5"></path></svg> Academic
</span>
<span class="badge-coffee" aria-label="Technology: SEO Optimized">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg> SEO
</span>
</div>
<div class="mt-auto">
<a href="https://kaylassafe.space" target="_blank" class="btn btn-outline-coffee btn-sm" aria-label="View Kayla Newkirk project">View project<span class="visually-hidden"> about Kayla Newkirk</span></a>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-6 mb-2">
<div class="card h-100">
<img srcset="/assets/pwlmyers-400-B-_a1Ue0.webp 400w, /assets/pwlmyers-800-C3zufgSe.webp 800w, /assets/pwlmyers-DeYjTU-s.webp 1200w" sizes="(max-width: 768px) 100vw, 50vw" src="/assets/pwlmyers-DeYjTU-s.webp" class="card-img-top" alt="P.W.L. Myers" loading="lazy">
<div class="card-body">
<h5 class="card-title">P.W.L. Myers</h5>
<p class="card-text">P.W.L. Myers provides leadership counseling and professional development services, empowering individuals to achieve their full potential through personalized guidance and strategic career support.</p>
<div class="d-flex flex-wrap gap-2 mb-3">
<span class="badge-coffee" aria-label="Technology: Responsive Design">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"></rect><line x1="12" y1="18" x2="12.01" y2="18"></line></svg> Responsive
</span>
<span class="badge-coffee" aria-label="Technology: SEO Optimized">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg> SEO
</span>
<span class="badge-coffee" aria-label="Technology: Accessibility">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5" r="1"></circle><path d="M12 22V8M5 12H2a10 10 0 0 0 20 0h-3"></path></svg> A11y
</span>
</div>
<div class="mt-auto">
<a href="https://pwlmyers.org" target="_blank" class="btn btn-outline-coffee btn-sm" aria-label="View P.W.L. Myers project">View project<span class="visually-hidden"> about P.W.L. Myers</span></a>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-6 mb-2">
<div class="card h-100">
<img srcset="/assets/pha-400-CXnsDuHV.webp 400w, /assets/pha-800-BePfxstH.webp 800w, /assets/pha-fSk2kxT2.webp 1200w" sizes="(max-width: 768px) 100vw, 50vw" src="/assets/pha-fSk2kxT2.webp" class="card-img-top" alt="Powerful Healing Arts" loading="lazy">
<div class="card-body">
<h5 class="card-title">Powerful Healing Arts</h5>
<p class="card-text">Powerful Healing Arts is a sanctuary for women seeking to move beyond physical healing into deeper, energetic and spiritual transformation.</p>
<div class="d-flex flex-wrap gap-2 mb-3">
<span class="badge-coffee" aria-label="Technology: CMS Integration">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg> Content
</span>
<span class="badge-coffee" aria-label="Technology: SEO Optimized">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg> SEO
</span>
<span class="badge-coffee" aria-label="Technology: Responsive Design">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"></rect><line x1="12" y1="18" x2="12.01" y2="18"></line></svg> Responsive
</span>
</div>
<div class="mt-auto">
<a href="https://powerfulhealingarts.com" target="_blank" class="btn btn-outline-coffee btn-sm" aria-label="View Powerful Healing Arts project">View project<span class="visually-hidden"> about Powerful Healing Arts</span></a>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-6 mb-2">
<div class="card h-100">
<img srcset="/assets/dissertationpath-400-536vXNkR.webp 400w, /assets/dissertationpath-800-YoXTMufk.webp 800w, /assets/dissertationpath-vl3e54ot.webp 1200w" sizes="(max-width: 768px) 100vw, 50vw" src="/assets/dissertationpath-vl3e54ot.webp" class="card-img-top" alt="DissertationPath" loading="lazy">
<div class="card-body">
<h5 class="card-title">DissertationPath</h5>
<p class="card-text">DissertationPath is a comprehensive platform designed to help doctoral candidates navigate the dissertation process with expert guidance.</p>
<div class="d-flex flex-wrap gap-2 mb-3">
<span class="badge-coffee" aria-label="Technology: Platform Architecture">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg> Platform
</span>
<span class="badge-coffee" aria-label="Technology: Educational Resources">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg> EdTech
</span>
<span class="badge-coffee" aria-label="Technology: Responsive Design">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"></rect><line x1="12" y1="18" x2="12.01" y2="18"></line></svg> Responsive
</span>
</div>
<div class="mt-auto">
<a href="https://dissertationpath.com" target="_blank" class="btn btn-outline-coffee btn-sm" aria-label="View DissertationPath project">View project<span class="visually-hidden"> about DissertationPath</span></a>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Testimonials Section -->
<section id="testimonials" class="section-padding section-alt">
<div class="container">
<h2>Testimonials</h2>
<div class="row g-4">
<div class="col-md-6">
<div class="card testimonial-card h-100">
<div class="card-body d-flex flex-column h-100">
<div class="testimonial-quote-icon"></div>
<div class="testimonial-header">
<div class="testimonial-avatar">MP</div>
<div class="testimonial-meta">
<h5>Margaret Pemu</h5>
<small>Pemu Counseling and Wellness</small>
</div>
</div>
<div class="testimonial-rating" aria-label="5 out of 5 stars">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
</div>
<div class="testimonial-content mb-4 flex-grow-1">
<p class="card-text testimonial-excerpt">"It was a pleasure working with Dustin on my counseling practice website. Throughout the process, he was thoughtful, responsive, and truly understood my vision..."</p>
<p class="card-text testimonial-full">"It was a pleasure working with Dustin on my counseling practice website. Throughout the process, he was thoughtful, responsive, and truly understood my vision. In addition to bringing my ideas to life with a clean, professional design, he made the entire process enjoyable and easy. His attention to detail, creativity, and technical skills are excellent. As a result of Dustin's work, I now have a website that truly represents my practice. There is no better person to recommend than him!"</p>
</div>
<button class="testimonial-toggle align-self-start" aria-label="Read full testimonial">Read More</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card testimonial-card h-100">
<div class="card-body d-flex flex-column h-100">
<div class="testimonial-quote-icon"></div>
<div class="testimonial-header">
<div class="testimonial-avatar">L</div>
<div class="testimonial-meta">
<h5>Laura</h5>
<small>Woman-Owned Small Business</small>
</div>
</div>
<div class="testimonial-rating" aria-label="5 out of 5 stars">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
</div>
<div class="testimonial-content mb-4 flex-grow-1">
<p class="card-text testimonial-excerpt">"Working with Dustin at d@n tech was an absolute pleasure from start to finish. He is funny, engaging, and listens well. He offered grace for my lack of technical skill..."</p>
<p class="card-text testimonial-full">"Working with Dustin at d@n tech was an absolute pleasure from start to finish. He is funny, engaging, and listens well. He offered grace for my lack of technical skill and worked from simple examples provided, which translated into a functional and funky-professional website that represents my brand.
His patience and technical skills are truly impressive. Furthermore, he was responsive throughout the entire process, always open to feedback, and quick to implement changes. What I appreciated most was how he spent time working toward what I wanted and was supportive in my goals."</p>
</div>
<button class="testimonial-toggle align-self-start" aria-label="Read full testimonial">Read More</button>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="section-padding">
<div class="coffee-stain stain-2" aria-hidden="true"></div>
<div class="container">
<h2>Contact Me</h2>
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card bg-transparent border-0 shadow-none">
<div class="card-body">
<p class="lead mb-4">Ready to start your next project? Let's connect over coffee (or email).</p>
<a href="mailto:info@dustin.coffee" class="btn btn-primary btn-lg mb-3">
<svg class="btn-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>
</svg>
Send an Email
</a>
<p class="card-text mt-3">View your invoices at <a href="https://invoice.dustin.coffee/client" target="_blank" class="text-decoration-underline">invoice.dustin.coffee</a></p>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
<footer class="bg-dark text-white text-center py-5" >
<div class="coffee-grounds-pattern" aria-hidden="true"></div>
<p>&copy; <span id="currentYear"></span> d@n tech. All rights reserved.</p>
<p><a href="tos.html" class="text-white">Terms of Service</a> | <a href="privacy.html" class="text-white">Privacy Policy</a></p>
</footer>
<script>
// Display the current year in the footer
document.getElementById('currentYear').textContent = new Date().getFullYear();
</script>
</div>
-1285
View File
File diff suppressed because one or more lines are too long
-185
View File
@@ -1,185 +0,0 @@
// ===== TYPEWRITER EFFECT =====
function initTypewriter() {
const el = document.querySelector('.typewriter');
if (!el) return;
const text = el.getAttribute('data-text');
let i = 0;
el.textContent = '';
el.style.opacity = '1';
function type() {
if (i < text.length) {
el.textContent += text.charAt(i);
i++;
setTimeout(type, 80);
}
}
setTimeout(type, 500);
}
// ===== SCROLL PROGRESS BAR =====
function initScrollProgress() {
const progressBar = document.querySelector('.scroll-progress');
if (!progressBar) return;
function updateProgress() {
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
const scrolled = window.scrollY;
const progress = (scrolled / docHeight) * 100;
progressBar.style.setProperty('--scroll-progress', `${progress}%`);
progressBar.setAttribute('aria-valuenow', Math.round(progress));
}
window.addEventListener('scroll', updateProgress, { passive: true });
updateProgress();
}
// ===== ANIMATED SKILLS BARS =====
function initSkillBars() {
const skillItems = document.querySelectorAll('.skill-item');
if (!skillItems.length) return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.3 });
skillItems.forEach(item => observer.observe(item));
}
// ===== PARTICLE BACKGROUND (removed) =====
// initParticles removed — grain texture + steam carry the atmosphere
// ===== INTERACTIVE TESTIMONIALS =====
function initTestimonials() {
const testimonialCards = document.querySelectorAll('.testimonial-card');
testimonialCards.forEach(card => {
const toggle = card.querySelector('.testimonial-toggle');
const excerpt = card.querySelector('.testimonial-excerpt');
const full = card.querySelector('.testimonial-full');
if (!toggle || !excerpt || !full) return;
toggle.addEventListener('click', () => {
const isExpanded = card.classList.contains('expanded');
card.classList.toggle('expanded');
toggle.textContent = isExpanded ? 'Read More' : 'Read Less';
toggle.setAttribute('aria-expanded', !isExpanded);
if (!isExpanded) {
excerpt.style.display = 'none';
full.style.display = 'block';
} else {
excerpt.style.display = 'block';
full.style.display = 'none';
}
});
});
}
// ===== PARALLAX EFFECT FOR DECORATIVE ELEMENTS =====
function initParallax() {
const floatingElements = document.querySelectorAll('.floating-bean, .floating-cup');
if (!floatingElements.length) return;
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
window.requestAnimationFrame(() => {
const scrolled = window.scrollY;
floatingElements.forEach((el, index) => {
const speed = 0.05 + (index * 0.02); // Vary speed per element
const yPos = -(scrolled * speed);
// Use CSS variable to avoid overwriting the float animation transform
el.style.transform = `translateY(${yPos}px)`;
});
ticking = false;
});
ticking = true;
}
}, { passive: true });
}
// ===== METRICS COUNTER ANIMATION =====
function initMetricsCounter() {
const gauges = document.querySelectorAll('.gauge');
if (!gauges.length) return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateGauge(entry.target);
observer.unobserve(entry.target);
}
});
}, { threshold: 0.5 });
gauges.forEach(gauge => {
// Store original values
gauge.dataset.target = gauge.getAttribute('data-score');
// Reset to 0 for animation start
gauge.setAttribute('data-score', '0');
gauge.style.setProperty('--percent', '0%');
observer.observe(gauge);
});
function animateGauge(gauge) {
// Check for reduced motion preference
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const target = parseInt(gauge.dataset.target, 10);
if (prefersReducedMotion) {
gauge.setAttribute('data-score', target);
gauge.style.setProperty('--percent', `${target}%`);
return;
}
const duration = 2000; // ms
const startTime = performance.now();
function update(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Easing (easeOutQuart)
const ease = 1 - Math.pow(1 - progress, 4);
const currentVal = Math.floor(ease * target);
gauge.setAttribute('data-score', currentVal);
gauge.style.setProperty('--percent', `${currentVal}%`);
if (progress < 1) {
requestAnimationFrame(update);
} else {
// Ensure final value is exact
gauge.setAttribute('data-score', target);
gauge.style.setProperty('--percent', `${target}%`);
}
}
requestAnimationFrame(update);
}
}
// ===== INITIALIZE ALL =====
document.addEventListener('DOMContentLoaded', () => {
initTypewriter();
initScrollProgress();
initSkillBars();
initTestimonials();
initParallax();
initMetricsCounter();
});
Generated Vendored
-1
View File
@@ -1 +0,0 @@
../esbuild/bin/esbuild
Generated Vendored
-1
View File
@@ -1 +0,0 @@
../nanoid/bin/nanoid.cjs
Generated Vendored
-1
View File
@@ -1 +0,0 @@
../rollup/dist/bin/rollup
Generated Vendored
-1
View File
@@ -1 +0,0 @@
../vite/bin/vite.js
-321
View File
@@ -1,321 +0,0 @@
{
"name": "webdev",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/@esbuild/linux-x64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
"integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
"integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/esbuild": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.27.3",
"@esbuild/android-arm": "0.27.3",
"@esbuild/android-arm64": "0.27.3",
"@esbuild/android-x64": "0.27.3",
"@esbuild/darwin-arm64": "0.27.3",
"@esbuild/darwin-x64": "0.27.3",
"@esbuild/freebsd-arm64": "0.27.3",
"@esbuild/freebsd-x64": "0.27.3",
"@esbuild/linux-arm": "0.27.3",
"@esbuild/linux-arm64": "0.27.3",
"@esbuild/linux-ia32": "0.27.3",
"@esbuild/linux-loong64": "0.27.3",
"@esbuild/linux-mips64el": "0.27.3",
"@esbuild/linux-ppc64": "0.27.3",
"@esbuild/linux-riscv64": "0.27.3",
"@esbuild/linux-s390x": "0.27.3",
"@esbuild/linux-x64": "0.27.3",
"@esbuild/netbsd-arm64": "0.27.3",
"@esbuild/netbsd-x64": "0.27.3",
"@esbuild/openbsd-arm64": "0.27.3",
"@esbuild/openbsd-x64": "0.27.3",
"@esbuild/openharmony-arm64": "0.27.3",
"@esbuild/sunos-x64": "0.27.3",
"@esbuild/win32-arm64": "0.27.3",
"@esbuild/win32-ia32": "0.27.3",
"@esbuild/win32-x64": "0.27.3"
}
},
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rollup": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
"integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.57.1",
"@rollup/rollup-android-arm64": "4.57.1",
"@rollup/rollup-darwin-arm64": "4.57.1",
"@rollup/rollup-darwin-x64": "4.57.1",
"@rollup/rollup-freebsd-arm64": "4.57.1",
"@rollup/rollup-freebsd-x64": "4.57.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
"@rollup/rollup-linux-arm-musleabihf": "4.57.1",
"@rollup/rollup-linux-arm64-gnu": "4.57.1",
"@rollup/rollup-linux-arm64-musl": "4.57.1",
"@rollup/rollup-linux-loong64-gnu": "4.57.1",
"@rollup/rollup-linux-loong64-musl": "4.57.1",
"@rollup/rollup-linux-ppc64-gnu": "4.57.1",
"@rollup/rollup-linux-ppc64-musl": "4.57.1",
"@rollup/rollup-linux-riscv64-gnu": "4.57.1",
"@rollup/rollup-linux-riscv64-musl": "4.57.1",
"@rollup/rollup-linux-s390x-gnu": "4.57.1",
"@rollup/rollup-linux-x64-gnu": "4.57.1",
"@rollup/rollup-linux-x64-musl": "4.57.1",
"@rollup/rollup-openbsd-x64": "4.57.1",
"@rollup/rollup-openharmony-arm64": "4.57.1",
"@rollup/rollup-win32-arm64-msvc": "4.57.1",
"@rollup/rollup-win32-ia32-msvc": "4.57.1",
"@rollup/rollup-win32-x64-gnu": "4.57.1",
"@rollup/rollup-win32-x64-msvc": "4.57.1",
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/vite": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rollup": "^4.43.0",
"tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
"lightningcss": "^1.21.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
"sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"jiti": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
}
}
}
-8
View File
@@ -1,8 +0,0 @@
{
"hash": "0641f74f",
"configHash": "22dbb824",
"lockfileHash": "7298aceb",
"browserHash": "41db486b",
"optimized": {},
"chunks": {}
}
-3
View File
@@ -1,3 +0,0 @@
{
"type": "module"
}
-3
View File
@@ -1,3 +0,0 @@
# esbuild
This is the Linux 64-bit binary for esbuild, a JavaScript bundler and minifier. See https://github.com/evanw/esbuild for details.
BIN
View File
Binary file not shown.
-20
View File
@@ -1,20 +0,0 @@
{
"name": "@esbuild/linux-x64",
"version": "0.27.3",
"description": "The Linux 64-bit binary for esbuild, a JavaScript bundler.",
"repository": {
"type": "git",
"url": "git+https://github.com/evanw/esbuild.git"
},
"license": "MIT",
"preferUnplugged": true,
"engines": {
"node": ">=18"
},
"os": [
"linux"
],
"cpu": [
"x64"
]
}
-3
View File
@@ -1,3 +0,0 @@
# `@rollup/rollup-linux-x64-gnu`
This is the **x86_64-unknown-linux-gnu** binary for `rollup`
-25
View File
@@ -1,25 +0,0 @@
{
"name": "@rollup/rollup-linux-x64-gnu",
"version": "4.57.1",
"os": [
"linux"
],
"cpu": [
"x64"
],
"files": [
"rollup.linux-x64-gnu.node"
],
"description": "Native bindings for Rollup",
"author": "Lukas Taegert-Atkinson",
"homepage": "https://rollupjs.org/",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/rollup/rollup.git"
},
"libc": [
"glibc"
],
"main": "./rollup.linux-x64-gnu.node"
}
Binary file not shown.
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation.
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
-15
View File
@@ -1,15 +0,0 @@
# Installation
> `npm install --save @types/estree`
# Summary
This package contains type definitions for estree (https://github.com/estree/estree).
# Details
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree.
### Additional Details
* Last updated: Fri, 06 Jun 2025 00:04:33 GMT
* Dependencies: none
# Credits
These definitions were written by [RReverser](https://github.com/RReverser).
-167
View File
@@ -1,167 +0,0 @@
declare namespace ESTree {
interface FlowTypeAnnotation extends Node {}
interface FlowBaseTypeAnnotation extends FlowTypeAnnotation {}
interface FlowLiteralTypeAnnotation extends FlowTypeAnnotation, Literal {}
interface FlowDeclaration extends Declaration {}
interface AnyTypeAnnotation extends FlowBaseTypeAnnotation {}
interface ArrayTypeAnnotation extends FlowTypeAnnotation {
elementType: FlowTypeAnnotation;
}
interface BooleanLiteralTypeAnnotation extends FlowLiteralTypeAnnotation {}
interface BooleanTypeAnnotation extends FlowBaseTypeAnnotation {}
interface ClassImplements extends Node {
id: Identifier;
typeParameters?: TypeParameterInstantiation | null;
}
interface ClassProperty {
key: Expression;
value?: Expression | null;
typeAnnotation?: TypeAnnotation | null;
computed: boolean;
static: boolean;
}
interface DeclareClass extends FlowDeclaration {
id: Identifier;
typeParameters?: TypeParameterDeclaration | null;
body: ObjectTypeAnnotation;
extends: InterfaceExtends[];
}
interface DeclareFunction extends FlowDeclaration {
id: Identifier;
}
interface DeclareModule extends FlowDeclaration {
id: Literal | Identifier;
body: BlockStatement;
}
interface DeclareVariable extends FlowDeclaration {
id: Identifier;
}
interface FunctionTypeAnnotation extends FlowTypeAnnotation {
params: FunctionTypeParam[];
returnType: FlowTypeAnnotation;
rest?: FunctionTypeParam | null;
typeParameters?: TypeParameterDeclaration | null;
}
interface FunctionTypeParam {
name: Identifier;
typeAnnotation: FlowTypeAnnotation;
optional: boolean;
}
interface GenericTypeAnnotation extends FlowTypeAnnotation {
id: Identifier | QualifiedTypeIdentifier;
typeParameters?: TypeParameterInstantiation | null;
}
interface InterfaceExtends extends Node {
id: Identifier | QualifiedTypeIdentifier;
typeParameters?: TypeParameterInstantiation | null;
}
interface InterfaceDeclaration extends FlowDeclaration {
id: Identifier;
typeParameters?: TypeParameterDeclaration | null;
extends: InterfaceExtends[];
body: ObjectTypeAnnotation;
}
interface IntersectionTypeAnnotation extends FlowTypeAnnotation {
types: FlowTypeAnnotation[];
}
interface MixedTypeAnnotation extends FlowBaseTypeAnnotation {}
interface NullableTypeAnnotation extends FlowTypeAnnotation {
typeAnnotation: TypeAnnotation;
}
interface NumberLiteralTypeAnnotation extends FlowLiteralTypeAnnotation {}
interface NumberTypeAnnotation extends FlowBaseTypeAnnotation {}
interface StringLiteralTypeAnnotation extends FlowLiteralTypeAnnotation {}
interface StringTypeAnnotation extends FlowBaseTypeAnnotation {}
interface TupleTypeAnnotation extends FlowTypeAnnotation {
types: FlowTypeAnnotation[];
}
interface TypeofTypeAnnotation extends FlowTypeAnnotation {
argument: FlowTypeAnnotation;
}
interface TypeAlias extends FlowDeclaration {
id: Identifier;
typeParameters?: TypeParameterDeclaration | null;
right: FlowTypeAnnotation;
}
interface TypeAnnotation extends Node {
typeAnnotation: FlowTypeAnnotation;
}
interface TypeCastExpression extends Expression {
expression: Expression;
typeAnnotation: TypeAnnotation;
}
interface TypeParameterDeclaration extends Node {
params: Identifier[];
}
interface TypeParameterInstantiation extends Node {
params: FlowTypeAnnotation[];
}
interface ObjectTypeAnnotation extends FlowTypeAnnotation {
properties: ObjectTypeProperty[];
indexers: ObjectTypeIndexer[];
callProperties: ObjectTypeCallProperty[];
}
interface ObjectTypeCallProperty extends Node {
value: FunctionTypeAnnotation;
static: boolean;
}
interface ObjectTypeIndexer extends Node {
id: Identifier;
key: FlowTypeAnnotation;
value: FlowTypeAnnotation;
static: boolean;
}
interface ObjectTypeProperty extends Node {
key: Expression;
value: FlowTypeAnnotation;
optional: boolean;
static: boolean;
}
interface QualifiedTypeIdentifier extends Node {
qualification: Identifier | QualifiedTypeIdentifier;
id: Identifier;
}
interface UnionTypeAnnotation extends FlowTypeAnnotation {
types: FlowTypeAnnotation[];
}
interface VoidTypeAnnotation extends FlowBaseTypeAnnotation {}
}
-694
View File
@@ -1,694 +0,0 @@
// This definition file follows a somewhat unusual format. ESTree allows
// runtime type checks based on the `type` parameter. In order to explain this
// to typescript we want to use discriminated union types:
// https://github.com/Microsoft/TypeScript/pull/9163
//
// For ESTree this is a bit tricky because the high level interfaces like
// Node or Function are pulling double duty. We want to pass common fields down
// to the interfaces that extend them (like Identifier or
// ArrowFunctionExpression), but you can't extend a type union or enforce
// common fields on them. So we've split the high level interfaces into two
// types, a base type which passes down inherited fields, and a type union of
// all types which extend the base type. Only the type union is exported, and
// the union is how other types refer to the collection of inheriting types.
//
// This makes the definitions file here somewhat more difficult to maintain,
// but it has the notable advantage of making ESTree much easier to use as
// an end user.
export interface BaseNodeWithoutComments {
// Every leaf interface that extends BaseNode must specify a type property.
// The type property should be a string literal. For example, Identifier
// has: `type: "Identifier"`
type: string;
loc?: SourceLocation | null | undefined;
range?: [number, number] | undefined;
}
export interface BaseNode extends BaseNodeWithoutComments {
leadingComments?: Comment[] | undefined;
trailingComments?: Comment[] | undefined;
}
export interface NodeMap {
AssignmentProperty: AssignmentProperty;
CatchClause: CatchClause;
Class: Class;
ClassBody: ClassBody;
Expression: Expression;
Function: Function;
Identifier: Identifier;
Literal: Literal;
MethodDefinition: MethodDefinition;
ModuleDeclaration: ModuleDeclaration;
ModuleSpecifier: ModuleSpecifier;
Pattern: Pattern;
PrivateIdentifier: PrivateIdentifier;
Program: Program;
Property: Property;
PropertyDefinition: PropertyDefinition;
SpreadElement: SpreadElement;
Statement: Statement;
Super: Super;
SwitchCase: SwitchCase;
TemplateElement: TemplateElement;
VariableDeclarator: VariableDeclarator;
}
export type Node = NodeMap[keyof NodeMap];
export interface Comment extends BaseNodeWithoutComments {
type: "Line" | "Block";
value: string;
}
export interface SourceLocation {
source?: string | null | undefined;
start: Position;
end: Position;
}
export interface Position {
/** >= 1 */
line: number;
/** >= 0 */
column: number;
}
export interface Program extends BaseNode {
type: "Program";
sourceType: "script" | "module";
body: Array<Directive | Statement | ModuleDeclaration>;
comments?: Comment[] | undefined;
}
export interface Directive extends BaseNode {
type: "ExpressionStatement";
expression: Literal;
directive: string;
}
export interface BaseFunction extends BaseNode {
params: Pattern[];
generator?: boolean | undefined;
async?: boolean | undefined;
// The body is either BlockStatement or Expression because arrow functions
// can have a body that's either. FunctionDeclarations and
// FunctionExpressions have only BlockStatement bodies.
body: BlockStatement | Expression;
}
export type Function = FunctionDeclaration | FunctionExpression | ArrowFunctionExpression;
export type Statement =
| ExpressionStatement
| BlockStatement
| StaticBlock
| EmptyStatement
| DebuggerStatement
| WithStatement
| ReturnStatement
| LabeledStatement
| BreakStatement
| ContinueStatement
| IfStatement
| SwitchStatement
| ThrowStatement
| TryStatement
| WhileStatement
| DoWhileStatement
| ForStatement
| ForInStatement
| ForOfStatement
| Declaration;
export interface BaseStatement extends BaseNode {}
export interface EmptyStatement extends BaseStatement {
type: "EmptyStatement";
}
export interface BlockStatement extends BaseStatement {
type: "BlockStatement";
body: Statement[];
innerComments?: Comment[] | undefined;
}
export interface StaticBlock extends Omit<BlockStatement, "type"> {
type: "StaticBlock";
}
export interface ExpressionStatement extends BaseStatement {
type: "ExpressionStatement";
expression: Expression;
}
export interface IfStatement extends BaseStatement {
type: "IfStatement";
test: Expression;
consequent: Statement;
alternate?: Statement | null | undefined;
}
export interface LabeledStatement extends BaseStatement {
type: "LabeledStatement";
label: Identifier;
body: Statement;
}
export interface BreakStatement extends BaseStatement {
type: "BreakStatement";
label?: Identifier | null | undefined;
}
export interface ContinueStatement extends BaseStatement {
type: "ContinueStatement";
label?: Identifier | null | undefined;
}
export interface WithStatement extends BaseStatement {
type: "WithStatement";
object: Expression;
body: Statement;
}
export interface SwitchStatement extends BaseStatement {
type: "SwitchStatement";
discriminant: Expression;
cases: SwitchCase[];
}
export interface ReturnStatement extends BaseStatement {
type: "ReturnStatement";
argument?: Expression | null | undefined;
}
export interface ThrowStatement extends BaseStatement {
type: "ThrowStatement";
argument: Expression;
}
export interface TryStatement extends BaseStatement {
type: "TryStatement";
block: BlockStatement;
handler?: CatchClause | null | undefined;
finalizer?: BlockStatement | null | undefined;
}
export interface WhileStatement extends BaseStatement {
type: "WhileStatement";
test: Expression;
body: Statement;
}
export interface DoWhileStatement extends BaseStatement {
type: "DoWhileStatement";
body: Statement;
test: Expression;
}
export interface ForStatement extends BaseStatement {
type: "ForStatement";
init?: VariableDeclaration | Expression | null | undefined;
test?: Expression | null | undefined;
update?: Expression | null | undefined;
body: Statement;
}
export interface BaseForXStatement extends BaseStatement {
left: VariableDeclaration | Pattern;
right: Expression;
body: Statement;
}
export interface ForInStatement extends BaseForXStatement {
type: "ForInStatement";
}
export interface DebuggerStatement extends BaseStatement {
type: "DebuggerStatement";
}
export type Declaration = FunctionDeclaration | VariableDeclaration | ClassDeclaration;
export interface BaseDeclaration extends BaseStatement {}
export interface MaybeNamedFunctionDeclaration extends BaseFunction, BaseDeclaration {
type: "FunctionDeclaration";
/** It is null when a function declaration is a part of the `export default function` statement */
id: Identifier | null;
body: BlockStatement;
}
export interface FunctionDeclaration extends MaybeNamedFunctionDeclaration {
id: Identifier;
}
export interface VariableDeclaration extends BaseDeclaration {
type: "VariableDeclaration";
declarations: VariableDeclarator[];
kind: "var" | "let" | "const" | "using" | "await using";
}
export interface VariableDeclarator extends BaseNode {
type: "VariableDeclarator";
id: Pattern;
init?: Expression | null | undefined;
}
export interface ExpressionMap {
ArrayExpression: ArrayExpression;
ArrowFunctionExpression: ArrowFunctionExpression;
AssignmentExpression: AssignmentExpression;
AwaitExpression: AwaitExpression;
BinaryExpression: BinaryExpression;
CallExpression: CallExpression;
ChainExpression: ChainExpression;
ClassExpression: ClassExpression;
ConditionalExpression: ConditionalExpression;
FunctionExpression: FunctionExpression;
Identifier: Identifier;
ImportExpression: ImportExpression;
Literal: Literal;
LogicalExpression: LogicalExpression;
MemberExpression: MemberExpression;
MetaProperty: MetaProperty;
NewExpression: NewExpression;
ObjectExpression: ObjectExpression;
SequenceExpression: SequenceExpression;
TaggedTemplateExpression: TaggedTemplateExpression;
TemplateLiteral: TemplateLiteral;
ThisExpression: ThisExpression;
UnaryExpression: UnaryExpression;
UpdateExpression: UpdateExpression;
YieldExpression: YieldExpression;
}
export type Expression = ExpressionMap[keyof ExpressionMap];
export interface BaseExpression extends BaseNode {}
export type ChainElement = SimpleCallExpression | MemberExpression;
export interface ChainExpression extends BaseExpression {
type: "ChainExpression";
expression: ChainElement;
}
export interface ThisExpression extends BaseExpression {
type: "ThisExpression";
}
export interface ArrayExpression extends BaseExpression {
type: "ArrayExpression";
elements: Array<Expression | SpreadElement | null>;
}
export interface ObjectExpression extends BaseExpression {
type: "ObjectExpression";
properties: Array<Property | SpreadElement>;
}
export interface PrivateIdentifier extends BaseNode {
type: "PrivateIdentifier";
name: string;
}
export interface Property extends BaseNode {
type: "Property";
key: Expression | PrivateIdentifier;
value: Expression | Pattern; // Could be an AssignmentProperty
kind: "init" | "get" | "set";
method: boolean;
shorthand: boolean;
computed: boolean;
}
export interface PropertyDefinition extends BaseNode {
type: "PropertyDefinition";
key: Expression | PrivateIdentifier;
value?: Expression | null | undefined;
computed: boolean;
static: boolean;
}
export interface FunctionExpression extends BaseFunction, BaseExpression {
id?: Identifier | null | undefined;
type: "FunctionExpression";
body: BlockStatement;
}
export interface SequenceExpression extends BaseExpression {
type: "SequenceExpression";
expressions: Expression[];
}
export interface UnaryExpression extends BaseExpression {
type: "UnaryExpression";
operator: UnaryOperator;
prefix: true;
argument: Expression;
}
export interface BinaryExpression extends BaseExpression {
type: "BinaryExpression";
operator: BinaryOperator;
left: Expression | PrivateIdentifier;
right: Expression;
}
export interface AssignmentExpression extends BaseExpression {
type: "AssignmentExpression";
operator: AssignmentOperator;
left: Pattern | MemberExpression;
right: Expression;
}
export interface UpdateExpression extends BaseExpression {
type: "UpdateExpression";
operator: UpdateOperator;
argument: Expression;
prefix: boolean;
}
export interface LogicalExpression extends BaseExpression {
type: "LogicalExpression";
operator: LogicalOperator;
left: Expression;
right: Expression;
}
export interface ConditionalExpression extends BaseExpression {
type: "ConditionalExpression";
test: Expression;
alternate: Expression;
consequent: Expression;
}
export interface BaseCallExpression extends BaseExpression {
callee: Expression | Super;
arguments: Array<Expression | SpreadElement>;
}
export type CallExpression = SimpleCallExpression | NewExpression;
export interface SimpleCallExpression extends BaseCallExpression {
type: "CallExpression";
optional: boolean;
}
export interface NewExpression extends BaseCallExpression {
type: "NewExpression";
}
export interface MemberExpression extends BaseExpression, BasePattern {
type: "MemberExpression";
object: Expression | Super;
property: Expression | PrivateIdentifier;
computed: boolean;
optional: boolean;
}
export type Pattern = Identifier | ObjectPattern | ArrayPattern | RestElement | AssignmentPattern | MemberExpression;
export interface BasePattern extends BaseNode {}
export interface SwitchCase extends BaseNode {
type: "SwitchCase";
test?: Expression | null | undefined;
consequent: Statement[];
}
export interface CatchClause extends BaseNode {
type: "CatchClause";
param: Pattern | null;
body: BlockStatement;
}
export interface Identifier extends BaseNode, BaseExpression, BasePattern {
type: "Identifier";
name: string;
}
export type Literal = SimpleLiteral | RegExpLiteral | BigIntLiteral;
export interface SimpleLiteral extends BaseNode, BaseExpression {
type: "Literal";
value: string | boolean | number | null;
raw?: string | undefined;
}
export interface RegExpLiteral extends BaseNode, BaseExpression {
type: "Literal";
value?: RegExp | null | undefined;
regex: {
pattern: string;
flags: string;
};
raw?: string | undefined;
}
export interface BigIntLiteral extends BaseNode, BaseExpression {
type: "Literal";
value?: bigint | null | undefined;
bigint: string;
raw?: string | undefined;
}
export type UnaryOperator = "-" | "+" | "!" | "~" | "typeof" | "void" | "delete";
export type BinaryOperator =
| "=="
| "!="
| "==="
| "!=="
| "<"
| "<="
| ">"
| ">="
| "<<"
| ">>"
| ">>>"
| "+"
| "-"
| "*"
| "/"
| "%"
| "**"
| "|"
| "^"
| "&"
| "in"
| "instanceof";
export type LogicalOperator = "||" | "&&" | "??";
export type AssignmentOperator =
| "="
| "+="
| "-="
| "*="
| "/="
| "%="
| "**="
| "<<="
| ">>="
| ">>>="
| "|="
| "^="
| "&="
| "||="
| "&&="
| "??=";
export type UpdateOperator = "++" | "--";
export interface ForOfStatement extends BaseForXStatement {
type: "ForOfStatement";
await: boolean;
}
export interface Super extends BaseNode {
type: "Super";
}
export interface SpreadElement extends BaseNode {
type: "SpreadElement";
argument: Expression;
}
export interface ArrowFunctionExpression extends BaseExpression, BaseFunction {
type: "ArrowFunctionExpression";
expression: boolean;
body: BlockStatement | Expression;
}
export interface YieldExpression extends BaseExpression {
type: "YieldExpression";
argument?: Expression | null | undefined;
delegate: boolean;
}
export interface TemplateLiteral extends BaseExpression {
type: "TemplateLiteral";
quasis: TemplateElement[];
expressions: Expression[];
}
export interface TaggedTemplateExpression extends BaseExpression {
type: "TaggedTemplateExpression";
tag: Expression;
quasi: TemplateLiteral;
}
export interface TemplateElement extends BaseNode {
type: "TemplateElement";
tail: boolean;
value: {
/** It is null when the template literal is tagged and the text has an invalid escape (e.g. - tag`\unicode and \u{55}`) */
cooked?: string | null | undefined;
raw: string;
};
}
export interface AssignmentProperty extends Property {
value: Pattern;
kind: "init";
method: boolean; // false
}
export interface ObjectPattern extends BasePattern {
type: "ObjectPattern";
properties: Array<AssignmentProperty | RestElement>;
}
export interface ArrayPattern extends BasePattern {
type: "ArrayPattern";
elements: Array<Pattern | null>;
}
export interface RestElement extends BasePattern {
type: "RestElement";
argument: Pattern;
}
export interface AssignmentPattern extends BasePattern {
type: "AssignmentPattern";
left: Pattern;
right: Expression;
}
export type Class = ClassDeclaration | ClassExpression;
export interface BaseClass extends BaseNode {
superClass?: Expression | null | undefined;
body: ClassBody;
}
export interface ClassBody extends BaseNode {
type: "ClassBody";
body: Array<MethodDefinition | PropertyDefinition | StaticBlock>;
}
export interface MethodDefinition extends BaseNode {
type: "MethodDefinition";
key: Expression | PrivateIdentifier;
value: FunctionExpression;
kind: "constructor" | "method" | "get" | "set";
computed: boolean;
static: boolean;
}
export interface MaybeNamedClassDeclaration extends BaseClass, BaseDeclaration {
type: "ClassDeclaration";
/** It is null when a class declaration is a part of the `export default class` statement */
id: Identifier | null;
}
export interface ClassDeclaration extends MaybeNamedClassDeclaration {
id: Identifier;
}
export interface ClassExpression extends BaseClass, BaseExpression {
type: "ClassExpression";
id?: Identifier | null | undefined;
}
export interface MetaProperty extends BaseExpression {
type: "MetaProperty";
meta: Identifier;
property: Identifier;
}
export type ModuleDeclaration =
| ImportDeclaration
| ExportNamedDeclaration
| ExportDefaultDeclaration
| ExportAllDeclaration;
export interface BaseModuleDeclaration extends BaseNode {}
export type ModuleSpecifier = ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier | ExportSpecifier;
export interface BaseModuleSpecifier extends BaseNode {
local: Identifier;
}
export interface ImportDeclaration extends BaseModuleDeclaration {
type: "ImportDeclaration";
specifiers: Array<ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier>;
attributes: ImportAttribute[];
source: Literal;
}
export interface ImportSpecifier extends BaseModuleSpecifier {
type: "ImportSpecifier";
imported: Identifier | Literal;
}
export interface ImportAttribute extends BaseNode {
type: "ImportAttribute";
key: Identifier | Literal;
value: Literal;
}
export interface ImportExpression extends BaseExpression {
type: "ImportExpression";
source: Expression;
options?: Expression | null | undefined;
}
export interface ImportDefaultSpecifier extends BaseModuleSpecifier {
type: "ImportDefaultSpecifier";
}
export interface ImportNamespaceSpecifier extends BaseModuleSpecifier {
type: "ImportNamespaceSpecifier";
}
export interface ExportNamedDeclaration extends BaseModuleDeclaration {
type: "ExportNamedDeclaration";
declaration?: Declaration | null | undefined;
specifiers: ExportSpecifier[];
attributes: ImportAttribute[];
source?: Literal | null | undefined;
}
export interface ExportSpecifier extends Omit<BaseModuleSpecifier, "local"> {
type: "ExportSpecifier";
local: Identifier | Literal;
exported: Identifier | Literal;
}
export interface ExportDefaultDeclaration extends BaseModuleDeclaration {
type: "ExportDefaultDeclaration";
declaration: MaybeNamedFunctionDeclaration | MaybeNamedClassDeclaration | Expression;
}
export interface ExportAllDeclaration extends BaseModuleDeclaration {
type: "ExportAllDeclaration";
exported: Identifier | Literal | null;
attributes: ImportAttribute[];
source: Literal;
}
export interface AwaitExpression extends BaseExpression {
type: "AwaitExpression";
argument: Expression;
}
-27
View File
@@ -1,27 +0,0 @@
{
"name": "@types/estree",
"version": "1.0.8",
"description": "TypeScript definitions for estree",
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree",
"license": "MIT",
"contributors": [
{
"name": "RReverser",
"githubUsername": "RReverser",
"url": "https://github.com/RReverser"
}
],
"main": "",
"types": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
"directory": "types/estree"
},
"scripts": {},
"dependencies": {},
"peerDependencies": {},
"typesPublisherContentHash": "7a167b6e4a4d9f6e9a2cb9fd3fc45c885f89cbdeb44b3e5961bb057a45c082fd",
"typeScriptVersion": "5.1",
"nonNpm": true
}
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Evan Wallace
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.
-3
View File
@@ -1,3 +0,0 @@
# esbuild
This is a JavaScript bundler and minifier. See https://github.com/evanw/esbuild and the [JavaScript API documentation](https://esbuild.github.io/api/) for details.
BIN
View File
Binary file not shown.
-289
View File
@@ -1,289 +0,0 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// lib/npm/node-platform.ts
var fs = require("fs");
var os = require("os");
var path = require("path");
var ESBUILD_BINARY_PATH = process.env.ESBUILD_BINARY_PATH || ESBUILD_BINARY_PATH;
var isValidBinaryPath = (x) => !!x && x !== "/usr/bin/esbuild";
var knownWindowsPackages = {
"win32 arm64 LE": "@esbuild/win32-arm64",
"win32 ia32 LE": "@esbuild/win32-ia32",
"win32 x64 LE": "@esbuild/win32-x64"
};
var knownUnixlikePackages = {
"aix ppc64 BE": "@esbuild/aix-ppc64",
"android arm64 LE": "@esbuild/android-arm64",
"darwin arm64 LE": "@esbuild/darwin-arm64",
"darwin x64 LE": "@esbuild/darwin-x64",
"freebsd arm64 LE": "@esbuild/freebsd-arm64",
"freebsd x64 LE": "@esbuild/freebsd-x64",
"linux arm LE": "@esbuild/linux-arm",
"linux arm64 LE": "@esbuild/linux-arm64",
"linux ia32 LE": "@esbuild/linux-ia32",
"linux mips64el LE": "@esbuild/linux-mips64el",
"linux ppc64 LE": "@esbuild/linux-ppc64",
"linux riscv64 LE": "@esbuild/linux-riscv64",
"linux s390x BE": "@esbuild/linux-s390x",
"linux x64 LE": "@esbuild/linux-x64",
"linux loong64 LE": "@esbuild/linux-loong64",
"netbsd arm64 LE": "@esbuild/netbsd-arm64",
"netbsd x64 LE": "@esbuild/netbsd-x64",
"openbsd arm64 LE": "@esbuild/openbsd-arm64",
"openbsd x64 LE": "@esbuild/openbsd-x64",
"sunos x64 LE": "@esbuild/sunos-x64"
};
var knownWebAssemblyFallbackPackages = {
"android arm LE": "@esbuild/android-arm",
"android x64 LE": "@esbuild/android-x64",
"openharmony arm64 LE": "@esbuild/openharmony-arm64"
};
function pkgAndSubpathForCurrentPlatform() {
let pkg;
let subpath;
let isWASM = false;
let platformKey = `${process.platform} ${os.arch()} ${os.endianness()}`;
if (platformKey in knownWindowsPackages) {
pkg = knownWindowsPackages[platformKey];
subpath = "esbuild.exe";
} else if (platformKey in knownUnixlikePackages) {
pkg = knownUnixlikePackages[platformKey];
subpath = "bin/esbuild";
} else if (platformKey in knownWebAssemblyFallbackPackages) {
pkg = knownWebAssemblyFallbackPackages[platformKey];
subpath = "bin/esbuild";
isWASM = true;
} else {
throw new Error(`Unsupported platform: ${platformKey}`);
}
return { pkg, subpath, isWASM };
}
function downloadedBinPath(pkg, subpath) {
const esbuildLibDir = path.dirname(require.resolve("esbuild"));
return path.join(esbuildLibDir, `downloaded-${pkg.replace("/", "-")}-${path.basename(subpath)}`);
}
// lib/npm/node-install.ts
var fs2 = require("fs");
var os2 = require("os");
var path2 = require("path");
var zlib = require("zlib");
var https = require("https");
var child_process = require("child_process");
var versionFromPackageJSON = require(path2.join(__dirname, "package.json")).version;
var toPath = path2.join(__dirname, "bin", "esbuild");
var isToPathJS = true;
function validateBinaryVersion(...command) {
command.push("--version");
let stdout;
try {
stdout = child_process.execFileSync(command.shift(), command, {
// Without this, this install script strangely crashes with the error
// "EACCES: permission denied, write" but only on Ubuntu Linux when node is
// installed from the Snap Store. This is not a problem when you download
// the official version of node. The problem appears to be that stderr
// (i.e. file descriptor 2) isn't writable?
//
// More info:
// - https://snapcraft.io/ (what the Snap Store is)
// - https://nodejs.org/dist/ (download the official version of node)
// - https://github.com/evanw/esbuild/issues/1711#issuecomment-1027554035
//
stdio: "pipe"
}).toString().trim();
} catch (err) {
if (os2.platform() === "darwin" && /_SecTrustEvaluateWithError/.test(err + "")) {
let os3 = "this version of macOS";
try {
os3 = "macOS " + child_process.execFileSync("sw_vers", ["-productVersion"]).toString().trim();
} catch {
}
throw new Error(`The "esbuild" package cannot be installed because ${os3} is too outdated.
The Go compiler (which esbuild relies on) no longer supports ${os3},
which means the "esbuild" binary executable can't be run. You can either:
* Update your version of macOS to one that the Go compiler supports
* Use the "esbuild-wasm" package instead of the "esbuild" package
* Build esbuild yourself using an older version of the Go compiler
`);
}
throw err;
}
if (stdout !== versionFromPackageJSON) {
throw new Error(`Expected ${JSON.stringify(versionFromPackageJSON)} but got ${JSON.stringify(stdout)}`);
}
}
function isYarn() {
const { npm_config_user_agent } = process.env;
if (npm_config_user_agent) {
return /\byarn\//.test(npm_config_user_agent);
}
return false;
}
function fetch(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location)
return fetch(res.headers.location).then(resolve, reject);
if (res.statusCode !== 200)
return reject(new Error(`Server responded with ${res.statusCode}`));
let chunks = [];
res.on("data", (chunk) => chunks.push(chunk));
res.on("end", () => resolve(Buffer.concat(chunks)));
}).on("error", reject);
});
}
function extractFileFromTarGzip(buffer, subpath) {
try {
buffer = zlib.unzipSync(buffer);
} catch (err) {
throw new Error(`Invalid gzip data in archive: ${err && err.message || err}`);
}
let str = (i, n) => String.fromCharCode(...buffer.subarray(i, i + n)).replace(/\0.*$/, "");
let offset = 0;
subpath = `package/${subpath}`;
while (offset < buffer.length) {
let name = str(offset, 100);
let size = parseInt(str(offset + 124, 12), 8);
offset += 512;
if (!isNaN(size)) {
if (name === subpath) return buffer.subarray(offset, offset + size);
offset += size + 511 & ~511;
}
}
throw new Error(`Could not find ${JSON.stringify(subpath)} in archive`);
}
function installUsingNPM(pkg, subpath, binPath) {
const env = { ...process.env, npm_config_global: void 0 };
const esbuildLibDir = path2.dirname(require.resolve("esbuild"));
const installDir = path2.join(esbuildLibDir, "npm-install");
fs2.mkdirSync(installDir);
try {
fs2.writeFileSync(path2.join(installDir, "package.json"), "{}");
child_process.execSync(
`npm install --loglevel=error --prefer-offline --no-audit --progress=false ${pkg}@${versionFromPackageJSON}`,
{ cwd: installDir, stdio: "pipe", env }
);
const installedBinPath = path2.join(installDir, "node_modules", pkg, subpath);
fs2.renameSync(installedBinPath, binPath);
} finally {
try {
removeRecursive(installDir);
} catch {
}
}
}
function removeRecursive(dir) {
for (const entry of fs2.readdirSync(dir)) {
const entryPath = path2.join(dir, entry);
let stats;
try {
stats = fs2.lstatSync(entryPath);
} catch {
continue;
}
if (stats.isDirectory()) removeRecursive(entryPath);
else fs2.unlinkSync(entryPath);
}
fs2.rmdirSync(dir);
}
function applyManualBinaryPathOverride(overridePath) {
const pathString = JSON.stringify(overridePath);
fs2.writeFileSync(toPath, `#!/usr/bin/env node
require('child_process').execFileSync(${pathString}, process.argv.slice(2), { stdio: 'inherit' });
`);
const libMain = path2.join(__dirname, "lib", "main.js");
const code = fs2.readFileSync(libMain, "utf8");
fs2.writeFileSync(libMain, `var ESBUILD_BINARY_PATH = ${pathString};
${code}`);
}
function maybeOptimizePackage(binPath) {
const { isWASM } = pkgAndSubpathForCurrentPlatform();
if (os2.platform() !== "win32" && !isYarn() && !isWASM) {
const tempPath = path2.join(__dirname, "bin-esbuild");
try {
fs2.linkSync(binPath, tempPath);
fs2.renameSync(tempPath, toPath);
isToPathJS = false;
fs2.unlinkSync(tempPath);
} catch {
}
}
}
async function downloadDirectlyFromNPM(pkg, subpath, binPath) {
const url = `https://registry.npmjs.org/${pkg}/-/${pkg.replace("@esbuild/", "")}-${versionFromPackageJSON}.tgz`;
console.error(`[esbuild] Trying to download ${JSON.stringify(url)}`);
try {
fs2.writeFileSync(binPath, extractFileFromTarGzip(await fetch(url), subpath));
fs2.chmodSync(binPath, 493);
} catch (e) {
console.error(`[esbuild] Failed to download ${JSON.stringify(url)}: ${e && e.message || e}`);
throw e;
}
}
async function checkAndPreparePackage() {
if (isValidBinaryPath(ESBUILD_BINARY_PATH)) {
if (!fs2.existsSync(ESBUILD_BINARY_PATH)) {
console.warn(`[esbuild] Ignoring bad configuration: ESBUILD_BINARY_PATH=${ESBUILD_BINARY_PATH}`);
} else {
applyManualBinaryPathOverride(ESBUILD_BINARY_PATH);
return;
}
}
const { pkg, subpath } = pkgAndSubpathForCurrentPlatform();
let binPath;
try {
binPath = require.resolve(`${pkg}/${subpath}`);
} catch (e) {
console.error(`[esbuild] Failed to find package "${pkg}" on the file system
This can happen if you use the "--no-optional" flag. The "optionalDependencies"
package.json feature is used by esbuild to install the correct binary executable
for your current platform. This install script will now attempt to work around
this. If that fails, you need to remove the "--no-optional" flag to use esbuild.
`);
binPath = downloadedBinPath(pkg, subpath);
try {
console.error(`[esbuild] Trying to install package "${pkg}" using npm`);
installUsingNPM(pkg, subpath, binPath);
} catch (e2) {
console.error(`[esbuild] Failed to install package "${pkg}" using npm: ${e2 && e2.message || e2}`);
try {
await downloadDirectlyFromNPM(pkg, subpath, binPath);
} catch (e3) {
throw new Error(`Failed to install package "${pkg}"`);
}
}
}
maybeOptimizePackage(binPath);
}
checkAndPreparePackage().then(() => {
if (isToPathJS) {
validateBinaryVersion(process.execPath, toPath);
} else {
validateBinaryVersion(toPath);
}
});
-716
View File
@@ -1,716 +0,0 @@
export type Platform = 'browser' | 'node' | 'neutral'
export type Format = 'iife' | 'cjs' | 'esm'
export type Loader = 'base64' | 'binary' | 'copy' | 'css' | 'dataurl' | 'default' | 'empty' | 'file' | 'js' | 'json' | 'jsx' | 'local-css' | 'text' | 'ts' | 'tsx'
export type LogLevel = 'verbose' | 'debug' | 'info' | 'warning' | 'error' | 'silent'
export type Charset = 'ascii' | 'utf8'
export type Drop = 'console' | 'debugger'
export type AbsPaths = 'code' | 'log' | 'metafile'
interface CommonOptions {
/** Documentation: https://esbuild.github.io/api/#sourcemap */
sourcemap?: boolean | 'linked' | 'inline' | 'external' | 'both'
/** Documentation: https://esbuild.github.io/api/#legal-comments */
legalComments?: 'none' | 'inline' | 'eof' | 'linked' | 'external'
/** Documentation: https://esbuild.github.io/api/#source-root */
sourceRoot?: string
/** Documentation: https://esbuild.github.io/api/#sources-content */
sourcesContent?: boolean
/** Documentation: https://esbuild.github.io/api/#format */
format?: Format
/** Documentation: https://esbuild.github.io/api/#global-name */
globalName?: string
/** Documentation: https://esbuild.github.io/api/#target */
target?: string | string[]
/** Documentation: https://esbuild.github.io/api/#supported */
supported?: Record<string, boolean>
/** Documentation: https://esbuild.github.io/api/#platform */
platform?: Platform
/** Documentation: https://esbuild.github.io/api/#mangle-props */
mangleProps?: RegExp
/** Documentation: https://esbuild.github.io/api/#mangle-props */
reserveProps?: RegExp
/** Documentation: https://esbuild.github.io/api/#mangle-props */
mangleQuoted?: boolean
/** Documentation: https://esbuild.github.io/api/#mangle-props */
mangleCache?: Record<string, string | false>
/** Documentation: https://esbuild.github.io/api/#drop */
drop?: Drop[]
/** Documentation: https://esbuild.github.io/api/#drop-labels */
dropLabels?: string[]
/** Documentation: https://esbuild.github.io/api/#minify */
minify?: boolean
/** Documentation: https://esbuild.github.io/api/#minify */
minifyWhitespace?: boolean
/** Documentation: https://esbuild.github.io/api/#minify */
minifyIdentifiers?: boolean
/** Documentation: https://esbuild.github.io/api/#minify */
minifySyntax?: boolean
/** Documentation: https://esbuild.github.io/api/#line-limit */
lineLimit?: number
/** Documentation: https://esbuild.github.io/api/#charset */
charset?: Charset
/** Documentation: https://esbuild.github.io/api/#tree-shaking */
treeShaking?: boolean
/** Documentation: https://esbuild.github.io/api/#ignore-annotations */
ignoreAnnotations?: boolean
/** Documentation: https://esbuild.github.io/api/#jsx */
jsx?: 'transform' | 'preserve' | 'automatic'
/** Documentation: https://esbuild.github.io/api/#jsx-factory */
jsxFactory?: string
/** Documentation: https://esbuild.github.io/api/#jsx-fragment */
jsxFragment?: string
/** Documentation: https://esbuild.github.io/api/#jsx-import-source */
jsxImportSource?: string
/** Documentation: https://esbuild.github.io/api/#jsx-development */
jsxDev?: boolean
/** Documentation: https://esbuild.github.io/api/#jsx-side-effects */
jsxSideEffects?: boolean
/** Documentation: https://esbuild.github.io/api/#define */
define?: { [key: string]: string }
/** Documentation: https://esbuild.github.io/api/#pure */
pure?: string[]
/** Documentation: https://esbuild.github.io/api/#keep-names */
keepNames?: boolean
/** Documentation: https://esbuild.github.io/api/#abs-paths */
absPaths?: AbsPaths[]
/** Documentation: https://esbuild.github.io/api/#color */
color?: boolean
/** Documentation: https://esbuild.github.io/api/#log-level */
logLevel?: LogLevel
/** Documentation: https://esbuild.github.io/api/#log-limit */
logLimit?: number
/** Documentation: https://esbuild.github.io/api/#log-override */
logOverride?: Record<string, LogLevel>
/** Documentation: https://esbuild.github.io/api/#tsconfig-raw */
tsconfigRaw?: string | TsconfigRaw
}
export interface TsconfigRaw {
compilerOptions?: {
alwaysStrict?: boolean
baseUrl?: string
experimentalDecorators?: boolean
importsNotUsedAsValues?: 'remove' | 'preserve' | 'error'
jsx?: 'preserve' | 'react-native' | 'react' | 'react-jsx' | 'react-jsxdev'
jsxFactory?: string
jsxFragmentFactory?: string
jsxImportSource?: string
paths?: Record<string, string[]>
preserveValueImports?: boolean
strict?: boolean
target?: string
useDefineForClassFields?: boolean
verbatimModuleSyntax?: boolean
}
}
export interface BuildOptions extends CommonOptions {
/** Documentation: https://esbuild.github.io/api/#bundle */
bundle?: boolean
/** Documentation: https://esbuild.github.io/api/#splitting */
splitting?: boolean
/** Documentation: https://esbuild.github.io/api/#preserve-symlinks */
preserveSymlinks?: boolean
/** Documentation: https://esbuild.github.io/api/#outfile */
outfile?: string
/** Documentation: https://esbuild.github.io/api/#metafile */
metafile?: boolean
/** Documentation: https://esbuild.github.io/api/#outdir */
outdir?: string
/** Documentation: https://esbuild.github.io/api/#outbase */
outbase?: string
/** Documentation: https://esbuild.github.io/api/#external */
external?: string[]
/** Documentation: https://esbuild.github.io/api/#packages */
packages?: 'bundle' | 'external'
/** Documentation: https://esbuild.github.io/api/#alias */
alias?: Record<string, string>
/** Documentation: https://esbuild.github.io/api/#loader */
loader?: { [ext: string]: Loader }
/** Documentation: https://esbuild.github.io/api/#resolve-extensions */
resolveExtensions?: string[]
/** Documentation: https://esbuild.github.io/api/#main-fields */
mainFields?: string[]
/** Documentation: https://esbuild.github.io/api/#conditions */
conditions?: string[]
/** Documentation: https://esbuild.github.io/api/#write */
write?: boolean
/** Documentation: https://esbuild.github.io/api/#allow-overwrite */
allowOverwrite?: boolean
/** Documentation: https://esbuild.github.io/api/#tsconfig */
tsconfig?: string
/** Documentation: https://esbuild.github.io/api/#out-extension */
outExtension?: { [ext: string]: string }
/** Documentation: https://esbuild.github.io/api/#public-path */
publicPath?: string
/** Documentation: https://esbuild.github.io/api/#entry-names */
entryNames?: string
/** Documentation: https://esbuild.github.io/api/#chunk-names */
chunkNames?: string
/** Documentation: https://esbuild.github.io/api/#asset-names */
assetNames?: string
/** Documentation: https://esbuild.github.io/api/#inject */
inject?: string[]
/** Documentation: https://esbuild.github.io/api/#banner */
banner?: { [type: string]: string }
/** Documentation: https://esbuild.github.io/api/#footer */
footer?: { [type: string]: string }
/** Documentation: https://esbuild.github.io/api/#entry-points */
entryPoints?: (string | { in: string, out: string })[] | Record<string, string>
/** Documentation: https://esbuild.github.io/api/#stdin */
stdin?: StdinOptions
/** Documentation: https://esbuild.github.io/plugins/ */
plugins?: Plugin[]
/** Documentation: https://esbuild.github.io/api/#working-directory */
absWorkingDir?: string
/** Documentation: https://esbuild.github.io/api/#node-paths */
nodePaths?: string[]; // The "NODE_PATH" variable from Node.js
}
export interface StdinOptions {
contents: string | Uint8Array
resolveDir?: string
sourcefile?: string
loader?: Loader
}
export interface Message {
id: string
pluginName: string
text: string
location: Location | null
notes: Note[]
/**
* Optional user-specified data that is passed through unmodified. You can
* use this to stash the original error, for example.
*/
detail: any
}
export interface Note {
text: string
location: Location | null
}
export interface Location {
file: string
namespace: string
/** 1-based */
line: number
/** 0-based, in bytes */
column: number
/** in bytes */
length: number
lineText: string
suggestion: string
}
export interface OutputFile {
path: string
contents: Uint8Array
hash: string
/** "contents" as text (changes automatically with "contents") */
readonly text: string
}
export interface BuildResult<ProvidedOptions extends BuildOptions = BuildOptions> {
errors: Message[]
warnings: Message[]
/** Only when "write: false" */
outputFiles: OutputFile[] | (ProvidedOptions['write'] extends false ? never : undefined)
/** Only when "metafile: true" */
metafile: Metafile | (ProvidedOptions['metafile'] extends true ? never : undefined)
/** Only when "mangleCache" is present */
mangleCache: Record<string, string | false> | (ProvidedOptions['mangleCache'] extends Object ? never : undefined)
}
export interface BuildFailure extends Error {
errors: Message[]
warnings: Message[]
}
/** Documentation: https://esbuild.github.io/api/#serve-arguments */
export interface ServeOptions {
port?: number
host?: string
servedir?: string
keyfile?: string
certfile?: string
fallback?: string
cors?: CORSOptions
onRequest?: (args: ServeOnRequestArgs) => void
}
/** Documentation: https://esbuild.github.io/api/#cors */
export interface CORSOptions {
origin?: string | string[]
}
export interface ServeOnRequestArgs {
remoteAddress: string
method: string
path: string
status: number
/** The time to generate the response, not to send it */
timeInMS: number
}
/** Documentation: https://esbuild.github.io/api/#serve-return-values */
export interface ServeResult {
port: number
hosts: string[]
}
export interface TransformOptions extends CommonOptions {
/** Documentation: https://esbuild.github.io/api/#sourcefile */
sourcefile?: string
/** Documentation: https://esbuild.github.io/api/#loader */
loader?: Loader
/** Documentation: https://esbuild.github.io/api/#banner */
banner?: string
/** Documentation: https://esbuild.github.io/api/#footer */
footer?: string
}
export interface TransformResult<ProvidedOptions extends TransformOptions = TransformOptions> {
code: string
map: string
warnings: Message[]
/** Only when "mangleCache" is present */
mangleCache: Record<string, string | false> | (ProvidedOptions['mangleCache'] extends Object ? never : undefined)
/** Only when "legalComments" is "external" */
legalComments: string | (ProvidedOptions['legalComments'] extends 'external' ? never : undefined)
}
export interface TransformFailure extends Error {
errors: Message[]
warnings: Message[]
}
export interface Plugin {
name: string
setup: (build: PluginBuild) => (void | Promise<void>)
}
export interface PluginBuild {
/** Documentation: https://esbuild.github.io/plugins/#build-options */
initialOptions: BuildOptions
/** Documentation: https://esbuild.github.io/plugins/#resolve */
resolve(path: string, options?: ResolveOptions): Promise<ResolveResult>
/** Documentation: https://esbuild.github.io/plugins/#on-start */
onStart(callback: () =>
(OnStartResult | null | void | Promise<OnStartResult | null | void>)): void
/** Documentation: https://esbuild.github.io/plugins/#on-end */
onEnd(callback: (result: BuildResult) =>
(OnEndResult | null | void | Promise<OnEndResult | null | void>)): void
/** Documentation: https://esbuild.github.io/plugins/#on-resolve */
onResolve(options: OnResolveOptions, callback: (args: OnResolveArgs) =>
(OnResolveResult | null | undefined | Promise<OnResolveResult | null | undefined>)): void
/** Documentation: https://esbuild.github.io/plugins/#on-load */
onLoad(options: OnLoadOptions, callback: (args: OnLoadArgs) =>
(OnLoadResult | null | undefined | Promise<OnLoadResult | null | undefined>)): void
/** Documentation: https://esbuild.github.io/plugins/#on-dispose */
onDispose(callback: () => void): void
// This is a full copy of the esbuild library in case you need it
esbuild: {
context: typeof context,
build: typeof build,
buildSync: typeof buildSync,
transform: typeof transform,
transformSync: typeof transformSync,
formatMessages: typeof formatMessages,
formatMessagesSync: typeof formatMessagesSync,
analyzeMetafile: typeof analyzeMetafile,
analyzeMetafileSync: typeof analyzeMetafileSync,
initialize: typeof initialize,
version: typeof version,
}
}
/** Documentation: https://esbuild.github.io/plugins/#resolve-options */
export interface ResolveOptions {
pluginName?: string
importer?: string
namespace?: string
resolveDir?: string
kind?: ImportKind
pluginData?: any
with?: Record<string, string>
}
/** Documentation: https://esbuild.github.io/plugins/#resolve-results */
export interface ResolveResult {
errors: Message[]
warnings: Message[]
path: string
external: boolean
sideEffects: boolean
namespace: string
suffix: string
pluginData: any
}
export interface OnStartResult {
errors?: PartialMessage[]
warnings?: PartialMessage[]
}
export interface OnEndResult {
errors?: PartialMessage[]
warnings?: PartialMessage[]
}
/** Documentation: https://esbuild.github.io/plugins/#on-resolve-options */
export interface OnResolveOptions {
filter: RegExp
namespace?: string
}
/** Documentation: https://esbuild.github.io/plugins/#on-resolve-arguments */
export interface OnResolveArgs {
path: string
importer: string
namespace: string
resolveDir: string
kind: ImportKind
pluginData: any
with: Record<string, string>
}
export type ImportKind =
| 'entry-point'
// JS
| 'import-statement'
| 'require-call'
| 'dynamic-import'
| 'require-resolve'
// CSS
| 'import-rule'
| 'composes-from'
| 'url-token'
/** Documentation: https://esbuild.github.io/plugins/#on-resolve-results */
export interface OnResolveResult {
pluginName?: string
errors?: PartialMessage[]
warnings?: PartialMessage[]
path?: string
external?: boolean
sideEffects?: boolean
namespace?: string
suffix?: string
pluginData?: any
watchFiles?: string[]
watchDirs?: string[]
}
/** Documentation: https://esbuild.github.io/plugins/#on-load-options */
export interface OnLoadOptions {
filter: RegExp
namespace?: string
}
/** Documentation: https://esbuild.github.io/plugins/#on-load-arguments */
export interface OnLoadArgs {
path: string
namespace: string
suffix: string
pluginData: any
with: Record<string, string>
}
/** Documentation: https://esbuild.github.io/plugins/#on-load-results */
export interface OnLoadResult {
pluginName?: string
errors?: PartialMessage[]
warnings?: PartialMessage[]
contents?: string | Uint8Array
resolveDir?: string
loader?: Loader
pluginData?: any
watchFiles?: string[]
watchDirs?: string[]
}
export interface PartialMessage {
id?: string
pluginName?: string
text?: string
location?: Partial<Location> | null
notes?: PartialNote[]
detail?: any
}
export interface PartialNote {
text?: string
location?: Partial<Location> | null
}
/** Documentation: https://esbuild.github.io/api/#metafile */
export interface Metafile {
inputs: {
[path: string]: {
bytes: number
imports: {
path: string
kind: ImportKind
external?: boolean
original?: string
with?: Record<string, string>
}[]
format?: 'cjs' | 'esm'
with?: Record<string, string>
}
}
outputs: {
[path: string]: {
bytes: number
inputs: {
[path: string]: {
bytesInOutput: number
}
}
imports: {
path: string
kind: ImportKind | 'file-loader'
external?: boolean
}[]
exports: string[]
entryPoint?: string
cssBundle?: string
}
}
}
export interface FormatMessagesOptions {
kind: 'error' | 'warning'
color?: boolean
terminalWidth?: number
}
export interface AnalyzeMetafileOptions {
color?: boolean
verbose?: boolean
}
/** Documentation: https://esbuild.github.io/api/#watch-arguments */
export interface WatchOptions {
delay?: number // In milliseconds
}
export interface BuildContext<ProvidedOptions extends BuildOptions = BuildOptions> {
/** Documentation: https://esbuild.github.io/api/#rebuild */
rebuild(): Promise<BuildResult<ProvidedOptions>>
/** Documentation: https://esbuild.github.io/api/#watch */
watch(options?: WatchOptions): Promise<void>
/** Documentation: https://esbuild.github.io/api/#serve */
serve(options?: ServeOptions): Promise<ServeResult>
cancel(): Promise<void>
dispose(): Promise<void>
}
// This is a TypeScript type-level function which replaces any keys in "In"
// that aren't in "Out" with "never". We use this to reject properties with
// typos in object literals. See: https://stackoverflow.com/questions/49580725
type SameShape<Out, In extends Out> = In & { [Key in Exclude<keyof In, keyof Out>]: never }
/**
* This function invokes the "esbuild" command-line tool for you. It returns a
* promise that either resolves with a "BuildResult" object or rejects with a
* "BuildFailure" object.
*
* - Works in node: yes
* - Works in browser: yes
*
* Documentation: https://esbuild.github.io/api/#build
*/
export declare function build<T extends BuildOptions>(options: SameShape<BuildOptions, T>): Promise<BuildResult<T>>
/**
* This is the advanced long-running form of "build" that supports additional
* features such as watch mode and a local development server.
*
* - Works in node: yes
* - Works in browser: no
*
* Documentation: https://esbuild.github.io/api/#build
*/
export declare function context<T extends BuildOptions>(options: SameShape<BuildOptions, T>): Promise<BuildContext<T>>
/**
* This function transforms a single JavaScript file. It can be used to minify
* JavaScript, convert TypeScript/JSX to JavaScript, or convert newer JavaScript
* to older JavaScript. It returns a promise that is either resolved with a
* "TransformResult" object or rejected with a "TransformFailure" object.
*
* - Works in node: yes
* - Works in browser: yes
*
* Documentation: https://esbuild.github.io/api/#transform
*/
export declare function transform<T extends TransformOptions>(input: string | Uint8Array, options?: SameShape<TransformOptions, T>): Promise<TransformResult<T>>
/**
* Converts log messages to formatted message strings suitable for printing in
* the terminal. This allows you to reuse the built-in behavior of esbuild's
* log message formatter. This is a batch-oriented API for efficiency.
*
* - Works in node: yes
* - Works in browser: yes
*/
export declare function formatMessages(messages: PartialMessage[], options: FormatMessagesOptions): Promise<string[]>
/**
* Pretty-prints an analysis of the metafile JSON to a string. This is just for
* convenience to be able to match esbuild's pretty-printing exactly. If you want
* to customize it, you can just inspect the data in the metafile yourself.
*
* - Works in node: yes
* - Works in browser: yes
*
* Documentation: https://esbuild.github.io/api/#analyze
*/
export declare function analyzeMetafile(metafile: Metafile | string, options?: AnalyzeMetafileOptions): Promise<string>
/**
* A synchronous version of "build".
*
* - Works in node: yes
* - Works in browser: no
*
* Documentation: https://esbuild.github.io/api/#build
*/
export declare function buildSync<T extends BuildOptions>(options: SameShape<BuildOptions, T>): BuildResult<T>
/**
* A synchronous version of "transform".
*
* - Works in node: yes
* - Works in browser: no
*
* Documentation: https://esbuild.github.io/api/#transform
*/
export declare function transformSync<T extends TransformOptions>(input: string | Uint8Array, options?: SameShape<TransformOptions, T>): TransformResult<T>
/**
* A synchronous version of "formatMessages".
*
* - Works in node: yes
* - Works in browser: no
*/
export declare function formatMessagesSync(messages: PartialMessage[], options: FormatMessagesOptions): string[]
/**
* A synchronous version of "analyzeMetafile".
*
* - Works in node: yes
* - Works in browser: no
*
* Documentation: https://esbuild.github.io/api/#analyze
*/
export declare function analyzeMetafileSync(metafile: Metafile | string, options?: AnalyzeMetafileOptions): string
/**
* This configures the browser-based version of esbuild. It is necessary to
* call this first and wait for the returned promise to be resolved before
* making other API calls when using esbuild in the browser.
*
* - Works in node: yes
* - Works in browser: yes ("options" is required)
*
* Documentation: https://esbuild.github.io/api/#browser
*/
export declare function initialize(options: InitializeOptions): Promise<void>
export interface InitializeOptions {
/**
* The URL of the "esbuild.wasm" file. This must be provided when running
* esbuild in the browser.
*/
wasmURL?: string | URL
/**
* The result of calling "new WebAssembly.Module(buffer)" where "buffer"
* is a typed array or ArrayBuffer containing the binary code of the
* "esbuild.wasm" file.
*
* You can use this as an alternative to "wasmURL" for environments where it's
* not possible to download the WebAssembly module.
*/
wasmModule?: WebAssembly.Module
/**
* By default esbuild runs the WebAssembly-based browser API in a web worker
* to avoid blocking the UI thread. This can be disabled by setting "worker"
* to false.
*/
worker?: boolean
}
export let version: string
// Call this function to terminate esbuild's child process. The child process
// is not terminated and re-created after each API call because it's more
// efficient to keep it around when there are multiple API calls.
//
// In node this happens automatically before the parent node process exits. So
// you only need to call this if you know you will not make any more esbuild
// API calls and you want to clean up resources.
//
// Unlike node, Deno lacks the necessary APIs to clean up child processes
// automatically. You must manually call stop() in Deno when you're done
// using esbuild or Deno will continue running forever.
//
// Another reason you might want to call this is if you are using esbuild from
// within a Deno test. Deno fails tests that create a child process without
// killing it before the test ends, so you have to call this function (and
// await the returned promise) in every Deno test that uses esbuild.
export declare function stop(): Promise<void>
// Note: These declarations exist to avoid type errors when you omit "dom" from
// "lib" in your "tsconfig.json" file. TypeScript confusingly declares the
// global "WebAssembly" type in "lib.dom.d.ts" even though it has nothing to do
// with the browser DOM and is present in many non-browser JavaScript runtimes
// (e.g. node and deno). Declaring it here allows esbuild's API to be used in
// these scenarios.
//
// There's an open issue about getting this problem corrected (although these
// declarations will need to remain even if this is fixed for backward
// compatibility with older TypeScript versions):
//
// https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/826
//
declare global {
namespace WebAssembly {
interface Module {
}
}
interface URL {
}
}
-2242
View File
File diff suppressed because it is too large Load Diff
-49
View File
@@ -1,49 +0,0 @@
{
"name": "esbuild",
"version": "0.27.3",
"description": "An extremely fast JavaScript and CSS bundler and minifier.",
"repository": {
"type": "git",
"url": "git+https://github.com/evanw/esbuild.git"
},
"scripts": {
"postinstall": "node install.js"
},
"main": "lib/main.js",
"types": "lib/main.d.ts",
"engines": {
"node": ">=18"
},
"bin": {
"esbuild": "bin/esbuild"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.27.3",
"@esbuild/android-arm": "0.27.3",
"@esbuild/android-arm64": "0.27.3",
"@esbuild/android-x64": "0.27.3",
"@esbuild/darwin-arm64": "0.27.3",
"@esbuild/darwin-x64": "0.27.3",
"@esbuild/freebsd-arm64": "0.27.3",
"@esbuild/freebsd-x64": "0.27.3",
"@esbuild/linux-arm": "0.27.3",
"@esbuild/linux-arm64": "0.27.3",
"@esbuild/linux-ia32": "0.27.3",
"@esbuild/linux-loong64": "0.27.3",
"@esbuild/linux-mips64el": "0.27.3",
"@esbuild/linux-ppc64": "0.27.3",
"@esbuild/linux-riscv64": "0.27.3",
"@esbuild/linux-s390x": "0.27.3",
"@esbuild/linux-x64": "0.27.3",
"@esbuild/netbsd-arm64": "0.27.3",
"@esbuild/netbsd-x64": "0.27.3",
"@esbuild/openbsd-arm64": "0.27.3",
"@esbuild/openbsd-x64": "0.27.3",
"@esbuild/openharmony-arm64": "0.27.3",
"@esbuild/sunos-x64": "0.27.3",
"@esbuild/win32-arm64": "0.27.3",
"@esbuild/win32-ia32": "0.27.3",
"@esbuild/win32-x64": "0.27.3"
},
"license": "MIT"
}
Generated Vendored
-7
View File
@@ -1,7 +0,0 @@
Copyright 2023 Abdullah Atta
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.
-91
View File
@@ -1,91 +0,0 @@
<p align="center">
<img src="https://github.com/thecodrr/fdir/raw/master/assets/fdir.gif" width="75%"/>
<h1 align="center">The Fastest Directory Crawler & Globber for NodeJS</h1>
<p align="center">
<a href="https://www.npmjs.com/package/fdir"><img src="https://img.shields.io/npm/v/fdir?style=for-the-badge"/></a>
<a href="https://www.npmjs.com/package/fdir"><img src="https://img.shields.io/npm/dw/fdir?style=for-the-badge"/></a>
<a href="https://codeclimate.com/github/thecodrr/fdir/maintainability"><img src="https://img.shields.io/codeclimate/maintainability-percentage/thecodrr/fdir?style=for-the-badge"/></a>
<a href="https://coveralls.io/github/thecodrr/fdir?branch=master"><img src="https://img.shields.io/coveralls/github/thecodrr/fdir?style=for-the-badge"/></a>
<a href="https://www.npmjs.com/package/fdir"><img src="https://img.shields.io/bundlephobia/minzip/fdir?style=for-the-badge"/></a>
<a href="https://www.producthunt.com/posts/fdir-every-millisecond-matters"><img src="https://img.shields.io/badge/ProductHunt-Upvote-red?style=for-the-badge&logo=product-hunt"/></a>
<a href="https://dev.to/thecodrr/how-i-wrote-the-fastest-directory-crawler-ever-3p9c"><img src="https://img.shields.io/badge/dev.to-Read%20Blog-black?style=for-the-badge&logo=dev.to"/></a>
<a href="./LICENSE"><img src="https://img.shields.io/github/license/thecodrr/fdir?style=for-the-badge"/></a>
</p>
</p>
**The Fastest:** Nothing similar (in the NodeJS world) beats `fdir` in speed. It can easily crawl a directory containing **1 million files in < 1 second.**
💡 **Stupidly Easy:** `fdir` uses expressive Builder pattern to build the crawler increasing code readability.
🤖 **Zero Dependencies\*:** `fdir` only uses NodeJS `fs` & `path` modules.
🕺 **Astonishingly Small:** < 2KB in size gzipped & minified.
🖮 **Hackable:** Extending `fdir` is extremely simple now that the new Builder API is here. Feel free to experiment around.
_\* `picomatch` must be installed manually by the user to support globbing._
## 🚄 Quickstart
### Installation
You can install using `npm`:
```sh
$ npm i fdir
```
or Yarn:
```sh
$ yarn add fdir
```
### Usage
```ts
import { fdir } from "fdir";
// create the builder
const api = new fdir().withFullPaths().crawl("path/to/dir");
// get all files in a directory synchronously
const files = api.sync();
// or asynchronously
api.withPromise().then((files) => {
// do something with the result here.
});
```
## Documentation:
Documentation for all methods is available [here](/documentation.md).
## 📊 Benchmarks:
Please check the benchmark against the latest version [here](/BENCHMARKS.md).
## 🙏Used by:
`fdir` is downloaded over 200k+ times a week by projects around the world. Here's a list of some notable projects using `fdir` in production:
> Note: if you think your project should be here, feel free to open an issue. Notable is anything with a considerable amount of GitHub stars.
1. [rollup/plugins](https://github.com/rollup/plugins)
2. [SuperchupuDev/tinyglobby](https://github.com/SuperchupuDev/tinyglobby)
3. [pulumi/pulumi](https://github.com/pulumi/pulumi)
4. [dotenvx/dotenvx](https://github.com/dotenvx/dotenvx)
5. [mdn/yari](https://github.com/mdn/yari)
6. [streetwriters/notesnook](https://github.com/streetwriters/notesnook)
7. [imba/imba](https://github.com/imba/imba)
8. [moroshko/react-scanner](https://github.com/moroshko/react-scanner)
9. [netlify/build](https://github.com/netlify/build)
10. [yassinedoghri/astro-i18next](https://github.com/yassinedoghri/astro-i18next)
11. [selfrefactor/rambda](https://github.com/selfrefactor/rambda)
12. [whyboris/Video-Hub-App](https://github.com/whyboris/Video-Hub-App)
## 🦮 LICENSE
Copyright &copy; 2024 Abdullah Atta under MIT. [Read full text here.](https://github.com/thecodrr/fdir/raw/master/LICENSE)
-588
View File
@@ -1,588 +0,0 @@
//#region rolldown:runtime
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
const path = __toESM(require("path"));
const fs = __toESM(require("fs"));
//#region src/utils.ts
function cleanPath(path$1) {
let normalized = (0, path.normalize)(path$1);
if (normalized.length > 1 && normalized[normalized.length - 1] === path.sep) normalized = normalized.substring(0, normalized.length - 1);
return normalized;
}
const SLASHES_REGEX = /[\\/]/g;
function convertSlashes(path$1, separator) {
return path$1.replace(SLASHES_REGEX, separator);
}
const WINDOWS_ROOT_DIR_REGEX = /^[a-z]:[\\/]$/i;
function isRootDirectory(path$1) {
return path$1 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path$1);
}
function normalizePath(path$1, options) {
const { resolvePaths, normalizePath: normalizePath$1, pathSeparator } = options;
const pathNeedsCleaning = process.platform === "win32" && path$1.includes("/") || path$1.startsWith(".");
if (resolvePaths) path$1 = (0, path.resolve)(path$1);
if (normalizePath$1 || pathNeedsCleaning) path$1 = cleanPath(path$1);
if (path$1 === ".") return "";
const needsSeperator = path$1[path$1.length - 1] !== pathSeparator;
return convertSlashes(needsSeperator ? path$1 + pathSeparator : path$1, pathSeparator);
}
//#endregion
//#region src/api/functions/join-path.ts
function joinPathWithBasePath(filename, directoryPath) {
return directoryPath + filename;
}
function joinPathWithRelativePath(root, options) {
return function(filename, directoryPath) {
const sameRoot = directoryPath.startsWith(root);
if (sameRoot) return directoryPath.slice(root.length) + filename;
else return convertSlashes((0, path.relative)(root, directoryPath), options.pathSeparator) + options.pathSeparator + filename;
};
}
function joinPath(filename) {
return filename;
}
function joinDirectoryPath(filename, directoryPath, separator) {
return directoryPath + filename + separator;
}
function build$7(root, options) {
const { relativePaths, includeBasePath } = options;
return relativePaths && root ? joinPathWithRelativePath(root, options) : includeBasePath ? joinPathWithBasePath : joinPath;
}
//#endregion
//#region src/api/functions/push-directory.ts
function pushDirectoryWithRelativePath(root) {
return function(directoryPath, paths) {
paths.push(directoryPath.substring(root.length) || ".");
};
}
function pushDirectoryFilterWithRelativePath(root) {
return function(directoryPath, paths, filters) {
const relativePath = directoryPath.substring(root.length) || ".";
if (filters.every((filter) => filter(relativePath, true))) paths.push(relativePath);
};
}
const pushDirectory = (directoryPath, paths) => {
paths.push(directoryPath || ".");
};
const pushDirectoryFilter = (directoryPath, paths, filters) => {
const path$1 = directoryPath || ".";
if (filters.every((filter) => filter(path$1, true))) paths.push(path$1);
};
const empty$2 = () => {};
function build$6(root, options) {
const { includeDirs, filters, relativePaths } = options;
if (!includeDirs) return empty$2;
if (relativePaths) return filters && filters.length ? pushDirectoryFilterWithRelativePath(root) : pushDirectoryWithRelativePath(root);
return filters && filters.length ? pushDirectoryFilter : pushDirectory;
}
//#endregion
//#region src/api/functions/push-file.ts
const pushFileFilterAndCount = (filename, _paths, counts, filters) => {
if (filters.every((filter) => filter(filename, false))) counts.files++;
};
const pushFileFilter = (filename, paths, _counts, filters) => {
if (filters.every((filter) => filter(filename, false))) paths.push(filename);
};
const pushFileCount = (_filename, _paths, counts, _filters) => {
counts.files++;
};
const pushFile = (filename, paths) => {
paths.push(filename);
};
const empty$1 = () => {};
function build$5(options) {
const { excludeFiles, filters, onlyCounts } = options;
if (excludeFiles) return empty$1;
if (filters && filters.length) return onlyCounts ? pushFileFilterAndCount : pushFileFilter;
else if (onlyCounts) return pushFileCount;
else return pushFile;
}
//#endregion
//#region src/api/functions/get-array.ts
const getArray = (paths) => {
return paths;
};
const getArrayGroup = () => {
return [""].slice(0, 0);
};
function build$4(options) {
return options.group ? getArrayGroup : getArray;
}
//#endregion
//#region src/api/functions/group-files.ts
const groupFiles = (groups, directory, files) => {
groups.push({
directory,
files,
dir: directory
});
};
const empty = () => {};
function build$3(options) {
return options.group ? groupFiles : empty;
}
//#endregion
//#region src/api/functions/resolve-symlink.ts
const resolveSymlinksAsync = function(path$1, state, callback$1) {
const { queue, fs: fs$1, options: { suppressErrors } } = state;
queue.enqueue();
fs$1.realpath(path$1, (error, resolvedPath) => {
if (error) return queue.dequeue(suppressErrors ? null : error, state);
fs$1.stat(resolvedPath, (error$1, stat) => {
if (error$1) return queue.dequeue(suppressErrors ? null : error$1, state);
if (stat.isDirectory() && isRecursive(path$1, resolvedPath, state)) return queue.dequeue(null, state);
callback$1(stat, resolvedPath);
queue.dequeue(null, state);
});
});
};
const resolveSymlinks = function(path$1, state, callback$1) {
const { queue, fs: fs$1, options: { suppressErrors } } = state;
queue.enqueue();
try {
const resolvedPath = fs$1.realpathSync(path$1);
const stat = fs$1.statSync(resolvedPath);
if (stat.isDirectory() && isRecursive(path$1, resolvedPath, state)) return;
callback$1(stat, resolvedPath);
} catch (e) {
if (!suppressErrors) throw e;
}
};
function build$2(options, isSynchronous) {
if (!options.resolveSymlinks || options.excludeSymlinks) return null;
return isSynchronous ? resolveSymlinks : resolveSymlinksAsync;
}
function isRecursive(path$1, resolved, state) {
if (state.options.useRealPaths) return isRecursiveUsingRealPaths(resolved, state);
let parent = (0, path.dirname)(path$1);
let depth = 1;
while (parent !== state.root && depth < 2) {
const resolvedPath = state.symlinks.get(parent);
const isSameRoot = !!resolvedPath && (resolvedPath === resolved || resolvedPath.startsWith(resolved) || resolved.startsWith(resolvedPath));
if (isSameRoot) depth++;
else parent = (0, path.dirname)(parent);
}
state.symlinks.set(path$1, resolved);
return depth > 1;
}
function isRecursiveUsingRealPaths(resolved, state) {
return state.visited.includes(resolved + state.options.pathSeparator);
}
//#endregion
//#region src/api/functions/invoke-callback.ts
const onlyCountsSync = (state) => {
return state.counts;
};
const groupsSync = (state) => {
return state.groups;
};
const defaultSync = (state) => {
return state.paths;
};
const limitFilesSync = (state) => {
return state.paths.slice(0, state.options.maxFiles);
};
const onlyCountsAsync = (state, error, callback$1) => {
report(error, callback$1, state.counts, state.options.suppressErrors);
return null;
};
const defaultAsync = (state, error, callback$1) => {
report(error, callback$1, state.paths, state.options.suppressErrors);
return null;
};
const limitFilesAsync = (state, error, callback$1) => {
report(error, callback$1, state.paths.slice(0, state.options.maxFiles), state.options.suppressErrors);
return null;
};
const groupsAsync = (state, error, callback$1) => {
report(error, callback$1, state.groups, state.options.suppressErrors);
return null;
};
function report(error, callback$1, output, suppressErrors) {
if (error && !suppressErrors) callback$1(error, output);
else callback$1(null, output);
}
function build$1(options, isSynchronous) {
const { onlyCounts, group, maxFiles } = options;
if (onlyCounts) return isSynchronous ? onlyCountsSync : onlyCountsAsync;
else if (group) return isSynchronous ? groupsSync : groupsAsync;
else if (maxFiles) return isSynchronous ? limitFilesSync : limitFilesAsync;
else return isSynchronous ? defaultSync : defaultAsync;
}
//#endregion
//#region src/api/functions/walk-directory.ts
const readdirOpts = { withFileTypes: true };
const walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
state.queue.enqueue();
if (currentDepth < 0) return state.queue.dequeue(null, state);
const { fs: fs$1 } = state;
state.visited.push(crawlPath);
state.counts.directories++;
fs$1.readdir(crawlPath || ".", readdirOpts, (error, entries = []) => {
callback$1(entries, directoryPath, currentDepth);
state.queue.dequeue(state.options.suppressErrors ? null : error, state);
});
};
const walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
const { fs: fs$1 } = state;
if (currentDepth < 0) return;
state.visited.push(crawlPath);
state.counts.directories++;
let entries = [];
try {
entries = fs$1.readdirSync(crawlPath || ".", readdirOpts);
} catch (e) {
if (!state.options.suppressErrors) throw e;
}
callback$1(entries, directoryPath, currentDepth);
};
function build(isSynchronous) {
return isSynchronous ? walkSync : walkAsync;
}
//#endregion
//#region src/api/queue.ts
/**
* This is a custom stateless queue to track concurrent async fs calls.
* It increments a counter whenever a call is queued and decrements it
* as soon as it completes. When the counter hits 0, it calls onQueueEmpty.
*/
var Queue = class {
count = 0;
constructor(onQueueEmpty) {
this.onQueueEmpty = onQueueEmpty;
}
enqueue() {
this.count++;
return this.count;
}
dequeue(error, output) {
if (this.onQueueEmpty && (--this.count <= 0 || error)) {
this.onQueueEmpty(error, output);
if (error) {
output.controller.abort();
this.onQueueEmpty = void 0;
}
}
}
};
//#endregion
//#region src/api/counter.ts
var Counter = class {
_files = 0;
_directories = 0;
set files(num) {
this._files = num;
}
get files() {
return this._files;
}
set directories(num) {
this._directories = num;
}
get directories() {
return this._directories;
}
/**
* @deprecated use `directories` instead
*/
/* c8 ignore next 3 */
get dirs() {
return this._directories;
}
};
//#endregion
//#region src/api/aborter.ts
/**
* AbortController is not supported on Node 14 so we use this until we can drop
* support for Node 14.
*/
var Aborter = class {
aborted = false;
abort() {
this.aborted = true;
}
};
//#endregion
//#region src/api/walker.ts
var Walker = class {
root;
isSynchronous;
state;
joinPath;
pushDirectory;
pushFile;
getArray;
groupFiles;
resolveSymlink;
walkDirectory;
callbackInvoker;
constructor(root, options, callback$1) {
this.isSynchronous = !callback$1;
this.callbackInvoker = build$1(options, this.isSynchronous);
this.root = normalizePath(root, options);
this.state = {
root: isRootDirectory(this.root) ? this.root : this.root.slice(0, -1),
paths: [""].slice(0, 0),
groups: [],
counts: new Counter(),
options,
queue: new Queue((error, state) => this.callbackInvoker(state, error, callback$1)),
symlinks: /* @__PURE__ */ new Map(),
visited: [""].slice(0, 0),
controller: new Aborter(),
fs: options.fs || fs
};
this.joinPath = build$7(this.root, options);
this.pushDirectory = build$6(this.root, options);
this.pushFile = build$5(options);
this.getArray = build$4(options);
this.groupFiles = build$3(options);
this.resolveSymlink = build$2(options, this.isSynchronous);
this.walkDirectory = build(this.isSynchronous);
}
start() {
this.pushDirectory(this.root, this.state.paths, this.state.options.filters);
this.walkDirectory(this.state, this.root, this.root, this.state.options.maxDepth, this.walk);
return this.isSynchronous ? this.callbackInvoker(this.state, null) : null;
}
walk = (entries, directoryPath, depth) => {
const { paths, options: { filters, resolveSymlinks: resolveSymlinks$1, excludeSymlinks, exclude, maxFiles, signal, useRealPaths, pathSeparator }, controller } = this.state;
if (controller.aborted || signal && signal.aborted || maxFiles && paths.length > maxFiles) return;
const files = this.getArray(this.state.paths);
for (let i = 0; i < entries.length; ++i) {
const entry = entries[i];
if (entry.isFile() || entry.isSymbolicLink() && !resolveSymlinks$1 && !excludeSymlinks) {
const filename = this.joinPath(entry.name, directoryPath);
this.pushFile(filename, files, this.state.counts, filters);
} else if (entry.isDirectory()) {
let path$1 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
if (exclude && exclude(entry.name, path$1)) continue;
this.pushDirectory(path$1, paths, filters);
this.walkDirectory(this.state, path$1, path$1, depth - 1, this.walk);
} else if (this.resolveSymlink && entry.isSymbolicLink()) {
let path$1 = joinPathWithBasePath(entry.name, directoryPath);
this.resolveSymlink(path$1, this.state, (stat, resolvedPath) => {
if (stat.isDirectory()) {
resolvedPath = normalizePath(resolvedPath, this.state.options);
if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path$1 + pathSeparator)) return;
this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path$1 + pathSeparator, depth - 1, this.walk);
} else {
resolvedPath = useRealPaths ? resolvedPath : path$1;
const filename = (0, path.basename)(resolvedPath);
const directoryPath$1 = normalizePath((0, path.dirname)(resolvedPath), this.state.options);
resolvedPath = this.joinPath(filename, directoryPath$1);
this.pushFile(resolvedPath, files, this.state.counts, filters);
}
});
}
}
this.groupFiles(this.state.groups, directoryPath, files);
};
};
//#endregion
//#region src/api/async.ts
function promise(root, options) {
return new Promise((resolve$1, reject) => {
callback(root, options, (err, output) => {
if (err) return reject(err);
resolve$1(output);
});
});
}
function callback(root, options, callback$1) {
let walker = new Walker(root, options, callback$1);
walker.start();
}
//#endregion
//#region src/api/sync.ts
function sync(root, options) {
const walker = new Walker(root, options);
return walker.start();
}
//#endregion
//#region src/builder/api-builder.ts
var APIBuilder = class {
constructor(root, options) {
this.root = root;
this.options = options;
}
withPromise() {
return promise(this.root, this.options);
}
withCallback(cb) {
callback(this.root, this.options, cb);
}
sync() {
return sync(this.root, this.options);
}
};
//#endregion
//#region src/builder/index.ts
let pm = null;
/* c8 ignore next 6 */
try {
require.resolve("picomatch");
pm = require("picomatch");
} catch {}
var Builder = class {
globCache = {};
options = {
maxDepth: Infinity,
suppressErrors: true,
pathSeparator: path.sep,
filters: []
};
globFunction;
constructor(options) {
this.options = {
...this.options,
...options
};
this.globFunction = this.options.globFunction;
}
group() {
this.options.group = true;
return this;
}
withPathSeparator(separator) {
this.options.pathSeparator = separator;
return this;
}
withBasePath() {
this.options.includeBasePath = true;
return this;
}
withRelativePaths() {
this.options.relativePaths = true;
return this;
}
withDirs() {
this.options.includeDirs = true;
return this;
}
withMaxDepth(depth) {
this.options.maxDepth = depth;
return this;
}
withMaxFiles(limit) {
this.options.maxFiles = limit;
return this;
}
withFullPaths() {
this.options.resolvePaths = true;
this.options.includeBasePath = true;
return this;
}
withErrors() {
this.options.suppressErrors = false;
return this;
}
withSymlinks({ resolvePaths = true } = {}) {
this.options.resolveSymlinks = true;
this.options.useRealPaths = resolvePaths;
return this.withFullPaths();
}
withAbortSignal(signal) {
this.options.signal = signal;
return this;
}
normalize() {
this.options.normalizePath = true;
return this;
}
filter(predicate) {
this.options.filters.push(predicate);
return this;
}
onlyDirs() {
this.options.excludeFiles = true;
this.options.includeDirs = true;
return this;
}
exclude(predicate) {
this.options.exclude = predicate;
return this;
}
onlyCounts() {
this.options.onlyCounts = true;
return this;
}
crawl(root) {
return new APIBuilder(root || ".", this.options);
}
withGlobFunction(fn) {
this.globFunction = fn;
return this;
}
/**
* @deprecated Pass options using the constructor instead:
* ```ts
* new fdir(options).crawl("/path/to/root");
* ```
* This method will be removed in v7.0
*/
/* c8 ignore next 4 */
crawlWithOptions(root, options) {
this.options = {
...this.options,
...options
};
return new APIBuilder(root || ".", this.options);
}
glob(...patterns) {
if (this.globFunction) return this.globWithOptions(patterns);
return this.globWithOptions(patterns, ...[{ dot: true }]);
}
globWithOptions(patterns, ...options) {
const globFn = this.globFunction || pm;
/* c8 ignore next 5 */
if (!globFn) throw new Error("Please specify a glob function to use glob matching.");
var isMatch = this.globCache[patterns.join("\0")];
if (!isMatch) {
isMatch = globFn(patterns, ...options);
this.globCache[patterns.join("\0")] = isMatch;
}
this.options.filters.push((path$1) => isMatch(path$1));
return this;
}
};
//#endregion
exports.fdir = Builder;
-155
View File
@@ -1,155 +0,0 @@
/// <reference types="node" />
import * as nativeFs from "fs";
import picomatch from "picomatch";
//#region src/api/aborter.d.ts
/**
* AbortController is not supported on Node 14 so we use this until we can drop
* support for Node 14.
*/
declare class Aborter {
aborted: boolean;
abort(): void;
}
//#endregion
//#region src/api/queue.d.ts
type OnQueueEmptyCallback = (error: Error | null, output: WalkerState) => void;
/**
* This is a custom stateless queue to track concurrent async fs calls.
* It increments a counter whenever a call is queued and decrements it
* as soon as it completes. When the counter hits 0, it calls onQueueEmpty.
*/
declare class Queue {
private onQueueEmpty?;
count: number;
constructor(onQueueEmpty?: OnQueueEmptyCallback | undefined);
enqueue(): number;
dequeue(error: Error | null, output: WalkerState): void;
}
//#endregion
//#region src/types.d.ts
type Counts = {
files: number;
directories: number;
/**
* @deprecated use `directories` instead. Will be removed in v7.0.
*/
dirs: number;
};
type Group = {
directory: string;
files: string[];
/**
* @deprecated use `directory` instead. Will be removed in v7.0.
*/
dir: string;
};
type GroupOutput = Group[];
type OnlyCountsOutput = Counts;
type PathsOutput = string[];
type Output = OnlyCountsOutput | PathsOutput | GroupOutput;
type FSLike = {
readdir: typeof nativeFs.readdir;
readdirSync: typeof nativeFs.readdirSync;
realpath: typeof nativeFs.realpath;
realpathSync: typeof nativeFs.realpathSync;
stat: typeof nativeFs.stat;
statSync: typeof nativeFs.statSync;
};
type WalkerState = {
root: string;
paths: string[];
groups: Group[];
counts: Counts;
options: Options;
queue: Queue;
controller: Aborter;
fs: FSLike;
symlinks: Map<string, string>;
visited: string[];
};
type ResultCallback<TOutput extends Output> = (error: Error | null, output: TOutput) => void;
type FilterPredicate = (path: string, isDirectory: boolean) => boolean;
type ExcludePredicate = (dirName: string, dirPath: string) => boolean;
type PathSeparator = "/" | "\\";
type Options<TGlobFunction = unknown> = {
includeBasePath?: boolean;
includeDirs?: boolean;
normalizePath?: boolean;
maxDepth: number;
maxFiles?: number;
resolvePaths?: boolean;
suppressErrors: boolean;
group?: boolean;
onlyCounts?: boolean;
filters: FilterPredicate[];
resolveSymlinks?: boolean;
useRealPaths?: boolean;
excludeFiles?: boolean;
excludeSymlinks?: boolean;
exclude?: ExcludePredicate;
relativePaths?: boolean;
pathSeparator: PathSeparator;
signal?: AbortSignal;
globFunction?: TGlobFunction;
fs?: FSLike;
};
type GlobMatcher = (test: string) => boolean;
type GlobFunction = (glob: string | string[], ...params: unknown[]) => GlobMatcher;
type GlobParams<T> = T extends ((globs: string | string[], ...params: infer TParams extends unknown[]) => GlobMatcher) ? TParams : [];
//#endregion
//#region src/builder/api-builder.d.ts
declare class APIBuilder<TReturnType extends Output> {
private readonly root;
private readonly options;
constructor(root: string, options: Options);
withPromise(): Promise<TReturnType>;
withCallback(cb: ResultCallback<TReturnType>): void;
sync(): TReturnType;
}
//#endregion
//#region src/builder/index.d.ts
declare class Builder<TReturnType extends Output = PathsOutput, TGlobFunction = typeof picomatch> {
private readonly globCache;
private options;
private globFunction?;
constructor(options?: Partial<Options<TGlobFunction>>);
group(): Builder<GroupOutput, TGlobFunction>;
withPathSeparator(separator: "/" | "\\"): this;
withBasePath(): this;
withRelativePaths(): this;
withDirs(): this;
withMaxDepth(depth: number): this;
withMaxFiles(limit: number): this;
withFullPaths(): this;
withErrors(): this;
withSymlinks({
resolvePaths
}?: {
resolvePaths?: boolean | undefined;
}): this;
withAbortSignal(signal: AbortSignal): this;
normalize(): this;
filter(predicate: FilterPredicate): this;
onlyDirs(): this;
exclude(predicate: ExcludePredicate): this;
onlyCounts(): Builder<OnlyCountsOutput, TGlobFunction>;
crawl(root?: string): APIBuilder<TReturnType>;
withGlobFunction<TFunc>(fn: TFunc): Builder<TReturnType, TFunc>;
/**
* @deprecated Pass options using the constructor instead:
* ```ts
* new fdir(options).crawl("/path/to/root");
* ```
* This method will be removed in v7.0
*/
crawlWithOptions(root: string, options: Partial<Options<TGlobFunction>>): APIBuilder<TReturnType>;
glob(...patterns: string[]): Builder<TReturnType, TGlobFunction>;
globWithOptions(patterns: string[]): Builder<TReturnType, TGlobFunction>;
globWithOptions(patterns: string[], ...options: GlobParams<TGlobFunction>): Builder<TReturnType, TGlobFunction>;
}
//#endregion
//#region src/index.d.ts
type Fdir = typeof Builder;
//#endregion
export { Counts, ExcludePredicate, FSLike, Fdir, FilterPredicate, GlobFunction, GlobMatcher, GlobParams, Group, GroupOutput, OnlyCountsOutput, Options, Output, PathSeparator, PathsOutput, ResultCallback, WalkerState, Builder as fdir };
-155
View File
@@ -1,155 +0,0 @@
/// <reference types="node" />
import * as nativeFs from "fs";
import picomatch from "picomatch";
//#region src/api/aborter.d.ts
/**
* AbortController is not supported on Node 14 so we use this until we can drop
* support for Node 14.
*/
declare class Aborter {
aborted: boolean;
abort(): void;
}
//#endregion
//#region src/api/queue.d.ts
type OnQueueEmptyCallback = (error: Error | null, output: WalkerState) => void;
/**
* This is a custom stateless queue to track concurrent async fs calls.
* It increments a counter whenever a call is queued and decrements it
* as soon as it completes. When the counter hits 0, it calls onQueueEmpty.
*/
declare class Queue {
private onQueueEmpty?;
count: number;
constructor(onQueueEmpty?: OnQueueEmptyCallback | undefined);
enqueue(): number;
dequeue(error: Error | null, output: WalkerState): void;
}
//#endregion
//#region src/types.d.ts
type Counts = {
files: number;
directories: number;
/**
* @deprecated use `directories` instead. Will be removed in v7.0.
*/
dirs: number;
};
type Group = {
directory: string;
files: string[];
/**
* @deprecated use `directory` instead. Will be removed in v7.0.
*/
dir: string;
};
type GroupOutput = Group[];
type OnlyCountsOutput = Counts;
type PathsOutput = string[];
type Output = OnlyCountsOutput | PathsOutput | GroupOutput;
type FSLike = {
readdir: typeof nativeFs.readdir;
readdirSync: typeof nativeFs.readdirSync;
realpath: typeof nativeFs.realpath;
realpathSync: typeof nativeFs.realpathSync;
stat: typeof nativeFs.stat;
statSync: typeof nativeFs.statSync;
};
type WalkerState = {
root: string;
paths: string[];
groups: Group[];
counts: Counts;
options: Options;
queue: Queue;
controller: Aborter;
fs: FSLike;
symlinks: Map<string, string>;
visited: string[];
};
type ResultCallback<TOutput extends Output> = (error: Error | null, output: TOutput) => void;
type FilterPredicate = (path: string, isDirectory: boolean) => boolean;
type ExcludePredicate = (dirName: string, dirPath: string) => boolean;
type PathSeparator = "/" | "\\";
type Options<TGlobFunction = unknown> = {
includeBasePath?: boolean;
includeDirs?: boolean;
normalizePath?: boolean;
maxDepth: number;
maxFiles?: number;
resolvePaths?: boolean;
suppressErrors: boolean;
group?: boolean;
onlyCounts?: boolean;
filters: FilterPredicate[];
resolveSymlinks?: boolean;
useRealPaths?: boolean;
excludeFiles?: boolean;
excludeSymlinks?: boolean;
exclude?: ExcludePredicate;
relativePaths?: boolean;
pathSeparator: PathSeparator;
signal?: AbortSignal;
globFunction?: TGlobFunction;
fs?: FSLike;
};
type GlobMatcher = (test: string) => boolean;
type GlobFunction = (glob: string | string[], ...params: unknown[]) => GlobMatcher;
type GlobParams<T> = T extends ((globs: string | string[], ...params: infer TParams extends unknown[]) => GlobMatcher) ? TParams : [];
//#endregion
//#region src/builder/api-builder.d.ts
declare class APIBuilder<TReturnType extends Output> {
private readonly root;
private readonly options;
constructor(root: string, options: Options);
withPromise(): Promise<TReturnType>;
withCallback(cb: ResultCallback<TReturnType>): void;
sync(): TReturnType;
}
//#endregion
//#region src/builder/index.d.ts
declare class Builder<TReturnType extends Output = PathsOutput, TGlobFunction = typeof picomatch> {
private readonly globCache;
private options;
private globFunction?;
constructor(options?: Partial<Options<TGlobFunction>>);
group(): Builder<GroupOutput, TGlobFunction>;
withPathSeparator(separator: "/" | "\\"): this;
withBasePath(): this;
withRelativePaths(): this;
withDirs(): this;
withMaxDepth(depth: number): this;
withMaxFiles(limit: number): this;
withFullPaths(): this;
withErrors(): this;
withSymlinks({
resolvePaths
}?: {
resolvePaths?: boolean | undefined;
}): this;
withAbortSignal(signal: AbortSignal): this;
normalize(): this;
filter(predicate: FilterPredicate): this;
onlyDirs(): this;
exclude(predicate: ExcludePredicate): this;
onlyCounts(): Builder<OnlyCountsOutput, TGlobFunction>;
crawl(root?: string): APIBuilder<TReturnType>;
withGlobFunction<TFunc>(fn: TFunc): Builder<TReturnType, TFunc>;
/**
* @deprecated Pass options using the constructor instead:
* ```ts
* new fdir(options).crawl("/path/to/root");
* ```
* This method will be removed in v7.0
*/
crawlWithOptions(root: string, options: Partial<Options<TGlobFunction>>): APIBuilder<TReturnType>;
glob(...patterns: string[]): Builder<TReturnType, TGlobFunction>;
globWithOptions(patterns: string[]): Builder<TReturnType, TGlobFunction>;
globWithOptions(patterns: string[], ...options: GlobParams<TGlobFunction>): Builder<TReturnType, TGlobFunction>;
}
//#endregion
//#region src/index.d.ts
type Fdir = typeof Builder;
//#endregion
export { Counts, ExcludePredicate, FSLike, Fdir, FilterPredicate, GlobFunction, GlobMatcher, GlobParams, Group, GroupOutput, OnlyCountsOutput, Options, Output, PathSeparator, PathsOutput, ResultCallback, WalkerState, Builder as fdir };
-570
View File
@@ -1,570 +0,0 @@
import { createRequire } from "module";
import { basename, dirname, normalize, relative, resolve, sep } from "path";
import * as nativeFs from "fs";
//#region rolldown:runtime
var __require = /* @__PURE__ */ createRequire(import.meta.url);
//#endregion
//#region src/utils.ts
function cleanPath(path) {
let normalized = normalize(path);
if (normalized.length > 1 && normalized[normalized.length - 1] === sep) normalized = normalized.substring(0, normalized.length - 1);
return normalized;
}
const SLASHES_REGEX = /[\\/]/g;
function convertSlashes(path, separator) {
return path.replace(SLASHES_REGEX, separator);
}
const WINDOWS_ROOT_DIR_REGEX = /^[a-z]:[\\/]$/i;
function isRootDirectory(path) {
return path === "/" || WINDOWS_ROOT_DIR_REGEX.test(path);
}
function normalizePath(path, options) {
const { resolvePaths, normalizePath: normalizePath$1, pathSeparator } = options;
const pathNeedsCleaning = process.platform === "win32" && path.includes("/") || path.startsWith(".");
if (resolvePaths) path = resolve(path);
if (normalizePath$1 || pathNeedsCleaning) path = cleanPath(path);
if (path === ".") return "";
const needsSeperator = path[path.length - 1] !== pathSeparator;
return convertSlashes(needsSeperator ? path + pathSeparator : path, pathSeparator);
}
//#endregion
//#region src/api/functions/join-path.ts
function joinPathWithBasePath(filename, directoryPath) {
return directoryPath + filename;
}
function joinPathWithRelativePath(root, options) {
return function(filename, directoryPath) {
const sameRoot = directoryPath.startsWith(root);
if (sameRoot) return directoryPath.slice(root.length) + filename;
else return convertSlashes(relative(root, directoryPath), options.pathSeparator) + options.pathSeparator + filename;
};
}
function joinPath(filename) {
return filename;
}
function joinDirectoryPath(filename, directoryPath, separator) {
return directoryPath + filename + separator;
}
function build$7(root, options) {
const { relativePaths, includeBasePath } = options;
return relativePaths && root ? joinPathWithRelativePath(root, options) : includeBasePath ? joinPathWithBasePath : joinPath;
}
//#endregion
//#region src/api/functions/push-directory.ts
function pushDirectoryWithRelativePath(root) {
return function(directoryPath, paths) {
paths.push(directoryPath.substring(root.length) || ".");
};
}
function pushDirectoryFilterWithRelativePath(root) {
return function(directoryPath, paths, filters) {
const relativePath = directoryPath.substring(root.length) || ".";
if (filters.every((filter) => filter(relativePath, true))) paths.push(relativePath);
};
}
const pushDirectory = (directoryPath, paths) => {
paths.push(directoryPath || ".");
};
const pushDirectoryFilter = (directoryPath, paths, filters) => {
const path = directoryPath || ".";
if (filters.every((filter) => filter(path, true))) paths.push(path);
};
const empty$2 = () => {};
function build$6(root, options) {
const { includeDirs, filters, relativePaths } = options;
if (!includeDirs) return empty$2;
if (relativePaths) return filters && filters.length ? pushDirectoryFilterWithRelativePath(root) : pushDirectoryWithRelativePath(root);
return filters && filters.length ? pushDirectoryFilter : pushDirectory;
}
//#endregion
//#region src/api/functions/push-file.ts
const pushFileFilterAndCount = (filename, _paths, counts, filters) => {
if (filters.every((filter) => filter(filename, false))) counts.files++;
};
const pushFileFilter = (filename, paths, _counts, filters) => {
if (filters.every((filter) => filter(filename, false))) paths.push(filename);
};
const pushFileCount = (_filename, _paths, counts, _filters) => {
counts.files++;
};
const pushFile = (filename, paths) => {
paths.push(filename);
};
const empty$1 = () => {};
function build$5(options) {
const { excludeFiles, filters, onlyCounts } = options;
if (excludeFiles) return empty$1;
if (filters && filters.length) return onlyCounts ? pushFileFilterAndCount : pushFileFilter;
else if (onlyCounts) return pushFileCount;
else return pushFile;
}
//#endregion
//#region src/api/functions/get-array.ts
const getArray = (paths) => {
return paths;
};
const getArrayGroup = () => {
return [""].slice(0, 0);
};
function build$4(options) {
return options.group ? getArrayGroup : getArray;
}
//#endregion
//#region src/api/functions/group-files.ts
const groupFiles = (groups, directory, files) => {
groups.push({
directory,
files,
dir: directory
});
};
const empty = () => {};
function build$3(options) {
return options.group ? groupFiles : empty;
}
//#endregion
//#region src/api/functions/resolve-symlink.ts
const resolveSymlinksAsync = function(path, state, callback$1) {
const { queue, fs, options: { suppressErrors } } = state;
queue.enqueue();
fs.realpath(path, (error, resolvedPath) => {
if (error) return queue.dequeue(suppressErrors ? null : error, state);
fs.stat(resolvedPath, (error$1, stat) => {
if (error$1) return queue.dequeue(suppressErrors ? null : error$1, state);
if (stat.isDirectory() && isRecursive(path, resolvedPath, state)) return queue.dequeue(null, state);
callback$1(stat, resolvedPath);
queue.dequeue(null, state);
});
});
};
const resolveSymlinks = function(path, state, callback$1) {
const { queue, fs, options: { suppressErrors } } = state;
queue.enqueue();
try {
const resolvedPath = fs.realpathSync(path);
const stat = fs.statSync(resolvedPath);
if (stat.isDirectory() && isRecursive(path, resolvedPath, state)) return;
callback$1(stat, resolvedPath);
} catch (e) {
if (!suppressErrors) throw e;
}
};
function build$2(options, isSynchronous) {
if (!options.resolveSymlinks || options.excludeSymlinks) return null;
return isSynchronous ? resolveSymlinks : resolveSymlinksAsync;
}
function isRecursive(path, resolved, state) {
if (state.options.useRealPaths) return isRecursiveUsingRealPaths(resolved, state);
let parent = dirname(path);
let depth = 1;
while (parent !== state.root && depth < 2) {
const resolvedPath = state.symlinks.get(parent);
const isSameRoot = !!resolvedPath && (resolvedPath === resolved || resolvedPath.startsWith(resolved) || resolved.startsWith(resolvedPath));
if (isSameRoot) depth++;
else parent = dirname(parent);
}
state.symlinks.set(path, resolved);
return depth > 1;
}
function isRecursiveUsingRealPaths(resolved, state) {
return state.visited.includes(resolved + state.options.pathSeparator);
}
//#endregion
//#region src/api/functions/invoke-callback.ts
const onlyCountsSync = (state) => {
return state.counts;
};
const groupsSync = (state) => {
return state.groups;
};
const defaultSync = (state) => {
return state.paths;
};
const limitFilesSync = (state) => {
return state.paths.slice(0, state.options.maxFiles);
};
const onlyCountsAsync = (state, error, callback$1) => {
report(error, callback$1, state.counts, state.options.suppressErrors);
return null;
};
const defaultAsync = (state, error, callback$1) => {
report(error, callback$1, state.paths, state.options.suppressErrors);
return null;
};
const limitFilesAsync = (state, error, callback$1) => {
report(error, callback$1, state.paths.slice(0, state.options.maxFiles), state.options.suppressErrors);
return null;
};
const groupsAsync = (state, error, callback$1) => {
report(error, callback$1, state.groups, state.options.suppressErrors);
return null;
};
function report(error, callback$1, output, suppressErrors) {
if (error && !suppressErrors) callback$1(error, output);
else callback$1(null, output);
}
function build$1(options, isSynchronous) {
const { onlyCounts, group, maxFiles } = options;
if (onlyCounts) return isSynchronous ? onlyCountsSync : onlyCountsAsync;
else if (group) return isSynchronous ? groupsSync : groupsAsync;
else if (maxFiles) return isSynchronous ? limitFilesSync : limitFilesAsync;
else return isSynchronous ? defaultSync : defaultAsync;
}
//#endregion
//#region src/api/functions/walk-directory.ts
const readdirOpts = { withFileTypes: true };
const walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
state.queue.enqueue();
if (currentDepth < 0) return state.queue.dequeue(null, state);
const { fs } = state;
state.visited.push(crawlPath);
state.counts.directories++;
fs.readdir(crawlPath || ".", readdirOpts, (error, entries = []) => {
callback$1(entries, directoryPath, currentDepth);
state.queue.dequeue(state.options.suppressErrors ? null : error, state);
});
};
const walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
const { fs } = state;
if (currentDepth < 0) return;
state.visited.push(crawlPath);
state.counts.directories++;
let entries = [];
try {
entries = fs.readdirSync(crawlPath || ".", readdirOpts);
} catch (e) {
if (!state.options.suppressErrors) throw e;
}
callback$1(entries, directoryPath, currentDepth);
};
function build(isSynchronous) {
return isSynchronous ? walkSync : walkAsync;
}
//#endregion
//#region src/api/queue.ts
/**
* This is a custom stateless queue to track concurrent async fs calls.
* It increments a counter whenever a call is queued and decrements it
* as soon as it completes. When the counter hits 0, it calls onQueueEmpty.
*/
var Queue = class {
count = 0;
constructor(onQueueEmpty) {
this.onQueueEmpty = onQueueEmpty;
}
enqueue() {
this.count++;
return this.count;
}
dequeue(error, output) {
if (this.onQueueEmpty && (--this.count <= 0 || error)) {
this.onQueueEmpty(error, output);
if (error) {
output.controller.abort();
this.onQueueEmpty = void 0;
}
}
}
};
//#endregion
//#region src/api/counter.ts
var Counter = class {
_files = 0;
_directories = 0;
set files(num) {
this._files = num;
}
get files() {
return this._files;
}
set directories(num) {
this._directories = num;
}
get directories() {
return this._directories;
}
/**
* @deprecated use `directories` instead
*/
/* c8 ignore next 3 */
get dirs() {
return this._directories;
}
};
//#endregion
//#region src/api/aborter.ts
/**
* AbortController is not supported on Node 14 so we use this until we can drop
* support for Node 14.
*/
var Aborter = class {
aborted = false;
abort() {
this.aborted = true;
}
};
//#endregion
//#region src/api/walker.ts
var Walker = class {
root;
isSynchronous;
state;
joinPath;
pushDirectory;
pushFile;
getArray;
groupFiles;
resolveSymlink;
walkDirectory;
callbackInvoker;
constructor(root, options, callback$1) {
this.isSynchronous = !callback$1;
this.callbackInvoker = build$1(options, this.isSynchronous);
this.root = normalizePath(root, options);
this.state = {
root: isRootDirectory(this.root) ? this.root : this.root.slice(0, -1),
paths: [""].slice(0, 0),
groups: [],
counts: new Counter(),
options,
queue: new Queue((error, state) => this.callbackInvoker(state, error, callback$1)),
symlinks: /* @__PURE__ */ new Map(),
visited: [""].slice(0, 0),
controller: new Aborter(),
fs: options.fs || nativeFs
};
this.joinPath = build$7(this.root, options);
this.pushDirectory = build$6(this.root, options);
this.pushFile = build$5(options);
this.getArray = build$4(options);
this.groupFiles = build$3(options);
this.resolveSymlink = build$2(options, this.isSynchronous);
this.walkDirectory = build(this.isSynchronous);
}
start() {
this.pushDirectory(this.root, this.state.paths, this.state.options.filters);
this.walkDirectory(this.state, this.root, this.root, this.state.options.maxDepth, this.walk);
return this.isSynchronous ? this.callbackInvoker(this.state, null) : null;
}
walk = (entries, directoryPath, depth) => {
const { paths, options: { filters, resolveSymlinks: resolveSymlinks$1, excludeSymlinks, exclude, maxFiles, signal, useRealPaths, pathSeparator }, controller } = this.state;
if (controller.aborted || signal && signal.aborted || maxFiles && paths.length > maxFiles) return;
const files = this.getArray(this.state.paths);
for (let i = 0; i < entries.length; ++i) {
const entry = entries[i];
if (entry.isFile() || entry.isSymbolicLink() && !resolveSymlinks$1 && !excludeSymlinks) {
const filename = this.joinPath(entry.name, directoryPath);
this.pushFile(filename, files, this.state.counts, filters);
} else if (entry.isDirectory()) {
let path = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
if (exclude && exclude(entry.name, path)) continue;
this.pushDirectory(path, paths, filters);
this.walkDirectory(this.state, path, path, depth - 1, this.walk);
} else if (this.resolveSymlink && entry.isSymbolicLink()) {
let path = joinPathWithBasePath(entry.name, directoryPath);
this.resolveSymlink(path, this.state, (stat, resolvedPath) => {
if (stat.isDirectory()) {
resolvedPath = normalizePath(resolvedPath, this.state.options);
if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path + pathSeparator)) return;
this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path + pathSeparator, depth - 1, this.walk);
} else {
resolvedPath = useRealPaths ? resolvedPath : path;
const filename = basename(resolvedPath);
const directoryPath$1 = normalizePath(dirname(resolvedPath), this.state.options);
resolvedPath = this.joinPath(filename, directoryPath$1);
this.pushFile(resolvedPath, files, this.state.counts, filters);
}
});
}
}
this.groupFiles(this.state.groups, directoryPath, files);
};
};
//#endregion
//#region src/api/async.ts
function promise(root, options) {
return new Promise((resolve$1, reject) => {
callback(root, options, (err, output) => {
if (err) return reject(err);
resolve$1(output);
});
});
}
function callback(root, options, callback$1) {
let walker = new Walker(root, options, callback$1);
walker.start();
}
//#endregion
//#region src/api/sync.ts
function sync(root, options) {
const walker = new Walker(root, options);
return walker.start();
}
//#endregion
//#region src/builder/api-builder.ts
var APIBuilder = class {
constructor(root, options) {
this.root = root;
this.options = options;
}
withPromise() {
return promise(this.root, this.options);
}
withCallback(cb) {
callback(this.root, this.options, cb);
}
sync() {
return sync(this.root, this.options);
}
};
//#endregion
//#region src/builder/index.ts
let pm = null;
/* c8 ignore next 6 */
try {
__require.resolve("picomatch");
pm = __require("picomatch");
} catch {}
var Builder = class {
globCache = {};
options = {
maxDepth: Infinity,
suppressErrors: true,
pathSeparator: sep,
filters: []
};
globFunction;
constructor(options) {
this.options = {
...this.options,
...options
};
this.globFunction = this.options.globFunction;
}
group() {
this.options.group = true;
return this;
}
withPathSeparator(separator) {
this.options.pathSeparator = separator;
return this;
}
withBasePath() {
this.options.includeBasePath = true;
return this;
}
withRelativePaths() {
this.options.relativePaths = true;
return this;
}
withDirs() {
this.options.includeDirs = true;
return this;
}
withMaxDepth(depth) {
this.options.maxDepth = depth;
return this;
}
withMaxFiles(limit) {
this.options.maxFiles = limit;
return this;
}
withFullPaths() {
this.options.resolvePaths = true;
this.options.includeBasePath = true;
return this;
}
withErrors() {
this.options.suppressErrors = false;
return this;
}
withSymlinks({ resolvePaths = true } = {}) {
this.options.resolveSymlinks = true;
this.options.useRealPaths = resolvePaths;
return this.withFullPaths();
}
withAbortSignal(signal) {
this.options.signal = signal;
return this;
}
normalize() {
this.options.normalizePath = true;
return this;
}
filter(predicate) {
this.options.filters.push(predicate);
return this;
}
onlyDirs() {
this.options.excludeFiles = true;
this.options.includeDirs = true;
return this;
}
exclude(predicate) {
this.options.exclude = predicate;
return this;
}
onlyCounts() {
this.options.onlyCounts = true;
return this;
}
crawl(root) {
return new APIBuilder(root || ".", this.options);
}
withGlobFunction(fn) {
this.globFunction = fn;
return this;
}
/**
* @deprecated Pass options using the constructor instead:
* ```ts
* new fdir(options).crawl("/path/to/root");
* ```
* This method will be removed in v7.0
*/
/* c8 ignore next 4 */
crawlWithOptions(root, options) {
this.options = {
...this.options,
...options
};
return new APIBuilder(root || ".", this.options);
}
glob(...patterns) {
if (this.globFunction) return this.globWithOptions(patterns);
return this.globWithOptions(patterns, ...[{ dot: true }]);
}
globWithOptions(patterns, ...options) {
const globFn = this.globFunction || pm;
/* c8 ignore next 5 */
if (!globFn) throw new Error("Please specify a glob function to use glob matching.");
var isMatch = this.globCache[patterns.join("\0")];
if (!isMatch) {
isMatch = globFn(patterns, ...options);
this.globCache[patterns.join("\0")] = isMatch;
}
this.options.filters.push((path) => isMatch(path));
return this;
}
};
//#endregion
export { Builder as fdir };
-103
View File
@@ -1,103 +0,0 @@
{
"name": "fdir",
"version": "6.5.0",
"description": "The fastest directory crawler & globbing alternative to glob, fast-glob, & tiny-glob. Crawls 1m files in < 1s",
"main": "./dist/index.cjs",
"types": "./dist/index.d.cts",
"type": "module",
"scripts": {
"prepublishOnly": "npm run test && npm run build",
"build": "tsdown",
"format": "prettier --write src __tests__ benchmarks",
"test": "vitest run __tests__/",
"test:coverage": "vitest run --coverage __tests__/",
"test:watch": "vitest __tests__/",
"bench": "ts-node benchmarks/benchmark.js",
"bench:glob": "ts-node benchmarks/glob-benchmark.ts",
"bench:fdir": "ts-node benchmarks/fdir-benchmark.ts",
"release": "./scripts/release.sh"
},
"engines": {
"node": ">=12.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/thecodrr/fdir.git"
},
"keywords": [
"util",
"os",
"sys",
"fs",
"walk",
"crawler",
"directory",
"files",
"io",
"tiny-glob",
"glob",
"fast-glob",
"speed",
"javascript",
"nodejs"
],
"author": "thecodrr <thecodrr@protonmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/thecodrr/fdir/issues"
},
"homepage": "https://github.com/thecodrr/fdir#readme",
"devDependencies": {
"@types/glob": "^8.1.0",
"@types/mock-fs": "^4.13.4",
"@types/node": "^20.9.4",
"@types/picomatch": "^4.0.0",
"@types/tap": "^15.0.11",
"@vitest/coverage-v8": "^0.34.6",
"all-files-in-tree": "^1.1.2",
"benny": "^3.7.1",
"csv-to-markdown-table": "^1.3.1",
"expect": "^29.7.0",
"fast-glob": "^3.3.2",
"fdir1": "npm:fdir@1.2.0",
"fdir2": "npm:fdir@2.1.0",
"fdir3": "npm:fdir@3.4.2",
"fdir4": "npm:fdir@4.1.0",
"fdir5": "npm:fdir@5.0.0",
"fs-readdir-recursive": "^1.1.0",
"get-all-files": "^4.1.0",
"glob": "^10.3.10",
"klaw-sync": "^6.0.0",
"mock-fs": "^5.2.0",
"picomatch": "^4.0.2",
"prettier": "^3.5.3",
"recur-readdir": "0.0.1",
"recursive-files": "^1.0.2",
"recursive-fs": "^2.1.0",
"recursive-readdir": "^2.2.3",
"rrdir": "^12.1.0",
"systeminformation": "^5.21.17",
"tiny-glob": "^0.2.9",
"ts-node": "^10.9.1",
"tsdown": "^0.12.5",
"typescript": "^5.3.2",
"vitest": "^0.34.6",
"walk-sync": "^3.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
},
"module": "./dist/index.mjs",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./package.json": "./package.json"
}
}
-20
View File
@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright 2017 Andrey Sitnik <andrey@sitnik.ru>
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.
-39
View File
@@ -1,39 +0,0 @@
# Nano ID
<img src="https://ai.github.io/nanoid/logo.svg" align="right"
alt="Nano ID logo by Anton Lovchikov" width="180" height="94">
**English** | [Русский](./README.ru.md) | [简体中文](./README.zh-CN.md) | [Bahasa Indonesia](./README.id-ID.md)
A tiny, secure, URL-friendly, unique string ID generator for JavaScript.
> “An amazing level of senseless perfectionism,
> which is simply impossible not to respect.”
* **Small.** 130 bytes (minified and gzipped). No dependencies.
[Size Limit] controls the size.
* **Fast.** It is 2 times faster than UUID.
* **Safe.** It uses hardware random generator. Can be used in clusters.
* **Short IDs.** It uses a larger alphabet than UUID (`A-Za-z0-9_-`).
So ID size was reduced from 36 to 21 symbols.
* **Portable.** Nano ID was ported
to [20 programming languages](#other-programming-languages).
```js
import { nanoid } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"
```
Supports modern browsers, IE [with Babel], Node.js and React Native.
[online tool]: https://gitpod.io/#https://github.com/ai/nanoid/
[with Babel]: https://developer.epages.com/blog/coding/how-to-transpile-node-modules-with-babel-and-webpack-in-a-monorepo/
[Size Limit]: https://github.com/ai/size-limit
<a href="https://evilmartians.com/?utm_source=nanoid">
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg"
alt="Sponsored by Evil Martians" width="236" height="54">
</a>
## Docs
Read full docs **[here](https://github.com/ai/nanoid#readme)**.
-69
View File
@@ -1,69 +0,0 @@
let random = async bytes => crypto.getRandomValues(new Uint8Array(bytes))
let customAlphabet = (alphabet, defaultSize = 21) => {
// First, a bitmask is necessary to generate the ID. The bitmask makes bytes
// values closer to the alphabet size. The bitmask calculates the closest
// `2^31 - 1` number, which exceeds the alphabet size.
// For example, the bitmask for the alphabet size 30 is 31 (00011111).
// `Math.clz32` is not used, because it is not available in browsers.
let mask = (2 << (Math.log(alphabet.length - 1) / Math.LN2)) - 1
// Though, the bitmask solution is not perfect since the bytes exceeding
// the alphabet size are refused. Therefore, to reliably generate the ID,
// the random bytes redundancy has to be satisfied.
// Note: every hardware random generator call is performance expensive,
// because the system call for entropy collection takes a lot of time.
// So, to avoid additional system calls, extra bytes are requested in advance.
// Next, a step determines how many random bytes to generate.
// The number of random bytes gets decided upon the ID size, mask,
// alphabet size, and magic number 1.6 (using 1.6 peaks at performance
// according to benchmarks).
// `-~f => Math.ceil(f)` if f is a float
// `-~i => i + 1` if i is an integer
let step = -~((1.6 * mask * defaultSize) / alphabet.length)
return async (size = defaultSize) => {
let id = ''
while (true) {
let bytes = crypto.getRandomValues(new Uint8Array(step))
// A compact alternative for `for (var i = 0; i < step; i++)`.
let i = step | 0
while (i--) {
// Adding `|| ''` refuses a random byte that exceeds the alphabet size.
id += alphabet[bytes[i] & mask] || ''
if (id.length === size) return id
}
}
}
}
let nanoid = async (size = 21) => {
let id = ''
let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)))
// A compact alternative for `for (var i = 0; i < step; i++)`.
while (size--) {
// It is incorrect to use bytes exceeding the alphabet size.
// The following mask reduces the random byte in the 0-255 value
// range to the 0-63 value range. Therefore, adding hacks, such
// as empty string fallback or magic numbers, is unneccessary because
// the bitmask trims bytes down to the alphabet size.
let byte = bytes[size] & 63
if (byte < 36) {
// `0-9a-z`
id += byte.toString(36)
} else if (byte < 62) {
// `A-Z`
id += (byte - 26).toString(36).toUpperCase()
} else if (byte < 63) {
id += '_'
} else {
id += '-'
}
}
return id
}
module.exports = { nanoid, customAlphabet, random }
-34
View File
@@ -1,34 +0,0 @@
let random = async bytes => crypto.getRandomValues(new Uint8Array(bytes))
let customAlphabet = (alphabet, defaultSize = 21) => {
let mask = (2 << (Math.log(alphabet.length - 1) / Math.LN2)) - 1
let step = -~((1.6 * mask * defaultSize) / alphabet.length)
return async (size = defaultSize) => {
let id = ''
while (true) {
let bytes = crypto.getRandomValues(new Uint8Array(step))
let i = step | 0
while (i--) {
id += alphabet[bytes[i] & mask] || ''
if (id.length === size) return id
}
}
}
}
let nanoid = async (size = 21) => {
let id = ''
let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)))
while (size--) {
let byte = bytes[size] & 63
if (byte < 36) {
id += byte.toString(36)
} else if (byte < 62) {
id += (byte - 26).toString(36).toUpperCase()
} else if (byte < 63) {
id += '_'
} else {
id += '-'
}
}
return id
}
export { nanoid, customAlphabet, random }
-71
View File
@@ -1,71 +0,0 @@
let crypto = require('crypto')
let { urlAlphabet } = require('../url-alphabet/index.cjs')
// `crypto.randomFill()` is a little faster than `crypto.randomBytes()`,
// because it is possible to use in combination with `Buffer.allocUnsafe()`.
let random = bytes =>
new Promise((resolve, reject) => {
// `Buffer.allocUnsafe()` is faster because it doesnt flush the memory.
// Memory flushing is unnecessary since the buffer allocation itself resets
// the memory with the new bytes.
crypto.randomFill(Buffer.allocUnsafe(bytes), (err, buf) => {
if (err) {
reject(err)
} else {
resolve(buf)
}
})
})
let customAlphabet = (alphabet, defaultSize = 21) => {
// First, a bitmask is necessary to generate the ID. The bitmask makes bytes
// values closer to the alphabet size. The bitmask calculates the closest
// `2^31 - 1` number, which exceeds the alphabet size.
// For example, the bitmask for the alphabet size 30 is 31 (00011111).
let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
// Though, the bitmask solution is not perfect since the bytes exceeding
// the alphabet size are refused. Therefore, to reliably generate the ID,
// the random bytes redundancy has to be satisfied.
// Note: every hardware random generator call is performance expensive,
// because the system call for entropy collection takes a lot of time.
// So, to avoid additional system calls, extra bytes are requested in advance.
// Next, a step determines how many random bytes to generate.
// The number of random bytes gets decided upon the ID size, mask,
// alphabet size, and magic number 1.6 (using 1.6 peaks at performance
// according to benchmarks).
let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)
let tick = (id, size = defaultSize) =>
random(step).then(bytes => {
// A compact alternative for `for (var i = 0; i < step; i++)`.
let i = step
while (i--) {
// Adding `|| ''` refuses a random byte that exceeds the alphabet size.
id += alphabet[bytes[i] & mask] || ''
if (id.length >= size) return id
}
return tick(id, size)
})
return size => tick('', size)
}
let nanoid = (size = 21) =>
random((size |= 0)).then(bytes => {
let id = ''
// A compact alternative for `for (var i = 0; i < step; i++)`.
while (size--) {
// It is incorrect to use bytes exceeding the alphabet size.
// The following mask reduces the random byte in the 0-255 value
// range to the 0-63 value range. Therefore, adding hacks, such
// as empty string fallback or magic numbers, is unneccessary because
// the bitmask trims bytes down to the alphabet size.
id += urlAlphabet[bytes[size] & 63]
}
return id
})
module.exports = { nanoid, customAlphabet, random }
-56
View File
@@ -1,56 +0,0 @@
/**
* Generate secure URL-friendly unique ID. The non-blocking version.
*
* By default, the ID will have 21 symbols to have a collision probability
* similar to UUID v4.
*
* ```js
* import { nanoid } from 'nanoid/async'
* nanoid().then(id => {
* model.id = id
* })
* ```
*
* @param size Size of the ID. The default size is 21.
* @returns A promise with a random string.
*/
export function nanoid(size?: number): Promise<string>
/**
* A low-level function.
* Generate secure unique ID with custom alphabet. The non-blocking version.
*
* Alphabet must contain 256 symbols or less. Otherwise, the generator
* will not be secure.
*
* @param alphabet Alphabet used to generate the ID.
* @param defaultSize Size of the ID. The default size is 21.
* @returns A function that returns a promise with a random string.
*
* ```js
* import { customAlphabet } from 'nanoid/async'
* const nanoid = customAlphabet('0123456789абвгдеё', 5)
* nanoid().then(id => {
* model.id = id //=> "8ё56а"
* })
* ```
*/
export function customAlphabet(
alphabet: string,
defaultSize?: number
): (size?: number) => Promise<string>
/**
* Generate an array of random bytes collected from hardware noise.
*
* ```js
* import { random } from 'nanoid/async'
* random(5).then(bytes => {
* bytes //=> [10, 67, 212, 67, 89]
* })
* ```
*
* @param bytes Size of the array.
* @returns A promise with a random bytes array.
*/
export function random(bytes: number): Promise<Uint8Array>
-35
View File
@@ -1,35 +0,0 @@
import crypto from 'crypto'
import { urlAlphabet } from '../url-alphabet/index.js'
let random = bytes =>
new Promise((resolve, reject) => {
crypto.randomFill(Buffer.allocUnsafe(bytes), (err, buf) => {
if (err) {
reject(err)
} else {
resolve(buf)
}
})
})
let customAlphabet = (alphabet, defaultSize = 21) => {
let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)
let tick = (id, size = defaultSize) =>
random(step).then(bytes => {
let i = step
while (i--) {
id += alphabet[bytes[i] & mask] || ''
if (id.length >= size) return id
}
return tick(id, size)
})
return size => tick('', size)
}
let nanoid = (size = 21) =>
random((size |= 0)).then(bytes => {
let id = ''
while (size--) {
id += urlAlphabet[bytes[size] & 63]
}
return id
})
export { nanoid, customAlphabet, random }
-26
View File
@@ -1,26 +0,0 @@
import { getRandomBytesAsync } from 'expo-random'
import { urlAlphabet } from '../url-alphabet/index.js'
let random = getRandomBytesAsync
let customAlphabet = (alphabet, defaultSize = 21) => {
let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)
let tick = (id, size = defaultSize) =>
random(step).then(bytes => {
let i = step
while (i--) {
id += alphabet[bytes[i] & mask] || ''
if (id.length >= size) return id
}
return tick(id, size)
})
return size => tick('', size)
}
let nanoid = (size = 21) =>
random((size |= 0)).then(bytes => {
let id = ''
while (size--) {
id += urlAlphabet[bytes[size] & 63]
}
return id
})
export { nanoid, customAlphabet, random }
-12
View File
@@ -1,12 +0,0 @@
{
"type": "module",
"main": "index.cjs",
"module": "index.js",
"react-native": {
"./index.js": "./index.native.js"
},
"browser": {
"./index.js": "./index.browser.js",
"./index.cjs": "./index.browser.cjs"
}
}
-55
View File
@@ -1,55 +0,0 @@
#!/usr/bin/env node
let { nanoid, customAlphabet } = require('..')
function print(msg) {
process.stdout.write(msg + '\n')
}
function error(msg) {
process.stderr.write(msg + '\n')
process.exit(1)
}
if (process.argv.includes('--help') || process.argv.includes('-h')) {
print(`
Usage
$ nanoid [options]
Options
-s, --size Generated ID size
-a, --alphabet Alphabet to use
-h, --help Show this help
Examples
$ nanoid --s 15
S9sBF77U6sDB8Yg
$ nanoid --size 10 --alphabet abc
bcabababca`)
process.exit()
}
let alphabet, size
for (let i = 2; i < process.argv.length; i++) {
let arg = process.argv[i]
if (arg === '--size' || arg === '-s') {
size = Number(process.argv[i + 1])
i += 1
if (Number.isNaN(size) || size <= 0) {
error('Size must be positive integer')
}
} else if (arg === '--alphabet' || arg === '-a') {
alphabet = process.argv[i + 1]
i += 1
} else {
error('Unknown argument ' + arg)
}
}
if (alphabet) {
let customNanoid = customAlphabet(alphabet, size)
print(customNanoid())
} else {
print(nanoid(size))
}
-72
View File
@@ -1,72 +0,0 @@
// This file replaces `index.js` in bundlers like webpack or Rollup,
// according to `browser` config in `package.json`.
let { urlAlphabet } = require('./url-alphabet/index.cjs')
let random = bytes => crypto.getRandomValues(new Uint8Array(bytes))
let customRandom = (alphabet, defaultSize, getRandom) => {
// First, a bitmask is necessary to generate the ID. The bitmask makes bytes
// values closer to the alphabet size. The bitmask calculates the closest
// `2^31 - 1` number, which exceeds the alphabet size.
// For example, the bitmask for the alphabet size 30 is 31 (00011111).
// `Math.clz32` is not used, because it is not available in browsers.
let mask = (2 << (Math.log(alphabet.length - 1) / Math.LN2)) - 1
// Though, the bitmask solution is not perfect since the bytes exceeding
// the alphabet size are refused. Therefore, to reliably generate the ID,
// the random bytes redundancy has to be satisfied.
// Note: every hardware random generator call is performance expensive,
// because the system call for entropy collection takes a lot of time.
// So, to avoid additional system calls, extra bytes are requested in advance.
// Next, a step determines how many random bytes to generate.
// The number of random bytes gets decided upon the ID size, mask,
// alphabet size, and magic number 1.6 (using 1.6 peaks at performance
// according to benchmarks).
// `-~f => Math.ceil(f)` if f is a float
// `-~i => i + 1` if i is an integer
let step = -~((1.6 * mask * defaultSize) / alphabet.length)
return (size = defaultSize) => {
let id = ''
while (true) {
let bytes = getRandom(step)
// A compact alternative for `for (var i = 0; i < step; i++)`.
let j = step | 0
while (j--) {
// Adding `|| ''` refuses a random byte that exceeds the alphabet size.
id += alphabet[bytes[j] & mask] || ''
if (id.length === size) return id
}
}
}
}
let customAlphabet = (alphabet, size = 21) =>
customRandom(alphabet, size, random)
let nanoid = (size = 21) =>
crypto.getRandomValues(new Uint8Array(size)).reduce((id, byte) => {
// It is incorrect to use bytes exceeding the alphabet size.
// The following mask reduces the random byte in the 0-255 value
// range to the 0-63 value range. Therefore, adding hacks, such
// as empty string fallback or magic numbers, is unneccessary because
// the bitmask trims bytes down to the alphabet size.
byte &= 63
if (byte < 36) {
// `0-9a-z`
id += byte.toString(36)
} else if (byte < 62) {
// `A-Z`
id += (byte - 26).toString(36).toUpperCase()
} else if (byte > 62) {
id += '-'
} else {
id += '_'
}
return id
}, '')
module.exports = { nanoid, customAlphabet, customRandom, urlAlphabet, random }
-34
View File
@@ -1,34 +0,0 @@
import { urlAlphabet } from './url-alphabet/index.js'
let random = bytes => crypto.getRandomValues(new Uint8Array(bytes))
let customRandom = (alphabet, defaultSize, getRandom) => {
let mask = (2 << (Math.log(alphabet.length - 1) / Math.LN2)) - 1
let step = -~((1.6 * mask * defaultSize) / alphabet.length)
return (size = defaultSize) => {
let id = ''
while (true) {
let bytes = getRandom(step)
let j = step | 0
while (j--) {
id += alphabet[bytes[j] & mask] || ''
if (id.length === size) return id
}
}
}
}
let customAlphabet = (alphabet, size = 21) =>
customRandom(alphabet, size, random)
let nanoid = (size = 21) =>
crypto.getRandomValues(new Uint8Array(size)).reduce((id, byte) => {
byte &= 63
if (byte < 36) {
id += byte.toString(36)
} else if (byte < 62) {
id += (byte - 26).toString(36).toUpperCase()
} else if (byte > 62) {
id += '-'
} else {
id += '_'
}
return id
}, '')
export { nanoid, customAlphabet, customRandom, urlAlphabet, random }
-85
View File
@@ -1,85 +0,0 @@
let crypto = require('crypto')
let { urlAlphabet } = require('./url-alphabet/index.cjs')
// It is best to make fewer, larger requests to the crypto module to
// avoid system call overhead. So, random numbers are generated in a
// pool. The pool is a Buffer that is larger than the initial random
// request size by this multiplier. The pool is enlarged if subsequent
// requests exceed the maximum buffer size.
const POOL_SIZE_MULTIPLIER = 128
let pool, poolOffset
let fillPool = bytes => {
if (!pool || pool.length < bytes) {
pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER)
crypto.randomFillSync(pool)
poolOffset = 0
} else if (poolOffset + bytes > pool.length) {
crypto.randomFillSync(pool)
poolOffset = 0
}
poolOffset += bytes
}
let random = bytes => {
// `|=` convert `bytes` to number to prevent `valueOf` abusing and pool pollution
fillPool((bytes |= 0))
return pool.subarray(poolOffset - bytes, poolOffset)
}
let customRandom = (alphabet, defaultSize, getRandom) => {
// First, a bitmask is necessary to generate the ID. The bitmask makes bytes
// values closer to the alphabet size. The bitmask calculates the closest
// `2^31 - 1` number, which exceeds the alphabet size.
// For example, the bitmask for the alphabet size 30 is 31 (00011111).
let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
// Though, the bitmask solution is not perfect since the bytes exceeding
// the alphabet size are refused. Therefore, to reliably generate the ID,
// the random bytes redundancy has to be satisfied.
// Note: every hardware random generator call is performance expensive,
// because the system call for entropy collection takes a lot of time.
// So, to avoid additional system calls, extra bytes are requested in advance.
// Next, a step determines how many random bytes to generate.
// The number of random bytes gets decided upon the ID size, mask,
// alphabet size, and magic number 1.6 (using 1.6 peaks at performance
// according to benchmarks).
let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)
return (size = defaultSize) => {
let id = ''
while (true) {
let bytes = getRandom(step)
// A compact alternative for `for (let i = 0; i < step; i++)`.
let i = step
while (i--) {
// Adding `|| ''` refuses a random byte that exceeds the alphabet size.
id += alphabet[bytes[i] & mask] || ''
if (id.length === size) return id
}
}
}
}
let customAlphabet = (alphabet, size = 21) =>
customRandom(alphabet, size, random)
let nanoid = (size = 21) => {
// `|=` convert `size` to number to prevent `valueOf` abusing and pool pollution
fillPool((size |= 0))
let id = ''
// We are reading directly from the random pool to avoid creating new array
for (let i = poolOffset - size; i < poolOffset; i++) {
// It is incorrect to use bytes exceeding the alphabet size.
// The following mask reduces the random byte in the 0-255 value
// range to the 0-63 value range. Therefore, adding hacks, such
// as empty string fallback or magic numbers, is unneccessary because
// the bitmask trims bytes down to the alphabet size.
id += urlAlphabet[pool[i] & 63]
}
return id
}
module.exports = { nanoid, customAlphabet, customRandom, urlAlphabet, random }
-91
View File
@@ -1,91 +0,0 @@
/**
* Generate secure URL-friendly unique ID.
*
* By default, the ID will have 21 symbols to have a collision probability
* similar to UUID v4.
*
* ```js
* import { nanoid } from 'nanoid'
* model.id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqL"
* ```
*
* @param size Size of the ID. The default size is 21.
* @returns A random string.
*/
export function nanoid(size?: number): string
/**
* Generate secure unique ID with custom alphabet.
*
* Alphabet must contain 256 symbols or less. Otherwise, the generator
* will not be secure.
*
* @param alphabet Alphabet used to generate the ID.
* @param defaultSize Size of the ID. The default size is 21.
* @returns A random string generator.
*
* ```js
* const { customAlphabet } = require('nanoid')
* const nanoid = customAlphabet('0123456789абвгдеё', 5)
* nanoid() //=> "8ё56а"
* ```
*/
export function customAlphabet(
alphabet: string,
defaultSize?: number
): (size?: number) => string
/**
* Generate unique ID with custom random generator and alphabet.
*
* Alphabet must contain 256 symbols or less. Otherwise, the generator
* will not be secure.
*
* ```js
* import { customRandom } from 'nanoid/format'
*
* const nanoid = customRandom('abcdef', 5, size => {
* const random = []
* for (let i = 0; i < size; i++) {
* random.push(randomByte())
* }
* return random
* })
*
* nanoid() //=> "fbaef"
* ```
*
* @param alphabet Alphabet used to generate a random string.
* @param size Size of the random string.
* @param random A random bytes generator.
* @returns A random string generator.
*/
export function customRandom(
alphabet: string,
size: number,
random: (bytes: number) => Uint8Array
): () => string
/**
* URL safe symbols.
*
* ```js
* import { urlAlphabet } from 'nanoid'
* const nanoid = customAlphabet(urlAlphabet, 10)
* nanoid() //=> "Uakgb_J5m9"
* ```
*/
export const urlAlphabet: string
/**
* Generate an array of random bytes collected from hardware noise.
*
* ```js
* import { customRandom, random } from 'nanoid'
* const nanoid = customRandom("abcdef", 5, random)
* ```
*
* @param bytes Size of the array.
* @returns An array of random bytes.
*/
export function random(bytes: number): Uint8Array
-91
View File
@@ -1,91 +0,0 @@
/**
* Generate secure URL-friendly unique ID.
*
* By default, the ID will have 21 symbols to have a collision probability
* similar to UUID v4.
*
* ```js
* import { nanoid } from 'nanoid'
* model.id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqL"
* ```
*
* @param size Size of the ID. The default size is 21.
* @returns A random string.
*/
export function nanoid(size?: number): string
/**
* Generate secure unique ID with custom alphabet.
*
* Alphabet must contain 256 symbols or less. Otherwise, the generator
* will not be secure.
*
* @param alphabet Alphabet used to generate the ID.
* @param defaultSize Size of the ID. The default size is 21.
* @returns A random string generator.
*
* ```js
* const { customAlphabet } = require('nanoid')
* const nanoid = customAlphabet('0123456789абвгдеё', 5)
* nanoid() //=> "8ё56а"
* ```
*/
export function customAlphabet(
alphabet: string,
defaultSize?: number
): (size?: number) => string
/**
* Generate unique ID with custom random generator and alphabet.
*
* Alphabet must contain 256 symbols or less. Otherwise, the generator
* will not be secure.
*
* ```js
* import { customRandom } from 'nanoid/format'
*
* const nanoid = customRandom('abcdef', 5, size => {
* const random = []
* for (let i = 0; i < size; i++) {
* random.push(randomByte())
* }
* return random
* })
*
* nanoid() //=> "fbaef"
* ```
*
* @param alphabet Alphabet used to generate a random string.
* @param size Size of the random string.
* @param random A random bytes generator.
* @returns A random string generator.
*/
export function customRandom(
alphabet: string,
size: number,
random: (bytes: number) => Uint8Array
): () => string
/**
* URL safe symbols.
*
* ```js
* import { urlAlphabet } from 'nanoid'
* const nanoid = customAlphabet(urlAlphabet, 10)
* nanoid() //=> "Uakgb_J5m9"
* ```
*/
export const urlAlphabet: string
/**
* Generate an array of random bytes collected from hardware noise.
*
* ```js
* import { customRandom, random } from 'nanoid'
* const nanoid = customRandom("abcdef", 5, random)
* ```
*
* @param bytes Size of the array.
* @returns An array of random bytes.
*/
export function random(bytes: number): Uint8Array
-45
View File
@@ -1,45 +0,0 @@
import crypto from 'crypto'
import { urlAlphabet } from './url-alphabet/index.js'
const POOL_SIZE_MULTIPLIER = 128
let pool, poolOffset
let fillPool = bytes => {
if (!pool || pool.length < bytes) {
pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER)
crypto.randomFillSync(pool)
poolOffset = 0
} else if (poolOffset + bytes > pool.length) {
crypto.randomFillSync(pool)
poolOffset = 0
}
poolOffset += bytes
}
let random = bytes => {
fillPool((bytes |= 0))
return pool.subarray(poolOffset - bytes, poolOffset)
}
let customRandom = (alphabet, defaultSize, getRandom) => {
let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)
return (size = defaultSize) => {
let id = ''
while (true) {
let bytes = getRandom(step)
let i = step
while (i--) {
id += alphabet[bytes[i] & mask] || ''
if (id.length === size) return id
}
}
}
}
let customAlphabet = (alphabet, size = 21) =>
customRandom(alphabet, size, random)
let nanoid = (size = 21) => {
fillPool((size |= 0))
let id = ''
for (let i = poolOffset - size; i < poolOffset; i++) {
id += urlAlphabet[pool[i] & 63]
}
return id
}
export { nanoid, customAlphabet, customRandom, urlAlphabet, random }
-1
View File
@@ -1 +0,0 @@
export let nanoid=(t=21)=>crypto.getRandomValues(new Uint8Array(t)).reduce(((t,e)=>t+=(e&=63)<36?e.toString(36):e<62?(e-26).toString(36).toUpperCase():e<63?"_":"-"),"");
-34
View File
@@ -1,34 +0,0 @@
// This alphabet uses `A-Za-z0-9_-` symbols.
// The order of characters is optimized for better gzip and brotli compression.
// References to the same file (works both for gzip and brotli):
// `'use`, `andom`, and `rict'`
// References to the brotli default dictionary:
// `-26T`, `1983`, `40px`, `75px`, `bush`, `jack`, `mind`, `very`, and `wolf`
let urlAlphabet =
'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
let customAlphabet = (alphabet, defaultSize = 21) => {
return (size = defaultSize) => {
let id = ''
// A compact alternative for `for (var i = 0; i < step; i++)`.
let i = size | 0
while (i--) {
// `| 0` is more compact and faster than `Math.floor()`.
id += alphabet[(Math.random() * alphabet.length) | 0]
}
return id
}
}
let nanoid = (size = 21) => {
let id = ''
// A compact alternative for `for (var i = 0; i < step; i++)`.
let i = size | 0
while (i--) {
// `| 0` is more compact and faster than `Math.floor()`.
id += urlAlphabet[(Math.random() * 64) | 0]
}
return id
}
module.exports = { nanoid, customAlphabet }
-33
View File
@@ -1,33 +0,0 @@
/**
* Generate URL-friendly unique ID. This method uses the non-secure
* predictable random generator with bigger collision probability.
*
* ```js
* import { nanoid } from 'nanoid/non-secure'
* model.id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqL"
* ```
*
* @param size Size of the ID. The default size is 21.
* @returns A random string.
*/
export function nanoid(size?: number): string
/**
* Generate a unique ID based on a custom alphabet.
* This method uses the non-secure predictable random generator
* with bigger collision probability.
*
* @param alphabet Alphabet used to generate the ID.
* @param defaultSize Size of the ID. The default size is 21.
* @returns A random string generator.
*
* ```js
* import { customAlphabet } from 'nanoid/non-secure'
* const nanoid = customAlphabet('0123456789абвгдеё', 5)
* model.id = //=> "8ё56а"
* ```
*/
export function customAlphabet(
alphabet: string,
defaultSize?: number
): (size?: number) => string
-21
View File
@@ -1,21 +0,0 @@
let urlAlphabet =
'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
let customAlphabet = (alphabet, defaultSize = 21) => {
return (size = defaultSize) => {
let id = ''
let i = size | 0
while (i--) {
id += alphabet[(Math.random() * alphabet.length) | 0]
}
return id
}
}
let nanoid = (size = 21) => {
let id = ''
let i = size | 0
while (i--) {
id += urlAlphabet[(Math.random() * 64) | 0]
}
return id
}
export { nanoid, customAlphabet }
-6
View File
@@ -1,6 +0,0 @@
{
"type": "module",
"main": "index.cjs",
"module": "index.js",
"react-native": "index.js"
}
-89
View File
@@ -1,89 +0,0 @@
{
"name": "nanoid",
"version": "3.3.11",
"description": "A tiny (116 bytes), secure URL-friendly unique string ID generator",
"keywords": [
"uuid",
"random",
"id",
"url"
],
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
},
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"author": "Andrey Sitnik <andrey@sitnik.ru>",
"license": "MIT",
"repository": "ai/nanoid",
"browser": {
"./index.js": "./index.browser.js",
"./async/index.js": "./async/index.browser.js",
"./async/index.cjs": "./async/index.browser.cjs",
"./index.cjs": "./index.browser.cjs"
},
"react-native": "index.js",
"bin": "./bin/nanoid.cjs",
"sideEffects": false,
"types": "./index.d.ts",
"type": "module",
"main": "index.cjs",
"module": "index.js",
"exports": {
".": {
"react-native": "./index.browser.js",
"browser": "./index.browser.js",
"require": {
"types": "./index.d.cts",
"default": "./index.cjs"
},
"import": {
"types": "./index.d.ts",
"default": "./index.js"
},
"default": "./index.js"
},
"./package.json": "./package.json",
"./async/package.json": "./async/package.json",
"./async": {
"browser": "./async/index.browser.js",
"require": {
"types": "./index.d.cts",
"default": "./async/index.cjs"
},
"import": {
"types": "./index.d.ts",
"default": "./async/index.js"
},
"default": "./async/index.js"
},
"./non-secure/package.json": "./non-secure/package.json",
"./non-secure": {
"require": {
"types": "./index.d.cts",
"default": "./non-secure/index.cjs"
},
"import": {
"types": "./index.d.ts",
"default": "./non-secure/index.js"
},
"default": "./non-secure/index.js"
},
"./url-alphabet/package.json": "./url-alphabet/package.json",
"./url-alphabet": {
"require": {
"types": "./index.d.cts",
"default": "./url-alphabet/index.cjs"
},
"import": {
"types": "./index.d.ts",
"default": "./url-alphabet/index.js"
},
"default": "./url-alphabet/index.js"
}
}
}
-7
View File
@@ -1,7 +0,0 @@
// This alphabet uses `A-Za-z0-9_-` symbols.
// The order of characters is optimized for better gzip and brotli compression.
// Same as in non-secure/index.js
let urlAlphabet =
'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
module.exports = { urlAlphabet }
-3
View File
@@ -1,3 +0,0 @@
let urlAlphabet =
'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
export { urlAlphabet }
-6
View File
@@ -1,6 +0,0 @@
{
"type": "module",
"main": "index.cjs",
"module": "index.js",
"react-native": "index.js"
}
-15
View File
@@ -1,15 +0,0 @@
ISC License
Copyright (c) 2021-2024 Oleksii Raspopov, Kostiantyn Denysov, Anton Verinov
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-21
View File
@@ -1,21 +0,0 @@
# picocolors
The tiniest and the fastest library for terminal output formatting with ANSI colors.
```javascript
import pc from "picocolors"
console.log(
pc.green(`How are ${pc.italic(`you`)} doing?`)
)
```
- **No dependencies.**
- **14 times** smaller and **2 times** faster than chalk.
- Used by popular tools like PostCSS, SVGO, Stylelint, and Browserslist.
- Node.js v6+ & browsers support. Support for both CJS and ESM projects.
- TypeScript type declarations included.
- [`NO_COLOR`](https://no-color.org/) friendly.
## Docs
Read **[full docs](https://github.com/alexeyraspopov/picocolors#readme)** on GitHub.
-25
View File
@@ -1,25 +0,0 @@
{
"name": "picocolors",
"version": "1.1.1",
"main": "./picocolors.js",
"types": "./picocolors.d.ts",
"browser": {
"./picocolors.js": "./picocolors.browser.js"
},
"sideEffects": false,
"description": "The tiniest and the fastest library for terminal output formatting with ANSI colors",
"files": [
"picocolors.*",
"types.d.ts"
],
"keywords": [
"terminal",
"colors",
"formatting",
"cli",
"console"
],
"author": "Alexey Raspopov",
"repository": "alexeyraspopov/picocolors",
"license": "ISC"
}
-4
View File
@@ -1,4 +0,0 @@
var x=String;
var create=function() {return {isColorSupported:false,reset:x,bold:x,dim:x,italic:x,underline:x,inverse:x,hidden:x,strikethrough:x,black:x,red:x,green:x,yellow:x,blue:x,magenta:x,cyan:x,white:x,gray:x,bgBlack:x,bgRed:x,bgGreen:x,bgYellow:x,bgBlue:x,bgMagenta:x,bgCyan:x,bgWhite:x,blackBright:x,redBright:x,greenBright:x,yellowBright:x,blueBright:x,magentaBright:x,cyanBright:x,whiteBright:x,bgBlackBright:x,bgRedBright:x,bgGreenBright:x,bgYellowBright:x,bgBlueBright:x,bgMagentaBright:x,bgCyanBright:x,bgWhiteBright:x}};
module.exports=create();
module.exports.createColors = create;
-5
View File
@@ -1,5 +0,0 @@
import { Colors } from "./types"
declare const picocolors: Colors & { createColors: (enabled?: boolean) => Colors }
export = picocolors
-75
View File
@@ -1,75 +0,0 @@
let p = process || {}, argv = p.argv || [], env = p.env || {}
let isColorSupported =
!(!!env.NO_COLOR || argv.includes("--no-color")) &&
(!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || ((p.stdout || {}).isTTY && env.TERM !== "dumb") || !!env.CI)
let formatter = (open, close, replace = open) =>
input => {
let string = "" + input, index = string.indexOf(close, open.length)
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close
}
let replaceClose = (string, close, replace, index) => {
let result = "", cursor = 0
do {
result += string.substring(cursor, index) + replace
cursor = index + close.length
index = string.indexOf(close, cursor)
} while (~index)
return result + string.substring(cursor)
}
let createColors = (enabled = isColorSupported) => {
let f = enabled ? formatter : () => String
return {
isColorSupported: enabled,
reset: f("\x1b[0m", "\x1b[0m"),
bold: f("\x1b[1m", "\x1b[22m", "\x1b[22m\x1b[1m"),
dim: f("\x1b[2m", "\x1b[22m", "\x1b[22m\x1b[2m"),
italic: f("\x1b[3m", "\x1b[23m"),
underline: f("\x1b[4m", "\x1b[24m"),
inverse: f("\x1b[7m", "\x1b[27m"),
hidden: f("\x1b[8m", "\x1b[28m"),
strikethrough: f("\x1b[9m", "\x1b[29m"),
black: f("\x1b[30m", "\x1b[39m"),
red: f("\x1b[31m", "\x1b[39m"),
green: f("\x1b[32m", "\x1b[39m"),
yellow: f("\x1b[33m", "\x1b[39m"),
blue: f("\x1b[34m", "\x1b[39m"),
magenta: f("\x1b[35m", "\x1b[39m"),
cyan: f("\x1b[36m", "\x1b[39m"),
white: f("\x1b[37m", "\x1b[39m"),
gray: f("\x1b[90m", "\x1b[39m"),
bgBlack: f("\x1b[40m", "\x1b[49m"),
bgRed: f("\x1b[41m", "\x1b[49m"),
bgGreen: f("\x1b[42m", "\x1b[49m"),
bgYellow: f("\x1b[43m", "\x1b[49m"),
bgBlue: f("\x1b[44m", "\x1b[49m"),
bgMagenta: f("\x1b[45m", "\x1b[49m"),
bgCyan: f("\x1b[46m", "\x1b[49m"),
bgWhite: f("\x1b[47m", "\x1b[49m"),
blackBright: f("\x1b[90m", "\x1b[39m"),
redBright: f("\x1b[91m", "\x1b[39m"),
greenBright: f("\x1b[92m", "\x1b[39m"),
yellowBright: f("\x1b[93m", "\x1b[39m"),
blueBright: f("\x1b[94m", "\x1b[39m"),
magentaBright: f("\x1b[95m", "\x1b[39m"),
cyanBright: f("\x1b[96m", "\x1b[39m"),
whiteBright: f("\x1b[97m", "\x1b[39m"),
bgBlackBright: f("\x1b[100m", "\x1b[49m"),
bgRedBright: f("\x1b[101m", "\x1b[49m"),
bgGreenBright: f("\x1b[102m", "\x1b[49m"),
bgYellowBright: f("\x1b[103m", "\x1b[49m"),
bgBlueBright: f("\x1b[104m", "\x1b[49m"),
bgMagentaBright: f("\x1b[105m", "\x1b[49m"),
bgCyanBright: f("\x1b[106m", "\x1b[49m"),
bgWhiteBright: f("\x1b[107m", "\x1b[49m"),
}
}
module.exports = createColors()
module.exports.createColors = createColors

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