Skip to content

Commit

Permalink
feat: next 15 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Viraj-10 committed Nov 14, 2024
1 parent d3e17b8 commit 355ddd5
Show file tree
Hide file tree
Showing 114 changed files with 3,737 additions and 62 deletions.
5 changes: 3 additions & 2 deletions packages/gluestack-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,19 @@
"fast-glob": "^3.3.2",
"find-package-json": "^1.2.0",
"fs-extra": "^11.1.0",
"jscodeshift": "^0.15.2",
"jscodeshift": "^17.1.1",
"ora": "^8.0.1",
"prettier": "^3.3.2",
"recast": "^0.23.9",
"simple-git": "^3.16.0",
"zod": "^3.22.4"
},
"devDependencies": {
"@babel/parser": "^7.26.2",
"@size-limit/preset-small-lib": "^8.2.4",
"@types/find-package-json": "^1.2.3",
"@types/fs-extra": "^11.0.1",
"@types/jscodeshift": "^0.11.11",
"@types/jscodeshift": "^0.12.0",
"@types/node": "^18.16.16",
"husky": "^8.0.3",
"jest": "^29.5.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/gluestack-cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const config = {
reactNativeCLIProject: 'react-native-cli',
tailwindConfigRootPath: 'example/storybook-nativewind/src/tailwind.config.js',
writableComponentsPath: 'components/ui',
branchName: 'patch',
branchName: 'main',
style: 'nativewind',
providerComponent: 'gluestack-ui-provider',
nativewindUtilPattern: '@gluestack-ui/nativewind-utils/',
Expand Down
87 changes: 87 additions & 0 deletions packages/gluestack-cli/src/util/add-patch-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import fs from 'fs';
import path from 'path';
import { log } from '@clack/prompts';

/**
* Sets up the patch file for react-native-web in the patches directory
* @param projectPath - Root path of the project
* @param templatePath - Path to the scripts/template directory
* @returns Promise with the result of the operation
*/
async function setupReactNativeWebPatch(
projectPath: string = process.cwd()
): Promise<boolean> {
try {
// Define paths
const templatePath = path.join(
__dirname,
'..',
'..',
'template',
'nextjs',
'next15'
);
const patchesDir = path.join(projectPath, 'patches');
const patchFileName = 'react-native-web+0.19.13.patch';

const sourcePatchPath = path.join(templatePath, patchFileName);
const targetPatchPath = path.join(patchesDir, patchFileName);

// Check if template patch file exists
if (!fs.existsSync(sourcePatchPath)) {
log.error(`Template patch file not found at: ${sourcePatchPath}`);
return false;
}

// Create patches directory if it doesn't exist
if (!fs.existsSync(patchesDir)) {
fs.mkdirSync(patchesDir, { recursive: true });
}

// Copy patch file if it doesn't exist or force update
if (!fs.existsSync(targetPatchPath)) {
fs.copyFileSync(sourcePatchPath, targetPatchPath);
} else {
// Compare files to see if they're different
const sourceContent = fs.readFileSync(sourcePatchPath, 'utf8');
const targetContent = fs.readFileSync(targetPatchPath, 'utf8');

if (sourceContent !== targetContent) {
// Backup existing file
const backupPath = `${targetPatchPath}.backup`;
fs.copyFileSync(targetPatchPath, backupPath);

// Update patch file
fs.copyFileSync(sourcePatchPath, targetPatchPath);
}
}

return true;
} catch (error) {
log.error(
error instanceof Error ? error.message : 'Unknown error occurred'
);
return false;
}
}

/**
* Validates the patch file setup
* @param patchPath - Path to the patch file
* @returns boolean indicating if patch file is valid
*/
function validatePatchFile(patchPath: string): boolean {
try {
if (!fs.existsSync(patchPath)) {
return false;
}

const content = fs.readFileSync(patchPath, 'utf8');
// Basic validation - check if it looks like a patch file
return content.includes('diff --git') || content.includes('@@');
} catch {
return false;
}
}

export { setupReactNativeWebPatch, validatePatchFile };
8 changes: 7 additions & 1 deletion packages/gluestack-cli/src/util/add/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
installDependencies,
projectRootPath,
removeHyphen,
findLockFileType,
promptVersionManager,
} from '..';

