kristerkari / react-native-sass-transformer

Use Sass to style your React Native apps.
MIT License
219 stars 19 forks source link

Conflict between react-native-sass-transformer and built-in CSS support in Expo SDK 51 (web only) #120

Closed iyosayi0x closed 1 month ago

iyosayi0x commented 1 month ago

This issue reports a conflict between the react-native-sass-transformer package and Expo SDK 51's built-in CSS support. While Expo offers built-in CSS functionality, it's currently limited to web projects (expo web). When using react-native-sass-transformer for styling in native apps (iOS/Android), styles are not being applied due to a potential conflict with the web-focused CSS support.

Expected behavior: react-native-sass-transformer should successfully process and apply SCSS styles for native apps (iOS/Android) in Expo projects using SDK 51.

Actual behavior: Styles defined in SCSS files are not reflected in the app when using react-native-sass-transformer with Expo SDK 51. This suggests a conflict with the built-in (web-only) CSS support.

Dependencies

Expo SDK version 51

Node.js version 20.11.1

React Native 0.74.3

Metro Config

const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname, {
  isCSSEnabled:false
})

/** @type {import('expo/metro-config').MetroConfig} */
module.exports = { 
  ...config,
  transformer: {
    ...config.transformer,
    assetPlugins: ['expo-asset/tools/hashAssetFiles'],
    babelTransformerPath: require.resolve('react-native-sass-transformer'),
  },
  resolver: {
    ...config.resolver,
    sourceExts: [...config.resolver.sourceExts, 'scss', 'sass'],
    assetExts:[...config.resolver.assetExts, 'ttf']
  },
}

Steps to reproduce

Create a New Expo App:

npx create-expo-app@latest

Install react-native-sass-transformer:

npm i --save-dev react-native-sass-transformer sass

Configure Metro:

const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname, {
  isCSSEnabled:false
})

/** @type {import('expo/metro-config').MetroConfig} */
module.exports = { 
  ...config,
  transformer: {
    ...config.transformer,
    assetPlugins: ['expo-asset/tools/hashAssetFiles'],
    babelTransformerPath: require.resolve('react-native-sass-transformer'),
  },
  resolver: {
    ...config.resolver,
    sourceExts: [...config.resolver.sourceExts, 'scss', 'sass'],
    assetExts:[...config.resolver.assetExts, 'ttf']
  },
}

Create styles.scss:

Inside your project's root directory, create a file named styles.scss and add your SCSS styles: SCSS

.logo {
  font-weight: bold;
  color: #f00; /* Red color */
}

Create index.js

Inside your app's entry point (App.js or similar), import and apply the styles from styles.scss: JavaScript

import React from 'react';
import { Text } from 'react-native';
import styles from './styles.scss'; // Import SCSS styles

const App = () => {
  return (
    <Text style={styles.logo}>
      Fooder
    </Text>
  );
};

export default App

Run the App: Bash

npx expo start
iyosayi0x commented 1 month ago

I was able to come up with a tempoary solution for sdk 51 & 50

Creating a custom transformer worker

I removed all the css processing that was done and ended up with this

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.transform = void 0;
/**
 * Copyright 2023-present 650 Industries (Expo). All rights reserved.
 * 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.
 */
const worker = __importStar(require("@expo/metro-config/build/transform-worker/metro-transform-worker"));

async function transform(config, projectRoot, filename, data, options) {
    const environment = options.customTransformOptions?.environment;
    const isClientEnvironment = environment !== 'node' && environment !== 'react-server';

    if (isClientEnvironment &&
        (filename.match(new RegExp(`^app/\\+html(\\.${options.platform})?\\.([tj]sx?|[cm]js)?$`)) ||
        filename.match(/\+api(\.(native|ios|android|web))?\.[tj]sx?$/))) {
        return worker.transform(config, projectRoot, filename, !options.minify
            ? Buffer.from('"> The server-only file was removed from the client JS bundle by Expo CLI."')
            : Buffer.from(''), options);
    }

    if (isClientEnvironment &&
        !filename.match(/\/node_modules\//) &&
        filename.match(/\+api(\.(native|ios|android|web))?\.[tj]sx?$/)) {
        return worker.transform(config, projectRoot, filename, Buffer.from(''), options);
    }

    return worker.transform(config, projectRoot, filename, data, options);
}

exports.transform = transform;
/**
 * A custom Metro transformer that adds support for processing Expo-specific bundler features.
 */
module.exports = {
    ...worker,
    transform,
};
//# sourceMappingURL=transform-worker.js.map

Update metro config

const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname, {
  isCSSEnabled: true,
});

config.transformerPath = require.resolve('./custom-transformer')
config.transformer.babelTransformerPath = require.resolve("react-native-sass-transformer")
config.transformer.assetPlugins = ['expo-asset/tools/hashAssetFiles'];
config.resolver.assetExts = [...config.resolver.assetExts, 'ttf'];
config.resolver.sourceExts = [...config.resolver.sourceExts, 'scss', 'sass'];

module.exports = config;