esm-styles 0.4.0 → 0.4.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.
package/dist/lib/build.js CHANGED
@@ -7,6 +7,38 @@ import fs from 'node:fs/promises';
7
7
  // import { inspect } from 'node:util'
8
8
  import _ from 'lodash';
9
9
  import * as esbuild from 'esbuild';
10
+ /**
11
+ * Create an esbuild plugin for path alias resolution.
12
+ * Handles prefixes like '@/' -> './source/'
13
+ */
14
+ function createAliasPlugin(aliases, sourcePath) {
15
+ // Sort aliases by length (longest first) to match most specific first
16
+ const sortedAliases = Object.entries(aliases).sort(([a], [b]) => b.length - a.length);
17
+ return {
18
+ name: 'esm-styles-alias',
19
+ setup(build) {
20
+ // Match any import that starts with one of our alias prefixes
21
+ const aliasPattern = new RegExp(`^(${sortedAliases.map(([key]) => escapeRegex(key)).join('|')})(/.*)?$`);
22
+ build.onResolve({ filter: aliasPattern }, (args) => {
23
+ for (const [alias, target] of sortedAliases) {
24
+ if (args.path === alias || args.path.startsWith(alias + '/')) {
25
+ const rest = args.path.slice(alias.length); // includes leading '/' or empty
26
+ const resolvedTarget = path.resolve(sourcePath, target);
27
+ const resolvedPath = path.join(resolvedTarget, rest);
28
+ return { path: resolvedPath };
29
+ }
30
+ }
31
+ return null;
32
+ });
33
+ },
34
+ };
35
+ }
36
+ /**
37
+ * Escape special regex characters in a string
38
+ */
39
+ function escapeRegex(str) {
40
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
41
+ }
10
42
  /**
11
43
  * Import a module with alias resolution using esbuild.
12
44
  * Falls back to direct import when no aliases are configured.
@@ -17,19 +49,14 @@ async function importWithAliases(filePath, aliases, sourcePath) {
17
49
  const fileUrl = pathToFileUrl(filePath).href + `?update=${Date.now()}`;
18
50
  return import(fileUrl);
19
51
  }
20
- // Resolve aliases relative to sourcePath
21
- const resolvedAliases = {};
22
- for (const [key, value] of Object.entries(aliases)) {
23
- resolvedAliases[key] = path.resolve(sourcePath, value);
24
- }
25
- // Use esbuild to bundle with alias resolution
52
+ // Use esbuild to bundle with alias resolution via plugin
26
53
  const result = await esbuild.build({
27
54
  entryPoints: [filePath],
28
55
  bundle: true,
29
56
  write: false,
30
57
  format: 'esm',
31
58
  platform: 'node',
32
- alias: resolvedAliases,
59
+ plugins: [createAliasPlugin(aliases, sourcePath)],
33
60
  // Prevent esbuild from trying to resolve node_modules
34
61
  packages: 'external',
35
62
  });
@@ -0,0 +1,183 @@
1
+ /**
2
+ * ESM Styles - TypeScript definitions for style objects
3
+ *
4
+ * Provides type safety and autocompletion for CSS properties
5
+ * while maintaining the flexible, nested syntax of esm-styles.
6
+ */
7
+ import type { Properties } from 'csstype';
8
+ /**
9
+ * CSS variable reference object (as generated by $theme/$device modules)
10
+ */
11
+ export interface CSSVariableRef {
12
+ var: string;
13
+ name?: string;
14
+ [key: string]: string | undefined;
15
+ }
16
+ /**
17
+ * Value that can be used for any CSS property
18
+ */
19
+ export type CSSValue = string | number | CSSVariableRef;
20
+ /**
21
+ * CSS Properties with support for CSS variable references
22
+ * Using csstype's Properties as base with extended value types
23
+ */
24
+ export type CSSProperties = {
25
+ [K in keyof Properties]?: Properties[K] | CSSValue;
26
+ };
27
+ /**
28
+ * Common pseudo-class and pseudo-element keys for better autocompletion
29
+ */
30
+ export type CommonPseudos = ':hover' | ':active' | ':focus' | ':focus-visible' | ':focus-within' | ':visited' | ':disabled' | ':enabled' | ':checked' | ':first-child' | ':last-child' | ':nth-child' | ':not' | ':has' | '::before' | '::after' | '::placeholder' | '::selection' | '::first-line' | '::first-letter';
31
+ /**
32
+ * Complete style object type.
33
+ *
34
+ * Due to TypeScript limitations with index signatures,
35
+ * we use a permissive approach that still provides
36
+ * autocompletion for CSS properties while allowing
37
+ * nested selectors.
38
+ *
39
+ * The tradeoff: typos in CSS property names won't be caught,
40
+ * but you get full autocompletion when typing.
41
+ */
42
+ export type StyleObject = CSSProperties & {
43
+ [K in CommonPseudos]?: StyleObject;
44
+ } & {
45
+ [selector: string]: StyleObject | CSSValue | string | undefined;
46
+ };
47
+ /**
48
+ * Root-level stylesheet with named selectors
49
+ */
50
+ export type StyleSheet = {
51
+ [selector: string]: StyleObject;
52
+ };
53
+ /**
54
+ * Strict CSS properties without nested selectors.
55
+ * Use this for leaf-level style objects where you want
56
+ * full type checking and no nested selectors are needed.
57
+ */
58
+ export type StrictCSSProperties = {
59
+ [K in keyof Properties]?: Properties[K] | CSSValue;
60
+ };
61
+ /**
62
+ * Strict style object with explicit nested selector type.
63
+ * Provides better type checking at the cost of more verbose syntax.
64
+ */
65
+ export interface StrictStyleObject extends StrictCSSProperties {
66
+ ':hover'?: StrictStyleObject;
67
+ ':active'?: StrictStyleObject;
68
+ ':focus'?: StrictStyleObject;
69
+ ':focus-visible'?: StrictStyleObject;
70
+ ':disabled'?: StrictStyleObject;
71
+ '::before'?: StrictStyleObject;
72
+ '::after'?: StrictStyleObject;
73
+ '::placeholder'?: StrictStyleObject;
74
+ }
75
+ /**
76
+ * Define a typed stylesheet with autocompletion.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import { defineStyles } from 'esm-styles'
81
+ * import $theme from './$theme'
82
+ *
83
+ * export default defineStyles({
84
+ * button: {
85
+ * backgroundColor: $theme.paper.bright,
86
+ * padding: '12px 24px',
87
+ *
88
+ * ':hover': {
89
+ * opacity: 0.9,
90
+ * },
91
+ *
92
+ * '@dark': {
93
+ * backgroundColor: $theme.paper.tinted,
94
+ * },
95
+ *
96
+ * __icon: {
97
+ * width: '20px',
98
+ * },
99
+ * },
100
+ * })
101
+ * ```
102
+ */
103
+ export declare function defineStyles<T extends StyleSheet>(styles: T): T;
104
+ /**
105
+ * Define a component style for named export.
106
+ * Use this in component files that are imported into floor modules.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * // button.styles.ts
111
+ * import { defineComponent } from 'esm-styles'
112
+ * import $theme from './$theme'
113
+ *
114
+ * export const button = defineComponent({
115
+ * padding: '12px 24px',
116
+ * backgroundColor: $theme.paper.bright,
117
+ *
118
+ * ':hover': { opacity: 0.9 },
119
+ * '@dark': { backgroundColor: $theme.paper.tinted },
120
+ *
121
+ * primary: { backgroundColor: 'blue' },
122
+ * __icon: { width: '20px' },
123
+ * })
124
+ * ```
125
+ *
126
+ * ```ts
127
+ * // components.styles.ts (floor module)
128
+ * import { defineStyles } from 'esm-styles'
129
+ * import { button } from './button.styles'
130
+ *
131
+ * export default defineStyles({ button })
132
+ * ```
133
+ */
134
+ export declare function defineComponent<T extends StyleObject>(styles: T): T;
135
+ /**
136
+ * Define a reusable style mixin (for spreading into other styles).
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * import { defineMixin } from 'esm-styles'
141
+ *
142
+ * export const flexCenter = defineMixin({
143
+ * display: 'flex',
144
+ * alignItems: 'center',
145
+ * justifyContent: 'center',
146
+ * })
147
+ *
148
+ * // Usage:
149
+ * export const card = defineComponent({
150
+ * ...flexCenter,
151
+ * padding: '24px',
152
+ * })
153
+ * ```
154
+ */
155
+ export declare function defineMixin<T extends StyleObject>(mixin: T): T;
156
+ /**
157
+ * Define strict CSS properties (no nesting, full type checking).
158
+ * Use for simple style objects where you want maximum type safety.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * const buttonBase = defineStrict({
163
+ * display: 'inline-flex',
164
+ * padding: '12px 24px',
165
+ * // backgroundColr: 'red', // ❌ TypeScript error!
166
+ * })
167
+ * ```
168
+ */
169
+ export declare function defineStrict<T extends StrictCSSProperties>(styles: T): T;
170
+ /**
171
+ * Leaf token with CSS variable reference
172
+ */
173
+ export interface ThemeToken extends CSSVariableRef {
174
+ var: string;
175
+ name: string;
176
+ }
177
+ /**
178
+ * Nested theme tokens structure
179
+ */
180
+ export type ThemeTokens = {
181
+ [key: string]: ThemeToken | ThemeTokens;
182
+ };
183
+ export type { Properties, Pseudos } from 'csstype';
@@ -0,0 +1,112 @@
1
+ /**
2
+ * ESM Styles - TypeScript definitions for style objects
3
+ *
4
+ * Provides type safety and autocompletion for CSS properties
5
+ * while maintaining the flexible, nested syntax of esm-styles.
6
+ */
7
+ // ============================================
8
+ // Helper Functions
9
+ // ============================================
10
+ /**
11
+ * Define a typed stylesheet with autocompletion.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { defineStyles } from 'esm-styles'
16
+ * import $theme from './$theme'
17
+ *
18
+ * export default defineStyles({
19
+ * button: {
20
+ * backgroundColor: $theme.paper.bright,
21
+ * padding: '12px 24px',
22
+ *
23
+ * ':hover': {
24
+ * opacity: 0.9,
25
+ * },
26
+ *
27
+ * '@dark': {
28
+ * backgroundColor: $theme.paper.tinted,
29
+ * },
30
+ *
31
+ * __icon: {
32
+ * width: '20px',
33
+ * },
34
+ * },
35
+ * })
36
+ * ```
37
+ */
38
+ export function defineStyles(styles) {
39
+ return styles;
40
+ }
41
+ /**
42
+ * Define a component style for named export.
43
+ * Use this in component files that are imported into floor modules.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * // button.styles.ts
48
+ * import { defineComponent } from 'esm-styles'
49
+ * import $theme from './$theme'
50
+ *
51
+ * export const button = defineComponent({
52
+ * padding: '12px 24px',
53
+ * backgroundColor: $theme.paper.bright,
54
+ *
55
+ * ':hover': { opacity: 0.9 },
56
+ * '@dark': { backgroundColor: $theme.paper.tinted },
57
+ *
58
+ * primary: { backgroundColor: 'blue' },
59
+ * __icon: { width: '20px' },
60
+ * })
61
+ * ```
62
+ *
63
+ * ```ts
64
+ * // components.styles.ts (floor module)
65
+ * import { defineStyles } from 'esm-styles'
66
+ * import { button } from './button.styles'
67
+ *
68
+ * export default defineStyles({ button })
69
+ * ```
70
+ */
71
+ export function defineComponent(styles) {
72
+ return styles;
73
+ }
74
+ /**
75
+ * Define a reusable style mixin (for spreading into other styles).
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * import { defineMixin } from 'esm-styles'
80
+ *
81
+ * export const flexCenter = defineMixin({
82
+ * display: 'flex',
83
+ * alignItems: 'center',
84
+ * justifyContent: 'center',
85
+ * })
86
+ *
87
+ * // Usage:
88
+ * export const card = defineComponent({
89
+ * ...flexCenter,
90
+ * padding: '24px',
91
+ * })
92
+ * ```
93
+ */
94
+ export function defineMixin(mixin) {
95
+ return mixin;
96
+ }
97
+ /**
98
+ * Define strict CSS properties (no nesting, full type checking).
99
+ * Use for simple style objects where you want maximum type safety.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const buttonBase = defineStrict({
104
+ * display: 'inline-flex',
105
+ * padding: '12px 24px',
106
+ * // backgroundColr: 'red', // ❌ TypeScript error!
107
+ * })
108
+ * ```
109
+ */
110
+ export function defineStrict(styles) {
111
+ return styles;
112
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esm-styles",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "A library for working with ESM styles",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -49,6 +49,7 @@
49
49
  "typescript": "^5.8.3"
50
50
  },
51
51
  "dependencies": {
52
+ "csstype": "^3.2.3",
52
53
  "esbuild": "^0.27.2",
53
54
  "js-beautify": "^1.15.4"
54
55
  },