A Svelte preprocessor that adds pipe syntax support to Svelte templates, allowing you to chain functions in a more readable way.
npm install svelte-pipe-preprocessor
Add the preprocessor to your Svelte configuration:
// svelte.config.js
import { createIDEFriendlyPipePreprocessor } from 'svelte-pipe-preprocessor';
export default {
preprocess: [
createIDEFriendlyPipePreprocessor({
pipePrefix: 'utils.', // Optional: prefix for pipe functions
debug: false, // Optional: enable debug logging
ideMode: true, // Optional: enable IDE-friendly mode for better IntelliSense
suppressIDEWarnings: true // Optional: suppress TypeScript warnings for pipe syntax
})
]
};
The preprocessor allows you to use pipe syntax in your Svelte templates:
<script>
import { formatCurrency, toUpperCase, truncate } from './utils.js';
let price = 29.99;
let text = "hello world";
let longText = "This is a very long text that needs to be truncated";
</script>
<!-- Basic pipe usage -->
<p>Price: {price | formatCurrency}</p>
<!-- Pipe with arguments -->
<p>Text: {text | toUpperCase}</p>
<!-- Multiple pipes chained -->
<p>Long text: {longText | truncate:20 | toUpperCase}</p>
<!-- Pipes with complex arguments -->
<p>Formatted: {price | formatCurrency:'USD' | toUpperCase}</p>
All pipe functions follow a simple pattern: they accept the value as the first parameter and return the transformed value.
// Basic pipe function
function myPipe(value, ...args) {
// Transform the value
return transformedValue;
}
// utils/string-pipes.js
export function capitalize(value) {
if (typeof value !== 'string') return value;
return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
}
export function truncate(value, length = 50) {
if (typeof value !== 'string') return value;
return value.length > length ? value.substring(0, length) + '...' : value;
}
export function reverse(value) {
if (typeof value !== 'string') return value;
return value.split('').reverse().join('');
}
export function slugify(value) {
if (typeof value !== 'string') return value;
return value
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
}
// utils/number-pipes.js
export function formatCurrency(value, currency = 'USD') {
if (typeof value !== 'number') return value;
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency
}).format(value);
}
export function round(value, decimals = 0) {
if (typeof value !== 'number') return value;
return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
}
export function percentage(value, decimals = 1) {
if (typeof value !== 'number') return value;
return `${(value * 100).toFixed(decimals)}%`;
}
export function formatNumber(value, locale = 'en-US') {
if (typeof value !== 'number') return value;
return new Intl.NumberFormat(locale).format(value);
}
// utils/array-pipes.js
export function filter(array, predicate) {
if (!Array.isArray(array)) return array;
return array.filter(predicate);
}
export function map(array, transform) {
if (!Array.isArray(array)) return array;
return array.map(transform);
}
export function slice(array, start, end) {
if (!Array.isArray(array)) return array;
return array.slice(start, end);
}
export function sort(array, compareFn) {
if (!Array.isArray(array)) return array;
return [...array].sort(compareFn);
}
export function unique(array) {
if (!Array.isArray(array)) return array;
return [...new Set(array)];
}
// utils/date-pipes.js
export function formatDate(value, format = 'short') {
if (!(value instanceof Date)) return value;
const options = {
short: { year: 'numeric', month: 'short', day: 'numeric' },
long: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long' },
time: { hour: '2-digit', minute: '2-digit' }
};
return new Intl.DateTimeFormat('en-US', options[format] || options.short).format(value);
}
export function relativeTime(value) {
if (!(value instanceof Date)) return value;
const now = new Date();
const diff = now - value;
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days} day${days === 1 ? '' : 's'} ago`;
if (hours > 0) return `${hours} hour${hours === 1 ? '' : 's'} ago`;
if (minutes > 0) return `${minutes} minute${minutes === 1 ? '' : 's'} ago`;
return 'Just now';
}
// utils/object-pipes.js
export function pick(obj, keys) {
if (typeof obj !== 'object' || obj === null) return obj;
const result = {};
keys.forEach(key => {
if (key in obj) result[key] = obj[key];
});
return result;
}
export function omit(obj, keys) {
if (typeof obj !== 'object' || obj === null) return obj;
const result = { ...obj };
keys.forEach(key => delete result[key]);
return result;
}
export function keys(obj) {
if (typeof obj !== 'object' || obj === null) return [];
return Object.keys(obj);
}
export function values(obj) {
if (typeof obj !== 'object' || obj === null) return [];
return Object.values(obj);
}
// utils/advanced-pipes.js
export function replace(value, searchValue, replaceValue) {
if (typeof value !== 'string') return value;
return value.replace(searchValue, replaceValue);
}
export function padStart(value, length, char = ' ') {
return String(value).padStart(length, char);
}
export function formatPhone(value, format = 'standard') {
if (typeof value !== 'string') return value;
const cleaned = value.replace(/\D/g, '');
if (format === 'standard') {
return cleaned.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
}
return cleaned;
}
// utils/complex-pipes.js
export function conditional(value, condition, trueValue, falseValue) {
return condition ? trueValue : falseValue;
}
export function safe(value, fallback = '') {
return value ?? fallback;
}
export function limit(value, min, max) {
if (typeof value !== 'number') return value;
return Math.min(Math.max(value, min), max);
}
export function interpolate(template, data) {
if (typeof template !== 'string') return template;
return template.replace(/\{(\w+)\}/g, (match, key) => data[key] || match);
}
<script>
import { capitalize, truncate, formatCurrency } from './utils/string-pipes.js';
import { formatDate, relativeTime } from './utils/date-pipes.js';
import { filter, map } from './utils/array-pipes.js';
let user = { name: 'john doe', email: '[email protected]' };
let posts = [
{ title: 'First Post', date: new Date('2024-01-01') },
{ title: 'Second Post', date: new Date('2024-01-15') }
];
</script>
<!-- String transformations -->
<p>Name: {user.name | capitalize}</p>
<p>Email: {user.email | truncate:20}</p>
<!-- Date formatting -->
<p>Posted: {posts[0].date | formatDate:'long'}</p>
<p>Time ago: {posts[0].date | relativeTime}</p>
<!-- Array operations -->
<p>Recent posts: {posts | filter:post => post.date > new Date('2024-01-10') | map:post => post.title}</p>
<!-- Complex transformations -->
<p>Formatted: {user.name | capitalize | truncate:10}</p>
Always handle different input types
function myPipe(value, ...args) {
if (typeof value !== 'string') return value;
// Your logic here
}
Provide sensible defaults
function truncate(value, length = 50) {
// Default length if not provided
}
Make pipes pure functions
// Good - pure function
function toUpperCase(value) {
return String(value).toUpperCase();
}
// Avoid - side effects
function badPipe(value) {
console.log(value); // Side effect
return value;
}
Use descriptive names
// Good
function formatCurrency(value) { }
function slugify(value) { }
// Avoid
function format(value) { }
function transform(value) { }
Group related pipes
// utils/string-pipes.js
export function toUpperCase(value) { }
export function toLowerCase(value) { }
export function capitalize(value) { }
// utils/number-pipes.js
export function formatCurrency(value) { }
export function round(value) { }
For better TypeScript support, you can create type definitions for your custom pipes:
// types/custom-pipes.d.ts
declare global {
// String pipes
function capitalize(value: string): string;
function truncate(value: string, length?: number): string;
// Number pipes
function formatCurrency(value: number, currency?: string): string;
function round(value: number, decimals?: number): number;
// Array pipes
function filter<T>(array: T[], predicate: (item: T) => boolean): T[];
function map<T, U>(array: T[], transform: (item: T) => U): U[];
}
export {};
Your pipe functions should accept the value as the first parameter:
// utils.js
export function formatCurrency(value, currency = 'USD') {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency
}).format(value);
}
export function toUpperCase(value) {
return String(value).toUpperCase();
}
export function truncate(value, length = 50) {
const str = String(value);
return str.length > length ? str.substring(0, length) + '...' : str;
}
pipePrefix
(string, optional)A prefix to add to all pipe function names. Useful for namespacing your pipe functions.
createIDEFriendlyPipePreprocessor({
pipePrefix: 'utils.'
})
debug
(boolean, optional)Enable debug logging to see how pipes are being transformed.
createIDEFriendlyPipePreprocessor({
debug: true
})
ideMode
(boolean, optional)Enable IDE-friendly mode for better IntelliSense support. Defaults to true
in development.
createIDEFriendlyPipePreprocessor({
ideMode: process.env.NODE_ENV === 'development'
})
suppressIDEWarnings
(boolean, optional)Suppress TypeScript warnings for pipe syntax in IDE mode. Defaults to true
.
createIDEFriendlyPipePreprocessor({
suppressIDEWarnings: true
})
If you're seeing errors about pipe syntax in your IDE, try these solutions:
Enable IDE mode in your preprocessor configuration:
createIDEFriendlyPipePreprocessor({
ideMode: true,
suppressIDEWarnings: true
})
Add ESLint configuration to ignore pipe syntax errors:
// .eslintrc.cjs
module.exports = {
overrides: [
{
files: ['*.svelte'],
rules: {
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': 'off'
}
}
]
};
Use VS Code settings for better Svelte support:
// .vscode/settings.json
{
"svelte.enable-ts-plugin": true,
"svelte.plugin.svelte.defaultScriptLanguage": "ts"
}
suppressIDEWarnings: true
or add // @ts-ignore
commentsThe preprocessor transforms pipe syntax like this:
{value | function1 | function2:arg1,arg2}
Into standard JavaScript function calls:
{function2(function1(value), arg1, arg2)}
{name | toUpperCase}
Becomes:
{toUpperCase(name)}
{text | trim | toLowerCase | capitalize}
Becomes:
{capitalize(toLowerCase(trim(text)))}
{price | formatCurrency:'EUR' | toUpperCase}
Becomes:
{toUpperCase(formatCurrency(price, 'EUR'))}
The package includes TypeScript definitions and is written in TypeScript for better type safety.
MIT
Contributions are welcome! Please feel free to submit a Pull Request.