Skip to content
Merged
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
54 changes: 54 additions & 0 deletions .github/workflows/upgrade-template-deps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Upgrade template dependencies

on:
schedule:
- cron: '0 0 * * 1' # Every Monday at midnight UTC
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
upgrade-template-deps:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

- name: Setup
uses: ./.github/actions/setup

- name: Run upgrade script
run: node scripts/upgrade-template-deps.mts

- name: Create or update pull request
run: |
if [ -z "$(git status --porcelain)" ]; then
echo "No changes to commit."
exit 0
fi

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

branch="upgrade-template-deps"

git checkout -B "$branch"
git add -A
git commit -m "chore: upgrade template dependencies"
git push -u origin "$branch" --force

existing_pr=$(gh pr list --head "$branch" --json number --jq '.[0].number')

if [ -n "$existing_pr" ]; then
echo "Updated existing PR #$existing_pr"
else
gh pr create \
--title "chore: upgrade template dependencies" \
--body "Automated upgrade of template dependencies via \`scripts/upgrade-template-deps.mts\`." \
--label "dependencies"
fi
env:
GH_TOKEN: ${{ github.token }}
8 changes: 8 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { defineConfig, globalIgnores } from 'eslint/config';
import { recommended, vitest, typechecked } from 'eslint-config-satya164';
import globals from 'globals';

export default defineConfig(
recommended,
Expand All @@ -24,6 +25,13 @@ export default defineConfig(
},
},

{
files: ['scripts/**'],
languageOptions: {
globals: globals.node,
},
},

