27 KiB
QWRK-Laconic Turborepo Migration Technical Specification
Implementation Starting Points
This migration specification supports multiple entry points depending on the current state of your repository:
Scenario 1: New Repository Implementation
- Follow all steps sequentially starting from "Repository Initialization"
- Execute all setup commands to build the complete architecture from scratch
Scenario 2: Existing Repository Conversion
If the repository already exists but requires Turborepo integration:
- Preserve existing package structure and Git history
- Add Turborepo configuration (
turbo.json
,pnpm-workspace.yaml
) - Adapt existing packages to the recommended structure
- Integrate shared package dependencies
- Skip initialization steps that would conflict with existing setup
Scenario 3: Partial Turborepo Implementation
For repositories with partial Turborepo architecture:
- Audit existing configuration against this specification
- Upgrade task definitions to latest syntax (
tasks
vs legacypipeline
) - Apply missing architectural components
- Align package structure with recommended patterns
- Integrate modular services approach
Prerequisite Validation
Run the following diagnostic commands to determine your starting point:
# Check for existing Turborepo configuration
[ -f turbo.json ] && echo "Existing turbo.json found, requires update" || echo "New turbo.json needed"
# Verify package manager
command -v pnpm >/dev/null 2>&1 && echo "pnpm installed" || echo "pnpm installation required"
# Check Next.js applications
find . -path "*/package.json" -exec grep -l "next" {} \; | xargs -I{} dirname {}
Architecture Overview
graph TD
subgraph "qwrk-laconic-mono"
subgraph "apps"
LW[laconic-web]
LD[laconic-deploy]
D[docs]
end
subgraph "packages"
UI[ui]
Utils[utils]
Services[services]
end
LW -->|imports| UI
LW -->|imports| Utils
LW -->|imports| Services
LD -->|imports| UI
LD -->|imports| Utils
LD -->|imports| Services
D -->|imports| UI
end
Technology Stack
Component | Technology | Version |
---|---|---|
Monorepo Manager | Turborepo | 2.0.0+ |
Package Manager | pnpm | 8.15.1+ |
Frontend Framework | Next.js | 15.0.0+ |
Linting/Formatting | Biome | 1.9.0+ |
UI Framework | Tailwind CSS | 3.4.0+ |
GraphQL Client | Apollo Client | 3.9.5+ |
Type Safety | TypeScript | 5.3.3+ |
Testing | Vitest | 1.2.0+ |
Repository Structure
qwrk-laconic-mono/
├── apps/
│ ├── laconic-web/ # Next.js 15 application
│ │ ├── src/
│ │ │ ├── app/ # App Router
│ │ │ ├── api/ # API Routes
│ │ │ │ └── graphql/ # GraphQL API endpoint
│ │ │ ├── components/ # App-specific components
│ │ │ ├── lib/ # App-specific utilities
│ │ │ ├── styles/ # Global styles
│ │ │ └── types/ # TypeScript types
│ ├── laconic-deploy/ # Migrated current frontend
│ └── docs/ # Documentation site
├── packages/
│ ├── ui/ # Shared UI components
│ │ ├── src/
│ │ │ ├── components/ # React components
│ │ │ ├── hooks/ # UI-related hooks
│ │ │ ├── styles/ # Component styles
│ │ │ └── index.ts # Entry point
│ ├── utils/ # Shared utilities
│ │ ├── src/
│ │ │ ├── helpers/ # Helper functions
│ │ │ ├── constants/ # Shared constants
│ │ │ └── index.ts # Entry point
│ └── services/ # Consolidated service modules
│ ├── src/
│ │ ├── graphql/ # GraphQL client
│ │ ├── deployer/ # Deployment service
│ │ └── index.ts # Entry point
├── turbo.json # Turborepo configuration
├── pnpm-workspace.yaml # PNPM workspace definition
├── biome.json # Biome configuration
├── .npmrc # NPM configuration
└── package.json # Root package.json
Implementation Guide
1. Repository Initialization
New Repository Approach
# Initialize Turborepo with pnpm
pnpm dlx create-turbo@latest qwrk-laconic-mono --package-manager=pnpm
cd qwrk-laconic-mono
# Clean default structure
rm -rf apps/docs apps/web
Existing Repository Approach
# If repository already exists and initialized:
git clone <existing-repo-url> qwrk-laconic-mono
cd qwrk-laconic-mono
# Create workspace structure if not exists
mkdir -p apps packages
# Save existing structure (optional)
find . -maxdepth 2 -type d -not -path "*/\.*" -not -path "./node_modules" \
-not -path "./apps" -not -path "./packages" > existing_directories.txt
# Create migration branch
git checkout -b feature/turborepo-migration
2. Configure Package Manager
# Configure pnpm
cat > .npmrc << EOL
shamefully-hoist=true
strict-peer-dependencies=false
auto-install-peers=true
node-linker=hoisted
resolution-mode=highest
EOL
# Configure workspace
cat > pnpm-workspace.yaml << EOL
packages:
- 'apps/*'
- 'packages/*'
EOL
3. Biome Configuration
# Install Biome
pnpm add -D -w @biomejs/biome
# Configure Biome
cat > biome.json << EOL
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"useExhaustiveDependencies": "error"
},
"suspicious": {
"noExplicitAny": "warn"
},
"style": {
"noNonNullAssertion": "error"
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingComma": "es5",
"semicolons": "always"
}
}
}
EOL
4. Turborepo Configuration
# Configure Turborepo
cat > turbo.json << EOL
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"globalEnv": ["NODE_ENV", "PORT", "NEXT_PUBLIC_*"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},
"dev": {
"cache": false,
"persistent": true,
"dependsOn": ["^build"]
},
"lint": {
"outputs": []
},
"format": {
"outputs": []
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"clean": {
"cache": false
}
}
}
EOL
5. Root Package Configuration
# Configure root package.json
cat > package.json << EOL
{
"name": "qwrk-laconic-mono",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "turbo build",
"dev": "turbo dev",
"lint": "turbo lint",
"format": "biome format --write .",
"test": "turbo test",
"clean": "turbo clean && rm -rf node_modules"
},
"devDependencies": {
"@biomejs/biome": "1.9.0",
"turbo": "latest"
},
"packageManager": "pnpm@8.15.1"
}
EOL
6. Initialize Applications
# Create Next.js applications
pnpm dlx create-next-app@latest apps/laconic-web \
--typescript \
--tailwind \
--app \
--src-dir \
--import-alias "@/*" \
--use-pnpm
pnpm dlx create-next-app@latest apps/docs \
--typescript \
--tailwind \
--app \
--src-dir \
--import-alias "@/*" \
--use-pnpm
7. Configure laconic-deploy placeholder
# Create placeholder for existing frontend migration
mkdir -p apps/laconic-deploy
cd apps/laconic-deploy
# Initialize package.json
cat > package.json << EOL
{
"name": "laconic-deploy",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "echo \"Not implemented yet\" && exit 0",
"build": "echo \"Not implemented yet\" && exit 0",
"lint": "echo \"Not implemented yet\" && exit 0",
"clean": "rm -rf node_modules .turbo"
}
}
EOL
cd ../..
8. Setup Shared Packages
8.1 UI Package
# Create UI package structure
mkdir -p packages/ui/{src,dist}
mkdir -p packages/ui/src/{components,hooks,styles}
# Create UI package.json
cat > packages/ui/package.json << EOL
{
"name": "@qwrk/ui",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": {
".": "./dist/index.js",
"./styles.css": "./dist/index.css"
},
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"lint": "biome lint src/",
"format": "biome format --write src/",
"clean": "rm -rf .turbo node_modules dist"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"dependencies": {
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"lucide-react": "^0.336.0",
"tailwind-merge": "^2.2.1"
},
"devDependencies": {
"@types/react": "^19.0.0",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.31",
"tailwindcss": "^3.3.5",
"tsup": "^8.0.1",
"typescript": "^5.3.3"
}
}
EOL
# Create tsup configuration
cat > packages/ui/tsup.config.ts << EOL
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
splitting: false,
sourcemap: true,
clean: true,
external: ['react', 'react-dom'],
treeshake: true,
minify: true
});
EOL
# Create package entry point
cat > packages/ui/src/index.ts << EOL
export * from './components';
export * from './hooks';
EOL
# Create exports file
cat > packages/ui/src/components/index.ts << EOL
// Export components
EOL
cat > packages/ui/src/hooks/index.ts << EOL
// Export hooks
EOL
8.2 Utils Package
# Create Utils package structure
mkdir -p packages/utils/{src,dist}
mkdir -p packages/utils/src/{helpers,constants}
# Create Utils package.json
cat > packages/utils/package.json << EOL
{
"name": "@qwrk/utils",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"lint": "biome lint src/",
"format": "biome format --write src/",
"clean": "rm -rf .turbo node_modules dist"
},
"devDependencies": {
"tsup": "^8.0.1",
"typescript": "^5.3.3"
}
}
EOL
# Create tsup configuration
cat > packages/utils/tsup.config.ts << EOL
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
splitting: false,
sourcemap: true,
clean: true,
treeshake: true
});
EOL
# Create package entry point
cat > packages/utils/src/index.ts << EOL
export * from './helpers';
export * from './constants';
EOL
# Create exports files
cat > packages/utils/src/helpers/index.ts << EOL
// Export helper functions
EOL
cat > packages/utils/src/constants/index.ts << EOL
// Export constants
EOL
8.3 Services Package
# Create Services package structure
mkdir -p packages/services/{src,dist}
mkdir -p packages/services/src/{graphql,deployer}
# Create Services package.json
cat > packages/services/package.json << EOL
{
"name": "@qwrk/services",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": {
".": "./dist/index.js",
"./graphql": "./dist/graphql/index.js",
"./deployer": "./dist/deployer/index.js"
},
"types": {
".": "./dist/index.d.ts",
"./graphql": "./dist/graphql/index.d.ts",
"./deployer": "./dist/deployer/index.d.ts"
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"lint": "biome lint src/",
"format": "biome format --write src/",
"clean": "rm -rf .turbo node_modules dist"
},
"dependencies": {
"@apollo/client": "^3.9.5",
"graphql": "^16.8.1"
},
"devDependencies": {
"tsup": "^8.0.1",
"typescript": "^5.3.3"
}
}
EOL
# Create tsup configuration
cat > packages/services/tsup.config.ts << EOL
import { defineConfig } from 'tsup';
export default defineConfig({
entry: [
'src/index.ts',
'src/graphql/index.ts',
'src/deployer/index.ts'
],
format: ['esm'],
dts: true,
splitting: true,
sourcemap: true,
clean: true,
treeshake: true
});
EOL
# Create package entry points
cat > packages/services/src/index.ts << EOL
export * from './graphql';
export * from './deployer';
EOL
cat > packages/services/src/graphql/index.ts << EOL
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
export function createApolloClient(apiUrl = '/api/graphql') {
const httpLink = new HttpLink({
uri: apiUrl,
});
const authLink = setContext((_, { headers }) => {
// Get the authentication token from local storage if it exists
const token = typeof window !== 'undefined'
? localStorage.getItem('authToken')
: null;
// Return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? \`Bearer \${token}\` : '',
},
};
});
return new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-and-network',
},
},
});
}
// Export other GraphQL utilities
export * from './hooks';
export * from './fragments';
EOL
# Create placeholder files
cat > packages/services/src/graphql/hooks.ts << EOL
// GraphQL hooks
EOL
cat > packages/services/src/graphql/fragments.ts << EOL
// GraphQL fragments
EOL
cat > packages/services/src/deployer/index.ts << EOL
// Deployer service implementation
export interface DeployOptions {
projectId: string;
branch: string;
commitHash: string;
}
export const deployProject = async (options: DeployOptions) => {
// Implementation
};
EOL
9. Configure Next.js App
9.1 GraphQL API Route
# Create GraphQL API route
mkdir -p apps/laconic-web/src/app/api/graphql
mkdir -p apps/laconic-web/src/graphql
# Create route handler
cat > apps/laconic-web/src/app/api/graphql/route.ts << EOL
import { ApolloServer } from '@apollo/server';
import { startServerAndCreateNextHandler } from '@as-integrations/next';
import { typeDefs } from '@/graphql/schema';
import { resolvers } from '@/graphql/resolvers';
const server = new ApolloServer({
typeDefs,
resolvers,
});
const handler = startServerAndCreateNextHandler(server, {
context: async (req) => {
// Extract user information from request headers or session
const user = req.headers.get('authorization')?.replace('Bearer ', '');
return {
user,
};
},
});
export { handler as GET, handler as POST };
EOL
# Add package.json dependencies
cd apps/laconic-web
pnpm add @apollo/server @as-integrations/next graphql
cd ../..
9.2 GraphQL Schema and Resolvers
# Create GraphQL schema
cat > apps/laconic-web/src/graphql/schema.ts << EOL
import { gql } from 'graphql-tag';
export const typeDefs = gql\`
enum Role {
Owner
Maintainer
Reader
}
enum Permission {
View
Edit
}
enum Environment {
Production
Preview
Development
}
enum DeploymentStatus {
Building
Ready
Error
Deleting
}
enum AuctionStatus {
completed
reveal
commit
expired
}
enum DomainStatus {
Live
Pending
}
type User {
id: String!
name: String
email: String!
organizations: [Organization!]
projects: [Project!]
isVerified: Boolean!
createdAt: String!
updatedAt: String!
gitHubToken: String
}
type Organization {
id: String!
name: String!
slug: String!
projects: [Project!]
createdAt: String!
updatedAt: String!
members: [OrganizationMember!]
}
type OrganizationMember {
id: String!
member: User!
role: Role!
createdAt: String!
updatedAt: String!
}
type Project {
id: String!
owner: User!
deployments: [Deployment!]
name: String!
repository: String!
prodBranch: String!
description: String
deployers: [Deployer!]
auctionId: String
fundsReleased: Boolean
template: String
framework: String
paymentAddress: String!
txHash: String!
webhooks: [String!]
members: [ProjectMember!]
environmentVariables: [EnvironmentVariable!]
createdAt: String!
updatedAt: String!
organization: Organization!
icon: String
baseDomains: [String!]
}
# Rest of the schema types...
type Query {
user: User!
organizations: [Organization!]
projects: [Project!]
projectsInOrganization(organizationSlug: String!): [Project!]
project(projectId: String!): Project
# Rest of the queries...
}
type Mutation {
addProjectMember(projectId: String!, data: AddProjectMemberInput): Boolean!
updateProjectMember(
projectMemberId: String!
data: UpdateProjectMemberInput
): Boolean!
# Rest of the mutations...
}
\`;
EOL
# Create resolvers
cat > apps/laconic-web/src/graphql/resolvers.ts << EOL
// Sample resolvers implementation
export const resolvers = {
Query: {
user: (_: any, __: any, context: any) => {
// Return the authenticated user
return context.user;
},
organizations: async (_: any, __: any, context: any) => {
// Implement fetching organizations for the user
return [];
},
// Implement other resolvers...
},
Mutation: {
// Implement mutations...
}
};
EOL
9.3 Authentication Setup
# Create authentication middleware
cat > apps/laconic-web/src/middleware.ts << EOL
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const authToken = request.cookies.get('authToken')?.value;
const isAuthRoute = request.nextUrl.pathname.startsWith('/auth');
const isApiRoute = request.nextUrl.pathname.startsWith('/api');
if (!authToken && !isAuthRoute && !isApiRoute) {
return NextResponse.redirect(new URL('/auth/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
EOL
10. App Router Structure Implementation
# Create necessary directories for App Router
mkdir -p apps/laconic-web/src/app/{dashboard,projects,settings,auth}
mkdir -p apps/laconic-web/src/app/projects/create/\[step\]
mkdir -p apps/laconic-web/src/app/projects/\[id\]/{settings,branches}
mkdir -p apps/laconic-web/src/app/projects/\[id\]/branches/{create,\[branchId\]}
mkdir -p apps/laconic-web/src/app/auth/{login,register}
# Create root page
cat > apps/laconic-web/src/app/page.tsx << EOL
import { redirect } from 'next/navigation';
export default function Home() {
redirect('/dashboard');
}
EOL
# Create dashboard page
cat > apps/laconic-web/src/app/dashboard/page.tsx << EOL
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
</div>
);
}
EOL
# Create loading and error states
cat > apps/laconic-web/src/app/dashboard/loading.tsx << EOL
export default function DashboardLoading() {
return <div>Loading dashboard...</div>;
}
EOL
cat > apps/laconic-web/src/app/dashboard/error.tsx << EOL
'use client';
import { useEffect } from 'react';
export default function DashboardError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error);
}, [error]);
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
EOL
# Create projects listing page
cat > apps/laconic-web/src/app/projects/page.tsx << EOL
export default function ProjectsPage() {
return (
<div>
<h1>Projects</h1>
</div>
);
}
EOL
# Create project creation page
cat > apps/laconic-web/src/app/projects/create/page.tsx << EOL
export default function CreateProjectPage() {
return (
<div>
<h1>Create Project</h1>
</div>
);
}
EOL
# Create project step page
cat > apps/laconic-web/src/app/projects/create/\[step\]/page.tsx << EOL
export default function CreateProjectStepPage({
params,
}: {
params: { step: string };
}) {
return (
<div>
<h1>Create Project - Step {params.step}</h1>
</div>
);
}
EOL
# Create login page
cat > apps/laconic-web/src/app/auth/login/page.tsx << EOL
export default function LoginPage() {
return (
<div>
<h1>Login</h1>
</div>
);
}
EOL
# Create register page
cat > apps/laconic-web/src/app/auth/register/page.tsx << EOL
export default function RegisterPage() {
return (
<div>
<h1>Register</h1>
</div>
);
}
EOL
Testing the Setup
- Install dependencies:
pnpm install
- Start the development server:
pnpm dev
Migration Strategy
flowchart TD
A[Initialize Turborepo] --> B[Configure Biome & Packages]
B --> C[Create Next.js Applications]
C --> D[Set Up API Routes]
D --> E[Implement GraphQL Client]
E --> F[Port UI Components]
F --> G[Implement App Router Structure]
G --> H[Migrate Core Features]
subgraph "Phase 1: Infrastructure"
A
B
C
end
subgraph "Phase 2: API Layer"
D
E
end
subgraph "Phase 3: UI Migration"
F
G
end
subgraph "Phase 4: Application Logic"
H
end
Key Migration Tasks
-
Infrastructure Setup:
- Initialize Turborepo structure ✓
- Configure Biome for linting/formatting ✓
- Set up package structure ✓
-
API Layer Implementation:
- Migrate GraphQL schema ✓
- Implement API routes ✓
- Set up Apollo client ✓
-
UI Component Migration:
- Create shared UI package ✓
- Port core UI components
- Implement design system foundation
-
Application Logic:
- Implement authentication flow
- Create project management features
- Implement deployment functionality
Repository Structural Migration
For existing repositories that require Turborepo conversion, follow this structural migration approach:
flowchart TB
subgraph "Initial State"
A[Existing Repository]
B[Frontend Application]
C[Backend Services]
D[Shared Libraries]
end
subgraph "Migration Process"
E[Create Migration Branch]
F[Add Turborepo Config]
G[Restructure Packages]
H[Refactor Dependencies]
I[Implement Build System]
end
subgraph "Final State"
J[apps/laconic-web]
K[apps/laconic-deploy]
L[apps/docs]
M[packages/ui]
N[packages/utils]
O[packages/services]
end
A --> E
E --> F
F --> G
G --> H
H --> I
I --> J & K & L & M & N & O
Repository Structure Analysis
Before migration, analyze the existing code structure:
# Identify package dependencies
find . -name "package.json" -not -path "*/node_modules/*" -exec jq -r '.dependencies | keys[]' {} \; | sort | uniq -c | sort -nr
# Identify file distribution
find . -type f -name "*.ts*" | grep -v "node_modules" | grep -v ".turbo" | grep -v "dist" | wc -l
# Analyze potential shared components
find . -type f -name "*.tsx" -not -path "*/node_modules/*" | xargs grep -l "export" | sort
Incremental Migration Strategy
-
Coexistence Phase
- Implement Turborepo alongside existing structure
- Gradually move components to new architecture
- Maintain backward compatibility during transition
-
Dual Build System
- Configure builds for both legacy and new structure
- Implement feature flags for incremental adoption
- Monitor performance metrics across both systems
-
Final Migration
- Complete transition to Turborepo architecture
- Remove legacy configuration
- Optimize build pipeline for new structure
Best Practices and Guidelines
TypeScript Configuration
Ensure strict type checking across all packages:
// tsconfig.json base configuration
{
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
GraphQL Code Generation
For type-safe GraphQL operations, integrate GraphQL Code Generator:
# Install GraphQL Code Generator
pnpm add -D -w @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo
# Create codegen configuration
cat > codegen.ts << EOL
import { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'apps/laconic-web/src/graphql/schema.ts',
documents: ['apps/laconic-web/src/**/*.tsx', 'packages/services/src/**/*.ts'],
generates: {
'packages/services/src/graphql/generated/': {
preset: 'client',
plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'],
},
},
};
export default config;
EOL
# Add script to root package.json
# "codegen": "graphql-codegen"
Continuous Integration
Set up GitHub Actions workflow:
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 8.15.1
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Lint
run: pnpm lint
- name: Build
run: pnpm build
- name: Test
run: pnpm test
Conclusion
This technical specification provides a comprehensive guide for migrating the qwrk-laconic repository to a modern Turborepo structure with Next.js 15. By following this guide, you can create a modular, maintainable monorepo that leverages the latest technologies and best practices for web development.
The migration approach prioritizes incremental adoption, allowing for gradual transition while maintaining backward compatibility with existing systems.