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

feat(create-pylon): add support for Deno runtime #42

Merged
merged 2 commits into from
Nov 7, 2024
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
2 changes: 1 addition & 1 deletion docs/pages/docs/faq.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,4 @@ If you encounter any issues or have suggestions for improvement, please feel fre
~~Currently, Pylon supports the [Bun](https://bun.sh) runtime. However, we have a open issue to support this [Runtimes](https://hono.dev/docs/getting-started/basic).~~
~~If you would like to see support for other runtimes, please let us know by upvoting [this issue](https://github.com/getcronit/pylon/issues/6) on GitHub.~~

As of Pylon v2, the framework now supports multiple runtimes, including Bun, Node.js and Cloudflare Workers. Other runtimes are also supported but require manual setup. For more information on the supported runtimes, refer to the [release notes](/docs/release-notes/v2.0.mdx).
As of Pylon v2, the framework now supports multiple runtimes, including Bun, Node.js, Cloudflare Workers and Deno. Other runtimes are also supported but require manual setup. For more information on the supported runtimes, refer to the [release notes](/docs/release-notes/v2.0.mdx).
4 changes: 2 additions & 2 deletions docs/pages/docs/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ npm create pylon my-pylon@latest
```

<Callout type="note" title="Info">
Pylon now supports multiple runtimes, including Node.js, Bun and Cloudflare
Workers.
Pylon now supports multiple runtimes, including Node.js, Bun, Cloudflare
Workers, and Deno.
</Callout>

This will create a new directory called `my-pylon` with a basic Pylon project structure.
Expand Down
1 change: 0 additions & 1 deletion packages/create-pylon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"chalk": "^5.3.0",
"commander": "^12.1.0",
"consola": "^3.2.3",
"detect-package-manager": "^3.0.2",
"@getcronit/pylon-telemetry": "^1.0.0"
},
"engines": {
Expand Down
145 changes: 145 additions & 0 deletions packages/create-pylon/src/detect-pm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import * as fs from 'node:fs'
import * as path from 'node:path'
import process from 'node:process'
import {execSync} from 'node:child_process'
import consola from 'consola'

// Helper function to check if a command exists
function isCommandAvailable(command: string): boolean {
try {
execSync(`${command} --version`, {stdio: 'ignore'})
return true
} catch (e) {
console.error(e)
return false
}
}

// Detect Bun
function isBun(): boolean {
// @ts-ignore: Bun may not be defined
return typeof Bun !== 'undefined' && isCommandAvailable('bun')
}

// Detect npm
function isNpm(): boolean {
return process.env.npm_execpath?.includes('npm') ?? false
}

// Detect Yarn
function isYarn(): boolean {
return process.env.npm_execpath?.includes('yarn') ?? false
}

// Detect Deno
function isDeno(): boolean {
// @ts-ignore: Deno may not be defined
return typeof Deno !== 'undefined' && isCommandAvailable('deno')
}

// Detect pnpm
function isPnpm(): boolean {
return process.env.npm_execpath?.includes('pnpm') ?? false
}

// Detect based on lock files
function detectByLockFiles(cwd: string): PackageManager | null {
if (fs.existsSync(path.join(cwd, 'bun.lockb'))) {
return 'bun'
}
if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {
return 'npm'
}
if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {
return 'yarn'
}
if (
fs.existsSync(path.join(cwd, 'deno.json')) ||
fs.existsSync(path.join(cwd, 'deno.lock'))
) {
return 'deno'
}
if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {
return 'pnpm'
}
return null
}

export type PackageManager =
| 'bun'
| 'npm'
| 'yarn'
| 'pnpm'
| 'deno'
| 'unknown'

// Main detection function
export function detectPackageManager({
preferredPm,
cwd = process.cwd()
}: {
preferredPm?: PackageManager
cwd?: string
}): PackageManager {
// Check the preferred package manager first
if (preferredPm && isCommandAvailable(preferredPm)) {
return preferredPm
}

// Proceed with detection logic
if (isBun()) {
return 'bun'
}
if (isNpm()) {
return 'npm'
}
if (isPnpm()) {
return 'pnpm'
}
if (isDeno()) {
return 'deno'
}
if (isYarn()) {
return 'yarn'
}

// Fallback to lock file detection
const lockFileDetection = detectByLockFiles(cwd)
if (lockFileDetection) {
consola.info(`Detected package manager by lock file: ${lockFileDetection}`)
if (isCommandAvailable(lockFileDetection)) {
return lockFileDetection
} else {
consola.warn(
`Lock file detected, but ${lockFileDetection} is not installed.`
)
}
}

return 'unknown'
}

type PackageManagerScript =
| 'bun'
| 'npm run'
| 'yarn'
| 'pnpm run'
| 'deno task'

// Run script detection
export function getRunScript(pm: PackageManager): PackageManagerScript {
switch (pm) {
case 'bun':
return 'bun'
case 'npm':
return 'npm run'
case 'yarn':
return 'yarn'
case 'pnpm':
return 'pnpm run'
case 'deno':
return 'deno task'
default:
throw new Error('Unknown package manager')
}
}
77 changes: 45 additions & 32 deletions packages/create-pylon/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ const runtimes: {
name: 'Cloudflare Workers',
website: 'https://workers.cloudflare.com',
templates: ['default']
},
{
key: 'deno',
name: 'Deno',
website: 'https://deno.land',
templates: ['default']
}
]

Expand Down Expand Up @@ -217,30 +223,15 @@ const createTemplate = async (options: {
consola.success(`Pylon created`)
}

import {detect} from 'detect-package-manager'
import {spawnSync} from 'child_process'
import {detectPackageManager, getRunScript, PackageManager} from './detect-pm'

const installDependencies = async (args: {
target: string
packageManager?: string
packageManager: PackageManager
}) => {
const target = path.resolve(args.target)

console.log('target', target)

if (!args.packageManager) {
args.packageManager = await detect({
cwd: target,
includeGlobalBun: true
})
}

if (!args.packageManager) {
throw new Error('No package manager found')
}

// yarn', 'npm', or 'pnpm', 'bun'
const {packageManager} = args
const packageManager = args.packageManager

let command = ''

Expand All @@ -257,6 +248,9 @@ const installDependencies = async (args: {
case 'bun':
command = 'bun install'
break
case 'deno':
command = 'deno install'
break
default:
throw new Error(`Invalid package manager: ${packageManager}`)
}
Expand Down Expand Up @@ -304,12 +298,22 @@ type ArgOptions = {
install: boolean
runtime: string
template: string
packageManager?: string
packageManager?: PackageManager
client?: boolean
clientPath?: string
clientPort?: string
}

const getPreferredPmByRuntime = (
runtime: string
): PackageManager | undefined => {
if (runtime === 'bun') {
return 'bun'
} else if (runtime === 'deno') {
return 'deno'
}
}

async function main(
targetDir: string | undefined,
options: ArgOptions,
Expand Down Expand Up @@ -413,11 +417,10 @@ async function main(
target
})

let packageManager = packageManagerArg

if (runtimeName === 'bun' && !packageManager) {
packageManager = 'bun'
}
const packageManager = detectPackageManager({
preferredPm: getPreferredPmByRuntime(runtime.key),
cwd: target
})

if (install) {
await installDependencies({target, packageManager})
Expand Down Expand Up @@ -465,29 +468,39 @@ async function main(

consola.start(`Updating pylon dev script to generate client`)

const devScriptPath = path.join(target, 'package.json')
let packagePath: string
let scriptKey: string
if (runtime.key === 'deno') {
packagePath = path.join(target, 'deno.json')
scriptKey = 'tasks'
} else {
packagePath = path.join(target, 'package.json')
scriptKey = 'scripts'
}

const devScript = JSON.parse(fs.readFileSync(devScriptPath, 'utf-8'))
const devScript = JSON.parse(fs.readFileSync(packagePath, 'utf-8'))

devScript.scripts = {
...devScript.scripts,
devScript[scriptKey] = {
...devScript[scriptKey],
dev:
devScript.scripts.dev +
devScript[scriptKey].dev +
` --client --client-port ${clientPort} --client-path ${clientPath}`
}

fs.writeFileSync(devScriptPath, JSON.stringify(devScript, null, 2))
fs.writeFileSync(packagePath, JSON.stringify(devScript, null, 2))

consola.success(`Pylon dev script updated`)
}

const runScript = getRunScript(packageManager)

const message = `
🎉 ${chalk.green.bold('Pylon created successfully.')}

💻 ${chalk.cyan.bold('Continue Developing')}
${chalk.yellow('Change directories:')} cd ${chalk.blue(target)}
${chalk.yellow('Start dev server:')} npm run start
${chalk.yellow('Deploy:')} npm run deploy
${chalk.yellow('Start dev server:')} ${runScript} dev
${chalk.yellow('Deploy:')} ${runScript} deploy

📖 ${chalk.cyan.bold('Explore Documentation')}
${chalk.underline.blue('https://pylon.cronit.io/docs')}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"denoland.vscode-deno"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"deno.enablePaths": [
"./"
],
"editor.inlayHints.enabled": "off"
}
12 changes: 12 additions & 0 deletions packages/create-pylon/templates/deno/default/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM denoland/deno

EXPOSE 3000

WORKDIR /app

ADD . /app

RUN deno install
RUN deno task build

CMD ["run", "-A", ".pylon/index.js"]
15 changes: 15 additions & 0 deletions packages/create-pylon/templates/deno/default/deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"imports": {
"@getcronit/pylon-dev": "npm:@getcronit/pylon-dev@^1.0.1",
"@getcronit/pylon": "npm:@getcronit/pylon@^2.2.1"
},
"tasks": {
"dev": "pylon dev -c 'deno run -A .pylon/index.js --config tsconfig.json'",
"build": "pylon build"
},
"compilerOptions": {
"jsx": "precompile",
"jsxImportSource": "hono/jsx"
},
"nodeModulesDir": "auto"
}
17 changes: 17 additions & 0 deletions packages/create-pylon/templates/deno/default/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {app} from '@getcronit/pylon'

