Skip to content

Commit

Permalink
refactor(app-vite): "quasar new" cmd -> generate script setup when po…
Browse files Browse the repository at this point in the history
…ssible; simplify algorithm
  • Loading branch information
rstoenescu committed Dec 6, 2024
1 parent 99fc448 commit 080e7ac
Show file tree
Hide file tree
Showing 23 changed files with 136 additions and 537 deletions.
225 changes: 114 additions & 111 deletions app-vite/lib/cmd/new.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import parseArgs from 'minimist'

import path from 'node:path'
import { relative, dirname, join } from 'node:path'
import fs from 'node:fs'
import fse from 'fs-extra'

Expand All @@ -21,12 +21,12 @@ function showHelp (returnCode) {
Quickly scaffold files.
Usage
$ quasar new <p|page> [-f <option>] <page_file_name>
$ quasar new <l|layout> [-f <option>] <layout_file_name>
$ quasar new <c|component> [-f <option>] <component_file_name>
$ quasar new <b|boot> [-f ts] <boot_name>
$ quasar new <s|store> [-f ts] <store_module_name>
$ quasar new ssrmiddleware [-f ts] <middleware_name>
$ quasar new <p|page> [-f <js|ts>] <page_file_name>
$ quasar new <l|layout> [-f <js|ts>] <layout_file_name>
$ quasar new <c|component> [-f <js|ts>] <component_file_name>
$ quasar new <b|boot> [-f <js|ts>] <boot_name>
$ quasar new <s|store> [-f <js|ts>] <store_module_name>
$ quasar new ssrmiddleware [-f <js|ts>] <middleware_name>
Examples
# Create src/pages/MyNewPage.vue:
Expand All @@ -47,21 +47,18 @@ function showHelp (returnCode) {
Options
--help, -h Displays this message
--format -f <option> (optional) Use a supported format for the template
Possible values:
* default - Default JS template
* ts-composition - TS composition API (default if using TS)
* ts-composition-setup - TS composition API with <script setup>
* ts-options - TS options API
* ts-class - [DEPRECATED] TS class style syntax
* ts - Plain TS template (for boot, store, and ssrmiddleware files)
--format -f <option> (optional) Use a supported format for the template.
This gets inferred automatically for your project.
Possible overriding values:
* js - JS template
* ts - TS template
`)
process.exit(returnCode)
}

function showError (message, param) {
function showError (message) {
console.log()
warn(`${ message }: ${ param }`)
warn(message)
showHelp(1)
}

Expand All @@ -79,19 +76,18 @@ if (argv._.length < 2) {
}

import { getCtx } from '../utils/get-ctx.js'
const { appPaths, cacheProxy } = getCtx()

const storeProvider = await cacheProxy.getModule('storeProvider')
const { appPaths, cacheProxy } = getCtx()
const hasTypescript = await cacheProxy.getModule('hasTypescript')

if (!argv.format) {
argv.format = argv.f = hasTypescript ? 'ts-composition' : 'default'
argv.format = argv.f = hasTypescript ? 'ts' : 'default'
}

/** @type {string[]} */
const [ rawType, ...names ] = argv._
/** @type {{ format: 'default'|'ts'|'ts-options'|'ts-class'|'ts-composition'|'ts-composition-setup'}} */
let { format } = argv
const { format } = argv

const typeAliasMap = {
p: 'page',
Expand All @@ -101,134 +97,141 @@ const typeAliasMap = {
b: 'boot'
}

if (![ ...Object.entries(typeAliasMap).flat(), 'ssrmiddleware' ].includes(rawType)) {
showError('Invalid asset type', rawType)
const validAssetTypes = [ ...Object.entries(typeAliasMap).flat(), 'ssrmiddleware' ]
if (validAssetTypes.includes(rawType) === false) {
showError(`Invalid asset type: ${ rawType } (valid values: ${ validAssetTypes.join('|') })`)
}

/** @type {'page'|'layout'|'component'|'store'|'boot'|'ssrmiddleware'} */
const type = typeAliasMap[ rawType ] || rawType

if (![ 'default', 'ts-options', 'ts-class', 'ts-composition', 'ts-composition-setup', 'ts' ].includes(format)) {
showError('Invalid asset format', format)
}

const isTypeScript = format === 'ts' || format.startsWith('ts-')

// If using a TS sub-format(e.g. ts-options) and the type is a plain file (e.g. boot) then
// set format to just TS as sub-formats(e.g. Composition API) doesn't matter for plain files.
if (isTypeScript && (type === 'boot' || type === 'store' || type === 'ssrmiddleware')) {
format = 'ts'
if ([ 'js', 'ts' ].includes(format) === false) {
showError(`Invalid asset format: ${ format } (valid values: js|ts)`)
}

function createFile (asset, file) {
const relativePath = path.relative(appPaths.appDir, file)
function createFile ({ targetFile, ext, reference }) {
const assetRelativePath = relative(appPaths.appDir, targetFile)

if (fs.existsSync(file)) {
warn(`${ relativePath } already exists.`, 'SKIPPED')
if (fs.existsSync(targetFile)) {
warn(`${ assetRelativePath } already exists.`, 'SKIPPED')
console.log()
return
}

fse.ensureDir(path.dirname(file))
let templatePath = path.join('templates/app', format)

templatePath = type === 'store'
? path.join(templatePath, 'store', storeProvider.name + (asset.ext || ''))
: path.join(templatePath, type + (asset.ext || ''))
fse.ensureDir(dirname(targetFile))
const templatePath = join('templates/app', format, `${ type }.${ ext }`)

fse.copy(
appPaths.resolve.cli(templatePath),
file,
targetFile,
err => {
if (err) {
console.warn(err)
warn(`Could not generate ${ relativePath }.`, 'FAIL')
warn(`Could not generate ${ assetRelativePath }.`, 'FAIL')
return
}

log(`Generated ${ type }: ${ relativePath }`)
if (asset.reference) {
log(`Make sure to reference it in ${ asset.reference }`)
log(`Generated ${ type }: ${ assetRelativePath }`)
if (reference) {
log(`Make sure to reference it in ${ reference }`)
}
log()
}
)
}

const resolveWithExtension = path =>
path + (fs.existsSync(appPaths.resolve.app(path + '.ts')) ? '.ts' : '.js')

const pathList = {
router: resolveWithExtension('src/router/routes'),
store: resolveWithExtension(`src/${ storeProvider.pathKey }/index`)
}
async function getAsset (type) {
if (type === 'page') {
return {
relativePath: 'src/pages',
ext: 'vue',
reference: `src/router/routes.${ format }`
}
}

const mapping = {
page: {
folder: 'src/pages',
ext: '.vue',
reference: pathList.router
},
layout: {
folder: 'src/layouts',
ext: '.vue',
reference: pathList.router
},
component: {
folder: 'src/components',
ext: '.vue'
},
store: {
folder: `src/${ storeProvider.pathKey }`,
install: true,
ext: isTypeScript ? '.ts' : '.js'
},
boot: {
folder: 'src/boot',
ext: isTypeScript ? '.ts' : '.js',
reference: 'quasar.config file > boot'
},
ssrmiddleware: {
folder: 'src-ssr/middlewares',
ext: isTypeScript ? '.ts' : '.js',
reference: 'quasar.config file > ssr > middlewares'
if (type === 'component') {
return {
relativePath: 'src/components',
ext: 'vue'
}
}
}

const asset = mapping[ type ]
if (type === 'boot') {
return {
relativePath: 'src/boot',
ext: format,
reference: 'quasar.config file > boot'
}
}

if (asset.install) {
const folder = appPaths.resolve.app(asset.folder)
if (type === 'ssrmiddleware') {
return {
relativePath: 'src-ssr/middlewares',
ext: format,
reference: 'quasar.config file > ssr > middlewares'
}
}

if (!storeProvider.isInstalled) {
await storeProvider.install()
if (type === 'layout') {
return {
relativePath: 'src/layouts',
ext: 'vue',
reference: `src/router/routes.${ format }`
}
}

if (!fs.existsSync(folder)) {
fse.ensureDir(folder)
fse.copy(
appPaths.resolve.cli(`templates/store/${ storeProvider.name }/${ format }`),
folder,
err => {
if (err) {
console.warn(err)
warn(`Could not generate ${ asset.folder }.`, 'FAIL')
return
if (type === 'store') {
const storeProvider = await cacheProxy.getModule('storeProvider')

const relativePath = `src/${ storeProvider.pathKey }`
const targetFolder = appPaths.resolve.app(relativePath)

if (!storeProvider.isInstalled) {
await storeProvider.install()
}

if (fs.existsSync(targetFolder) === false) {
fse.ensureDir(targetFolder)
fse.copy(
appPaths.resolve.cli(`templates/store/${ storeProvider.name }/${ format }`),
targetFolder,
err => {
if (err) {
console.warn(err)
warn(`Could not generate ${ relativePath }.`, 'FAIL')
return
}

log(`Generated ${ relativePath }`)
log()
}
)
}

log(`Generated ${ asset.folder }`)
log()
}
)
return {
relativePath,
ext: format
}
}
}

names.forEach(name => {
const hasExtension = !asset.ext || (asset.ext && name.endsWith(asset.ext))
const ext = hasExtension ? '' : asset.ext
async function generate () {
const { relativePath, ext, reference } = await getAsset(type)
const fullExt = `.${ ext }`

// TODO: add template rendering for flexibility
const file = appPaths.resolve.app(path.join(asset.folder, name + ext))
names.forEach(name => {
const file = join(
relativePath,
name + (name.endsWith(fullExt) ? '' : fullExt)
)
const targetFile = appPaths.resolve.app(file)

createFile({
targetFile,
ext,
reference
})
})
}

createFile(asset, file)
})
generate()
28 changes: 0 additions & 28 deletions app-vite/templates/app/default/babelrc

This file was deleted.

21 changes: 0 additions & 21 deletions app-vite/templates/app/default/store/pinia.js

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
11 changes: 11 additions & 0 deletions app-vite/templates/app/js/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineStore, acceptHMRUpdate } from 'pinia'

export const useMyStore = defineStore('myStore', {
state: () => ({}),
getters: {},
actions: {}
})

if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useMyStore, import.meta.hot))
}
10 changes: 0 additions & 10 deletions app-vite/templates/app/ts-class/component.vue

This file was deleted.

Loading

0 comments on commit 080e7ac

Please sign in to comment.