Skip to content

feat(enable): add cmd to enable features #62

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ npx nuxthub <command>
## Usage

```bash
USAGE nuxthub init|deploy|link|unlink|open|manage|login|logout|logs|whoami
USAGE nuxthub init|deploy|disable|link|unlink|open|manage|login|logout|logs|whoami|database|ensure|enable

COMMANDS

init Initialize a fresh NuxtHUb project, alias of nuxi init -t hub.
deploy Deploy your project to NuxtHub.
disable Disable a specific NuxtHub feature in your project.
preview Preview your project locally (using wrangler pages dev).
link Link a local directory to a NuxtHub project.
unlink Unlink a local directory from a NuxtHub project.
Expand All @@ -34,8 +35,9 @@ COMMANDS
logout Logout the current authenticated user.
logs Display the logs of a deployment.
whoami Shows the username of the currently logged in user.
database Manage database migrations.
ensure Ensure the NuxtHub Core module is installed and registered in the project.
database Manage database migrations.
ensure Ensure the NuxtHub Core module is installed and registered in the project.
enable Enable a specific NuxtHub feature in your project.

Use nuxthub <command> --help for more information about a command.
```
Expand All @@ -57,7 +59,7 @@ nuxthub deploy --preview

See [how to deploy with a GitHub action](https://hub.nuxt.com/docs/getting-started/deploy#github-action).

https://github.com/user-attachments/assets/9d7d9206-1ee3-4f8f-a29d-8b9dd09b9913
[https://github.com/user-attachments/assets/9d7d9206-1ee3-4f8f-a29d-8b9dd09b9913](https://github.com/user-attachments/assets/9d7d9206-1ee3-4f8f-a29d-8b9dd09b9913)

## Preview before deploy

Expand All @@ -68,6 +70,7 @@ nuxthub preview
```

Current limitations:

- The `--remote` flag is not supported
- `hubAI()` will ask you connect within the terminal with wrangler
- `hubBrowser()` is not supported as not supported by `wrangler pages dev`
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"jiti": "^2.4.2",
"listhen": "^1.9.0",
"load-json-file": "^7.0.1",
"magicast": "^0.3.5",
"mime": "^4.0.7",
"ofetch": "^1.4.1",
"open": "^10.1.1",
Expand Down
24 changes: 19 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

93 changes: 93 additions & 0 deletions src/commands/disable.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { consola } from 'consola'
import { colors } from 'consola/utils'
import { defineCommand } from 'citty'
import {
getNuxtConfig,
getFeatureConfig,
isValidFeature,
isFeatureEnabled,
FEATURE_CONFIG
} from '../utils/index.mjs'
import { updateConfig } from 'c12/update'

function removeFeatureConfig(config, featureConfig) {
const featureKey = featureConfig.key

if (featureKey && config.hub && config.hub[featureKey] === true) {
delete config.hub[featureKey]

if (Object.keys(config.hub).length === 0) {
delete config.hub
}
}

if (featureConfig.nitroExperimental && config.nitro?.experimental) {
Object.keys(featureConfig.nitroExperimental).forEach(key => {
if (config.nitro.experimental[key] !== undefined) {
delete config.nitro.experimental[key]
}
})

if (Object.keys(config.nitro.experimental).length === 0) {
delete config.nitro.experimental

if (Object.keys(config.nitro).length === 0) {
delete config.nitro
}
}
}
}

export default defineCommand({
meta: {
name: 'disable',
description: 'Disable a specific NuxtHub feature in your project.',
},
args: {
feature: {
type: 'positional',
description: 'The NuxtHub feature to disable (ai, autorag, blob, browser, cache, database, kv, openapi, realtime, vectorize)',
required: true,
}
},
async run({ args }) {
const feature = args.feature.toLowerCase()

if (!isValidFeature(feature)) {
consola.error(`Invalid feature: ${colors.red(feature)}`)
consola.info(`Available features: ${Object.keys(FEATURE_CONFIG).map(f => colors.cyan(f)).join(', ')}`)
return 1
}

const featureConfig = getFeatureConfig(feature)
const cwd = process.cwd()
const nuxtConfig = await getNuxtConfig(cwd)

// check if feature is enabled
if (!isFeatureEnabled(nuxtConfig, featureConfig)) {
consola.info(`NuxtHub ${colors.cyan(feature)} feature is not enabled in this project.`)
return 0
}

try {
await updateConfig({
cwd,
configFile: 'nuxt.config',
onUpdate: (config) => {
if (!isFeatureEnabled(config, featureConfig)) {
consola.info(`NuxtHub ${colors.cyan(feature)} feature is not enabled in this project.`)
return false
}
removeFeatureConfig(config, featureConfig)
}
})

consola.success(`NuxtHub ${colors.cyan(feature)} feature has been disabled in your project.`)
} catch (error) {
consola.error(`Failed to disable ${colors.cyan(feature)}: ${error.message}`)
return 1
}

return 0
},
})
153 changes: 153 additions & 0 deletions src/commands/enable.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { consola } from 'consola'
import { colors } from 'consola/utils'
import { defineCommand } from 'citty'
import { getNuxtConfig, getFeatureConfig, isValidFeature, isFeatureEnabled, FEATURE_CONFIG } from '../utils/index.mjs'
import { updateConfig } from 'c12/update'

function generateInitialConfig(featureKey, featureConfig) {
let configContent = `export default defineNuxtConfig({`

// add hub config if featureKey exists
if (featureKey) {
configContent += `
hub: {
${featureKey}: true
}`
}

if (featureConfig.nitroExperimental) {
// add comma if we already have hub config
configContent += featureKey ? `,` : ``
configContent += `
nitro: {
experimental: {`

Object.entries(featureConfig.nitroExperimental).forEach(([feature, value], index, array) => {
configContent += `
${feature}: ${value}`
// add comma if it's not the last entry
if (index < array.length - 1) {
configContent += ','
}
})

configContent += `
}
}`
}

configContent += `
})`

return configContent
}

function applyAdditionalConfig(config, featureConfig) {
if (featureConfig.nitroExperimental) {
config.nitro = config.nitro || {}
config.nitro.experimental = config.nitro.experimental || {}

Object.entries(featureConfig.nitroExperimental).forEach(([feature, value]) => {
config.nitro.experimental[feature] = value
})
}
}

export default defineCommand({
meta: {
name: 'enable',
description: 'Enable a specific NuxtHub feature in your project.',
},
args: {
feature: {
type: 'positional',
description: 'The NuxtHub feature to enable (ai, autorag, blob, browser, cache, database, kv, openapi, realtime, vectorize)',
required: true,
}
},
async run({ args }) {
const feature = args.feature.toLowerCase()

if (!isValidFeature(feature)) {
consola.error(`Invalid feature: ${colors.red(feature)}`)
consola.info(`Available features: ${Object.keys(FEATURE_CONFIG).map(f => colors.cyan(f)).join(', ')}`)
return 1
}

const featureConfig = getFeatureConfig(feature)
const featureKey = featureConfig.key
const requiresConfig = featureConfig.requiresConfig

const cwd = process.cwd()
const nuxtConfig = await getNuxtConfig(cwd)

// check if feature is enabled
if (isFeatureEnabled(nuxtConfig, featureConfig)) {
consola.info(`NuxtHub ${colors.cyan(feature)} feature is already enabled in this project.`)
} else {

if (requiresConfig) {
consola.warn(`The ${colors.cyan(feature)} feature requires additional configuration and cannot be enabled with just a flag.`)
consola.info(`Please refer to the documentation for configuration details: ${colors.underline(featureConfig.docs)}`)

// managing exit codes
return 1
}

try {
let configCreated = false
const { created } = await updateConfig({
cwd,
configFile: 'nuxt.config',

// if the config file doesn't exist, create it
onCreate: () => {
configCreated = true
return generateInitialConfig(featureKey, featureConfig)
},

onUpdate: (config) => {
if (configCreated) {
if (featureKey) {
config.hub = config.hub || {}
config.hub[featureKey] = true
}

applyAdditionalConfig(config, featureConfig)
return
}

if (isFeatureEnabled(config, featureConfig)) {
consola.info(`NuxtHub ${colors.cyan(feature)} feature is already enabled in this project.`)
return false
}

if (featureKey) {
config.hub = config.hub || {}
config.hub[featureKey] = true
}

applyAdditionalConfig(config, featureConfig)
}
})

if (created) {
consola.success(`Created new Nuxt config with ${colors.cyan(feature)} feature enabled.`)
} else {
consola.success(`NuxtHub ${colors.cyan(feature)} feature has been enabled in your project.`)
}
} catch (error) {
consola.error(`Failed to enable ${colors.cyan(feature)}: ${error.message}`)
return 1
}
}

const docsUrl = featureConfig.docs

if (!requiresConfig || !isFeatureEnabled(nuxtConfig, featureConfig)) {
consola.info(`Learn more about the ${colors.cyan(feature)} feature at: ${colors.underline(docsUrl)}`)
}

return 0
},
})
Loading