@ripple-ts/prettier-plugin 0.2.169 → 0.2.170

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ripple-ts/prettier-plugin",
3
- "version": "0.2.169",
3
+ "version": "0.2.170",
4
4
  "description": "Ripple plugin for Prettier",
5
5
  "type": "module",
6
6
  "module": "src/index.js",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "prettier": "^3.6.2",
28
- "ripple": "0.2.169"
28
+ "ripple": "0.2.170"
29
29
  },
30
30
  "dependencies": {},
31
31
  "files": [
package/src/index.js CHANGED
@@ -21,11 +21,6 @@ const {
21
21
  } = builders;
22
22
  const { willBreak } = utils;
23
23
 
24
- // Embed function - not needed for now
25
- export function embed(path, options) {
26
- return null;
27
- }
28
-
29
24
  export const languages = [
30
25
  {
31
26
  name: 'ripple',
@@ -39,8 +34,7 @@ export const parsers = {
39
34
  ripple: {
40
35
  astFormat: 'ripple-ast',
41
36
  parse(text, parsers, options) {
42
- const ast = parse(text);
43
- return ast;
37
+ return parse(text);
44
38
  },
45
39
 
46
40
  locStart(node) {
@@ -66,11 +60,50 @@ export const printers = {
66
60
  }
67
61
  return typeof parts === 'string' ? parts : parts;
68
62
  },
63
+ embed(path, options) {
64
+ const node = path.getValue();
65
+
66
+ // Handle StyleSheet nodes inside style tags
67
+ if (node.type === 'StyleSheet' && node.source) {
68
+ // Return async function that will be called by Prettier
69
+ return async (textToDoc) => {
70
+ try {
71
+ // Format CSS using Prettier's textToDoc
72
+ const body = await textToDoc(node.source, {
73
+ parser: 'css',
74
+ });
75
+
76
+ // Return the formatted CSS
77
+ // Note: printElement will wrap this in indent(), so we don't add indent here
78
+ return body;
79
+ } catch (error) {
80
+ // If CSS has syntax errors, return original unformatted content
81
+ console.error('Error formatting CSS:', error);
82
+ return node.source;
83
+ }
84
+ };
85
+ }
86
+
87
+ return null;
88
+ },
69
89
  getVisitorKeys(node) {
90
+ // Exclude metadata and raw text properties that shouldn't be traversed
91
+ // The css property is specifically excluded so embed() can handle it
92
+ const excludedKeys = new Set([
93
+ 'start',
94
+ 'end',
95
+ 'loc',
96
+ 'metadata',
97
+ 'css', // Handled by embed()
98
+ 'raw',
99
+ 'regex',
100
+ ]);
101
+
70
102
  const keys = Object.keys(node).filter((key) => {
71
- return key === 'start' || key === 'end' || key === 'loc' || key === 'metadata' || 'css'
72
- ? false
73
- : typeof node[key] === 'object' && node[key] !== null;
103
+ if (excludedKeys.has(key)) {
104
+ return false;
105
+ }
106
+ return typeof node[key] === 'object' && node[key] !== null;
74
107
  });
75
108
 
76
109
  return keys;
@@ -1923,61 +1956,6 @@ function printRippleNode(node, path, options, print, args) {
1923
1956
  }
1924
1957
  break;
1925
1958
 
1926
- case 'StyleSheet':
1927
- nodeContent = printStyleSheet(node, path, options, print);
1928
- break;
1929
- case 'Rule':
1930
- nodeContent = printCSSRule(node, path, options, print);
1931
- break;
1932
-
1933
- case 'Declaration':
1934
- nodeContent = printCSSDeclaration(node, path, options, print);
1935
- break;
1936
-
1937
- case 'Atrule':
1938
- nodeContent = printCSSAtrule(node, path, options, print);
1939
- break;
1940
-
1941
- case 'SelectorList':
1942
- nodeContent = printCSSSelectorList(node, path, options, print);
1943
- break;
1944
-
1945
- case 'ComplexSelector':
1946
- nodeContent = printCSSComplexSelector(node, path, options, print);
1947
- break;
1948
-
1949
- case 'RelativeSelector':
1950
- nodeContent = printCSSRelativeSelector(node, path, options, print);
1951
- break;
1952
-
1953
- case 'TypeSelector':
1954
- nodeContent = printCSSTypeSelector(node, path, options, print);
1955
- break;
1956
-
1957
- case 'IdSelector':
1958
- nodeContent = printCSSIdSelector(node, path, options, print);
1959
- break;
1960
-
1961
- case 'ClassSelector':
1962
- nodeContent = printCSSClassSelector(node, path, options, print);
1963
- break;
1964
-
1965
- case 'NestingSelector':
1966
- nodeContent = printCSSNestingSelector(node, path, options, print);
1967
- break;
1968
-
1969
- case 'PseudoClassSelector':
1970
- nodeContent = printCSSPseudoClassSelector(node, path, options, print);
1971
- break;
1972
-
1973
- case 'PseudoElementSelector':
1974
- nodeContent = printCSSPseudoElementSelector(node, path, options, print);
1975
- break;
1976
-
1977
- case 'Block':
1978
- nodeContent = printCSSBlock(node, path, options, print);
1979
- break;
1980
-
1981
1959
  case 'Attribute':
1982
1960
  nodeContent = printAttribute(node, path, options, print);
1983
1961
  break;
@@ -4319,215 +4297,6 @@ function printTSIndexedAccessType(node, path, options, print) {
4319
4297
  return concat([path.call(print, 'objectType'), '[', path.call(print, 'indexType'), ']']);
4320
4298
  }
4321
4299
 
4322
- function printStyleSheet(node, path, options, print) {
4323
- // StyleSheet contains CSS rules in the 'body' property
4324
- if (node.body && node.body.length > 0) {
4325
- const cssItems = [];
4326
-
4327
- // Process each item in the stylesheet body
4328
- for (let i = 0; i < node.body.length; i++) {
4329
- const item = path.call(print, 'body', i);
4330
- if (item) {
4331
- cssItems.push(item);
4332
- }
4333
- }
4334
-
4335
- // Structure the CSS with proper indentation and spacing
4336
- // Check for blank lines between CSS items and preserve them
4337
- const result = [];
4338
- for (let i = 0; i < cssItems.length; i++) {
4339
- result.push(cssItems[i]);
4340
- if (i < cssItems.length - 1) {
4341
- // Check if there are blank lines between current and next item
4342
- const currentItem = node.body[i];
4343
- const nextItem = node.body[i + 1];
4344
-
4345
- // Check for blank lines in the original CSS source between rules
4346
- let hasBlankLine = false;
4347
- if (
4348
- node.source &&
4349
- typeof currentItem.end === 'number' &&
4350
- typeof nextItem.start === 'number'
4351
- ) {
4352
- const textBetween = node.source.substring(currentItem.end, nextItem.start);
4353
- // Count newlines in the text between the rules
4354
- const newlineCount = (textBetween.match(/\n/g) || []).length;
4355
- // If there are 2 or more newlines, there's at least one blank line
4356
- hasBlankLine = newlineCount >= 2;
4357
- }
4358
- if (hasBlankLine) {
4359
- // If there are blank lines, add an extra hardline (to create a blank line)
4360
- result.push(hardline, hardline);
4361
- } else {
4362
- result.push(hardline);
4363
- }
4364
- }
4365
- }
4366
-
4367
- return concat(result);
4368
- }
4369
-
4370
- // If no body, return empty string
4371
- return '';
4372
- }
4373
-
4374
- function printCSSRule(node, path, options, print) {
4375
- // CSS Rule has prelude (selector) and block (declarations)
4376
- const selector = path.call(print, 'prelude');
4377
- const block = path.call(print, 'block');
4378
-
4379
- return group([selector, ' {', indent([hardline, block]), hardline, '}']);
4380
- }
4381
-
4382
- function printCSSDeclaration(node, path, options, print) {
4383
- // CSS Declaration has property and value
4384
- const parts = [node.property];
4385
-
4386
- if (node.value) {
4387
- parts.push(': ');
4388
- const value = path.call(print, 'value');
4389
- parts.push(value);
4390
- }
4391
-
4392
- parts.push(';');
4393
- return concat(parts);
4394
- }
4395
-
4396
- function printCSSAtrule(node, path, options, print) {
4397
- // CSS At-rule like @media, @keyframes, etc.
4398
- const parts = ['@', node.name];
4399
-
4400
- if (node.prelude) {
4401
- parts.push(' ');
4402
- const prelude = path.call(print, 'prelude');
4403
- parts.push(prelude);
4404
- }
4405
-
4406
- if (node.block) {
4407
- const block = path.call(print, 'block');
4408
- parts.push(' {');
4409
- parts.push(indent([hardline, block]));
4410
- parts.push(hardline, '}');
4411
- } else {
4412
- parts.push(';');
4413
- }
4414
-
4415
- return group(parts);
4416
- }
4417
-
4418
- function printCSSSelectorList(node, path, options, print) {
4419
- // SelectorList contains multiple selectors
4420
- if (node.children && node.children.length > 0) {
4421
- const selectors = [];
4422
- for (let i = 0; i < node.children.length; i++) {
4423
- const selector = path.call(print, 'children', i);
4424
- selectors.push(selector);
4425
- }
4426
- // Join selectors with comma and line break for proper CSS formatting
4427
- return join([',', hardline], selectors);
4428
- }
4429
- return '';
4430
- }
4431
-
4432
- function printCSSComplexSelector(node, path, options, print) {
4433
- // ComplexSelector contains selector components
4434
- if (node.children && node.children.length > 0) {
4435
- const selectorParts = [];
4436
- for (let i = 0; i < node.children.length; i++) {
4437
- const part = path.call(print, 'children', i);
4438
- selectorParts.push(part);
4439
- }
4440
- return concat(selectorParts);
4441
- }
4442
- return '';
4443
- }
4444
-
4445
- function printCSSRelativeSelector(node, path, options, print) {
4446
- // RelativeSelector contains selector components in the 'selectors' property
4447
- const parts = [];
4448
-
4449
- // Print combinator if it exists (e.g., +, >, ~, or space)
4450
- if (node.combinator) {
4451
- if (node.combinator.name === ' ') {
4452
- // Space combinator (descendant selector)
4453
- parts.push(' ');
4454
- } else {
4455
- // Other combinators (+, >, ~)
4456
- parts.push(' ', node.combinator.name, ' ');
4457
- }
4458
- }
4459
-
4460
- if (node.selectors && node.selectors.length > 0) {
4461
- const selectorParts = [];
4462
- for (let i = 0; i < node.selectors.length; i++) {
4463
- const part = path.call(print, 'selectors', i);
4464
- selectorParts.push(part);
4465
- }
4466
- parts.push(...selectorParts);
4467
- }
4468
-
4469
- return concat(parts);
4470
- }
4471
-
4472
- function printCSSTypeSelector(node, path, options, print) {
4473
- // TypeSelector for element names like 'div', 'body', 'p', etc.
4474
- return node.name || '';
4475
- }
4476
-
4477
- function printCSSIdSelector(node, path, options, print) {
4478
- // IdSelector for #id
4479
- return concat(['#', node.name || '']);
4480
- }
4481
-
4482
- function printCSSClassSelector(node, path, options, print) {
4483
- // ClassSelector for .class
4484
- return concat(['.', node.name || '']);
4485
- }
4486
-
4487
- function printCSSNestingSelector(node, path, options, print) {
4488
- // NestingSelector for & (parent reference in nested CSS)
4489
- return '&';
4490
- }
4491
-
4492
- function printCSSPseudoClassSelector(node, path, options, print) {
4493
- // PseudoClassSelector for :hover, :global(), etc.
4494
- const parts = [':', node.name || ''];
4495
-
4496
- // If it has args (like :global(.classname)), print them
4497
- if (node.args !== null && node.args !== undefined) {
4498
- parts.push('(', node.args, ')');
4499
- }
4500
-
4501
- return concat(parts);
4502
- }
4503
-
4504
- function printCSSPseudoElementSelector(node, path, options, print) {
4505
- // PseudoElementSelector for ::before, ::after, etc.
4506
- const parts = ['::', node.name || ''];
4507
-
4508
- // If it has args (like ::slotted(span)), print them
4509
- if (node.args !== null && node.args !== undefined) {
4510
- parts.push('(', node.args, ')');
4511
- }
4512
-
4513
- return concat(parts);
4514
- }
4515
-
4516
- function printCSSBlock(node, path, options, print) {
4517
- // CSS Block contains declarations
4518
- if (node.children && node.children.length > 0) {
4519
- const declarations = [];
4520
- for (let i = 0; i < node.children.length; i++) {
4521
- const decl = path.call(print, 'children', i);
4522
- if (decl) {
4523
- declarations.push(decl);
4524
- }
4525
- }
4526
- return join(hardline, declarations);
4527
- }
4528
- return '';
4529
- }
4530
-
4531
4300
  function shouldInlineSingleChild(parentNode, firstChild, childDoc) {
4532
4301
  if (!firstChild || childDoc == null) {
4533
4302
  return false;
package/src/index.test.js CHANGED
@@ -1135,6 +1135,59 @@ const [obj1, obj2] = arrayOfObjects;`;
1135
1135
  expect(result).toBeWithNewline(expected);
1136
1136
  });
1137
1137
 
1138
+ it('should keep css @keyframes syntax intact', async () => {
1139
+ const input = `export component App() {
1140
+ <style>
1141
+ /* Scoped keyframe - only usable within Parent */
1142
+ @keyframes slideIn {
1143
+ from { transform: translateX(-100%); }
1144
+ to { transform: translateX(0); }
1145
+ }
1146
+
1147
+ /* Global keyframe - usable in any component */
1148
+ @keyframes -global-fadeIn {
1149
+ 0% { opacity: 0; }
1150
+ 100% { opacity: 1; }
1151
+ }
1152
+
1153
+ .parent {
1154
+ animation: slideIn 1s;
1155
+ }
1156
+ </style>
1157
+ }`;
1158
+
1159
+ const expected = `export component App() {
1160
+ <style>
1161
+ /* Scoped keyframe - only usable within Parent */
1162
+ @keyframes slideIn {
1163
+ from {
1164
+ transform: translateX(-100%);
1165
+ }
1166
+ to {
1167
+ transform: translateX(0);
1168
+ }
1169
+ }
1170
+
1171
+ /* Global keyframe - usable in any component */
1172
+ @keyframes -global-fadeIn {
1173
+ 0% {
1174
+ opacity: 0;
1175
+ }
1176
+ 100% {
1177
+ opacity: 1;
1178
+ }
1179
+ }
1180
+
1181
+ .parent {
1182
+ animation: slideIn 1s;
1183
+ }
1184
+ </style>
1185
+ }`;
1186
+
1187
+ const result = await format(input, { singleQuote: true, printWidth: 100 });
1188
+ expect(result).toBeWithNewline(expected);
1189
+ });
1190
+
1138
1191
  it('should keep TrackedMap short syntax intact', async () => {
1139
1192
  const expected = `const map = new #Map([['key1', 'value1'], ['key2', 'value2']]);
1140
1193
  const set = new #Set([1, 2, 3]);`;