better-svelte-email 1.0.0-beta.2 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,158 +0,0 @@
1
- import { tailwindToCSS } from 'tw-to-css';
2
- /**
3
- * Initialize Tailwind converter with config
4
- */
5
- export function createTailwindConverter(config) {
6
- const { twi } = tailwindToCSS({ config });
7
- return twi;
8
- }
9
- /**
10
- * Transform Tailwind classes to inline styles and responsive classes
11
- */
12
- export function transformTailwindClasses(classString, tailwindConverter) {
13
- // Split classes
14
- const classes = classString.trim().split(/\s+/).filter(Boolean);
15
- // Separate responsive from non-responsive classes
16
- const responsiveClasses = [];
17
- const nonResponsiveClasses = [];
18
- for (const cls of classes) {
19
- // Responsive classes have format: sm:, md:, lg:, xl:, 2xl:
20
- if (/^(sm|md|lg|xl|2xl):/.test(cls)) {
21
- responsiveClasses.push(cls);
22
- }
23
- else {
24
- nonResponsiveClasses.push(cls);
25
- }
26
- }
27
- // Convert non-responsive classes to CSS
28
- let inlineStyles = '';
29
- const invalidClasses = [];
30
- if (nonResponsiveClasses.length > 0) {
31
- const classesStr = nonResponsiveClasses.join(' ');
32
- try {
33
- // Generate CSS from Tailwind classes
34
- const css = tailwindConverter(classesStr, {
35
- merge: false,
36
- ignoreMediaQueries: true
37
- });
38
- // Extract styles from CSS
39
- const styles = extractStylesFromCSS(css, nonResponsiveClasses);
40
- inlineStyles = styles.validStyles;
41
- invalidClasses.push(...styles.invalidClasses);
42
- }
43
- catch (error) {
44
- console.warn('Failed to convert Tailwind classes:', error);
45
- }
46
- }
47
- return {
48
- inlineStyles,
49
- responsiveClasses,
50
- invalidClasses
51
- };
52
- }
53
- /**
54
- * Extract CSS properties from generated CSS
55
- * Handles the format: .classname { prop: value; }
56
- */
57
- function extractStylesFromCSS(css, originalClasses) {
58
- const invalidClasses = [];
59
- const styleProperties = [];
60
- // Remove media queries (we handle those separately)
61
- const cssWithoutMedia = css.replace(/@media[^{]+\{(?:[^{}]|\{[^{}]*\})*\}/g, '');
62
- // Create a map of class name -> CSS rules
63
- const classMap = new Map();
64
- // Match .classname { rules }
65
- const classRegex = /\.([^\s{]+)\s*\{([^}]+)\}/g;
66
- let match;
67
- while ((match = classRegex.exec(cssWithoutMedia)) !== null) {
68
- const className = match[1];
69
- const rules = match[2].replace(/\\/g, '').trim();
70
- // Normalize class name (tw-to-css might transform special chars)
71
- const normalizedClass = className.replace(/\\/g, '').replace(/[:#\-[\]/.%!_]+/g, '_');
72
- classMap.set(normalizedClass, rules);
73
- }
74
- // For each original class, try to find its CSS
75
- for (const originalClass of originalClasses) {
76
- // Normalize the original class name to match what tw-to-css produces
77
- const normalized = originalClass.replace(/[:#\-[\]/.%!_]+/g, '_');
78
- if (classMap.has(normalized)) {
79
- const rules = classMap.get(normalized);
80
- // Ensure rules end with semicolon for proper concatenation
81
- const rulesWithSemicolon = rules.trim().endsWith(';') ? rules.trim() : rules.trim() + ';';
82
- styleProperties.push(rulesWithSemicolon);
83
- }
84
- else {
85
- // Class not found - might be invalid Tailwind
86
- invalidClasses.push(originalClass);
87
- }
88
- }
89
- // Combine all style properties with space separator
90
- const validStyles = styleProperties.join(' ').trim();
91
- return { validStyles, invalidClasses };
92
- }
93
- /**
94
- * Generate media query CSS for responsive classes
95
- */
96
- export function generateMediaQueries(responsiveClasses, tailwindConverter, tailwindConfig) {
97
- if (responsiveClasses.length === 0) {
98
- return [];
99
- }
100
- const mediaQueries = [];
101
- // Default breakpoints (can be overridden by config)
102
- const breakpoints = {
103
- sm: '475px',
104
- md: '768px',
105
- lg: '1024px',
106
- xl: '1280px',
107
- '2xl': '1536px',
108
- ...tailwindConfig?.theme?.screens
109
- };
110
- // Group classes by breakpoint
111
- const classesByBreakpoint = new Map();
112
- for (const cls of responsiveClasses) {
113
- const match = cls.match(/^(sm|md|lg|xl|2xl):(.+)/);
114
- if (match) {
115
- const [, breakpoint] = match;
116
- if (!classesByBreakpoint.has(breakpoint)) {
117
- classesByBreakpoint.set(breakpoint, []);
118
- }
119
- classesByBreakpoint.get(breakpoint).push(cls);
120
- }
121
- }
122
- // Generate CSS for each breakpoint
123
- for (const [breakpoint, classes] of classesByBreakpoint) {
124
- const breakpointValue = breakpoints[breakpoint];
125
- if (!breakpointValue)
126
- continue;
127
- // Generate full CSS including media queries
128
- const fullCSS = tailwindConverter(classes.join(' '), {
129
- merge: false,
130
- ignoreMediaQueries: false
131
- });
132
- // Extract just the media query portion
133
- const mediaQueryRegex = new RegExp(`@media[^{]*\\{([^{}]|\\{[^{}]*\\})*\\}`, 'g');
134
- let match;
135
- while ((match = mediaQueryRegex.exec(fullCSS)) !== null) {
136
- const mediaQueryBlock = match[0];
137
- // Make all rules !important for email clients
138
- const withImportant = mediaQueryBlock.replace(/([a-z-]+)\s*:\s*([^;!}]+)/gi, '$1: $2 !important');
139
- // Parse out the query and content
140
- const queryMatch = withImportant.match(/@media\s*([^{]+)/);
141
- if (queryMatch) {
142
- const query = `@media ${queryMatch[1].trim()}`;
143
- mediaQueries.push({
144
- query,
145
- className: `responsive-${breakpoint}`,
146
- rules: withImportant
147
- });
148
- }
149
- }
150
- }
151
- return mediaQueries;
152
- }
153
- /**
154
- * Sanitize class names for use in CSS (replace special characters)
155
- */
156
- export function sanitizeClassName(className) {
157
- return className.replace(/[:#\-[\]/.%!]+/g, '_');
158
- }
@@ -1,125 +0,0 @@
1
- import type { TailwindConfig } from 'tw-to-css';
2
- /**
3
- * Options for the preprocessor
4
- */
5
- export interface PreprocessorOptions {
6
- /**
7
- * Custom Tailwind configuration
8
- */
9
- tailwindConfig?: TailwindConfig;
10
- /**
11
- * Path to folder containing email components
12
- * @default '/src/lib/emails'
13
- */
14
- pathToEmailFolder?: string;
15
- /**
16
- * Enable debug logging
17
- * @default false
18
- */
19
- debug?: boolean;
20
- }
21
- /**
22
- * Represents a class attribute found in the AST
23
- */
24
- export interface ClassAttribute {
25
- /**
26
- * Raw class string (e.g., "text-red-500 sm:bg-blue")
27
- */
28
- raw: string;
29
- /**
30
- * Start position in source code
31
- */
32
- start: number;
33
- /**
34
- * End position in source code
35
- */
36
- end: number;
37
- /**
38
- * Parent element/component name
39
- */
40
- elementName: string;
41
- /**
42
- * Whether this is a static string or dynamic expression
43
- */
44
- isStatic: boolean;
45
- }
46
- /**
47
- * Represents a style attribute found in the AST
48
- */
49
- export interface StyleAttribute {
50
- /**
51
- * Raw style string (e.g., "background-color: red;")
52
- */
53
- raw: string;
54
- /**
55
- * Start position in source code
56
- */
57
- start: number;
58
- /**
59
- * End position in source code
60
- */
61
- end: number;
62
- /**
63
- * Parent element/component name
64
- */
65
- elementName: string;
66
- }
67
- /**
68
- * Result of transforming Tailwind classes
69
- */
70
- export interface TransformResult {
71
- /**
72
- * CSS styles for inline styleString prop
73
- */
74
- inlineStyles: string;
75
- /**
76
- * Responsive classes to keep in class attribute
77
- */
78
- responsiveClasses: string[];
79
- /**
80
- * Classes that couldn't be converted (warnings)
81
- */
82
- invalidClasses: string[];
83
- }
84
- /**
85
- * Media query CSS to inject into head
86
- */
87
- export interface MediaQueryStyle {
88
- /**
89
- * Media query condition (e.g., "@media (max-width: 475px)")
90
- */
91
- query: string;
92
- /**
93
- * CSS class name
94
- */
95
- className: string;
96
- /**
97
- * CSS rules
98
- */
99
- rules: string;
100
- }
101
- /**
102
- * Information about a component's transformations
103
- */
104
- export interface ComponentTransform {
105
- /**
106
- * Original source code
107
- */
108
- originalCode: string;
109
- /**
110
- * Transformed source code
111
- */
112
- transformedCode: string;
113
- /**
114
- * Media queries to inject
115
- */
116
- mediaQueries: MediaQueryStyle[];
117
- /**
118
- * Whether <Head> component was found
119
- */
120
- hasHead: boolean;
121
- /**
122
- * Warnings encountered during transformation
123
- */
124
- warnings: string[];
125
- }
@@ -1 +0,0 @@
1
- export {};