Skip to content

Commit

Permalink
feat(create-pylon): add support for Deno runtime (#42)
Browse files Browse the repository at this point in the history
* feat(create-pylon): add support for Deno runtime

- Added support for detecting Deno as a package manager
- Created a new file, detect-pm.ts, to handle package manager detection logic
- Updated index.ts to include Deno as a runtime option
- Added Deno-specific files and configurations to the default Deno template

closes #41

* docs: add deno as supported runtime
  • Loading branch information
schettn authored Nov 7, 2024
1 parent 7345f9c commit de29921
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 39 deletions.
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

1 comment on commit de29921

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for pylon-docs ready!

✅ Preview
https://pylon-docs-h6jd6u8xn-schettns-projects.vercel.app

Built with commit de29921.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.