Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
node_modules
npm-debug.log
build
.git
.gitignore
.github
.vscode
.yarn/cache
.yarn/install-state.gz
coverage
.nyc_output
dist
*.log
.DS_Store
Thumbs.db
.env.local
.env.development.local
.env.test.local
.env.production.local
4 changes: 4 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SKIP_PREFLIGHT_CHECK=true
NODE_OPTIONS=--openssl-legacy-provider
PORT=9999
PORT=3001
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"postman.settings.dotenv-detection-notification-visibility": false
}
Binary file added .yarn/install-state.gz
Binary file not shown.
79 changes: 79 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Use Node.js 20 with Alpine Linux for smaller image size
FROM node:20-alpine

# Install necessary packages for Electron and GUI applications
RUN apk add --no-cache \
chromium \
nss \
freetype \
freetype-dev \
harfbuzz \
ca-certificates \
ttf-freefont \
xvfb \
dbus \
gtk+3.0 \
libxss \
gconf \
alsa-lib \
at-spi2-atk \
libnss3 \
libxcomposite \
libxcursor \
libxdamage \
libxext \
libxfixes \
libxi \
libxrandr \
libxtst \
cups-libs \
mesa-dri-gallium

# Set Chromium path for Electron
ENV CHROMIUM_PATH=/usr/bin/chromium-browser

# Set working directory
WORKDIR /app

# Copy package files
COPY package.json yarn.lock* package-lock.json* ./

# Install dependencies
RUN yarn install --frozen-lockfile

# Copy source code
COPY . .

# Create node-sass compatibility wrapper (from our previous fix)
RUN mkdir -p node_modules/node-sass/lib && \
echo '{"name":"node-sass","version":"4.12.0","description":"Wrapper around libsass","main":"lib/index.js","license":"MIT"}' > node_modules/node-sass/package.json

COPY docker/node-sass-wrapper.js node_modules/node-sass/lib/index.js

# Set environment variables
ENV NODE_OPTIONS=--openssl-legacy-provider
ENV SKIP_PREFLIGHT_CHECK=true
ENV PORT=9999
ENV DISPLAY=:99

# Expose port
EXPOSE 9999

# Create startup script that handles X11 forwarding for Electron
RUN echo '#!/bin/sh\n\
# Start Xvfb for headless display\n\
Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &\n\
export DISPLAY=:99\n\
\n\
# Wait a moment for Xvfb to start\n\
sleep 2\n\
\n\
# Check if we should run in development or production mode\n\
if [ "$NODE_ENV" = "production" ]; then\n\
yarn build\n\
else\n\
yarn start\n\
fi' > /app/docker-entrypoint.sh && chmod +x /app/docker-entrypoint.sh

# Default command
CMD ["/app/docker-entrypoint.sh"]
77 changes: 77 additions & 0 deletions description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# βœ… Node.js v20 Compatibility Update

This update resolves several issues that previously prevented the application from running on modern Node.js versions (v20+), particularly on **Windows systems**. The changes ensure compatibility while maintaining full existing functionality.

---

## πŸ›  Summary of Changes

### 1. πŸ”„ Replaced `node-sass` with `sass`

- **Removed:** `node-sass@4.12.0` (caused native compilation failures on Node.js v20)
- **Added:** `sass@1.89.2` (Dart Sass – pure JS, no native bindings)
- **Symlink:** Created `node-sass β†’ sass` to support legacy `sass-loader` expectations

### 2. πŸ” Fixed OpenSSL 3.0 Compatibility (Node.js v17+)

- **Updated `package.json` scripts:**
- Added: `NODE_OPTIONS=--openssl-legacy-provider`
- **Fixes:** `digital envelope routines::unsupported` error
- **Applied To:**
- `react` script
- `build` script

### 3. 🚫 Bypassed Create React App Preflight Checks

- **Added `.env` file:**
- `SKIP_PREFLIGHT_CHECK=true`
- **Purpose:** Prevents `babel-jest` version warnings from blocking the app

### 4. 🧹 Cleaned Up Package Manager Conflicts

- **Removed:** `package-lock.json`
_(Prevents Yarn/npm version resolution inconsistencies)_

---

## πŸ“‚ Files Modified

- `package.json`
– Updated scripts to include `NODE_OPTIONS`

- `.env`
– Added for CRA preflight check bypass

### Dependencies

- **Removed:** `node-sass`
- **Added:** `sass`

---

