Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better support and improvements #18

Merged
merged 3 commits into from
Jan 7, 2025
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
6 changes: 3 additions & 3 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
}
30 changes: 16 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,27 @@ on:

jobs:
test:
name: Test Node.js ${{ matrix.node-version }} on ${{ matrix.os }}

strategy:
matrix:
os: [ubuntu-latest]
node-version: [18.x]

runs-on: ${{ matrix.os }}
name: Test and Lint
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2.2.2
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 6.34.0
- uses: actions/setup-node@v2
version: 8
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
node-version: 18
cache: 'pnpm'

- name: Install Dependencies
run: pnpm install
run: pnpm install --frozen-lockfile

- name: Run Linter
run: pnpm lint

- name: Run Tests
run: pnpm test

- name: Build
run: pnpm build
27 changes: 16 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,32 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Setup Node.js 20.x
uses: actions/setup-node@v2
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 20.x
node-version: 18

- name: Setup pnpm
run: npm install -g pnpm
uses: pnpm/action-setup@v2
with:
version: 8

- name: Install Dependencies
run: pnpm i
run: pnpm install --frozen-lockfile

- name: Create Release PR or Publish Packages
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
with:
publish: pnpm release
version: pnpm version
commit: 'chore: update package versions'
title: 'chore: update package versions'
publish: pnpm run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Send a Slack notification if a publish happens
if: steps.changesets.outputs.published == 'true'
run: my-slack-bot send-notification --message "A new version of ${GITHUB_REPOSITORY} was published!"
60 changes: 60 additions & 0 deletions __tests__/codegen.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import fs from 'fs/promises';
import { generateDeclarations, generateLinkFunction } from '../src/codegen';
import { getRoutesMap } from '../src/utils';

jest.mock('fs/promises');
jest.mock('../src/utils');

describe('codegen', () => {
const mockAppDir = '/mock/app';
const mockDeclarationPath = '/mock/declarations.d.ts';
const mockUtilsPath = '/mock/utils.ts';

beforeEach(() => {
jest.resetAllMocks();
});

describe('generateDeclarations', () => {
it('should generate declarations correctly', async () => {
const mockRoutesMap = {
'/': { path: '/', params: {}, isDynamic: false },
'/blog/[slug]': {
path: '/blog/[slug]',
params: { slug: '' },
isDynamic: true,
},
};

(getRoutesMap as jest.Mock).mockResolvedValue(mockRoutesMap);

await generateDeclarations(mockAppDir, mockDeclarationPath);

expect(fs.writeFile).toHaveBeenCalledWith(
mockDeclarationPath,
expect.stringContaining('const routes ='),
);
});
});

describe('generateLinkFunction', () => {
it('should generate link function correctly', async () => {
const mockRoutesMap = {
'/': { path: '/', params: {}, isDynamic: false },
'/blog/[slug]': {
path: '/blog/[slug]',
params: { slug: '' },
isDynamic: true,
},
};

(getRoutesMap as jest.Mock).mockResolvedValue(mockRoutesMap);

await generateLinkFunction(mockAppDir, mockUtilsPath);

expect(fs.writeFile).toHaveBeenCalledWith(
mockUtilsPath,
expect.stringContaining('const link$ ='),
);
});
});
});
3 changes: 3 additions & 0 deletions __tests__/mock-app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function About() {
return <h1>About</h1>;
}
3 changes: 3 additions & 0 deletions __tests__/mock-app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function BlogPost({ params }: { params: { slug: string } }) {
return <h1>Blog Post: {params.slug}</h1>;
}
3 changes: 3 additions & 0 deletions __tests__/mock-app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Home() {
return <h1>Home</h1>;
}
3 changes: 3 additions & 0 deletions __tests__/mock-app/products/[...categories]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Products({ params }: { params: { categories: string[] } }) {
return <h1>Products: {params.categories.join(', ')}</h1>;
}
3 changes: 3 additions & 0 deletions __tests__/mock-app/settings/[[...sections]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Settings({ params }: { params: { sections?: string[] } }) {
return <h1>Settings: {params.sections?.join(', ') || 'General'}</h1>;
}
85 changes: 85 additions & 0 deletions __tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import path from 'path';
import {
cleanPath,
isValidPath,
isDynamicPath,
isCatchAllRoute,
isOptionalCatchAllRoute,
generateRoutes,
} from '../src/utils';

describe('utils', () => {
describe('cleanPath', () => {
it('should clean the path correctly', () => {
const input = '/app/(auth)/login/page.tsx';
const expected = '/login';
expect(cleanPath(input, '/app')).toBe(expected);
});
});

describe('isValidPath', () => {
it('should return true for valid paths', () => {
expect(isValidPath('validPath')).toBe(true);
});

it('should return false for paths starting with underscore', () => {
expect(isValidPath('_invalidPath')).toBe(false);
});

it('should return false for paths starting with dot', () => {
expect(isValidPath('.invalidPath')).toBe(false);
});
});

describe('isDynamicPath', () => {
it('should return true for dynamic paths', () => {
expect(isDynamicPath('[id]')).toBe(true);
});

it('should return false for static paths', () => {
expect(isDynamicPath('static')).toBe(false);
});
});

describe('isCatchAllRoute', () => {
it('should return true for catch-all routes', () => {
expect(isCatchAllRoute('[...slug]')).toBe(true);
});

it('should return false for non-catch-all routes', () => {
expect(isCatchAllRoute('[id]')).toBe(false);
});
});

describe('isOptionalCatchAllRoute', () => {
it('should return true for optional catch-all routes', () => {
expect(isOptionalCatchAllRoute('[[...slug]]')).toBe(true);
});

it('should return false for non-optional catch-all routes', () => {
expect(isOptionalCatchAllRoute('[...slug]')).toBe(false);
});
});

describe('generateRoutes', () => {
it('should generate routes correctly', async () => {
const mockAppDir = path.join(__dirname, 'mock-app');
const routes = await generateRoutes(mockAppDir);

expect(routes).toEqual(
expect.arrayContaining([
expect.objectContaining({ path: '/about' }),
expect.objectContaining({ path: '/blog/[slug]', isDynamic: true }),
expect.objectContaining({
path: '/products/[...categories]',
isCatchAll: true,
}),
expect.objectContaining({
path: '/settings/[[...sections]]',
isOptionalCatchAll: true,
}),
]),
);
});
});
});
15 changes: 15 additions & 0 deletions jest.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src', '<rootDir>/__tests__'],
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
extensionsToTreatAsEsm: ['.ts'],
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
transform: {
'^.+\\.tsx?$': ['ts-jest', { useESM: true }],
},
transformIgnorePatterns: ['node_modules/(?!(chalk)/)'],
};
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
"format": "prettier --write .",
"lint": "eslint --ext .ts,.tsx src",
"build": "tsup src/index.ts --dts",
"release": "pnpm run build && changeset publish",
"changeset": "changeset",
"release": "pnpm build && changeset publish",
"version": "changeset version"
"version": "changeset version",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"exports": {
".": {
Expand All @@ -40,14 +43,17 @@
"type": "module",
"devDependencies": {
"@changesets/cli": "^2.27.1",
"@types/jest": "^29.5.12",
"@types/webpack": "^5.28.5",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.2",
"jest": "^29.7.0",
"next": "^14.0.4",
"prettier": "^3.1.1",
"ts-jest": "^29.2.5",
"tsup": "^8.0.1",
"typescript": "^5.3.3"
},
Expand Down
Loading
Loading