export const graphql = {
Query: {
hello: () => {
return 'Hello, world!'
}
},
Mutation: {}
}

Deno.serve(
{
port: 3000
},
app.fetch
)
6 changes: 3 additions & 3 deletions packages/pylon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ docker run -p 3000:3000 my-pylon

Designed to be flexible, Pylon can be run on various platforms, including:

| <img src="https://bun.sh/logo.svg" width="48px" height="48px" alt="Bun.js logo"> | <img src="https://nodejs.org/static/logos/jsIconWhite.svg" width="48px" height="48px" alt="Node.JS"> | <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQgW7cAlhYN23JXGKy9Uji4Ae2mnHOR9eXX9g&s" width="48px" height="48px" alt="Gmail logo"> |
| :------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------: |
| Bun.js | Node.js | Cloudflare Workers |
| <img src="https://bun.sh/logo.svg" width="48px" height="48px" alt="Bun.js logo"> | <img src="https://nodejs.org/static/logos/jsIconWhite.svg" width="48px" height="48px" alt="Node.JS"> | <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQgW7cAlhYN23JXGKy9Uji4Ae2mnHOR9eXX9g&s" width="48px" height="48px" alt="Gmail logo"> | <img src="https://deno.land/logo.svg" width="48px" height="48px" alt="Deno logo"> |
| :------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------: | --------------------------------------------------------------------------------- |
| Bun.js | Node.js | Cloudflare Workers | Deno |

## Features

Expand Down
Loading