Skip to content

Commit

Permalink
feat: pergel core (#54)
Browse files Browse the repository at this point in the history
* feat: add checkOptions function to module setup

* feat: pergelSetup

* feat: pergel modules

* update README.md with TODO list
  • Loading branch information
productdevbook authored Dec 10, 2023
1 parent 7b4f26d commit 84a2008
Show file tree
Hide file tree
Showing 15 changed files with 638 additions and 99 deletions.
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default antfu(
'n/prefer-global/process': 'off',
'node/prefer-global/process': 'off',
'ts/consistent-type-definitions': 'off',
'ts/ban-ts-comment': 'off',
},
},
{
Expand Down
2 changes: 0 additions & 2 deletions packages/nuxt/.eslintignore

This file was deleted.

4 changes: 0 additions & 4 deletions packages/nuxt/.eslintrc

This file was deleted.

96 changes: 4 additions & 92 deletions packages/nuxt/README.md
Original file line number Diff line number Diff line change
@@ -1,94 +1,6 @@
<!--
Get your module up and running quickly.
## Pergel Nuxt Module

Find and replace all on all files (CMD+SHIFT+F):
- Name: My Module
- Package name: my-module
- Description: My new Nuxt module
-->
# TODO

# My Module

[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
[![Nuxt][nuxt-src]][nuxt-href]

My new Nuxt module for doing amazing things.

- [&nbsp;Release Notes](/CHANGELOG.md)
<!-- - [🏀 Online playground](https://stackblitz.com/github/your-org/my-module?file=playground%2Fapp.vue) -->
<!-- - [📖 &nbsp;Documentation](https://example.com) -->

## Features

<!-- Highlight some of the features your module provide here -->
-&nbsp;Foo
- 🚠 &nbsp;Bar
- 🌲 &nbsp;Baz

## Quick Setup

1. Add `my-module` dependency to your project

```bash
# Using pnpm
pnpm add -D my-module

# Using yarn
yarn add --dev my-module

# Using npm
npm install --save-dev my-module
```

2. Add `my-module` to the `modules` section of `nuxt.config.ts`

```js
export default defineNuxtConfig({
modules: [
'my-module'
]
})
```

That's it! You can now use My Module in your Nuxt app ✨

## Development

```bash
# Install dependencies
npm install

# Generate type stubs
npm run dev:prepare

# Develop with the playground
npm run dev

# Build the playground
npm run dev:build

# Run ESLint
npm run lint

# Run Vitest
npm run test
npm run test:watch

# Release new version
npm run release
```

<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/my-module/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-version-href]: https://npmjs.com/package/my-module

[npm-downloads-src]: https://img.shields.io/npm/dm/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-downloads-href]: https://npmjs.com/package/my-module

[license-src]: https://img.shields.io/npm/l/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
[license-href]: https://npmjs.com/package/my-module

[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
[nuxt-href]: https://nuxt.com
- [] Add tests
- [] Add docs
2 changes: 1 addition & 1 deletion packages/nuxt/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineBuildConfig } from 'unbuild'
import pkg from './package.json'

const external = [
// eslint-disable-next-line ts/ban-ts-comment

// @ts-expect-error
...Object.keys(pkg.peerDependencies || {}),
]
Expand Down
104 changes: 104 additions & 0 deletions packages/nuxt/src/core/setupModules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { join } from 'node:path'
import { readdirSync } from 'node:fs'
import type { Nuxt } from '@nuxt/schema'
import type { Resolver } from '@nuxt/kit'
import consola from 'consola'
import type { PergelModule, ResolvedPergelOptions } from './types'
import { generatePergelTemplate } from './utils/pergelFunctionTemplate'

export async function setupModules(data: {
resolver: Resolver
nuxt: Nuxt
options: ResolvedPergelOptions
}) {
const projects = data.options.rootOptions.projects
const modulesMap = new Map<string, PergelModule<any>>()

for await (const [projectKey, modules] of Object.entries(projects)) {
for await (const [moduleKey, moduleValue] of Object.entries(modules)) {
if (typeof moduleValue === 'boolean' && moduleValue === false)
continue

if (
(typeof moduleValue === 'object' && moduleValue === null)
|| (typeof moduleValue === 'object' && !moduleValue)
)
continue

if (typeof moduleValue === 'string' && moduleValue === '')
continue
modulesMap.set(`${projectKey}/${moduleKey}`, moduleValue as any)
}
}

const modulesArray = Array.from(modulesMap)

data.options._contents = []
for await (const m of modulesArray) {
const [projectname, moduleName] = m[0].split('/')

data.options.resolvedModule.name = moduleName
data.options.resolvedModule.projectName = projectname

data.options.resolvedModule.projectDir = join(data.options.resolvedOptions.resolveDir.pergelRoot, projectname)
data.options.resolvedModule.moduleDir = join(data.options.resolvedOptions.resolveDir.pergelRoot, projectname, moduleName)

data.options.resolvedModule.templateDir.project = join(data.options.resolvedOptions.dir.pergel, projectname)
data.options.resolvedModule.templateDir.module = join(data.options.resolvedOptions.dir.pergel, projectname, moduleName)

data.options.resolvedModule.dir.project = join(data.options.resolvedOptions.dir.pergel, projectname)
data.options.resolvedModule.dir.module = join(data.options.resolvedOptions.dir.pergel, projectname, moduleName)

const selectedModule = data.nuxt._pergel.modules.find(module => module === moduleName)
if (Array.isArray(m) && selectedModule) {
let pergelModule: PergelModule

try {
const getIndexExt = () => {
const datas = readdirSync(data.resolver.resolve(join('runtime/modules', moduleName)))
const indexExt = datas.find(file => file.includes('index') && !file.includes('.d.'))
if (!indexExt)
throw new Error(`Module ${moduleName} does not have index file`)

return indexExt
}
const indexPath = getIndexExt()

pergelModule = await import(
data.resolver.resolve(join('runtime/modules', moduleName, indexPath))
).then(m => m.default).catch((res) => {
consola.error(`Module ${moduleName} failed to import`)
consola.error(res)
})
}
catch (error) {
consola.error(`Module ${moduleName} failed to import`)
throw error
}

// Throw error if input is not a function
if (typeof pergelModule !== 'function')
throw new TypeError(`Nuxt module should be a function: ${pergelModule}`)

const resolvedModule = await pergelModule(data.options, data.nuxt)

if (resolvedModule === false /* setup aborted */ || resolvedModule === undefined /* setup failed */ || typeof resolvedModule === 'string' /* setup failed */) {
consola.error(`Module ${moduleName} failed to setup`)
continue
}
}
}

for (const item of data.options._contents) {
if (!item.projectName || !item.moduleName) {
throw new Error(`Project name or module name is missing in
${JSON.stringify(item).replace(/"/g, '')}
`)
}
}

generatePergelTemplate({
nuxt: data.nuxt,
options: data.options,
})
}
110 changes: 110 additions & 0 deletions packages/nuxt/src/core/setupPergel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { join, resolve } from 'node:path'
import type { Nuxt } from '@nuxt/schema'
import { addTemplate } from '@nuxt/kit'
import defu from 'defu'
import type { PergelOptions, ResolvedPergelOptions } from './types'
import { rootFolderSync } from './utils/rootFolderSync'

export async function setupPergel(options: PergelOptions, nuxt: Nuxt) {
const rootDir = options.rootDir ? options.rootDir : './'
const pergelDir = join(rootDir, options.pergelDir ?? 'pergel')
const readmePath = join(pergelDir, 'README.yaml')

const resolveDir = resolve(join(nuxt.options.rootDir, rootDir))
const resolvePergelDir = resolve(join(nuxt.options.rootDir, pergelDir))
const resolveReadmePath = resolve(join(nuxt.options.rootDir, readmePath))

const { projectNames } = rootFolderSync(resolvePergelDir, options)

const pergelType = addTemplate({
filename: 'pergel/types.ts',
write: true,
getContents: () => {
return /* TypeScript */ `
export type ProjectName = ${projectNames.map((projectName) => {
return `'${projectName}'`
}).join(' | ')}
export type Module = ${nuxt._pergel.modules.map((module) => { return `'${module}'` }).join(' | ')}
export type PergelGlobalContextOmitModule = Omit<PergelGlobalContext, 'moduleName'>
export interface PergelGlobalContext {
projectName: ProjectName
moduleName: Module
}
`.trim().replace(/ {10}/g, '')
},
})

nuxt.options.alias['#pergel'] = pergelType.dst
nuxt.options.nitro.alias ??= {}
nuxt.options.nitro.alias['#pergel'] = pergelType.dst

const resolvedOptions = defu(options, {
projects: {
},
esnext: true,
pergelDir,
rootDir,
} as PergelOptions)

const resolvedPergelOptions = defu({
rootOptions: resolvedOptions,
resolvedOptions: {
projectNames,
dir: {
pergel: resolvedOptions.pergelDir ?? 'pergel',
readme: join('pergel', 'README.yaml'),
root: options.rootDir ?? './',
},
resolveDir: {
root: resolveDir,
pergelRoot: resolve(resolveDir, resolvedOptions.pergelDir ?? 'pergel'),
readmeDir: resolve(resolveReadmePath),
},
templateDir: {
root: 'pergel',
},
},
resolvedModule: {
dir: {
project: '',
module: '',
},
moduleDir: '',
name: '',
projectDir: '',
projectName: '',
templateDir: {
module: '',
project: '',
root: 'pergel',
},
},
} as ResolvedPergelOptions, {
rootOptions: resolvedOptions,
resolvedOptions: {
projectNames,
dir: {
pergel: resolvedOptions.pergelDir ?? 'pergel',
readme: join('pergel', 'README.yaml'),
root: options.rootDir ?? './',
},
resolveDir: {
readmeDir: resolve(resolveReadmePath),
root: resolveDir,
pergelRoot: resolve(resolveDir, resolvedOptions.pergelDir ?? 'pergel'),
},
templateDir: {
root: 'pergel',
},
},
resolvedModule: {

},
} as ResolvedPergelOptions)

return {
resolvedPergelOptions,
}
}
20 changes: 20 additions & 0 deletions packages/nuxt/src/core/utils/checkOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import consola from 'consola'
import type { PergelOptions } from '../types'

export async function checkOptions(
options: PergelOptions,
) {
let status = true

// project1, newProject, etc.
const projectNames = Object.keys(options.projects)

if (projectNames.length === 0) {
consola.error('No projects found in pergel config')
status = false
}

return {
status,
}
}
Loading

0 comments on commit 84a2008

Please sign in to comment.