## βœ… Testing Results

| Task | Status |
| ---------------- | ------------------------------------------------- |
| `yarn install` | βœ… No errors |
| `yarn start` | βœ… React dev server + Electron app launch |
| `yarn build` | βœ… Production build successful |
| SCSS Compilation | βœ… Works with deprecation warnings (non-breaking) |

---

## πŸ§ͺ Compatibility

- **Node.js:** `v20.16.0` βœ…
- **OS:** Windows 10 / 11 βœ…
- **Functionality:** All features preserved βœ…

---

This update ensures the application is fully compatible with modern development environments while preserving existing functionality.

---

#### Future plans

- Add docker to ensure everyone has same dependencies across all environments
47 changes: 47 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
version: "3.8"

services:
pennywise:
build: .
container_name: pennywise-app
ports:
- "9999:9999"
volumes:
- .:/app
- /app/node_modules
- /tmp/.X11-unix:/tmp/.X11-unix:rw
environment:
- NODE_ENV=development
- NODE_OPTIONS=--openssl-legacy-provider
- SKIP_PREFLIGHT_CHECK=true
- PORT=9999
- DISPLAY=:99
stdin_open: true
tty: true
privileged: true
networks:
- pennywise-network

# Production build service
pennywise-prod:
build: .
container_name: pennywise-prod
ports:
- "9999:9999"
environment:
- NODE_ENV=production
- NODE_OPTIONS=--openssl-legacy-provider
- SKIP_PREFLIGHT_CHECK=true
- PORT=9999
- DISPLAY=:99
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix:rw
privileged: true
networks:
- pennywise-network
profiles:
- production

networks:
pennywise-network:
driver: bridge
111 changes: 111 additions & 0 deletions docker/node-sass-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Node-sass compatibility wrapper for modern sass package
// This wrapper translates the old node-sass API to the new sass API

const sass = require("sass");

// Convert old node-sass options to new sass options
function convertOptions(options) {
const newOptions = {
data: options.data,
file: options.file,
includePaths: options.includePaths || [],
outputStyle: options.outputStyle || "nested",
sourceMap: options.sourceMap,
sourceMapContents: options.sourceMapContents,
sourceMapEmbed: options.sourceMapEmbed,
sourceMapRoot: options.sourceMapRoot,
indentedSyntax: options.indentedSyntax,
};

// Handle deprecated precision option
if (options.precision !== undefined) {
console.warn(
"node-sass precision option is deprecated and ignored in sass"
);
}

return newOptions;
}

// Convert sass result to node-sass format
function convertResult(result) {
return {
css: result.css,
map: result.map,
stats: {
entry: result.loadedUrls?.[0]?.pathname || "",
start: Date.now(),
end: Date.now(),
duration: 0,
includedFiles: result.loadedUrls?.map((url) => url.pathname) || [],
},
};
}

// Synchronous render function
function renderSync(options) {
try {
const convertedOptions = convertOptions(options);

if (options.data) {
const result = sass.compileString(options.data, {
loadPaths: convertedOptions.includePaths,
style: convertedOptions.outputStyle,
sourceMap: convertedOptions.sourceMap,
sourceMapIncludeSources: convertedOptions.sourceMapContents,
});
return convertResult(result);
} else if (options.file) {
const result = sass.compile(options.file, {
loadPaths: convertedOptions.includePaths,
style: convertedOptions.outputStyle,
sourceMap: convertedOptions.sourceMap,
sourceMapIncludeSources: convertedOptions.sourceMapContents,
});
return convertResult(result);
} else {
throw new Error("Either data or file option must be specified");
}
} catch (error) {
// Convert sass error to node-sass format
const nodesassError = new Error(error.message);
nodesassError.status = 1;
nodesassError.file = error.file || options.file;
nodesassError.line = error.line;
nodeassError.column = error.column;
throw nodeassError;
}
}

// Asynchronous render function
function render(options, callback) {
try {
const result = renderSync(options);
if (callback) {
setTimeout(() => callback(null, result), 0);
}
return result;
} catch (error) {
if (callback) {
setTimeout(() => callback(error), 0);
} else {
throw error;
}
}
}

module.exports = {
render,
renderSync,
// Additional exports for compatibility
info: "node-sass compatibility wrapper for sass",
types: {
Boolean: "boolean",
Color: "color",
List: "list",
Map: "map",
Null: "null",
Number: "number",
String: "string",
},
};
Loading