const _homeDir = os.homedir();
Expand Down Expand Up @@ -63,7 +65,11 @@ const componentAdder = async ({
})
)
.then(async () => {
await installDependencies(updatedComponents);
let versionManager: string | null = findLockFileType();
if (!versionManager) {
versionManager = await promptVersionManager();
}
await installDependencies(updatedComponents, versionManager);
log.success(
`\x1b[32mDone!\x1b[0m Added new \x1b[1mgluestack-ui\x1b[0m ${count === 1 ? 'component' : 'components'} into project`
);
Expand Down
117 changes: 117 additions & 0 deletions packages/gluestack-cli/src/util/check-next-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { log } from '@clack/prompts';
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';

interface PackageJson {
dependencies?: {
next?: string;
[key: string]: string | undefined;
};
devDependencies?: {
next?: string;
[key: string]: string | undefined;
};
}

interface VersionResult {
version: string;
majorVersion: number;
isNextjs15OrHigher: boolean;
error?: string;
}

/**
* Cleans version string by removing special characters (^, ~, >, <, =, v)
* @param version - Raw version string from package.json
*/
const cleanVersionString = (version: string): string => {
return version.replace(/[^\d.]/g, '');
};

/**
* Gets the Next.js major version from package.json
* @param projectPath - Path to the project root directory
* @returns Promise containing version information and status
*/
async function getNextjsVersion(
projectPath: string = process.cwd()
): Promise<VersionResult> {
try {
const packageJsonPath = join(projectPath, 'package.json');

// Check if package.json exists
if (!existsSync(packageJsonPath)) {
return {
version: '',
majorVersion: 0,
isNextjs15OrHigher: false,
error: 'package.json not found',
};
}

// Read and parse package.json
const packageJson: PackageJson = JSON.parse(
readFileSync(packageJsonPath, 'utf8')
);

// Check both dependencies and devDependencies for next
const nextVersion =
packageJson.dependencies?.next || packageJson.devDependencies?.next;

if (!nextVersion) {
return {
version: '',
majorVersion: 0,
isNextjs15OrHigher: false,
error: 'Next.js not found in dependencies',
};
}

// Clean the version string and get major version
const cleanVersion = cleanVersionString(nextVersion);
const majorVersion = parseInt(cleanVersion.split('.')[0], 10);

// Validate that majorVersion is a number
if (isNaN(majorVersion)) {
return {
version: cleanVersion,
majorVersion: 0,
isNextjs15OrHigher: false,
error: 'Invalid version format',
};
}

return {
version: cleanVersion,
majorVersion,
isNextjs15OrHigher: majorVersion >= 15,
};
} catch (error) {
return {
version: '',
majorVersion: 0,
isNextjs15OrHigher: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}

export { getNextjsVersion, VersionResult, PackageJson };

// Example usage with types:

export async function checkNextVersion(): Promise<boolean | undefined> {
try {
const result: VersionResult = await getNextjsVersion();

if (result.error) {
log.error(`Error: ${result.error}`);
return false;
}

return result.isNextjs15OrHigher;
} catch (error) {
log.error(`Unexpected error: ${error}`);
return undefined;
}
}
22 changes: 16 additions & 6 deletions packages/gluestack-cli/src/util/config/next-config-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { join, relative } from 'path';
import { execSync } from 'child_process';
import { ensureFilesPromise } from '..';
import { commonInitialization } from '../init';

import { addReactNativeWebPatch } from '../init/addReactNativeWebPatch';
//next project type initialization
async function getNextProjectType(cwd: string): Promise<string | undefined> {
const files = await fg.glob('**/*', {
Expand Down Expand Up @@ -72,7 +72,8 @@ async function resolvedNextJsPaths(resultConfig: NextResolvedConfig) {
//project specific initialization: nextjs
async function initNatiwindNextApp(
resolvedConfig: NextResolvedConfig,
permission: boolean
permission: boolean,
isNextjs15: boolean | undefined
) {
try {
const NextTransformer = join(
Expand All @@ -84,10 +85,15 @@ async function initNatiwindNextApp(
let nextTransformerPath = '';
let fileType = '';

if (nextConfigPath?.endsWith('.mjs') || nextConfigPath?.endsWith('.ts')) {
if (isNextjs15) {
await addReactNativeWebPatch();
}
if (nextConfigPath?.endsWith('.mjs')) {
fileType = 'mjs';
} else if (nextConfigPath?.endsWith('.js')) {
fileType = 'js';
} else if (nextConfigPath?.endsWith('.ts')) {
fileType = 'ts';
}
nextTransformerPath = join(
`${NextTransformer}/next-config-${fileType}-transform.ts`
Expand All @@ -101,8 +107,9 @@ async function initNatiwindNextApp(
resolvedConfig.app.registry
) {
// if app router add registry file to root
const registryPath = isNextjs15 ? ['nextjs', 'next15'] : ['common'];
const registryContent = await readFile(
join(__dirname, config.templatesDir, 'common', 'registry.tsx'),
join(__dirname, config.templatesDir, ...registryPath, 'registry.tsx'),
'utf8'
);
await writeFile(resolvedConfig.app.registry, registryContent, 'utf8');
Expand Down Expand Up @@ -141,7 +148,10 @@ async function initNatiwindNextApp(
}
}

async function generateConfigNextApp(permission: boolean) {
async function generateConfigNextApp(
permission: boolean,
isNextjs15: boolean | undefined
) {
const projectType = await getNextProjectType(_currDir);
const entryPath = await getFilePath(['**/*layout.*', '**/*_app.*']);
const globalCssPath = await getFilePath([
Expand Down Expand Up @@ -207,7 +217,7 @@ async function generateConfigNextApp(permission: boolean) {
];
const filesEnsured = await ensureFilesPromise(filesTobeEnsured);
if (permission && filesEnsured) {
await initNatiwindNextApp(resolvedConfig, permission);
await await initNatiwindNextApp(resolvedConfig, permission, isNextjs15);
}
}

Expand Down
7 changes: 4 additions & 3 deletions packages/gluestack-cli/src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ const pullComponentRepo = async (targetpath: string): Promise<void> => {
}
if (!success) {
s.stop('\x1b[31m' + 'Pulling failed!' + '\x1b[0m');
throw new Error('Error pulling remote branch!')
throw new Error('Error pulling remote branch!');
} else s.stop('Git pull successful.');
};

Expand All @@ -165,7 +165,7 @@ const wait = (msec: number): Promise<void> =>
});

//checking from cwd
function findLockFileType(): string | null {
export function findLockFileType(): string | null {
const lockFiles: { [key: string]: string } = {
'package-lock.json': 'npm',
'yarn.lock': 'yarn',
Expand Down Expand Up @@ -201,7 +201,7 @@ function getPackageMangerFlag(options: any) {
}
}

const promptVersionManager = async (): Promise<any> => {
export const promptVersionManager = async (): Promise<any> => {
const packageManager = await select({
message:
'No lockfile detected. Please select a package manager to install dependencies:',
Expand Down Expand Up @@ -232,6 +232,7 @@ async function ensureLegacyPeerDeps(): Promise<void> {

const installDependencies = async (
input: string[] | string,
versionManager: string | null,
additionalDependencies?: ComponentConfig | undefined
): Promise<void> => {
try {
Expand Down
16 changes: 16 additions & 0 deletions packages/gluestack-cli/src/util/init/addReactNativeWebPatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { log, spinner } from '@clack/prompts';
import { setupReactNativeWebPatch } from '../add-patch-file';
import { execSync } from 'child_process';
import { updatePackageJson } from './modify-package-json';

export async function addReactNativeWebPatch() {
const s = spinner();
s.start('⏳ Adding react-native-web patch');
const isPatchAdded = await setupReactNativeWebPatch();
if (!isPatchAdded) {
log.error('Failed to add react-native-web patch');
process.exit(1);
}
updatePackageJson(process.cwd());
s.stop(`\x1b[32mReact-native-web patch added.\x1b[0m`);
}
Loading

0 comments on commit 355ddd5

Please sign in to comment.