From e739e0911335e15090b34b981bd1f776e62ec4d0 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Mon, 25 Nov 2024 12:42:22 -0600 Subject: [PATCH 01/17] Correctly parse functions like counter, url, etc, added a unit test --- packages/shared/src/transform-value.js | 572 +++++++++++++------------ 1 file changed, 304 insertions(+), 268 deletions(-) diff --git a/packages/shared/src/transform-value.js b/packages/shared/src/transform-value.js index adc4d6166..06c3c8a19 100644 --- a/packages/shared/src/transform-value.js +++ b/packages/shared/src/transform-value.js @@ -7,272 +7,308 @@ * @flow strict */ -import type { StyleXOptions } from './common-types'; + import type { StyleXOptions } from './common-types'; -import normalizeValue from './utils/normalize-value'; - -/** - * Convert a CSS value in JS to the final CSS string value - */ -export default function transformValue( - key: string, - rawValue: string | number, - options: StyleXOptions, -): string { - const value = - typeof rawValue === 'number' - ? String(Math.round(rawValue * 10000) / 10000) + getNumberSuffix(key) - : rawValue; - - // content is one of the values that needs to wrapped in quotes. - // Users may write `''` without thinking about it, so we fix that. - if ( - (key === 'content' || - key === 'hyphenateCharacter' || - key === 'hyphenate-character') && - typeof value === 'string' - ) { - const val = value.trim(); - if (val.match(/^attr\([a-zA-Z0-9-]+\)$/)) { - return val; - } - if ( - !( - (val.startsWith('"') && val.endsWith('"')) || - (val.startsWith("'") && val.endsWith("'")) - ) - ) { - return `"${val}"`; - } - } - - return normalizeValue(value, key, options); -} - -export function getNumberSuffix(key: string): string { - if (unitlessNumberProperties.has(key) || key.startsWith('--')) { - return ''; - } - if (!(key in numberPropertySuffixes)) { - return 'px'; - } - - const suffix = numberPropertySuffixes[key]; - if (suffix == null) { - return 'px'; - } else { - return suffix; - } -} - -const unitlessNumberProperties = new Set([ - 'WebkitLineClamp', - 'animationIterationCount', - 'aspectRatio', - 'borderImageOutset', - 'borderImageSlice', - 'borderImageWidth', - 'counterSet', - 'counterReset', - 'columnCount', - 'flex', - 'flexGrow', - 'flexShrink', - 'flexOrder', - 'gridRow', - 'gridRowStart', - 'gridRowEnd', - 'gridColumn', - 'gridColumnStart', - 'gridColumnEnd', - 'gridArea', - 'fontWeight', - 'hyphenateLimitChars', - 'lineClamp', - 'lineHeight', - 'maskBorderOutset', - 'maskBorderSlice', - 'maskBorderWidth', - 'opacity', - 'order', - 'orphans', - 'tabSize', - 'widows', - 'zIndex', - 'fillOpacity', - 'floodOpacity', - 'rotate', - 'scale', - 'shapeImageThreshold', - 'stopOpacity', - 'strokeDasharray', - 'strokeDashoffset', - 'strokeMiterlimit', - 'strokeOpacity', - 'strokeWidth', - 'scale', - 'mathDepth', -]); - -// List of properties that have custom suffixes for numbers -const numberPropertySuffixes: { +[key: string]: string } = { - animationDelay: 'ms', - animationDuration: 'ms', - transitionDelay: 'ms', - transitionDuration: 'ms', - voiceDuration: 'ms', -}; - -export const timeUnits: Set = new Set( - Object.keys(numberPropertySuffixes), -); - -export const lengthUnits: Set = new Set([ - 'backgroundPositionX', - 'backgroundPositionY', - 'blockSize', - 'borderBlockEndWidth', - 'borderBlockStartWidth', - 'borderBlockWidth', - 'borderVerticalWidth', - 'borderVerticalWidth', - 'borderBottomLeftRadius', - 'borderBottomRightRadius', - 'borderBottomWidth', - 'borderEndEndRadius', - 'borderEndStartRadius', - 'borderInlineEndWidth', - 'borderEndWidth', - 'borderInlineStartWidth', - 'borderStartWidth', - 'borderInlineWidth', - 'borderHorizontalWidth', - 'borderLeftWidth', - 'borderRightWidth', - 'borderSpacing', - 'borderStartEndRadius', - 'borderStartStartRadius', - 'borderTopLeftRadius', - 'borderTopRightRadius', - 'borderTopWidth', - 'bottom', - 'columnGap', - 'columnRuleWidth', - 'columnWidth', - 'containIntrinsicBlockSize', - 'containIntrinsicHeight', - 'containIntrinsicInlineSize', - 'containIntrinsicWidth', - 'flexBasis', - 'fontSize', - 'fontSmooth', - 'height', - 'inlineSize', - 'insetBlockEnd', - 'insetBlockStart', - 'insetInlineEnd', - 'insetInlineStart', - 'left', - 'letterSpacing', - 'marginBlockEnd', - 'marginBlockStart', - 'marginBottom', - 'marginInlineEnd', - 'marginEnd', - 'marginInlineStart', - 'marginStart', - 'marginLeft', - 'marginRight', - 'marginTop', - 'maxBlockSize', - 'maxHeight', - 'maxInlineSize', - 'maxWidth', - 'minBlockSize', - 'minHeight', - 'minInlineSize', - 'minWidth', - 'offsetDistance', - 'outlineOffset', - 'outlineWidth', - 'overflowClipMargin', - 'paddingBlockEnd', - 'paddingBlockStart', - 'paddingBottom', - 'paddingInlineEnd', - 'paddingEnd', - 'paddingInlineStart', - 'paddingStart', - 'paddingLeft', - 'paddingRight', - 'paddingTop', - 'perspective', - 'right', - 'rowGap', - 'scrollMarginBlockEnd', - 'scrollMarginBlockStart', - 'scrollMarginBottom', - 'scrollMarginInlineEnd', - 'scrollMarginInlineStart', - 'scrollMarginLeft', - 'scrollMarginRight', - 'scrollMarginTop', - 'scrollPaddingBlockEnd', - 'scrollPaddingBlockStart', - 'scrollPaddingBottom', - 'scrollPaddingInlineEnd', - 'scrollPaddingInlineStart', - 'scrollPaddingLeft', - 'scrollPaddingRight', - 'scrollPaddingTop', - 'scrollSnapMarginBottom', - 'scrollSnapMarginLeft', - 'scrollSnapMarginRight', - 'scrollSnapMarginTop', - 'shapeMargin', - 'tabSize', - 'textDecorationThickness', - 'textIndent', - 'textUnderlineOffset', - 'top', - 'transformOrigin', - 'translate', - 'verticalAlign', - 'width', - 'wordSpacing', - 'border', - 'borderBlock', - 'borderBlockEnd', - 'borderBlockStart', - 'borderBottom', - 'borderLeft', - 'borderRadius', - 'borderRight', - 'borderTop', - 'borderWidth', - 'columnRule', - 'containIntrinsicSize', - 'gap', - 'inset', - 'insetBlock', - 'insetInline', - 'margin', - 'marginBlock', - 'marginVertical', - 'marginInline', - 'marginHorizontal', - 'offset', - 'outline', - 'padding', - 'paddingBlock', - 'paddingVertical', - 'paddingInline', - 'paddingHorizontal', - 'scrollMargin', - 'scrollMarginBlock', - 'scrollMarginInline', - 'scrollPadding', - 'scrollPaddingBlock', - 'scrollPaddingInline', - 'scrollSnapMargin', -]); + import normalizeValue from './utils/normalize-value'; + + /** + * Convert a CSS value in JS to the final CSS string value + */ + export default function transformValue( + key: string, + rawValue: string | number, + options: StyleXOptions, + ): string { + const value = + typeof rawValue === 'number' + ? String(Math.round(rawValue * 10000) / 10000) + getNumberSuffix(key) + : rawValue; + + if ( + (key === 'content' || + key === 'hyphenateCharacter' || + key === 'hyphenate-character') && + typeof value === 'string' + ) { + const val = value.trim(); + + const cssContentFunctions = [ + 'attr', + 'counter', + 'counters', + 'url', + 'linear-gradient', + 'image-set' + ]; + + const cssContentKeywords = new Set([ + 'normal', + 'none', + 'open-quote', + 'close-quote', + 'no-open-quote', + 'no-close-quote', + 'inherit', + 'initial', + 'revert', + 'revert-layer', + 'unset' + ]); + + const hasCssFunction = cssContentFunctions.some(func => + val.startsWith(`${func}(`) && val.endsWith(')') + ); + + const isKeyword = cssContentKeywords.has(val); + + const containsMixedContent = val.split(' ').some(part => { + const trimmedPart = part.trim(); + return cssContentFunctions.some(func => trimmedPart.startsWith(`${func}(`)) || + cssContentKeywords.has(trimmedPart); + }); + + // Leave value unchanged if it: + // 1. Is a CSS function + // 2. Is a CSS keyword + // 3. Contains mixed content (functions/keywords with strings) + // 4. Already has matching quotes + if (hasCssFunction || + isKeyword || + containsMixedContent || + (val.startsWith('"') && val.endsWith('"')) || + (val.startsWith("'") && val.endsWith("'"))) { + return val; + } + return `"${val}"`; + } + + return normalizeValue(value, key, options); + } + + export function getNumberSuffix(key: string): string { + if (unitlessNumberProperties.has(key) || key.startsWith('--')) { + return ''; + } + if (!(key in numberPropertySuffixes)) { + return 'px'; + } + + const suffix = numberPropertySuffixes[key]; + if (suffix == null) { + return 'px'; + } else { + return suffix; + } + } + + const unitlessNumberProperties = new Set([ + 'WebkitLineClamp', + 'animationIterationCount', + 'aspectRatio', + 'borderImageOutset', + 'borderImageSlice', + 'borderImageWidth', + 'counterSet', + 'counterReset', + 'columnCount', + 'flex', + 'flexGrow', + 'flexShrink', + 'flexOrder', + 'gridRow', + 'gridRowStart', + 'gridRowEnd', + 'gridColumn', + 'gridColumnStart', + 'gridColumnEnd', + 'gridArea', + 'fontWeight', + 'hyphenateLimitChars', + 'lineClamp', + 'lineHeight', + 'maskBorderOutset', + 'maskBorderSlice', + 'maskBorderWidth', + 'opacity', + 'order', + 'orphans', + 'tabSize', + 'widows', + 'zIndex', + 'fillOpacity', + 'floodOpacity', + 'rotate', + 'scale', + 'shapeImageThreshold', + 'stopOpacity', + 'strokeDasharray', + 'strokeDashoffset', + 'strokeMiterlimit', + 'strokeOpacity', + 'strokeWidth', + 'scale', + 'mathDepth', + ]); + + // List of properties that have custom suffixes for numbers + const numberPropertySuffixes: { +[key: string]: string } = { + animationDelay: 'ms', + animationDuration: 'ms', + transitionDelay: 'ms', + transitionDuration: 'ms', + voiceDuration: 'ms', + }; + + export const timeUnits: Set = new Set( + Object.keys(numberPropertySuffixes), + ); + + export const lengthUnits: Set = new Set([ + 'backgroundPositionX', + 'backgroundPositionY', + 'blockSize', + 'borderBlockEndWidth', + 'borderBlockStartWidth', + 'borderBlockWidth', + 'borderVerticalWidth', + 'borderVerticalWidth', + 'borderBottomLeftRadius', + 'borderBottomRightRadius', + 'borderBottomWidth', + 'borderEndEndRadius', + 'borderEndStartRadius', + 'borderInlineEndWidth', + 'borderEndWidth', + 'borderInlineStartWidth', + 'borderStartWidth', + 'borderInlineWidth', + 'borderHorizontalWidth', + 'borderLeftWidth', + 'borderRightWidth', + 'borderSpacing', + 'borderStartEndRadius', + 'borderStartStartRadius', + 'borderTopLeftRadius', + 'borderTopRightRadius', + 'borderTopWidth', + 'bottom', + 'columnGap', + 'columnRuleWidth', + 'columnWidth', + 'containIntrinsicBlockSize', + 'containIntrinsicHeight', + 'containIntrinsicInlineSize', + 'containIntrinsicWidth', + 'flexBasis', + 'fontSize', + 'fontSmooth', + 'height', + 'inlineSize', + 'insetBlockEnd', + 'insetBlockStart', + 'insetInlineEnd', + 'insetInlineStart', + 'left', + 'letterSpacing', + 'marginBlockEnd', + 'marginBlockStart', + 'marginBottom', + 'marginInlineEnd', + 'marginEnd', + 'marginInlineStart', + 'marginStart', + 'marginLeft', + 'marginRight', + 'marginTop', + 'maxBlockSize', + 'maxHeight', + 'maxInlineSize', + 'maxWidth', + 'minBlockSize', + 'minHeight', + 'minInlineSize', + 'minWidth', + 'offsetDistance', + 'outlineOffset', + 'outlineWidth', + 'overflowClipMargin', + 'paddingBlockEnd', + 'paddingBlockStart', + 'paddingBottom', + 'paddingInlineEnd', + 'paddingEnd', + 'paddingInlineStart', + 'paddingStart', + 'paddingLeft', + 'paddingRight', + 'paddingTop', + 'perspective', + 'right', + 'rowGap', + 'scrollMarginBlockEnd', + 'scrollMarginBlockStart', + 'scrollMarginBottom', + 'scrollMarginInlineEnd', + 'scrollMarginInlineStart', + 'scrollMarginLeft', + 'scrollMarginRight', + 'scrollMarginTop', + 'scrollPaddingBlockEnd', + 'scrollPaddingBlockStart', + 'scrollPaddingBottom', + 'scrollPaddingInlineEnd', + 'scrollPaddingInlineStart', + 'scrollPaddingLeft', + 'scrollPaddingRight', + 'scrollPaddingTop', + 'scrollSnapMarginBottom', + 'scrollSnapMarginLeft', + 'scrollSnapMarginRight', + 'scrollSnapMarginTop', + 'shapeMargin', + 'tabSize', + 'textDecorationThickness', + 'textIndent', + 'textUnderlineOffset', + 'top', + 'transformOrigin', + 'translate', + 'verticalAlign', + 'width', + 'wordSpacing', + 'border', + 'borderBlock', + 'borderBlockEnd', + 'borderBlockStart', + 'borderBottom', + 'borderLeft', + 'borderRadius', + 'borderRight', + 'borderTop', + 'borderWidth', + 'columnRule', + 'containIntrinsicSize', + 'gap', + 'inset', + 'insetBlock', + 'insetInline', + 'margin', + 'marginBlock', + 'marginVertical', + 'marginInline', + 'marginHorizontal', + 'offset', + 'outline', + 'padding', + 'paddingBlock', + 'paddingVertical', + 'paddingInline', + 'paddingHorizontal', + 'scrollMargin', + 'scrollMarginBlock', + 'scrollMarginInline', + 'scrollPadding', + 'scrollPaddingBlock', + 'scrollPaddingInline', + 'scrollSnapMargin', + ]); \ No newline at end of file From 0566af326feb61ca70ccedcbd210169bffdd7175 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Mon, 25 Nov 2024 12:44:11 -0600 Subject: [PATCH 02/17] Unit test for transform value features --- .../__tests__/stylex-transform-value.js | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 packages/shared/__tests__/stylex-transform-value.js diff --git a/packages/shared/__tests__/stylex-transform-value.js b/packages/shared/__tests__/stylex-transform-value.js new file mode 100644 index 000000000..7a2423b9b --- /dev/null +++ b/packages/shared/__tests__/stylex-transform-value.js @@ -0,0 +1,84 @@ +/** +* Copyright (c) Meta Platforms, Inc. and affiliates. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +* +* @flow strict +*/ +import transformValue from '../src/transform-value'; + +describe('transformValue content property tests', () => { + test('preserves CSS functions without quotes', () => { + const functions = [ + ['counters(div, ".")', 'counters(div, ".")'], + ['counter(chapter)', 'counter(chapter)'], + ['counter(chapter, upper-roman)', 'counter(chapter, upper-roman)'], + ['attr(href)', 'attr(href)'], + ['url(image.jpg)', 'url(image.jpg)'], + ['linear-gradient(#e66465, #9198e5)', 'linear-gradient(#e66465, #9198e5)'], + ['image-set("image1x.png" 1x, "image2x.png" 2x)', 'image-set("image1x.png" 1x, "image2x.png" 2x)'] + ]; + + functions.forEach(([input, expected]) => { + expect(transformValue('content', input, {})).toBe(expected); + }); + }); + + test('preserves CSS keywords without quotes', () => { + const keywords = [ + 'normal', + 'none', + 'open-quote', + 'close-quote', + 'no-open-quote', + 'no-close-quote', + 'inherit', + 'initial', + 'revert', + 'revert-layer', + 'unset' + ]; + + keywords.forEach(keyword => { + expect(transformValue('content', keyword, {})).toBe(keyword); + }); + }); + + test('handles mixed content values', () => { + const mixedValues = [ + ['open-quote counter(chapter)', 'open-quote counter(chapter)'], + ['"prefix" url(image.jpg)', '"prefix" url(image.jpg)'], + ['url("test.png") / "Alt text"', 'url("test.png") / "Alt text"'], + ['open-quote counter(chapter) close-quote', 'open-quote counter(chapter) close-quote'] + ]; + + mixedValues.forEach(([input, expected]) => { + expect(transformValue('content', input, {})).toBe(expected); + }); + }); + + test('adds quotes to plain strings', () => { + const strings = [ + ['Hello world', '"Hello world"'], + ['Simple text', '"Simple text"'], + ['123', '"123"'] + ]; + + strings.forEach(([input, expected]) => { + expect(transformValue('content', input, {})).toBe(expected); + }); + }); + + test('preserves existing quotes', () => { + const quotedStrings = [ + ['"already quoted"', '"already quoted"'], + ["'single quotes'", "'single quotes'"], + ['"mixed "nested" quotes"', '"mixed "nested" quotes"'] + ]; + + quotedStrings.forEach(([input, expected]) => { + expect(transformValue('content', input, {})).toBe(expected); + }); + }); +}); \ No newline at end of file From 554931b35266ddb254abc0a69c5092055aedb4d7 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Mon, 25 Nov 2024 12:47:00 -0600 Subject: [PATCH 03/17] =?UTF-8?q?=C3=82Ran=20prettier/lint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/stylex-transform-value.js | 151 +++-- packages/shared/src/transform-value.js | 613 +++++++++--------- 2 files changed, 389 insertions(+), 375 deletions(-) diff --git a/packages/shared/__tests__/stylex-transform-value.js b/packages/shared/__tests__/stylex-transform-value.js index 7a2423b9b..f4f90a01b 100644 --- a/packages/shared/__tests__/stylex-transform-value.js +++ b/packages/shared/__tests__/stylex-transform-value.js @@ -1,84 +1,93 @@ /** -* Copyright (c) Meta Platforms, Inc. and affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @flow strict -*/ + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * + */ import transformValue from '../src/transform-value'; describe('transformValue content property tests', () => { - test('preserves CSS functions without quotes', () => { - const functions = [ - ['counters(div, ".")', 'counters(div, ".")'], - ['counter(chapter)', 'counter(chapter)'], - ['counter(chapter, upper-roman)', 'counter(chapter, upper-roman)'], - ['attr(href)', 'attr(href)'], - ['url(image.jpg)', 'url(image.jpg)'], - ['linear-gradient(#e66465, #9198e5)', 'linear-gradient(#e66465, #9198e5)'], - ['image-set("image1x.png" 1x, "image2x.png" 2x)', 'image-set("image1x.png" 1x, "image2x.png" 2x)'] - ]; + test('preserves CSS functions without quotes', () => { + const functions = [ + ['counters(div, ".")', 'counters(div, ".")'], + ['counter(chapter)', 'counter(chapter)'], + ['counter(chapter, upper-roman)', 'counter(chapter, upper-roman)'], + ['attr(href)', 'attr(href)'], + ['url(image.jpg)', 'url(image.jpg)'], + [ + 'linear-gradient(#e66465, #9198e5)', + 'linear-gradient(#e66465, #9198e5)', + ], + [ + 'image-set("image1x.png" 1x, "image2x.png" 2x)', + 'image-set("image1x.png" 1x, "image2x.png" 2x)', + ], + ]; - functions.forEach(([input, expected]) => { - expect(transformValue('content', input, {})).toBe(expected); - }); - }); + functions.forEach(([input, expected]) => { + expect(transformValue('content', input, {})).toBe(expected); + }); + }); - test('preserves CSS keywords without quotes', () => { - const keywords = [ - 'normal', - 'none', - 'open-quote', - 'close-quote', - 'no-open-quote', - 'no-close-quote', - 'inherit', - 'initial', - 'revert', - 'revert-layer', - 'unset' - ]; + test('preserves CSS keywords without quotes', () => { + const keywords = [ + 'normal', + 'none', + 'open-quote', + 'close-quote', + 'no-open-quote', + 'no-close-quote', + 'inherit', + 'initial', + 'revert', + 'revert-layer', + 'unset', + ]; - keywords.forEach(keyword => { - expect(transformValue('content', keyword, {})).toBe(keyword); - }); - }); + keywords.forEach((keyword) => { + expect(transformValue('content', keyword, {})).toBe(keyword); + }); + }); - test('handles mixed content values', () => { - const mixedValues = [ - ['open-quote counter(chapter)', 'open-quote counter(chapter)'], - ['"prefix" url(image.jpg)', '"prefix" url(image.jpg)'], - ['url("test.png") / "Alt text"', 'url("test.png") / "Alt text"'], - ['open-quote counter(chapter) close-quote', 'open-quote counter(chapter) close-quote'] - ]; + test('handles mixed content values', () => { + const mixedValues = [ + ['open-quote counter(chapter)', 'open-quote counter(chapter)'], + ['"prefix" url(image.jpg)', '"prefix" url(image.jpg)'], + ['url("test.png") / "Alt text"', 'url("test.png") / "Alt text"'], + [ + 'open-quote counter(chapter) close-quote', + 'open-quote counter(chapter) close-quote', + ], + ]; - mixedValues.forEach(([input, expected]) => { - expect(transformValue('content', input, {})).toBe(expected); - }); - }); + mixedValues.forEach(([input, expected]) => { + expect(transformValue('content', input, {})).toBe(expected); + }); + }); - test('adds quotes to plain strings', () => { - const strings = [ - ['Hello world', '"Hello world"'], - ['Simple text', '"Simple text"'], - ['123', '"123"'] - ]; + test('adds quotes to plain strings', () => { + const strings = [ + ['Hello world', '"Hello world"'], + ['Simple text', '"Simple text"'], + ['123', '"123"'], + ]; - strings.forEach(([input, expected]) => { - expect(transformValue('content', input, {})).toBe(expected); - }); - }); + strings.forEach(([input, expected]) => { + expect(transformValue('content', input, {})).toBe(expected); + }); + }); - test('preserves existing quotes', () => { - const quotedStrings = [ - ['"already quoted"', '"already quoted"'], - ["'single quotes'", "'single quotes'"], - ['"mixed "nested" quotes"', '"mixed "nested" quotes"'] - ]; + test('preserves existing quotes', () => { + const quotedStrings = [ + ['"already quoted"', '"already quoted"'], + ["'single quotes'", "'single quotes'"], + ['"mixed "nested" quotes"', '"mixed "nested" quotes"'], + ]; - quotedStrings.forEach(([input, expected]) => { - expect(transformValue('content', input, {})).toBe(expected); - }); - }); -}); \ No newline at end of file + quotedStrings.forEach(([input, expected]) => { + expect(transformValue('content', input, {})).toBe(expected); + }); + }); +}); diff --git a/packages/shared/src/transform-value.js b/packages/shared/src/transform-value.js index 06c3c8a19..b96101b5d 100644 --- a/packages/shared/src/transform-value.js +++ b/packages/shared/src/transform-value.js @@ -7,308 +7,313 @@ * @flow strict */ - import type { StyleXOptions } from './common-types'; +import type { StyleXOptions } from './common-types'; - import normalizeValue from './utils/normalize-value'; - - /** - * Convert a CSS value in JS to the final CSS string value - */ - export default function transformValue( - key: string, - rawValue: string | number, - options: StyleXOptions, - ): string { - const value = - typeof rawValue === 'number' - ? String(Math.round(rawValue * 10000) / 10000) + getNumberSuffix(key) - : rawValue; - - if ( - (key === 'content' || - key === 'hyphenateCharacter' || - key === 'hyphenate-character') && - typeof value === 'string' - ) { - const val = value.trim(); - - const cssContentFunctions = [ - 'attr', - 'counter', - 'counters', - 'url', - 'linear-gradient', - 'image-set' - ]; - - const cssContentKeywords = new Set([ - 'normal', - 'none', - 'open-quote', - 'close-quote', - 'no-open-quote', - 'no-close-quote', - 'inherit', - 'initial', - 'revert', - 'revert-layer', - 'unset' - ]); - - const hasCssFunction = cssContentFunctions.some(func => - val.startsWith(`${func}(`) && val.endsWith(')') - ); - - const isKeyword = cssContentKeywords.has(val); - - const containsMixedContent = val.split(' ').some(part => { - const trimmedPart = part.trim(); - return cssContentFunctions.some(func => trimmedPart.startsWith(`${func}(`)) || - cssContentKeywords.has(trimmedPart); - }); - - // Leave value unchanged if it: - // 1. Is a CSS function - // 2. Is a CSS keyword - // 3. Contains mixed content (functions/keywords with strings) - // 4. Already has matching quotes - if (hasCssFunction || - isKeyword || - containsMixedContent || - (val.startsWith('"') && val.endsWith('"')) || - (val.startsWith("'") && val.endsWith("'"))) { - return val; - } - return `"${val}"`; - } - - return normalizeValue(value, key, options); - } - - export function getNumberSuffix(key: string): string { - if (unitlessNumberProperties.has(key) || key.startsWith('--')) { - return ''; - } - if (!(key in numberPropertySuffixes)) { - return 'px'; - } - - const suffix = numberPropertySuffixes[key]; - if (suffix == null) { - return 'px'; - } else { - return suffix; - } - } - - const unitlessNumberProperties = new Set([ - 'WebkitLineClamp', - 'animationIterationCount', - 'aspectRatio', - 'borderImageOutset', - 'borderImageSlice', - 'borderImageWidth', - 'counterSet', - 'counterReset', - 'columnCount', - 'flex', - 'flexGrow', - 'flexShrink', - 'flexOrder', - 'gridRow', - 'gridRowStart', - 'gridRowEnd', - 'gridColumn', - 'gridColumnStart', - 'gridColumnEnd', - 'gridArea', - 'fontWeight', - 'hyphenateLimitChars', - 'lineClamp', - 'lineHeight', - 'maskBorderOutset', - 'maskBorderSlice', - 'maskBorderWidth', - 'opacity', - 'order', - 'orphans', - 'tabSize', - 'widows', - 'zIndex', - 'fillOpacity', - 'floodOpacity', - 'rotate', - 'scale', - 'shapeImageThreshold', - 'stopOpacity', - 'strokeDasharray', - 'strokeDashoffset', - 'strokeMiterlimit', - 'strokeOpacity', - 'strokeWidth', - 'scale', - 'mathDepth', - ]); - - // List of properties that have custom suffixes for numbers - const numberPropertySuffixes: { +[key: string]: string } = { - animationDelay: 'ms', - animationDuration: 'ms', - transitionDelay: 'ms', - transitionDuration: 'ms', - voiceDuration: 'ms', - }; - - export const timeUnits: Set = new Set( - Object.keys(numberPropertySuffixes), - ); - - export const lengthUnits: Set = new Set([ - 'backgroundPositionX', - 'backgroundPositionY', - 'blockSize', - 'borderBlockEndWidth', - 'borderBlockStartWidth', - 'borderBlockWidth', - 'borderVerticalWidth', - 'borderVerticalWidth', - 'borderBottomLeftRadius', - 'borderBottomRightRadius', - 'borderBottomWidth', - 'borderEndEndRadius', - 'borderEndStartRadius', - 'borderInlineEndWidth', - 'borderEndWidth', - 'borderInlineStartWidth', - 'borderStartWidth', - 'borderInlineWidth', - 'borderHorizontalWidth', - 'borderLeftWidth', - 'borderRightWidth', - 'borderSpacing', - 'borderStartEndRadius', - 'borderStartStartRadius', - 'borderTopLeftRadius', - 'borderTopRightRadius', - 'borderTopWidth', - 'bottom', - 'columnGap', - 'columnRuleWidth', - 'columnWidth', - 'containIntrinsicBlockSize', - 'containIntrinsicHeight', - 'containIntrinsicInlineSize', - 'containIntrinsicWidth', - 'flexBasis', - 'fontSize', - 'fontSmooth', - 'height', - 'inlineSize', - 'insetBlockEnd', - 'insetBlockStart', - 'insetInlineEnd', - 'insetInlineStart', - 'left', - 'letterSpacing', - 'marginBlockEnd', - 'marginBlockStart', - 'marginBottom', - 'marginInlineEnd', - 'marginEnd', - 'marginInlineStart', - 'marginStart', - 'marginLeft', - 'marginRight', - 'marginTop', - 'maxBlockSize', - 'maxHeight', - 'maxInlineSize', - 'maxWidth', - 'minBlockSize', - 'minHeight', - 'minInlineSize', - 'minWidth', - 'offsetDistance', - 'outlineOffset', - 'outlineWidth', - 'overflowClipMargin', - 'paddingBlockEnd', - 'paddingBlockStart', - 'paddingBottom', - 'paddingInlineEnd', - 'paddingEnd', - 'paddingInlineStart', - 'paddingStart', - 'paddingLeft', - 'paddingRight', - 'paddingTop', - 'perspective', - 'right', - 'rowGap', - 'scrollMarginBlockEnd', - 'scrollMarginBlockStart', - 'scrollMarginBottom', - 'scrollMarginInlineEnd', - 'scrollMarginInlineStart', - 'scrollMarginLeft', - 'scrollMarginRight', - 'scrollMarginTop', - 'scrollPaddingBlockEnd', - 'scrollPaddingBlockStart', - 'scrollPaddingBottom', - 'scrollPaddingInlineEnd', - 'scrollPaddingInlineStart', - 'scrollPaddingLeft', - 'scrollPaddingRight', - 'scrollPaddingTop', - 'scrollSnapMarginBottom', - 'scrollSnapMarginLeft', - 'scrollSnapMarginRight', - 'scrollSnapMarginTop', - 'shapeMargin', - 'tabSize', - 'textDecorationThickness', - 'textIndent', - 'textUnderlineOffset', - 'top', - 'transformOrigin', - 'translate', - 'verticalAlign', - 'width', - 'wordSpacing', - 'border', - 'borderBlock', - 'borderBlockEnd', - 'borderBlockStart', - 'borderBottom', - 'borderLeft', - 'borderRadius', - 'borderRight', - 'borderTop', - 'borderWidth', - 'columnRule', - 'containIntrinsicSize', - 'gap', - 'inset', - 'insetBlock', - 'insetInline', - 'margin', - 'marginBlock', - 'marginVertical', - 'marginInline', - 'marginHorizontal', - 'offset', - 'outline', - 'padding', - 'paddingBlock', - 'paddingVertical', - 'paddingInline', - 'paddingHorizontal', - 'scrollMargin', - 'scrollMarginBlock', - 'scrollMarginInline', - 'scrollPadding', - 'scrollPaddingBlock', - 'scrollPaddingInline', - 'scrollSnapMargin', - ]); \ No newline at end of file +import normalizeValue from './utils/normalize-value'; + +/** + * Convert a CSS value in JS to the final CSS string value + */ +export default function transformValue( + key: string, + rawValue: string | number, + options: StyleXOptions, +): string { + const value = + typeof rawValue === 'number' + ? String(Math.round(rawValue * 10000) / 10000) + getNumberSuffix(key) + : rawValue; + + if ( + (key === 'content' || + key === 'hyphenateCharacter' || + key === 'hyphenate-character') && + typeof value === 'string' + ) { + const val = value.trim(); + + const cssContentFunctions = [ + 'attr', + 'counter', + 'counters', + 'url', + 'linear-gradient', + 'image-set', + ]; + + const cssContentKeywords = new Set([ + 'normal', + 'none', + 'open-quote', + 'close-quote', + 'no-open-quote', + 'no-close-quote', + 'inherit', + 'initial', + 'revert', + 'revert-layer', + 'unset', + ]); + + const hasCssFunction = cssContentFunctions.some( + (func) => val.startsWith(`${func}(`) && val.endsWith(')'), + ); + + const isKeyword = cssContentKeywords.has(val); + + const containsMixedContent = val.split(' ').some((part) => { + const trimmedPart = part.trim(); + return ( + cssContentFunctions.some((func) => + trimmedPart.startsWith(`${func}(`), + ) || cssContentKeywords.has(trimmedPart) + ); + }); + + // Leave value unchanged if it: + // 1. Is a CSS function + // 2. Is a CSS keyword + // 3. Contains mixed content (functions/keywords with strings) + // 4. Already has matching quotes + if ( + hasCssFunction || + isKeyword || + containsMixedContent || + (val.startsWith('"') && val.endsWith('"')) || + (val.startsWith("'") && val.endsWith("'")) + ) { + return val; + } + return `"${val}"`; + } + + return normalizeValue(value, key, options); +} + +export function getNumberSuffix(key: string): string { + if (unitlessNumberProperties.has(key) || key.startsWith('--')) { + return ''; + } + if (!(key in numberPropertySuffixes)) { + return 'px'; + } + + const suffix = numberPropertySuffixes[key]; + if (suffix == null) { + return 'px'; + } else { + return suffix; + } +} + +const unitlessNumberProperties = new Set([ + 'WebkitLineClamp', + 'animationIterationCount', + 'aspectRatio', + 'borderImageOutset', + 'borderImageSlice', + 'borderImageWidth', + 'counterSet', + 'counterReset', + 'columnCount', + 'flex', + 'flexGrow', + 'flexShrink', + 'flexOrder', + 'gridRow', + 'gridRowStart', + 'gridRowEnd', + 'gridColumn', + 'gridColumnStart', + 'gridColumnEnd', + 'gridArea', + 'fontWeight', + 'hyphenateLimitChars', + 'lineClamp', + 'lineHeight', + 'maskBorderOutset', + 'maskBorderSlice', + 'maskBorderWidth', + 'opacity', + 'order', + 'orphans', + 'tabSize', + 'widows', + 'zIndex', + 'fillOpacity', + 'floodOpacity', + 'rotate', + 'scale', + 'shapeImageThreshold', + 'stopOpacity', + 'strokeDasharray', + 'strokeDashoffset', + 'strokeMiterlimit', + 'strokeOpacity', + 'strokeWidth', + 'scale', + 'mathDepth', +]); + +// List of properties that have custom suffixes for numbers +const numberPropertySuffixes: { +[key: string]: string } = { + animationDelay: 'ms', + animationDuration: 'ms', + transitionDelay: 'ms', + transitionDuration: 'ms', + voiceDuration: 'ms', +}; + +export const timeUnits: Set = new Set( + Object.keys(numberPropertySuffixes), +); + +export const lengthUnits: Set = new Set([ + 'backgroundPositionX', + 'backgroundPositionY', + 'blockSize', + 'borderBlockEndWidth', + 'borderBlockStartWidth', + 'borderBlockWidth', + 'borderVerticalWidth', + 'borderVerticalWidth', + 'borderBottomLeftRadius', + 'borderBottomRightRadius', + 'borderBottomWidth', + 'borderEndEndRadius', + 'borderEndStartRadius', + 'borderInlineEndWidth', + 'borderEndWidth', + 'borderInlineStartWidth', + 'borderStartWidth', + 'borderInlineWidth', + 'borderHorizontalWidth', + 'borderLeftWidth', + 'borderRightWidth', + 'borderSpacing', + 'borderStartEndRadius', + 'borderStartStartRadius', + 'borderTopLeftRadius', + 'borderTopRightRadius', + 'borderTopWidth', + 'bottom', + 'columnGap', + 'columnRuleWidth', + 'columnWidth', + 'containIntrinsicBlockSize', + 'containIntrinsicHeight', + 'containIntrinsicInlineSize', + 'containIntrinsicWidth', + 'flexBasis', + 'fontSize', + 'fontSmooth', + 'height', + 'inlineSize', + 'insetBlockEnd', + 'insetBlockStart', + 'insetInlineEnd', + 'insetInlineStart', + 'left', + 'letterSpacing', + 'marginBlockEnd', + 'marginBlockStart', + 'marginBottom', + 'marginInlineEnd', + 'marginEnd', + 'marginInlineStart', + 'marginStart', + 'marginLeft', + 'marginRight', + 'marginTop', + 'maxBlockSize', + 'maxHeight', + 'maxInlineSize', + 'maxWidth', + 'minBlockSize', + 'minHeight', + 'minInlineSize', + 'minWidth', + 'offsetDistance', + 'outlineOffset', + 'outlineWidth', + 'overflowClipMargin', + 'paddingBlockEnd', + 'paddingBlockStart', + 'paddingBottom', + 'paddingInlineEnd', + 'paddingEnd', + 'paddingInlineStart', + 'paddingStart', + 'paddingLeft', + 'paddingRight', + 'paddingTop', + 'perspective', + 'right', + 'rowGap', + 'scrollMarginBlockEnd', + 'scrollMarginBlockStart', + 'scrollMarginBottom', + 'scrollMarginInlineEnd', + 'scrollMarginInlineStart', + 'scrollMarginLeft', + 'scrollMarginRight', + 'scrollMarginTop', + 'scrollPaddingBlockEnd', + 'scrollPaddingBlockStart', + 'scrollPaddingBottom', + 'scrollPaddingInlineEnd', + 'scrollPaddingInlineStart', + 'scrollPaddingLeft', + 'scrollPaddingRight', + 'scrollPaddingTop', + 'scrollSnapMarginBottom', + 'scrollSnapMarginLeft', + 'scrollSnapMarginRight', + 'scrollSnapMarginTop', + 'shapeMargin', + 'tabSize', + 'textDecorationThickness', + 'textIndent', + 'textUnderlineOffset', + 'top', + 'transformOrigin', + 'translate', + 'verticalAlign', + 'width', + 'wordSpacing', + 'border', + 'borderBlock', + 'borderBlockEnd', + 'borderBlockStart', + 'borderBottom', + 'borderLeft', + 'borderRadius', + 'borderRight', + 'borderTop', + 'borderWidth', + 'columnRule', + 'containIntrinsicSize', + 'gap', + 'inset', + 'insetBlock', + 'insetInline', + 'margin', + 'marginBlock', + 'marginVertical', + 'marginInline', + 'marginHorizontal', + 'offset', + 'outline', + 'padding', + 'paddingBlock', + 'paddingVertical', + 'paddingInline', + 'paddingHorizontal', + 'scrollMargin', + 'scrollMarginBlock', + 'scrollMarginInline', + 'scrollPadding', + 'scrollPaddingBlock', + 'scrollPaddingInline', + 'scrollSnapMargin', +]); From bd44f72d0d48fb7ec09c7c649cad88fddb6f69e5 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Wed, 27 Nov 2024 15:17:02 -0600 Subject: [PATCH 04/17] PR changes after review, fixed transform value function --- .../__tests__/stylex-transform-value.js | 58 +++++++------------ packages/shared/src/transform-value.js | 49 +++++----------- 2 files changed, 35 insertions(+), 72 deletions(-) diff --git a/packages/shared/__tests__/stylex-transform-value.js b/packages/shared/__tests__/stylex-transform-value.js index f4f90a01b..acbf9b1de 100644 --- a/packages/shared/__tests__/stylex-transform-value.js +++ b/packages/shared/__tests__/stylex-transform-value.js @@ -11,23 +11,19 @@ import transformValue from '../src/transform-value'; describe('transformValue content property tests', () => { test('preserves CSS functions without quotes', () => { const functions = [ - ['counters(div, ".")', 'counters(div, ".")'], - ['counter(chapter)', 'counter(chapter)'], - ['counter(chapter, upper-roman)', 'counter(chapter, upper-roman)'], - ['attr(href)', 'attr(href)'], - ['url(image.jpg)', 'url(image.jpg)'], - [ - 'linear-gradient(#e66465, #9198e5)', - 'linear-gradient(#e66465, #9198e5)', - ], - [ - 'image-set("image1x.png" 1x, "image2x.png" 2x)', - 'image-set("image1x.png" 1x, "image2x.png" 2x)', - ], + 'counters(div, ".")', + 'counter(chapter)', + 'counter(chapter, upper-roman)', + 'attr(href)', + 'url(image.jpg)', + 'linear-gradient(#e66465, #9198e5)', + 'image-set("image1x.png" 1x, "image2x.png" 2x)', + '"prefix"attr(href)', + 'url(foo.jpg)attr(alt)', ]; - functions.forEach(([input, expected]) => { - expect(transformValue('content', input, {})).toBe(expected); + functions.forEach(input => { + expect(transformValue('content', input, {})).toBe(input); }); }); @@ -46,24 +42,24 @@ describe('transformValue content property tests', () => { 'unset', ]; - keywords.forEach((keyword) => { + keywords.forEach(keyword => { expect(transformValue('content', keyword, {})).toBe(keyword); }); }); test('handles mixed content values', () => { const mixedValues = [ - ['open-quote counter(chapter)', 'open-quote counter(chapter)'], - ['"prefix" url(image.jpg)', '"prefix" url(image.jpg)'], - ['url("test.png") / "Alt text"', 'url("test.png") / "Alt text"'], - [ - 'open-quote counter(chapter) close-quote', - 'open-quote counter(chapter) close-quote', - ], + 'open-quote counter(chapter)', + '"prefix"url(image.jpg)', + 'url("test.png")/"Alt text"', + 'open-quotecounter(chapter)close-quote', + 'attr(href)normal', + '"text"attr(href)"more text"', + 'counter(x)"text"counter(y)', ]; - mixedValues.forEach(([input, expected]) => { - expect(transformValue('content', input, {})).toBe(expected); + mixedValues.forEach(input => { + expect(transformValue('content', input, {})).toBe(input); }); }); @@ -78,16 +74,4 @@ describe('transformValue content property tests', () => { expect(transformValue('content', input, {})).toBe(expected); }); }); - - test('preserves existing quotes', () => { - const quotedStrings = [ - ['"already quoted"', '"already quoted"'], - ["'single quotes'", "'single quotes'"], - ['"mixed "nested" quotes"', '"mixed "nested" quotes"'], - ]; - - quotedStrings.forEach(([input, expected]) => { - expect(transformValue('content', input, {})).toBe(expected); - }); - }); }); diff --git a/packages/shared/src/transform-value.js b/packages/shared/src/transform-value.js index b96101b5d..770784a72 100644 --- a/packages/shared/src/transform-value.js +++ b/packages/shared/src/transform-value.js @@ -33,12 +33,12 @@ export default function transformValue( const val = value.trim(); const cssContentFunctions = [ - 'attr', - 'counter', - 'counters', - 'url', - 'linear-gradient', - 'image-set', + 'attr(', + 'counter(', + 'counters(', + 'url(', + 'linear-gradient(', + 'image-set(', ]; const cssContentKeywords = new Set([ @@ -55,33 +55,12 @@ export default function transformValue( 'unset', ]); - const hasCssFunction = cssContentFunctions.some( - (func) => val.startsWith(`${func}(`) && val.endsWith(')'), - ); - + const isCssFunction = cssContentFunctions.some(func => val.includes(func)); const isKeyword = cssContentKeywords.has(val); + const hasMatchingQuotes = + (val.match(/"/g)?.length >= 2) || (val.match(/'/g)?.length >= 2); - const containsMixedContent = val.split(' ').some((part) => { - const trimmedPart = part.trim(); - return ( - cssContentFunctions.some((func) => - trimmedPart.startsWith(`${func}(`), - ) || cssContentKeywords.has(trimmedPart) - ); - }); - - // Leave value unchanged if it: - // 1. Is a CSS function - // 2. Is a CSS keyword - // 3. Contains mixed content (functions/keywords with strings) - // 4. Already has matching quotes - if ( - hasCssFunction || - isKeyword || - containsMixedContent || - (val.startsWith('"') && val.endsWith('"')) || - (val.startsWith("'") && val.endsWith("'")) - ) { + if (isCssFunction || isKeyword || hasMatchingQuotes) { return val; } return `"${val}"`; @@ -158,10 +137,10 @@ const unitlessNumberProperties = new Set([ // List of properties that have custom suffixes for numbers const numberPropertySuffixes: { +[key: string]: string } = { animationDelay: 'ms', - animationDuration: 'ms', - transitionDelay: 'ms', - transitionDuration: 'ms', - voiceDuration: 'ms', + animationDuration: 'ms', + transitionDelay: 'ms', + transitionDuration: 'ms', + voiceDuration: 'ms', }; export const timeUnits: Set = new Set( From 628425716a85ff78b2f1d39f80419bfe99d82f3a Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Wed, 27 Nov 2024 15:21:27 -0600 Subject: [PATCH 05/17] lint and prettier --- .../shared/__tests__/stylex-transform-value.js | 6 +++--- packages/shared/src/transform-value.js | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/shared/__tests__/stylex-transform-value.js b/packages/shared/__tests__/stylex-transform-value.js index acbf9b1de..779301677 100644 --- a/packages/shared/__tests__/stylex-transform-value.js +++ b/packages/shared/__tests__/stylex-transform-value.js @@ -22,7 +22,7 @@ describe('transformValue content property tests', () => { 'url(foo.jpg)attr(alt)', ]; - functions.forEach(input => { + functions.forEach((input) => { expect(transformValue('content', input, {})).toBe(input); }); }); @@ -42,7 +42,7 @@ describe('transformValue content property tests', () => { 'unset', ]; - keywords.forEach(keyword => { + keywords.forEach((keyword) => { expect(transformValue('content', keyword, {})).toBe(keyword); }); }); @@ -58,7 +58,7 @@ describe('transformValue content property tests', () => { 'counter(x)"text"counter(y)', ]; - mixedValues.forEach(input => { + mixedValues.forEach((input) => { expect(transformValue('content', input, {})).toBe(input); }); }); diff --git a/packages/shared/src/transform-value.js b/packages/shared/src/transform-value.js index 770784a72..6a8e7a189 100644 --- a/packages/shared/src/transform-value.js +++ b/packages/shared/src/transform-value.js @@ -55,10 +55,12 @@ export default function transformValue( 'unset', ]); - const isCssFunction = cssContentFunctions.some(func => val.includes(func)); + const isCssFunction = cssContentFunctions.some((func) => + val.includes(func), + ); const isKeyword = cssContentKeywords.has(val); const hasMatchingQuotes = - (val.match(/"/g)?.length >= 2) || (val.match(/'/g)?.length >= 2); + val.match(/"/g)?.length >= 2 || val.match(/'/g)?.length >= 2; if (isCssFunction || isKeyword || hasMatchingQuotes) { return val; @@ -137,10 +139,10 @@ const unitlessNumberProperties = new Set([ // List of properties that have custom suffixes for numbers const numberPropertySuffixes: { +[key: string]: string } = { animationDelay: 'ms', - animationDuration: 'ms', - transitionDelay: 'ms', - transitionDuration: 'ms', - voiceDuration: 'ms', + animationDuration: 'ms', + transitionDelay: 'ms', + transitionDuration: 'ms', + voiceDuration: 'ms', }; export const timeUnits: Set = new Set( From 2608e81cb9ab488c4885c9afe28d976d5c6f3691 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Wed, 27 Nov 2024 15:30:28 -0600 Subject: [PATCH 06/17] Final fixes for flow test --- packages/shared/src/transform-value.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/shared/src/transform-value.js b/packages/shared/src/transform-value.js index 6a8e7a189..c40b028aa 100644 --- a/packages/shared/src/transform-value.js +++ b/packages/shared/src/transform-value.js @@ -60,7 +60,8 @@ export default function transformValue( ); const isKeyword = cssContentKeywords.has(val); const hasMatchingQuotes = - val.match(/"/g)?.length >= 2 || val.match(/'/g)?.length >= 2; + (val.match(/"/g)?.length ?? 0) >= 2 || + (val.match(/'/g)?.length ?? 0) >= 2; if (isCssFunction || isKeyword || hasMatchingQuotes) { return val; From 86b44896f428a484768b7e1d6dc8adda9eac2204 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Mon, 16 Dec 2024 19:51:45 +0100 Subject: [PATCH 07/17] First approach on aliases, unit test added --- .../stylex-transform-alias-config-test.js | 158 ++++++++++++++++++ .../babel-plugin/src/utils/state-manager.js | 110 ++++++++++-- 2 files changed, 257 insertions(+), 11 deletions(-) create mode 100644 packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js diff --git a/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js b/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js new file mode 100644 index 000000000..617ff4936 --- /dev/null +++ b/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js @@ -0,0 +1,158 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * + */ + + +'use strict'; + +import path from 'path'; +import fs from 'fs'; +import os from 'os'; +import StateManager from '../src/utils/state-manager'; + +describe('StyleX Alias Configuration', () => { + let tmpDir; + let state; + + beforeEach(() => { + // Create a temporary directory for our test files + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'stylex-test-')); + + // Create a mock babel state + state = { + file: { + metadata: {}, + }, + filename: path.join(tmpDir, 'src/components/Button.js'), + }; + }); + + afterEach(() => { + // Clean up temporary directory + fs.rmSync(tmpDir, { recursive: true, force: true }); + }); + + const setupFiles = (files) => { + for (const [filePath, content] of Object.entries(files)) { + const fullPath = path.join(tmpDir, filePath); + fs.mkdirSync(path.dirname(fullPath), { recursive: true }); + fs.writeFileSync(fullPath, JSON.stringify(content, null, 2)); + } + }; + + test('discovers aliases from package.json', () => { + setupFiles({ + 'package.json': { + name: 'test-package', + stylex: { + aliases: { + '@components': './src/components', + '@utils/*': ['./src/utils/*'], + }, + }, + }, + }); + + const manager = new StateManager(state); + + expect(manager.options.aliases).toEqual({ + '@components': ['./src/components'], + '@utils/*': ['./src/utils/*'], + }); + }); + + test('discovers aliases from tsconfig.json', () => { + setupFiles({ + 'package.json': { name: 'test-package' }, + 'tsconfig.json': { + compilerOptions: { + baseUrl: '.', + paths: { + '@components/*': ['src/components/*'], + '@utils/*': ['src/utils/*'], + }, + }, + }, + }); + + const manager = new StateManager(state); + + expect(manager.options.aliases).toEqual({ + '@components': ['src/components'], + '@utils': ['src/utils'], + }); + }); + + test('merges aliases from both package.json and tsconfig.json', () => { + setupFiles({ + 'package.json': { + name: 'test-package', + stylex: { + aliases: { + '@components': './src/components', + }, + }, + }, + 'tsconfig.json': { + compilerOptions: { + baseUrl: '.', + paths: { + '@utils/*': ['src/utils/*'], + }, + }, + }, + }); + + const manager = new StateManager(state); + + expect(manager.options.aliases).toEqual({ + '@components': ['./src/components'], + '@utils': ['src/utils'], + }); + }); + + test('manual configuration overrides discovered aliases', () => { + setupFiles({ + 'package.json': { + name: 'test-package', + stylex: { + aliases: { + '@components': './src/components', + }, + }, + }, + }); + + state.opts = { + aliases: { + '@components': './custom/path', + }, + }; + + const manager = new StateManager(state); + + expect(manager.options.aliases).toEqual({ + '@components': ['./custom/path'], + }); + }); + + test('handles missing configuration files gracefully', () => { + const manager = new StateManager(state); + expect(manager.options.aliases).toBeNull(); + }); + + test('handles invalid JSON files gracefully', () => { + setupFiles({ + 'package.json': '{invalid json', + 'tsconfig.json': '{also invalid', + }); + + const manager = new StateManager(state); + expect(manager.options.aliases).toBeNull(); + }); +}); diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index 6918ff488..a9d4813fd 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -23,6 +23,7 @@ import { addDefault, addNamed } from '@babel/helper-module-imports'; import type { ImportOptions } from '@babel/helper-module-imports'; import * as pathUtils from '../babel-path-utils'; import { buildResolver } from 'esm-resolve'; +import JSON5 from 'json5'; type ImportAdditionOptions = Omit< Partial, @@ -262,17 +263,7 @@ export default class StateManager { 'options.aliases', ); - const aliases: StyleXStateOptions['aliases'] = - aliasesOption == null - ? aliasesOption - : Object.fromEntries( - Object.entries(aliasesOption).map(([key, value]) => { - if (typeof value === 'string') { - return [key, [value]]; - } - return [key, value]; - }), - ); + const aliases = this.loadAliases(aliasesOption); const opts: StyleXStateOptions = { aliases, @@ -623,6 +614,103 @@ export default class StateManager { ): void { this.styleVarsToKeep.add(memberExpression); } + + loadAliases( + manualAliases: ?$ReadOnly<{ [string]: string | $ReadOnlyArray }>, + ): ?$ReadOnly<{ [string]: $ReadOnlyArray }> { + if (!this.filename) { + return manualAliases ? this.normalizeAliases(manualAliases) : null; + } + + let packageAliases = {}; + let tsconfigAliases = {}; + const projectDir = this.findProjectRoot(this.filename); + + // Load aliases from package.json + try { + const packageJsonPath = path.join(projectDir, 'package.json'); + if (fs.existsSync(packageJsonPath)) { + const packageJson = JSON.parse( + fs.readFileSync(packageJsonPath, 'utf8'), + ); + if (packageJson.stylex?.aliases) { + packageAliases = packageJson.stylex.aliases; + } + } + } catch (err) { + console.warn('Failed to load aliases from package.json:', err.message); + } + + // Load aliases from tsconfig.json + try { + const tsconfigPath = path.join(projectDir, 'tsconfig.json'); + if (fs.existsSync(tsconfigPath)) { + const tsconfig = JSON5.parse(fs.readFileSync(tsconfigPath, 'utf8')); + const baseUrl = tsconfig.compilerOptions?.baseUrl || '.'; + if (tsconfig.compilerOptions?.paths) { + tsconfigAliases = Object.fromEntries( + Object.entries(tsconfig.compilerOptions.paths).map( + ([key, value]) => [ + key.replace(/\/\*$/, ''), + Array.isArray(value) + ? value.map((p) => + this.normalizePath( + path.join(baseUrl, p.replace(/\/\*$/, '')), + ), + ) + : [ + this.normalizePath( + path.join(baseUrl, value.replace(/\/\*$/, '')), + ), + ], + ], + ), + ); + } + } + } catch (err) { + console.warn('Failed to load aliases from tsconfig.json:', err.message); + } + + // Merge aliases with priority: manual > package.json > tsconfig.json + const mergedAliases = { + ...tsconfigAliases, + ...packageAliases, + ...(manualAliases || {}), + }; + + return Object.keys(mergedAliases).length > 0 + ? this.normalizeAliases(mergedAliases) + : null; + } + + normalizeAliases( + aliases: $ReadOnly<{ [string]: string | $ReadOnlyArray }>, + ): $ReadOnly<{ [string]: $ReadOnlyArray }> { + return Object.fromEntries( + Object.entries(aliases).map(([key, value]) => [ + key, + Array.isArray(value) + ? value.map((p) => this.normalizePath(p)) + : [this.normalizePath(value)], + ]), + ); + } + + findProjectRoot(filePath: string): string { + const dir = path.dirname(filePath); + if (fs.existsSync(path.join(dir, 'package.json'))) { + return dir; + } + if (dir === path.parse(dir).root) { + return dir; + } + return this.findProjectRoot(dir); + } + + normalizePath(filePath: string): string { + return filePath.split(path.sep).join('/'); + } } function possibleAliasedPaths( From d098d0068174bc6232e0f782879dc95a013aa285 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Mon, 16 Dec 2024 19:58:07 +0100 Subject: [PATCH 08/17] First approach on aliases, unit test added --- packages/babel-plugin/src/utils/state-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index a9d4813fd..265163db4 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -672,7 +672,7 @@ export default class StateManager { console.warn('Failed to load aliases from tsconfig.json:', err.message); } - // Merge aliases with priority: manual > package.json > tsconfig.json + // Merge aliases in priority: manual > package.json > tsconfig.json const mergedAliases = { ...tsconfigAliases, ...packageAliases, From 4cdd041da64b01a530b940a14d744e1af3b2d455 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Tue, 17 Dec 2024 19:40:16 +0100 Subject: [PATCH 09/17] Considered deno.json, fix pr review comments --- package-lock.json | 4 ++ package.json | 3 + .../stylex-transform-alias-config-test.js | 60 ++++++++++++------- .../babel-plugin/src/utils/state-manager.js | 57 +++++++++++++----- 4 files changed, 88 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6b113c438..3c80b98a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,9 @@ "packages/*", "apps/*" ], + "dependencies": { + "json5": "^2.2.3" + }, "devDependencies": { "@babel/cli": "^7.25.7", "@babel/core": "^7.25.8", @@ -19743,6 +19746,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", "bin": { "json5": "lib/cli.js" }, diff --git a/package.json b/package.json index 572ecdd7e..45414f856 100644 --- a/package.json +++ b/package.json @@ -77,5 +77,8 @@ } } ] + }, + "dependencies": { + "json5": "^2.2.3" } } diff --git a/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js b/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js index 617ff4936..a14293acd 100644 --- a/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js +++ b/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js @@ -45,15 +45,13 @@ describe('StyleX Alias Configuration', () => { } }; - test('discovers aliases from package.json', () => { + test('discovers aliases from package.json imports', () => { setupFiles({ 'package.json': { name: 'test-package', - stylex: { - aliases: { - '@components': './src/components', - '@utils/*': ['./src/utils/*'], - }, + imports: { + '#components': './src/components', + '#utils/*': './src/utils/*', }, }, }); @@ -61,8 +59,8 @@ describe('StyleX Alias Configuration', () => { const manager = new StateManager(state); expect(manager.options.aliases).toEqual({ - '@components': ['./src/components'], - '@utils/*': ['./src/utils/*'], + 'components': ['./src/components'], + 'utils/*': ['./src/utils/*'], }); }); @@ -88,14 +86,31 @@ describe('StyleX Alias Configuration', () => { }); }); - test('merges aliases from both package.json and tsconfig.json', () => { + test('discovers aliases from deno.json', () => { + setupFiles({ + 'package.json': { name: 'test-package' }, + 'deno.json': { + imports: { + '@components/': './src/components/', + '@utils/': './src/utils/', + }, + }, + }); + + const manager = new StateManager(state); + + expect(manager.options.aliases).toEqual({ + '@components/': ['./src/components/'], + '@utils/': ['./src/utils/'], + }); + }); + + test('merges aliases from all config files', () => { setupFiles({ 'package.json': { name: 'test-package', - stylex: { - aliases: { - '@components': './src/components', - }, + imports: { + '#components': './src/components', }, }, 'tsconfig.json': { @@ -106,13 +121,19 @@ describe('StyleX Alias Configuration', () => { }, }, }, + 'deno.json': { + imports: { + '@styles/': './src/styles/', + }, + }, }); const manager = new StateManager(state); expect(manager.options.aliases).toEqual({ - '@components': ['./src/components'], + 'components': ['./src/components'], '@utils': ['src/utils'], + '@styles/': ['./src/styles/'], }); }); @@ -120,24 +141,22 @@ describe('StyleX Alias Configuration', () => { setupFiles({ 'package.json': { name: 'test-package', - stylex: { - aliases: { - '@components': './src/components', - }, + imports: { + '#components': './src/components', }, }, }); state.opts = { aliases: { - '@components': './custom/path', + 'components': './custom/path', }, }; const manager = new StateManager(state); expect(manager.options.aliases).toEqual({ - '@components': ['./custom/path'], + 'components': ['./custom/path'], }); }); @@ -150,6 +169,7 @@ describe('StyleX Alias Configuration', () => { setupFiles({ 'package.json': '{invalid json', 'tsconfig.json': '{also invalid', + 'deno.json': '{more invalid', }); const manager = new StateManager(state); diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index 265163db4..a7b019524 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -624,17 +624,34 @@ export default class StateManager { let packageAliases = {}; let tsconfigAliases = {}; - const projectDir = this.findProjectRoot(this.filename); + let denoAliases = {}; - // Load aliases from package.json + const pkgInfo = this.getPackageNameAndPath(this.filename); + if (!pkgInfo) { + return manualAliases ? this.normalizeAliases(manualAliases) : null; + } + + const [_packageName, projectDir] = pkgInfo; + + // Load aliases from package.json imports field try { const packageJsonPath = path.join(projectDir, 'package.json'); if (fs.existsSync(packageJsonPath)) { const packageJson = JSON.parse( fs.readFileSync(packageJsonPath, 'utf8'), ); - if (packageJson.stylex?.aliases) { - packageAliases = packageJson.stylex.aliases; + + // Handle Node.js native imports + const imports = packageJson.imports; + if (imports && typeof imports === 'object') { + packageAliases = Object.fromEntries( + Object.entries(imports) + .filter(([key]) => key.startsWith('#')) + .map(([key, value]) => [ + key.slice(1), + Array.isArray(value) ? value : [value], + ]) + ); } } } catch (err) { @@ -672,8 +689,27 @@ export default class StateManager { console.warn('Failed to load aliases from tsconfig.json:', err.message); } - // Merge aliases in priority: manual > package.json > tsconfig.json + // Load aliases from deno.json + try { + const denoConfigPath = path.join(projectDir, 'deno.json'); + if (fs.existsSync(denoConfigPath)) { + const denoConfig = JSON5.parse(fs.readFileSync(denoConfigPath, 'utf8')); + if (denoConfig.imports) { + denoAliases = Object.fromEntries( + Object.entries(denoConfig.imports).map(([key, value]) => [ + key, + Array.isArray(value) ? value : [value], + ]) + ); + } + } + } catch (err) { + console.warn('Failed to load aliases from deno.json:', err.message); + } + + // Merge aliases in priority: manual > package.json > tsconfig.json > deno.json const mergedAliases = { + ...denoAliases, ...tsconfigAliases, ...packageAliases, ...(manualAliases || {}), @@ -697,17 +733,6 @@ export default class StateManager { ); } - findProjectRoot(filePath: string): string { - const dir = path.dirname(filePath); - if (fs.existsSync(path.join(dir, 'package.json'))) { - return dir; - } - if (dir === path.parse(dir).root) { - return dir; - } - return this.findProjectRoot(dir); - } - normalizePath(filePath: string): string { return filePath.split(path.sep).join('/'); } From 58e52715188c6435b7d71df19efee26835b8a27d Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Tue, 17 Dec 2024 19:42:12 +0100 Subject: [PATCH 10/17] Fixed test fail --- .../__tests__/stylex-transform-alias-config-test.js | 9 ++++----- packages/babel-plugin/src/utils/state-manager.js | 10 +++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js b/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js index a14293acd..64c86eaa9 100644 --- a/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js +++ b/packages/babel-plugin/__tests__/stylex-transform-alias-config-test.js @@ -7,7 +7,6 @@ * */ - 'use strict'; import path from 'path'; @@ -59,7 +58,7 @@ describe('StyleX Alias Configuration', () => { const manager = new StateManager(state); expect(manager.options.aliases).toEqual({ - 'components': ['./src/components'], + components: ['./src/components'], 'utils/*': ['./src/utils/*'], }); }); @@ -131,7 +130,7 @@ describe('StyleX Alias Configuration', () => { const manager = new StateManager(state); expect(manager.options.aliases).toEqual({ - 'components': ['./src/components'], + components: ['./src/components'], '@utils': ['src/utils'], '@styles/': ['./src/styles/'], }); @@ -149,14 +148,14 @@ describe('StyleX Alias Configuration', () => { state.opts = { aliases: { - 'components': './custom/path', + components: './custom/path', }, }; const manager = new StateManager(state); expect(manager.options.aliases).toEqual({ - 'components': ['./custom/path'], + components: ['./custom/path'], }); }); diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index a7b019524..b630d330f 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -640,17 +640,17 @@ export default class StateManager { const packageJson = JSON.parse( fs.readFileSync(packageJsonPath, 'utf8'), ); - + // Handle Node.js native imports const imports = packageJson.imports; if (imports && typeof imports === 'object') { packageAliases = Object.fromEntries( Object.entries(imports) - .filter(([key]) => key.startsWith('#')) + .filter(([key]) => key.startsWith('#')) .map(([key, value]) => [ - key.slice(1), + key.slice(1), Array.isArray(value) ? value : [value], - ]) + ]), ); } } @@ -699,7 +699,7 @@ export default class StateManager { Object.entries(denoConfig.imports).map(([key, value]) => [ key, Array.isArray(value) ? value : [value], - ]) + ]), ); } } From 65be7f88a58fca0c6c91de7a3f11dbe7c84eb340 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Tue, 17 Dec 2024 20:08:50 +0100 Subject: [PATCH 11/17] Fixed flow and prettier tests failing --- flow-typed/json5.js | 25 +++++++++++++++++++ .../babel-plugin/src/utils/state-manager.js | 11 ++++---- 2 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 flow-typed/json5.js diff --git a/flow-typed/json5.js b/flow-typed/json5.js new file mode 100644 index 000000000..7c9921c44 --- /dev/null +++ b/flow-typed/json5.js @@ -0,0 +1,25 @@ +// @flow +type TSConfig = { + compilerOptions?: { + baseUrl?: string, + paths?: { [key: string]: Array }, + }, +}; + +type DenoConfig = { + imports?: { [key: string]: string | Array }, +}; + +type PackageJSON = { + name?: string, + imports?: { [key: string]: string | Array }, +}; + +type ConfigType = TSConfig | DenoConfig | PackageJSON; + +declare module 'json5' { + declare module.exports: { + parse: (input: string) => mixed, + stringify: (value: mixed, replacer?: ?Function | ?Array, space?: string | number) => string, + }; +} \ No newline at end of file diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index b630d330f..d16331c55 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -637,9 +637,8 @@ export default class StateManager { try { const packageJsonPath = path.join(projectDir, 'package.json'); if (fs.existsSync(packageJsonPath)) { - const packageJson = JSON.parse( - fs.readFileSync(packageJsonPath, 'utf8'), - ); + const rawConfig = JSON5.parse(fs.readFileSync(packageJsonPath, 'utf8')); + const packageJson = ((rawConfig: any): PackageJSON); // Handle Node.js native imports const imports = packageJson.imports; @@ -662,7 +661,8 @@ export default class StateManager { try { const tsconfigPath = path.join(projectDir, 'tsconfig.json'); if (fs.existsSync(tsconfigPath)) { - const tsconfig = JSON5.parse(fs.readFileSync(tsconfigPath, 'utf8')); + const rawConfig = JSON5.parse(fs.readFileSync(tsconfigPath, 'utf8')); + const tsconfig = ((rawConfig: any): TSConfig); const baseUrl = tsconfig.compilerOptions?.baseUrl || '.'; if (tsconfig.compilerOptions?.paths) { tsconfigAliases = Object.fromEntries( @@ -693,7 +693,8 @@ export default class StateManager { try { const denoConfigPath = path.join(projectDir, 'deno.json'); if (fs.existsSync(denoConfigPath)) { - const denoConfig = JSON5.parse(fs.readFileSync(denoConfigPath, 'utf8')); + const rawConfig = JSON5.parse(fs.readFileSync(denoConfigPath, 'utf8')); + const denoConfig = ((rawConfig: any): DenoConfig); if (denoConfig.imports) { denoAliases = Object.fromEntries( Object.entries(denoConfig.imports).map(([key, value]) => [ From 2ffdd78df67f12c01d8908ce195b21c5a3455971 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Tue, 17 Dec 2024 20:17:19 +0100 Subject: [PATCH 12/17] Fixed test fail --- flow-typed/json5.js | 8 ++++++-- packages/babel-plugin/src/utils/state-manager.js | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/flow-typed/json5.js b/flow-typed/json5.js index 7c9921c44..56187dfda 100644 --- a/flow-typed/json5.js +++ b/flow-typed/json5.js @@ -20,6 +20,10 @@ type ConfigType = TSConfig | DenoConfig | PackageJSON; declare module 'json5' { declare module.exports: { parse: (input: string) => mixed, - stringify: (value: mixed, replacer?: ?Function | ?Array, space?: string | number) => string, + stringify: ( + value: mixed, + replacer?: ?Function | ?Array, + space?: string | number, + ) => string, }; -} \ No newline at end of file +} diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index d16331c55..e52d978dd 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -638,7 +638,7 @@ export default class StateManager { const packageJsonPath = path.join(projectDir, 'package.json'); if (fs.existsSync(packageJsonPath)) { const rawConfig = JSON5.parse(fs.readFileSync(packageJsonPath, 'utf8')); - const packageJson = ((rawConfig: any): PackageJSON); + const packageJson = (rawConfig: any) as PackageJSON; // Handle Node.js native imports const imports = packageJson.imports; @@ -662,7 +662,7 @@ export default class StateManager { const tsconfigPath = path.join(projectDir, 'tsconfig.json'); if (fs.existsSync(tsconfigPath)) { const rawConfig = JSON5.parse(fs.readFileSync(tsconfigPath, 'utf8')); - const tsconfig = ((rawConfig: any): TSConfig); + const tsconfig = (rawConfig: any) as TSConfig; const baseUrl = tsconfig.compilerOptions?.baseUrl || '.'; if (tsconfig.compilerOptions?.paths) { tsconfigAliases = Object.fromEntries( @@ -694,7 +694,7 @@ export default class StateManager { const denoConfigPath = path.join(projectDir, 'deno.json'); if (fs.existsSync(denoConfigPath)) { const rawConfig = JSON5.parse(fs.readFileSync(denoConfigPath, 'utf8')); - const denoConfig = ((rawConfig: any): DenoConfig); + const denoConfig = (rawConfig: any) as DenoConfig; if (denoConfig.imports) { denoAliases = Object.fromEntries( Object.entries(denoConfig.imports).map(([key, value]) => [ From eb2935ff9e3d8bcbb7ca0f6068b03642bf80acfe Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Tue, 17 Dec 2024 20:27:38 +0100 Subject: [PATCH 13/17] flow type error fix --- packages/babel-plugin/src/utils/state-manager.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index e52d978dd..a736f3157 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -638,7 +638,7 @@ export default class StateManager { const packageJsonPath = path.join(projectDir, 'package.json'); if (fs.existsSync(packageJsonPath)) { const rawConfig = JSON5.parse(fs.readFileSync(packageJsonPath, 'utf8')); - const packageJson = (rawConfig: any) as PackageJSON; + const packageJson: PackageJSON = (rawConfig: any); // Handle Node.js native imports const imports = packageJson.imports; @@ -662,7 +662,7 @@ export default class StateManager { const tsconfigPath = path.join(projectDir, 'tsconfig.json'); if (fs.existsSync(tsconfigPath)) { const rawConfig = JSON5.parse(fs.readFileSync(tsconfigPath, 'utf8')); - const tsconfig = (rawConfig: any) as TSConfig; + const tsconfig: TSConfig = (rawConfig: any); const baseUrl = tsconfig.compilerOptions?.baseUrl || '.'; if (tsconfig.compilerOptions?.paths) { tsconfigAliases = Object.fromEntries( @@ -694,7 +694,7 @@ export default class StateManager { const denoConfigPath = path.join(projectDir, 'deno.json'); if (fs.existsSync(denoConfigPath)) { const rawConfig = JSON5.parse(fs.readFileSync(denoConfigPath, 'utf8')); - const denoConfig = (rawConfig: any) as DenoConfig; + const denoConfig: DenoConfig = (rawConfig: any); if (denoConfig.imports) { denoAliases = Object.fromEntries( Object.entries(denoConfig.imports).map(([key, value]) => [ From c1656f2a5e221e6cb11db715ccac8124d7f2ffcb Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Tue, 17 Dec 2024 20:33:48 +0100 Subject: [PATCH 14/17] flow type error fix --- packages/babel-plugin/src/utils/state-manager.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index a736f3157..bbb9b0fa0 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -638,7 +638,7 @@ export default class StateManager { const packageJsonPath = path.join(projectDir, 'package.json'); if (fs.existsSync(packageJsonPath)) { const rawConfig = JSON5.parse(fs.readFileSync(packageJsonPath, 'utf8')); - const packageJson: PackageJSON = (rawConfig: any); + const packageJson = rawConfig as PackageJSON; // Handle Node.js native imports const imports = packageJson.imports; @@ -662,7 +662,7 @@ export default class StateManager { const tsconfigPath = path.join(projectDir, 'tsconfig.json'); if (fs.existsSync(tsconfigPath)) { const rawConfig = JSON5.parse(fs.readFileSync(tsconfigPath, 'utf8')); - const tsconfig: TSConfig = (rawConfig: any); + const tsconfig = rawConfig as TSConfig; const baseUrl = tsconfig.compilerOptions?.baseUrl || '.'; if (tsconfig.compilerOptions?.paths) { tsconfigAliases = Object.fromEntries( @@ -694,7 +694,7 @@ export default class StateManager { const denoConfigPath = path.join(projectDir, 'deno.json'); if (fs.existsSync(denoConfigPath)) { const rawConfig = JSON5.parse(fs.readFileSync(denoConfigPath, 'utf8')); - const denoConfig: DenoConfig = (rawConfig: any); + const denoConfig = rawConfig as DenoConfig; if (denoConfig.imports) { denoAliases = Object.fromEntries( Object.entries(denoConfig.imports).map(([key, value]) => [ From f8e15f1db154bc80f2b287beae5d5be5b97a6ca8 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Tue, 17 Dec 2024 20:40:11 +0100 Subject: [PATCH 15/17] verification functions for casting configs --- .../babel-plugin/src/utils/state-manager.js | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index bbb9b0fa0..156aead5d 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -638,7 +638,10 @@ export default class StateManager { const packageJsonPath = path.join(projectDir, 'package.json'); if (fs.existsSync(packageJsonPath)) { const rawConfig = JSON5.parse(fs.readFileSync(packageJsonPath, 'utf8')); - const packageJson = rawConfig as PackageJSON; + if (!isPackageJSON(rawConfig)) { + throw new Error('Invalid package.json format'); + } + const packageJson = rawConfig; // Handle Node.js native imports const imports = packageJson.imports; @@ -662,7 +665,10 @@ export default class StateManager { const tsconfigPath = path.join(projectDir, 'tsconfig.json'); if (fs.existsSync(tsconfigPath)) { const rawConfig = JSON5.parse(fs.readFileSync(tsconfigPath, 'utf8')); - const tsconfig = rawConfig as TSConfig; + if (!isTSConfig(rawConfig)) { + throw new Error('Invalid tsconfig.json format'); + } + const tsconfig = rawConfig; const baseUrl = tsconfig.compilerOptions?.baseUrl || '.'; if (tsconfig.compilerOptions?.paths) { tsconfigAliases = Object.fromEntries( @@ -694,7 +700,10 @@ export default class StateManager { const denoConfigPath = path.join(projectDir, 'deno.json'); if (fs.existsSync(denoConfigPath)) { const rawConfig = JSON5.parse(fs.readFileSync(denoConfigPath, 'utf8')); - const denoConfig = rawConfig as DenoConfig; + if (!isDenoConfig(rawConfig)) { + throw new Error('Invalid deno.json format'); + } + const denoConfig = rawConfig; if (denoConfig.imports) { denoAliases = Object.fromEntries( Object.entries(denoConfig.imports).map(([key, value]) => [ @@ -878,3 +887,29 @@ const getProgramStatement = (path: NodePath<>): NodePath<> => { } return programPath; }; + +function isPackageJSON(obj: mixed): boolean %checks { + return ( + obj != null && + typeof obj === 'object' && + (!('imports' in obj) || typeof obj.imports === 'object') + ); +} + +function isTSConfig(obj: mixed): boolean %checks { + return ( + obj != null && + typeof obj === 'object' && + 'compilerOptions' in obj && + obj.compilerOptions != null && + typeof obj.compilerOptions === 'object' + ); +} + +function isDenoConfig(obj: mixed): boolean %checks { + return ( + obj != null && + typeof obj === 'object' && + (!('imports' in obj) || typeof obj.imports === 'object') + ); +} From 2875f7e46d53a4dddc9692032cfe3c203e4df901 Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Tue, 17 Dec 2024 20:49:05 +0100 Subject: [PATCH 16/17] verification functions for casting configs --- package-lock.json | 63 +++---------------- .../babel-plugin/src/utils/state-manager.js | 24 ++++--- 2 files changed, 24 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c80b98a4..c5a91145f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27010,6 +27010,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -27025,6 +27026,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -27040,6 +27042,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -27055,6 +27058,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -27070,6 +27074,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -27085,6 +27090,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -27100,6 +27106,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -27115,6 +27122,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -27130,6 +27138,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -45923,60 +45932,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" - }, - "@next/swc-darwin-arm64": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.1.tgz", - "integrity": "sha512-JyxnGCS4qT67hdOKQ0CkgFTp+PXub5W1wsGvIq98TNbF3YEIN7iDekYhYsZzc8Ov0pWEsghQt+tANdidITCLaw==", - "optional": true - }, - "@next/swc-darwin-x64": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.1.tgz", - "integrity": "sha512-625Z7bb5AyIzswF9hvfZWa+HTwFZw+Jn3lOBNZB87lUS0iuCYDHqk3ujuHCkiyPtSC0xFBtYDLcrZ11mF/ap3w==", - "optional": true - }, - "@next/swc-linux-arm64-gnu": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.1.tgz", - "integrity": "sha512-iVpn3KG3DprFXzVHM09kvb//4CNNXBQ9NB/pTm8LO+vnnnaObnzFdS5KM+w1okwa32xH0g8EvZIhoB3fI3mS1g==", - "optional": true - }, - "@next/swc-linux-arm64-musl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.1.tgz", - "integrity": "sha512-mVsGyMxTLWZXyD5sen6kGOTYVOO67lZjLApIj/JsTEEohDDt1im2nkspzfV5MvhfS7diDw6Rp/xvAQaWZTv1Ww==", - "optional": true - }, - "@next/swc-linux-x64-gnu": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.1.tgz", - "integrity": "sha512-wMqf90uDWN001NqCM/auRl3+qVVeKfjJdT9XW+RMIOf+rhUzadmYJu++tp2y+hUbb6GTRhT+VjQzcgg/QTD9NQ==", - "optional": true - }, - "@next/swc-linux-x64-musl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.1.tgz", - "integrity": "sha512-ol1X1e24w4j4QwdeNjfX0f+Nza25n+ymY0T2frTyalVczUmzkVD7QGgPTZMHfR1aLrO69hBs0G3QBYaj22J5GQ==", - "optional": true - }, - "@next/swc-win32-arm64-msvc": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.1.tgz", - "integrity": "sha512-WEmTEeWs6yRUEnUlahTgvZteh5RJc4sEjCQIodJlZZ5/VJwVP8p2L7l6VhzQhT4h7KvLx/Ed4UViBdne6zpIsw==", - "optional": true - }, - "@next/swc-win32-ia32-msvc": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.1.tgz", - "integrity": "sha512-oFpHphN4ygAgZUKjzga7SoH2VGbEJXZa/KL8bHCAwCjDWle6R1SpiGOdUdA8EJ9YsG1TYWpzY6FTbUA+iAJeww==", - "optional": true - }, - "@next/swc-win32-x64-msvc": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.1.tgz", - "integrity": "sha512-FFp3nOJ/5qSpeWT0BZQ+YE1pSMk4IMpkME/1DwKBwhg4mJLB9L+6EXuJi4JEwaJdl5iN+UUlmUD3IsR1kx5fAg==", - "optional": true } } } diff --git a/packages/babel-plugin/src/utils/state-manager.js b/packages/babel-plugin/src/utils/state-manager.js index 156aead5d..5db952d96 100644 --- a/packages/babel-plugin/src/utils/state-manager.js +++ b/packages/babel-plugin/src/utils/state-manager.js @@ -637,11 +637,13 @@ export default class StateManager { try { const packageJsonPath = path.join(projectDir, 'package.json'); if (fs.existsSync(packageJsonPath)) { - const rawConfig = JSON5.parse(fs.readFileSync(packageJsonPath, 'utf8')); + const rawConfig: mixed = JSON5.parse( + fs.readFileSync(packageJsonPath, 'utf8'), + ); if (!isPackageJSON(rawConfig)) { throw new Error('Invalid package.json format'); } - const packageJson = rawConfig; + const packageJson: PackageJSON = rawConfig as $FlowFixMe; // Handle Node.js native imports const imports = packageJson.imports; @@ -664,11 +666,13 @@ export default class StateManager { try { const tsconfigPath = path.join(projectDir, 'tsconfig.json'); if (fs.existsSync(tsconfigPath)) { - const rawConfig = JSON5.parse(fs.readFileSync(tsconfigPath, 'utf8')); + const rawConfig: mixed = JSON5.parse( + fs.readFileSync(tsconfigPath, 'utf8'), + ); if (!isTSConfig(rawConfig)) { throw new Error('Invalid tsconfig.json format'); } - const tsconfig = rawConfig; + const tsconfig: TSConfig = rawConfig as $FlowFixMe; const baseUrl = tsconfig.compilerOptions?.baseUrl || '.'; if (tsconfig.compilerOptions?.paths) { tsconfigAliases = Object.fromEntries( @@ -699,11 +703,13 @@ export default class StateManager { try { const denoConfigPath = path.join(projectDir, 'deno.json'); if (fs.existsSync(denoConfigPath)) { - const rawConfig = JSON5.parse(fs.readFileSync(denoConfigPath, 'utf8')); + const rawConfig: mixed = JSON5.parse( + fs.readFileSync(denoConfigPath, 'utf8'), + ); if (!isDenoConfig(rawConfig)) { throw new Error('Invalid deno.json format'); } - const denoConfig = rawConfig; + const denoConfig: DenoConfig = rawConfig as $FlowFixMe; if (denoConfig.imports) { denoAliases = Object.fromEntries( Object.entries(denoConfig.imports).map(([key, value]) => [ @@ -888,7 +894,7 @@ const getProgramStatement = (path: NodePath<>): NodePath<> => { return programPath; }; -function isPackageJSON(obj: mixed): boolean %checks { +function isPackageJSON(obj: mixed): boolean { return ( obj != null && typeof obj === 'object' && @@ -896,7 +902,7 @@ function isPackageJSON(obj: mixed): boolean %checks { ); } -function isTSConfig(obj: mixed): boolean %checks { +function isTSConfig(obj: mixed): boolean { return ( obj != null && typeof obj === 'object' && @@ -906,7 +912,7 @@ function isTSConfig(obj: mixed): boolean %checks { ); } -function isDenoConfig(obj: mixed): boolean %checks { +function isDenoConfig(obj: mixed): boolean { return ( obj != null && typeof obj === 'object' && From 1c2100aff4737dd95244536cf45f9299ca16d6ae Mon Sep 17 00:00:00 2001 From: p0nch000 Date: Wed, 18 Dec 2024 18:03:43 +0100 Subject: [PATCH 17/17] Correct install of json5 dependency --- package-lock.json | 72 ++++++++++++++++++++++++------ package.json | 3 -- packages/babel-plugin/package.json | 9 ++-- 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index c5a91145f..a656c82e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,6 @@ "packages/*", "apps/*" ], - "dependencies": { - "json5": "^2.2.3" - }, "devDependencies": { "@babel/cli": "^7.25.7", "@babel/core": "^7.25.8", @@ -26904,7 +26901,8 @@ "@babel/types": "^7.25.8", "@stylexjs/shared": "0.9.3", "@stylexjs/stylex": "0.9.3", - "esm-resolve": "^1.0.11" + "esm-resolve": "^1.0.11", + "json5": "^2.2.3" } }, "packages/cli": { @@ -27010,7 +27008,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -27026,7 +27023,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -27042,7 +27038,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -27058,7 +27053,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -27074,7 +27068,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -27090,7 +27083,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -27106,7 +27098,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -27122,7 +27113,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -27138,7 +27128,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -31883,7 +31872,8 @@ "@babel/types": "^7.25.8", "@stylexjs/shared": "0.9.3", "@stylexjs/stylex": "0.9.3", - "esm-resolve": "^1.0.11" + "esm-resolve": "^1.0.11", + "json5": "^2.2.3" } }, "@stylexjs/cli": { @@ -45932,6 +45922,60 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" + }, + "@next/swc-darwin-arm64": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.1.tgz", + "integrity": "sha512-JyxnGCS4qT67hdOKQ0CkgFTp+PXub5W1wsGvIq98TNbF3YEIN7iDekYhYsZzc8Ov0pWEsghQt+tANdidITCLaw==", + "optional": true + }, + "@next/swc-darwin-x64": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.1.tgz", + "integrity": "sha512-625Z7bb5AyIzswF9hvfZWa+HTwFZw+Jn3lOBNZB87lUS0iuCYDHqk3ujuHCkiyPtSC0xFBtYDLcrZ11mF/ap3w==", + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.1.tgz", + "integrity": "sha512-iVpn3KG3DprFXzVHM09kvb//4CNNXBQ9NB/pTm8LO+vnnnaObnzFdS5KM+w1okwa32xH0g8EvZIhoB3fI3mS1g==", + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.1.tgz", + "integrity": "sha512-mVsGyMxTLWZXyD5sen6kGOTYVOO67lZjLApIj/JsTEEohDDt1im2nkspzfV5MvhfS7diDw6Rp/xvAQaWZTv1Ww==", + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.1.tgz", + "integrity": "sha512-wMqf90uDWN001NqCM/auRl3+qVVeKfjJdT9XW+RMIOf+rhUzadmYJu++tp2y+hUbb6GTRhT+VjQzcgg/QTD9NQ==", + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.1.tgz", + "integrity": "sha512-ol1X1e24w4j4QwdeNjfX0f+Nza25n+ymY0T2frTyalVczUmzkVD7QGgPTZMHfR1aLrO69hBs0G3QBYaj22J5GQ==", + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.1.tgz", + "integrity": "sha512-WEmTEeWs6yRUEnUlahTgvZteh5RJc4sEjCQIodJlZZ5/VJwVP8p2L7l6VhzQhT4h7KvLx/Ed4UViBdne6zpIsw==", + "optional": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.1.tgz", + "integrity": "sha512-oFpHphN4ygAgZUKjzga7SoH2VGbEJXZa/KL8bHCAwCjDWle6R1SpiGOdUdA8EJ9YsG1TYWpzY6FTbUA+iAJeww==", + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.1.tgz", + "integrity": "sha512-FFp3nOJ/5qSpeWT0BZQ+YE1pSMk4IMpkME/1DwKBwhg4mJLB9L+6EXuJi4JEwaJdl5iN+UUlmUD3IsR1kx5fAg==", + "optional": true } } } diff --git a/package.json b/package.json index 45414f856..572ecdd7e 100644 --- a/package.json +++ b/package.json @@ -77,8 +77,5 @@ } } ] - }, - "dependencies": { - "json5": "^2.2.3" } } diff --git a/packages/babel-plugin/package.json b/packages/babel-plugin/package.json index ec9e4d9a2..ec0205d36 100644 --- a/packages/babel-plugin/package.json +++ b/packages/babel-plugin/package.json @@ -13,13 +13,14 @@ "test": "jest" }, "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@stylexjs/shared": "0.9.3", - "@stylexjs/stylex": "0.9.3", "@babel/core": "^7.25.8", + "@babel/helper-module-imports": "^7.22.15", "@babel/traverse": "^7.25.7", "@babel/types": "^7.25.8", - "esm-resolve": "^1.0.11" + "@stylexjs/shared": "0.9.3", + "@stylexjs/stylex": "0.9.3", + "esm-resolve": "^1.0.11", + "json5": "^2.2.3" }, "jest": { "verbose": true,