globalIgnores([
'**/.next/',
'**/.expo/',
Expand Down
2 changes: 1 addition & 1 deletion packages/create-react-native-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"github-username": "^6.0.0",
"kleur": "^4.1.4",
"ora": "^5.4.1",
"pigment": "^0.3.11",
"pigment": "^0.4.0",
"validate-npm-package-name": "^4.0.0"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/create-react-native-library/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const FALLBACK_BOB_VERSION = '0.40.13';
export const FALLBACK_NITRO_MODULES_VERSION = '0.29.8';
export const SUPPORTED_MONOREPO_CONFIG_VERSION = '0.3.3';
export const SUPPORTED_REACT_NATIVE_VERSION = '0.83.0';
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import dedent from 'dedent';
import fs from 'fs-extra';
import getLatestVersion from 'get-latest-version';
import https from 'https';
import path from 'path';
import { SUPPORTED_MONOREPO_CONFIG_VERSION } from '../constants';
import type { TemplateConfiguration } from '../template';
import sortObjectKeys from '../utils/sortObjectKeys';
import { spawn } from '../utils/spawn';
import dedent from 'dedent';

const FILES_TO_DELETE = [
'__tests__',
Expand Down Expand Up @@ -216,7 +217,7 @@ export default async function generateExampleApp({

const PACKAGES_TO_ADD_DEV = {
'react-native-builder-bob': `^${config.versions.bob}`,
'react-native-monorepo-config': `^0.3.3`,
'react-native-monorepo-config': `^${SUPPORTED_MONOREPO_CONFIG_VERSION}`,
};

if (
Expand Down
4 changes: 4 additions & 0 deletions packages/create-react-native-library/src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ export async function applyTemplate(
const files = await fs.readdir(source);

for (const f of files) {
if (f.startsWith('~')) {
continue;
}

let name;

try {
Expand Down
143 changes: 34 additions & 109 deletions packages/create-react-native-library/src/utils/configureTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import fs from 'fs-extra';
import path from 'node:path';
import { applyTemplate, type TemplateConfiguration } from '../template';
import sortObjectKeys from './sortObjectKeys';
import { SUPPORTED_REACT_NATIVE_VERSION } from '../constants';

type Tool = {
name: string;
description: string;
package: Record<string, unknown>;
condition?: (config: TemplateConfiguration) => boolean;
};

Expand All @@ -21,109 +19,26 @@ type Options = {
const ESLINT = {
name: 'ESLint with Prettier',
description: 'Lint and format code',
package: {
scripts: {
lint: 'eslint "**/*.{js,ts,tsx}"',
},
prettier: {
quoteProps: 'consistent',
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
useTabs: false,
},
devDependencies: {
'@eslint/compat': '^1.3.2',
'@eslint/eslintrc': '^3.3.1',
'@eslint/js': '^9.35.0',
'@react-native/eslint-config': SUPPORTED_REACT_NATIVE_VERSION,
'eslint-config-prettier': '^10.1.8',
'eslint-plugin-prettier': '^5.5.4',
'eslint': '^9.35.0',
'prettier': '^2.8.8',
},
},
};

const LEFTHOOK = {
name: 'Lefthook with Commitlint',
description: 'Manage Git hooks and lint commit messages',
package: {
commitlint: {
extends: ['@commitlint/config-conventional'],
},
devDependencies: {
'@commitlint/config-conventional': '^19.8.1',
'commitlint': '^19.8.1',
'lefthook': '^2.0.3',
},
},
};

const RELEASE_IT = {
name: 'Release It',
description: 'Automate versioning and package publishing tasks',
package: {
'scripts': {
release: 'release-it --only-version',
},
'release-it': {
git: {
// eslint-disable-next-line no-template-curly-in-string
commitMessage: 'chore: release ${version}',
// eslint-disable-next-line no-template-curly-in-string
tagName: 'v${version}',
},
npm: {
publish: true,
},
github: {
release: true,
},
plugins: {
'@release-it/conventional-changelog': {
preset: {
name: 'angular',
},
},
},
},
'devDependencies': {
'release-it': '^19.0.4',
'@release-it/conventional-changelog': '^10.0.1',
},
},
};

const JEST = {
name: 'Jest',
description: 'Test JavaScript and TypeScript code',
package: {
scripts: {
test: 'jest',
},
jest: {
preset: 'react-native',
modulePathIgnorePatterns: [
'<rootDir>/example/node_modules',
'<rootDir>/lib/',
],
},
devDependencies: {
'@types/jest': '^29.5.14',
'jest': '^29.7.0',
},
},
};

const TURBOREPO = {
name: 'Turborepo',
description: 'Cache build outputs on CI',
package: {
devDependencies: {
turbo: '^2.5.6',
},
},
};

export const AVAILABLE_TOOLS = {
Expand Down Expand Up @@ -168,39 +83,49 @@ export async function configureTools({
continue;
}

const files = path.resolve(__dirname, `../../templates/tools/${key}`);
const toolDir = path.resolve(__dirname, `../../templates/tools/${key}`);

if (fs.existsSync(files)) {
await applyTemplate(config, files, root);
if (fs.existsSync(toolDir)) {
await applyTemplate(config, toolDir, root);
}

for (const [key, value] of Object.entries(tool.package)) {
if (
typeof value === 'object' &&
value !== null &&
!Array.isArray(value)
) {
if (typeof packageJson[key] === 'object' || packageJson[key] == null) {
packageJson[key] = {
...packageJson[key],
...value,
};
const pkgPath = path.join(toolDir, '~package.json');

if (fs.existsSync(pkgPath)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const toolPkg = (await fs.readJson(pkgPath)) as Record<string, unknown>;

for (const [field, value] of Object.entries(toolPkg)) {
if (
typeof value === 'object' &&
value !== null &&
!Array.isArray(value)
) {
if (
key === 'dependencies' ||
key === 'devDependencies' ||
key === 'peerDependencies'
typeof packageJson[field] === 'object' ||
packageJson[field] == null
) {
// @ts-expect-error: We know they are objects here
packageJson[key] = sortObjectKeys(packageJson[key]);
packageJson[field] = {
...packageJson[field],
...value,
};

if (
field === 'dependencies' ||
field === 'devDependencies' ||
field === 'peerDependencies'
) {
// @ts-expect-error: We know they are objects here
packageJson[field] = sortObjectKeys(packageJson[field]);
}
} else {
throw new Error(
`Cannot merge '${field}' field because it is not an object (got '${String(packageJson[field])}').`
);
}
} else {
throw new Error(
`Cannot merge '${key}' field because it is not an object (got '${String(packageJson[key])}').`
);
packageJson[field] = value;
}
} else {
packageJson[key] = value;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"scripts": {
"lint": "eslint \"**/*.{js,ts,tsx}\""
},
"prettier": {
"quoteProps": "consistent",
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false
},
"devDependencies": {
"@eslint/compat": "^1.3.2",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.35.0",
"@react-native/eslint-config": "0.83.0",
"eslint": "^9.35.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"prettier": "^2.8.8"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"scripts": {
"test": "jest"
},
"jest": {
"preset": "react-native",
"modulePathIgnorePatterns": [
"<rootDir>/example/node_modules",
"<rootDir>/lib/"
]
},
"devDependencies": {
"@types/jest": "^29.5.14",
"jest": "^29.7.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"commitlint": {
"extends": ["@commitlint/config-conventional"]
},
"devDependencies": {
"@commitlint/config-conventional": "^19.8.1",
"commitlint": "^19.8.1",
"lefthook": "^2.0.3"
}
}
Loading
Loading